Key Concepts

Modular-rest is designed to minimize the amount of code needed to create a RESTful backend. With just a single call to the createRest function, you can have a fully functional RESTful backend up and running. To make it flexible and customizable, modular-rest introduces several key concepts that you should understand to effectively build your own logic on top of it.

Configuration

The configuration object passed to the createRest function is the foundation of modular-rest. This object contains all the necessary information for modular-rest to set up the server. You can learn more about configuring your server in the Quick Start section.

Modules

Modules are the building blocks for implementing your business logic on top of modular-rest. Each module has its own dedicated directory where all relevant files and data structures are organized. This modular approach allows you to add unlimited modules to your project and scale it according to your needs. Learn more about working with modules in the Modules section.

Install Server App

Thank you for choosing modular-rest to build your app. You can install the server app in two ways:

1. Create a new project

In this way, you can create a new project with modular-rest server app. it will create a new folder with your project name and setup the project for you.

Just use below command:

sh
npm create @modular-rest/server@latest my-project
sh
yarn create @modular-rest/server my-project

And now you can start your project with below commands:

sh
cd my-project
npm install
npm start

2. Add modular-rest server client to your project

It assumed that you have initialized a project with npm, then use below command to install modular-rest server client.

sh
npm i @modular-rest/server --save
sh
yarn add @modular-rest/server

Now you can use modular-rest server client in your project.

ts
import { createRest } from '@modular-rest/server';

const app = createRest({
  port: '80',
  mongo: {
    mongoBaseAddress: 'mongodb://localhost:27017',
    dbPrefix: 'mrest_'
  },
  onBeforeInit: (koaApp) => {
    // do something before init with the koa app
  }
})

Configuration Overview

This guide provides an overview and detailed explanation of the configuration options available for @modular-rest/server.

Quick Start

To get started, you need to require @modular-rest/server and call createRest with your configuration object:

javascript
const { createRest } = require("@modular-rest/server");

const app = createRest({
  port: "80",
  // Additional configuration options...
});

Health Chek

You may need to check the server health, just request to below endpoint:

bash
GET:[base_url]/verify/ready
# {"status":"success"}

Configuration Summary Table

Server and Middleware Configuration

  • cors: Defines Cross-Origin Resource Sharing (CORS) options to control how resources on your server can be requested from another domain.
  • port: Specifies the port number on which the server will listen for requests.
  • dontListen: If set to true, the server setup is done but it won't start listening. This is useful for cases where you want to perform tests or when integrating with another server.

Modules and Upload Directory

  • modulesPath: The directory path where your module files (router.js, db.js) are located.
  • uploadDirectory: The root directory where uploaded files are stored, you can mount this directory to a CDN or a cloud storage service.

Static Files

  • staticPath: Provides detailed options for serving static files from your server, such as the root directory, caching options, and whether to serve gzipped content.

Initialization Hooks

  • onBeforeInit: A function that is called before the Koa application is initialized. This allows you to add or configure middleware and routes.
  • onAfterInit: Similar to onBeforeInit, but this function is called after the application has been initialized.

Database Configuration

  • mongo: Contains MongoDB configuration details like the database address, prefix for database names, and more.

Security and Authentication

  • keypair: RSA keys used for authentication purposes.
  • adminUser: Credentials for an admin user, typically used for initial setup or administrative tasks.

Customization and Extensions

  • verificationCodeGeneratorMethod,
  • collectionDefinitions,
  • permissionGroups,
  • authTriggers

These properties allow for extending the functionality of @modular-rest/server by adding custom verification code generation logic, defining additional database collections, setting up permission groups, and specifying triggers for database operations.

Example Configuration

Here's an example demonstrating how to configure some of these properties:

javascript
const { createRest } = require("@modular-rest/server");

const app = createRest({
  port: 3000,
  modulesPath: "./modules",
  staticPath: {
    rootDir: "./public",
    notFoundFile: "404.html",
    log: true,
  },
  mongo: {
    mongoBaseAddress: "mongodb://localhost:27017",
    dbPrefix: "myApp_",
  },
  onBeforeInit: (koaApp) => {
    // Custom middleware
    koaApp.use(customMiddleware());
  },
  adminUser: {
    email: "admin@example.com",
    password: "securepassword",
  },
});

This guide should give you a clear understanding of how to configure @modular-rest/server for your project, along with some examples to get you started.

Modules

Modules are the building blocks your logics on top of modular-rest. each module has a specific directory and all relevant files and data structure are placed in that directory. hence you can add unlimited modules to your project and scale it as much as you want.

Structure

All modules should be placed in the modules directory that you define and introduce in the configuration object. Each module should have its own directory with the following structure, and all files should be placed in that directory but none of theme are required.

  • db.js: to define the database models and their relationships.
  • functions: to define functions that you want to be invoked by client library.
  • router.js: to define the routes and their handlers.

You can add more files and directories to your module based on your needs, but the above files are be recognized by modular-rest and will be imported to the server logic automatically on startup.

Use Cases

Let's see some examples to understand the concept of modules better.

Blog Website:

Assume you want to create a blog website where people come and write their own blog posts, read other people's posts, and comment on them.

To modularize this project, you can create three modules:

  • users: to manage users and their profiles.
  • posts: to manage blog posts and their categories, tags, and content.
  • comments: to manage comments on blog posts and their replies.

E-commerce Website:

Assume you want to create an e-commerce website where people come and buy products, add them to their cart, and pay for them.

To modularize this project, you can create three modules:

  • users: to manage users and their profiles.
  • products: to manage products and their categories, prices, and descriptions.
  • orders: to manage orders and their statuses, like pending, shipped, and delivered.

Video Editing Platform:

Assume you want to create a video editing platform where people come and upload their videos, edit them, and share them with others.

To modularize this project, you can create three modules:

  • users: to manage users and their profiles.
  • media-library: to manage media files like videos, images, and audio files.
  • editor-engine: to manage the video editing process, like trimming, cropping, and adding effects to the videos.

Concept

In Modular-rest you have mongodb database support out of the box. you just need to define your data models in db.[js|ts] files. they have to be located in modules directory. for example if you have a module named user you have to create a file named db.[js|ts] in modules/user directory.

How to Define a Collection

defineCollection(options): CollectionDefinition

To have define any collection in your database you haveto use below method in your db.[js|ts] file and export an array of CollectionDefinition instances.

Example

typescript
import { defineCollection } from '@modular-rest/server';

export default [
  defineCollection({
    database: 'users',
    collection: 'info',
    // schema: Schema,
    // permissions: Permission[]
    // trigger: DatabaseTrigger[]
  })
]

Parameters

ParameterTypeDescription
options{ collection: string; database: string; permissions: Permission[]; schema: Schema; triggers: DatabaseTrigger[]; }The options for the collection
options.collectionstringThe name of the collection to be configured
options.databasestringThe name of the database where the collection resides
options.permissionsPermission[]List of permissions controlling access to the collection
options.schemaSchemaMongoose schema definition for the collection See https://mongoosejs.com/docs/5.x/docs/guide.html
options.triggers?DatabaseTrigger[]Optional database triggers for custom operations

Returns

CollectionDefinition

A new instance of CollectionDefinition

Schema

You can define data stracture for your collection by passing a mongoose schema to schema option.

typescript
import { Schema } from '@modular-rest/server';

const userSchema = new Schema({ 
	name: String,
	age: Number
});

defineCollection({
	database: 'users',
	collection: 'info',
	schema: userSchema, 
	permissions: Permission[]
	trigger: DatabaseTrigger[]
})

File Schema

Modular-rest has a predefined file schema that you it is necessary to use this schema if your collection needs to store files.

Note: Modular-rest does not store the file directly in the database. Instead, it places the file in the upload directory specified in the config object. The file information is then recorded in the database.

typescript
import { schemas } from '@modular-rest/server';

const userSchema = new Schema({
	name: String,
	age: Number,

	// Added this file to the parent schema
	avatar: schemas.file
});

Permissions

The permission system in this framework provides a robust way to control access to your application's resources. It works by matching permission types that users have against those required by different parts of the system. Read more

Triggers

Linking Collections

You can link any collection from same database into an schema to perform populate queries, but let me tell you what it is simply:

Populate query is a query that you can use to get data from linked collections. for example if you have a collection named user and you have a collection named post that each post has an author. you can link user collection into post collection and then you can use populate query to get author of each post, it you the user data in author field of each post.

More info on populate queries.

typescript
import { Schema } from '@modular-rest/server';

const userSchema = new Schema({
	name: String,
	age: Number
});

const postSchema = new Schema({
	title: String,
	content: String,
	author: {
		type: Schema.Types.ObjectId,
		ref: 'user'
	}
});

Full Example

Let's see a full example of db.ts file:

typescript
import { Schema, schemas, CollectionDefinition, Permission, DatabaseTrigger } from '@modular-rest/server';

const userSchema = new Schema({
	name: String,
	age: Number,

	// Added this file to the parent schema
	avatar: schemas.file
});

const postSchema = new Schema({
	title: String,
	content: String,
	author: {
		type: Schema.Types.ObjectId,
		ref: 'user'
	}
});

const userPermissions = [
	new Permission({
		new Permission({
			type: 'god_access',
			read: true,
			write: true,
		}),
		new Permission({
			type: 'user_access',
			read: true,
			write: true,
			onlyOwnData: true,
		}),
		new Permission({
			type: 'anonymous_access',
			read: true,
		}),
	})
];

const userTriggers = [
	new DatabaseTrigger('insert-one',
		(data) => {
			// send email to user
		}
	})
];

module.exports = [
	new CollectionDefinition({
		db: 'user',
		name: 'info',
		schema: userSchema,
		permissions: userPermissions,
		trigger: userTriggers
	}),
	new CollectionDefinition({
		db: 'user',
		name: 'post',
		schema: postSchema,
		permissions: userPermissions,
		trigger: userTriggers
	})
]

Concept

In the Modular-rest, functions are a powerful feature that allows developers to define and manage custom logic seamlessly within their APIs. Functions serve as a mechanism to remove traditional API development, eliminating the need to write routers directly.

By defining a function, a router will be generated for it automatically, allowing the client library to focus solely on the API call. This feature supports more dynamic and flexible application designs, ensuring that specific actions can be encapsulated and reused while enforcing permissions and maintaining security.

Define a Function

defineFunction(options): object

To define a function you need to create a functions.[js|ts] in each module of your app and return am array called functions, and then define all your functions with calling the defineFunction method.

The defineFunction method serves as a core utility for creating custom functions dynamically. This method allows you to specify various parameters, including the name of the function, the permissions required for access, and the corresponding logic that should be executed when the function is invoked.

Example

Here is an example illustrating how to use the defineFunction method effectively:

typescript
// /modules/myModule/functions.ts

import { defineFunction } from "@modular-rest/server";

const getServerTime = defineFunction({
  name: "getServerTime",
  permissionTypes: ["anonymous_access"],
  callback: (params) => {
    // return your data only
    return `
      Welcome, ${params.username}!
      The current server time is ${new Date().toLocaleString()}.
    `;

    // error handling,
    // client gets error code 400, and the message
    // throw new Error('An error occurred');
  },
});

module.exports.functions = [getServerTime];

In this example, we define a function named getServerTime that requires the user permission type to access. When the function is called, it will return a message containing the current server time and the username of the user who invoked the function.


By utilizing the defineFunction method, developers are empowered to create custom functionality effortlessly within the Modular REST framework, enhancing both the versatility and security of their applications.

Parameters

ParameterTypeDescription
options{ callback: (args) => any; name: string; permissionTypes: string[]; }The function definition options. See DefinedFunction for detailed parameter descriptions.
options.callback(args) => anyThe actual function implementation
options.namestringUnique name of the function
options.permissionTypesstring[]List of permission types required to run the function

Returns

object

The defined function object which system will use to generate a router for the function, generall the client library will use the router to call the function.

NameTypeDescription
callback()(args) => anyThe actual function implementation
namestringUnique name of the function
permissionTypesstring[]List of permission types required to run the function

Throws

If function name already exists, permission types are missing, or callback is invalid

Custom Route

In modular rest you have still the traditional way to create a route, by creating a router.js file in your module directory. This file should export below properties and will be taken automatically by the modular rest on the startup.

  • name: the name of the route, mostly same as the module name.
  • main: the main router object.

Note: route system is based on koa-router package which is a plugin for express.js framework.

Example

Assume you have a module named flowers and you want to create a list/id route for it. You can create a router.js file in the modules/flowers directory with the following content:

js
const Router = require('koa-router');
const name = 'flowers';

const flowerRouter = new Router();

flowerRouter.get('/list', (ctx) => {
	ctx.body = 'This is a list of flowers: Rose, Lily, Tulip';
});

flowerRouter.post('/:id', (ctx) => {
	const id = ctx.params.id;
	ctx.body = `Request Body: ${JSON.stringify(ctx.request.body)} and id: ${id}`;
    
})

module.exports.name = name;
module.exports.main = flowerRouter;

Now you can access your apis by sending a request to the following urls:

  • GET http://localhost:80/flowers/list
  • POST http://localhost:80/flowers/1

Database Utilities

Contains utilities related to database operations.

Get Collection

getCollection<T>(db, collection): Model<T>

Function

Gets a Mongoose model for a specific collection getCollection

Example

typescript
const userModel = getCollection('myapp', 'users');
const users = await userModel.find();

Type Parameters

Type Parameter
T

Parameters

ParameterTypeDescription
dbstringDatabase name
collectionstringCollection name

Returns

Model<T>

Mongoose model for the collection

Throws

If the collection doesn't exist

File Utilities

File service for handling file storage and retrieval.

This service provides functionality for storing, retrieving, and managing files. It handles file storage on disk and maintains file metadata in the database. Files are organized by format and tag in the upload directory.

Methods

getFile()

getFile(fileId): Promise<IFile>

Retrieves a file document from the database

Example

typescript
import { fileService } from '@modular-rest/server';

const fileDoc = await fileService.getFile('file123');
console.log('File details:', fileDoc);

Parameters

ParameterTypeDescription
fileIdstringFile ID to retrieve

Returns

Promise<IFile>

Promise resolving to file document

Throws

If collection model is not found or file is not found


getFileLink(fileId): Promise<string>

Retrieves the public URL link for a file

Example

typescript
import { fileService } from '@modular-rest/server';

const link = await fileService.getFileLink('file123');
// Returns: '/static/jpeg/profile/1234567890.jpeg'

Parameters

ParameterTypeDescription
fileIdstringFile ID to get link for

Returns

Promise<string>

Promise resolving to file URL

Throws

If static path root is not defined or file is not found


getFilePath()

getFilePath(fileId): Promise<string>

Gets the full filesystem path for a file

Example

typescript
import { fileService } from '@modular-rest/server';

const path = await fileService.getFilePath('file123');
// Returns: '/uploads/jpeg/profile/1234567890.jpeg'

Parameters

ParameterTypeDescription
fileIdstringFile ID to get path for

Returns

Promise<string>

Promise resolving to full file path

Throws

If upload directory is not set or file is not found


removeFile()

removeFile(fileId): Promise<void>

Removes a file from both database and disk

Example

typescript
import { fileService } from '@modular-rest/server';

try {
  await fileService.removeFile('file123');
  console.log('File removed successfully');
} catch (error) {
  console.error('Failed to remove file:', error);
}

Parameters

ParameterTypeDescription
fileIdstringFile ID to remove

Returns

Promise<void>

Promise resolving when file is removed

Throws

If file is not found or removal fails

Router Utilities

When you develop custom APIs in router.[js|ts] files, you might need to use some utilities to standardize your responses, handle errors, and manage pagination. The following utilities are available to help you with these tasks.

Reply

create(status, detail): ResponseObject

Creates a response object with the given status and detail.

Example

typescript
import { reply } from '@modular-rest/server';

// inside the router
const response = reply.create("s", { message: "Hello, world!" });
ctx.body = response;
ctx.status = 200;

Parameters

ParameterTypeDescription
statusResponseStatusThe status of the response. Can be "s" for success, "f" for fail, or "e" for error.
detailRecord<string, any>The detail of the response. Can contain any additional information about the response.

Returns

ResponseObject

The response object with the given status and detail.

Paginator Maker

create(count, perPage, page): PaginationResult

Creates a pagination object based on the given parameters.

Example

typescript
import { paginator } from '@modular-rest/server';

const pagination = paginator.create(100, 10, 1);
// json response will be like this
// {
//   pages: 10,
//   page: 1,
//   from: 0,
//   to: 10,
// }

Parameters

ParameterTypeDescription
countnumberThe total number of items to paginate.
perPagenumberThe number of items to display per page.
pagenumberThe current page number.

Returns

PaginationResult

An object containing pagination information.

Auth Middleware

auth(ctx, next): Promise<void>

Authentication middleware that secures routes by validating user tokens and managing access control.

This middleware performs several key functions:

  1. Validates that the incoming request contains an authorization token in the header
  2. Verifies the token is valid by checking against the user management service
  3. Retrieves the associated user object if the token is valid
  4. Attaches the authenticated User object on ctx.state.user for use in subsequent middleware/routes
  5. Throws appropriate HTTP errors (401, 412) if authentication fails

The middleware integrates with the permission system to enable role-based access control. The attached user object provides methods like hasPermission() to check specific permissions.

Common usage patterns:

  • Protecting sensitive API endpoints
  • Implementing role-based access control
  • Getting the current authenticated user
  • Validating user permissions before allowing actions

Example

typescript
// Inside the router.ts file
import { auth } from '@modular-rest/server';
import { Router } from 'koa-router';

const name = 'flowers';

const flowerRouter = new Router();

flowerRouter.get('/list', auth, (ctx) => {
 // Get the authenticated user
 const user = ctx.state.user;

 // Then you can check the user's role and permission
 if(user.hasPermission('get_flower')) {
   ctx.body = 'This is a list of flowers: Rose, Lily, Tulip';
 } else {
   ctx.status = 403;
   ctx.body = 'You are not authorized to access this resource';
 }
});

module.exports.name = name;
module.exports.main = flowerRouter;

Parameters

ParameterTypeDescription
ctxContextKoa Context object containing request/response data
nextNextFunction to invoke next middleware

Returns

Promise<void>

Throws

401 - If no authorization header is present

Throws

412 - If token validation fails

UserManager Service

User manager class for handling user operations

This service provides functionality for managing users, including:

  • User registration and authentication
  • Password management
  • Token generation and verification
  • Temporary ID handling for password reset and verification

Methods

changePassword()

changePassword(query, newPass): Promise<void>

Changes a user's password

Example

typescript
import { userManager } from '@modular-rest/server';

try {
  await userManager.changePassword(
    { email: 'user@example.com' },
    'newpassword123'
  );
  console.log('Password changed successfully');
} catch (error) {
  console.error('Failed to change password:', error);
}

Parameters

ParameterTypeDescription
queryRecord<string, any>Query to find the user
newPassstringThe new password

Returns

Promise<void>

Promise resolving when password is changed

Throws

If user is not found or password change fails


changePasswordForTemporaryID()

changePasswordForTemporaryID(id, password, code): Promise<string>

Changes password for a temporary ID

Example

typescript
import { userManager } from '@modular-rest/server';

try {
  const token = await userManager.changePasswordForTemporaryID(
    'user@example.com',
    'newpassword123',
    '123456'
  );
  console.log('Password changed successfully');
} catch (error) {
  console.error('Failed to change password:', error);
}

Parameters

ParameterTypeDescription
idstringThe temporary ID
passwordstringThe new password
codestringThe verification code

Returns

Promise<string>

Promise resolving to the JWT token

Throws

If verification code is invalid or user is not found


generateVerificationCode()

generateVerificationCode(id, idType): string

Generates a verification code for a user

Example

typescript
import { userManager } from '@modular-rest/server';

const code = userManager.generateVerificationCode('user@example.com', 'email');
// Returns: '123' (default) or custom generated code

Parameters

ParameterTypeDescription
idstringUser ID or identifier
idTypestringType of ID (email, phone)

Returns

string

Verification code


getUserById()

getUserById(id): Promise<User>

Gets a user by their ID

Example

typescript
import { userManager } from '@modular-rest/server';

try {
  const user = await userManager.getUserById('user123');
  console.log('User details:', user);
} catch (error) {
  console.error('Failed to get user:', error);
}

Parameters

ParameterTypeDescription
idstringThe ID of the user

Returns

Promise<User>

Promise resolving to the user

Throws

If user model is not found or user is not found


getUserByIdentity()

getUserByIdentity(id, idType): Promise<User>

Gets a user by their identity (email or phone)

Example

typescript
import { userManager } from '@modular-rest/server';

// Get user by email
const user = await userManager.getUserByIdentity('user@example.com', 'email');

// Get user by phone
const user = await userManager.getUserByIdentity('+1234567890', 'phone');

Parameters

ParameterTypeDescription
idstringThe identity of the user
idTypestringThe type of the identity (phone or email)

Returns

Promise<User>

Promise resolving to the user

Throws

If user model is not found or user is not found


getUserByToken()

getUserByToken(token): Promise<User>

Gets a user by their JWT token

Example

typescript
import { userManager } from '@modular-rest/server';

try {
  const user = await userManager.getUserByToken('jwt.token.here');
  console.log('Authenticated user:', user);
} catch (error) {
  console.error('Invalid token:', error);
}

Parameters

ParameterTypeDescription
tokenstringThe JWT token of the user

Returns

Promise<User>

Promise resolving to the user

Throws

If token is invalid or user is not found


isCodeValid()

isCodeValid(id, code): boolean

Checks if a verification code is valid

Example

typescript
import { userManager } from '@modular-rest/server';

const isValid = userManager.isCodeValid('user123', '123');
if (isValid) {
  // Proceed with verification
}

Parameters

ParameterTypeDescription
idstringThe ID of the user
codestringThe verification code

Returns

boolean

Whether the verification code is valid


issueTokenForUser()

issueTokenForUser(email): Promise<string>

Issues a JWT token for a user by email

Example

typescript
import { userManager } from '@modular-rest/server';

try {
  const token = await userManager.issueTokenForUser('user@example.com');
  console.log('Issued token:', token);
} catch (error) {
  console.error('Failed to issue token:', error);
}

Parameters

ParameterTypeDescription
emailstringThe email of the user

Returns

Promise<string>

Promise resolving to the JWT token

Throws

If user is not found


loginAnonymous()

loginAnonymous(): Promise<string>

Logs in an anonymous user and returns their JWT token

Example

typescript
import { userManager } from '@modular-rest/server';

const token = await userManager.loginAnonymous();
console.log('Anonymous token:', token);

Returns

Promise<string>

Promise resolving to the JWT token


loginUser()

loginUser(id?, idType?, password?): Promise<string>

Logs in a user and returns their JWT token

Example

typescript
import { userManager } from '@modular-rest/server';

try {
  // Login with email
  const token = await userManager.loginUser('user@example.com', 'email', 'password123');

  // Login with phone
  const token = await userManager.loginUser('+1234567890', 'phone', 'password123');
} catch (error) {
  console.error('Login failed:', error);
}

Parameters

ParameterTypeDefault valueDescription
id?string''The ID of the user (email or phone)
idType?string''The type of the ID (phone or email)
password?string''The password of the user

Returns

Promise<string>

Promise resolving to the JWT token

Throws

If user is not found or credentials are invalid


registerTemporaryID()

registerTemporaryID(id, type, code): string

Registers a temporary ID for verification or password reset

Example

typescript
import { userManager } from '@modular-rest/server';

const tempId = userManager.registerTemporaryID('user@example.com', 'password_reset', '123456');

Parameters

ParameterTypeDescription
idstringThe ID to register
typestringThe type of temporary ID
codestringThe verification code

Returns

string

The registered ID


registerUser()

registerUser(detail): Promise<string>

Registers a new user

Example

typescript
import { userManager } from '@modular-rest/server';

try {
  const token = await userManager.registerUser({
    email: 'user@example.com',
    password: 'secure123',
    permissionGroup: 'user',
    phone: '+1234567890'
  });
  console.log('User registered successfully');
} catch (error) {
  console.error('Registration failed:', error);
}

Parameters

ParameterTypeDescription
detailUserRegistrationDetailUser registration details

Returns

Promise<string>

Promise resolving to the JWT token

Throws

If user model is not found or registration fails


setCustomVerificationCodeGeneratorMethod()

setCustomVerificationCodeGeneratorMethod(generatorMethod): void

Sets a custom method for generating verification codes

Example

typescript
import { userManager } from '@modular-rest/server';

userManager.setCustomVerificationCodeGeneratorMethod((id, type) => {
  return Math.random().toString(36).substring(2, 8).toUpperCase();
});

Parameters

ParameterTypeDescription
generatorMethod(id, idType) => stringFunction that generates verification codes

Returns

void


submitPasswordForTemporaryID()

submitPasswordForTemporaryID(id, password, code): Promise<string>

Submits a password for a temporary ID

Example

typescript
import { userManager } from '@modular-rest/server';

try {
  const token = await userManager.submitPasswordForTemporaryID(
    'user@example.com',
    'newpassword123',
    '123456'
  );
  console.log('Password set successfully');
} catch (error) {
  console.error('Failed to set password:', error);
}

Parameters

ParameterTypeDescription
idstringThe temporary ID
passwordstringThe new password
codestringThe verification code

Returns

Promise<string>

Promise resolving to the JWT token

Throws

If verification code is invalid or user is not found

Concept

The permission system in this framework provides a robust way to control access to your application's resources. It works by matching permission types that users have against those required by different parts of the system.

How It Works?

At its core, the permission system uses access types - special flags like user_access, advanced_settings, or custom types you define. These access types are used in two key places:

  1. Permission: When defining collections or functions, you provide a list of Permission instances that specify which access types are required. Each Permission instance defines what operations (read/write) are allowed for a specific access type. For example, one Permission might allow read access for user_access, while another Permission enables write access for advanced_settings.

  2. Permission Group: Each user is assigned certain access types through their permission group. When they try to access a resource, the system checks if they have the required permission types.

The system only allows an operation when there's a match between the permission types required by the resource and those assigned to the user making the request.

Permission

Permission Group

CORS

Cross-Origin Resource Sharing (CORS) is a security feature implemented in web browsers to prevent malicious websites from accessing resources and data from another domain without permission. By default, web browsers enforce the same-origin policy, which restricts web pages from making requests to a different domain than the one that served the web page. CORS provides a way for servers to declare who can access their assets and under what conditions, enhancing security while enabling controlled cross-origin requests.

Understanding CORS

CORS is essential for modern web applications that integrate resources from different origins. For instance, if your web application hosted at http://example.com tries to request resources from http://api.example.com, the browser will block these requests unless the server at http://api.example.com includes the appropriate CORS headers in its responses to indicate that such requests are allowed.

The CORS mechanism involves the browser sending an Origin header with the origin of the requesting site to the server. The server then decides whether to allow or deny the request based on its CORS policy. If allowed, the server includes the Access-Control-Allow-Origin header in its response, specifying which origins can access the resources.

CORS Configuration

The @modular-rest/server framework uses koa/cors middleware to configure CORS policies. Here's how you can set it up:

CORS Middleware Options

Below is a detailed explanation of the CORS configuration options provided by koa/cors in @modular-rest/server:

Example Configuration

Here's an example of how to configure CORS in your @modular-rest/server application:

javascript
import { createRest } from '@modular-rest/server';

const corsOptions = {
    origin: 'https://www.example.com',
    allowMethods: ['GET', 'POST'],
    credentials: true,
    secureContext: true,
};

const app = createRest({
    port: 3000,
    cors: corsOptions,
    // Other configuration options...
});

In this configuration:

  • CORS requests are only allowed from https://www.example.com.
  • Only GET and POST methods are permitted.
  • Credentials are allowed in cross-origin requests.
  • Secure context headers are enabled for added security.

Conclusion

Properly configuring CORS is crucial for securing your application and enabling necessary cross-origin requests. @modular-rest/server simplifies this process by integrating koa/cors middleware, providing a flexible and powerful way to define your CORS policy. By understanding and utilizing these settings, developers can ensure that their web applications are secure and functional across different domains.