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.
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 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.
Thank you for choosing modular-rest
to build your app. You can install the server app in two ways:
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:
npm create @modular-rest/server@latest my-project
yarn create @modular-rest/server my-project
And now you can start your project with below commands:
cd my-project
npm install
npm start
It assumed that you have initialized a project with npm, then use below command to install modular-rest server client.
npm i @modular-rest/server --save
yarn add @modular-rest/server
Now you can use modular-rest server client in your project.
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
}
})
This guide provides an overview and detailed explanation of the configuration options available for @modular-rest/server
.
To get started, you need to require @modular-rest/server
and call createRest
with your configuration object:
const { createRest } = require("@modular-rest/server");
const app = createRest({
port: "80",
// Additional configuration options...
});
You may need to check the server health, just request to below endpoint:
GET:[base_url]/verify/ready
# {"status":"success"}
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.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.staticPath
: Provides detailed options for serving static files from your server, such as the root directory, caching options, and whether to serve gzipped content.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.mongo
: Contains MongoDB configuration details like the database address, prefix for database names, and more.keypair
: RSA keys used for authentication purposes.adminUser
: Credentials for an admin user, typically used for initial setup or administrative tasks.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.
Here's an example demonstrating how to configure some of these properties:
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 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.
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.
Let's see some examples to understand the concept of modules better.
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.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.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.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.
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.
import { defineCollection } from '@modular-rest/server';
export default [
defineCollection({
database: 'users',
collection: 'info',
// schema: Schema,
// permissions: Permission[]
// trigger: DatabaseTrigger[]
})
]
Parameter | Type | Description |
---|---|---|
options | { collection : string ; database : string ; permissions : Permission []; schema : Schema ; triggers : DatabaseTrigger []; } | The options for the collection |
options.collection | string | The name of the collection to be configured |
options.database | string | The name of the database where the collection resides |
options.permissions | Permission [] | List of permissions controlling access to the collection |
options.schema | Schema | Mongoose schema definition for the collection See https://mongoosejs.com/docs/5.x/docs/guide.html |
options.triggers ? | DatabaseTrigger [] | Optional database triggers for custom operations |
A new instance of CollectionDefinition
You can define data stracture for your collection by passing a mongoose schema to schema
option.
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[]
})
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.
import { schemas } from '@modular-rest/server';
const userSchema = new Schema({
name: String,
age: Number,
// Added this file to the parent schema
avatar: schemas.file
});
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
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.
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'
}
});
Let's see a full example of db.ts
file:
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
})
]
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.
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.
Here is an example illustrating how to use the defineFunction
method effectively:
// /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.
Parameter | Type | Description |
---|---|---|
options | { callback : (args ) => any ; name : string ; permissionTypes : string []; } | The function definition options. See DefinedFunction for detailed parameter descriptions. |
options.callback | (args ) => any | The actual function implementation |
options.name | string | Unique name of the function |
options.permissionTypes | string [] | List of permission types required to run the function |
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.
Name | Type | Description |
---|---|---|
callback() | (args ) => any | The actual function implementation |
name | string | Unique name of the function |
permissionTypes | string [] | List of permission types required to run the function |
If function name already exists, permission types are missing, or callback is invalid
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.
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:
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
Contains utilities related to database operations.
getCollection<
T
>(db
,collection
):Model
<T
>
Function
Gets a Mongoose model for a specific collection getCollection
const userModel = getCollection('myapp', 'users');
const users = await userModel.find();
Type Parameter |
---|
T |
Parameter | Type | Description |
---|---|---|
db | string | Database name |
collection | string | Collection name |
Model
<T
>
Mongoose model for the collection
If the collection doesn't exist
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.
getFile(
fileId
):Promise
<IFile
>
Retrieves a file document from the database
import { fileService } from '@modular-rest/server';
const fileDoc = await fileService.getFile('file123');
console.log('File details:', fileDoc);
Parameter | Type | Description |
---|---|---|
fileId | string | File ID to retrieve |
Promise
<IFile
>
Promise resolving to file document
If collection model is not found or file is not found
getFileLink(
fileId
):Promise
<string
>
Retrieves the public URL link for a file
import { fileService } from '@modular-rest/server';
const link = await fileService.getFileLink('file123');
// Returns: '/static/jpeg/profile/1234567890.jpeg'
Parameter | Type | Description |
---|---|---|
fileId | string | File ID to get link for |
Promise
<string
>
Promise resolving to file URL
If static path root is not defined or file is not found
getFilePath(
fileId
):Promise
<string
>
Gets the full filesystem path for a file
import { fileService } from '@modular-rest/server';
const path = await fileService.getFilePath('file123');
// Returns: '/uploads/jpeg/profile/1234567890.jpeg'
Parameter | Type | Description |
---|---|---|
fileId | string | File ID to get path for |
Promise
<string
>
Promise resolving to full file path
If upload directory is not set or file is not found
removeFile(
fileId
):Promise
<void
>
Removes a file from both database and disk
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);
}
Parameter | Type | Description |
---|---|---|
fileId | string | File ID to remove |
Promise
<void
>
Promise resolving when file is removed
If file is not found or removal fails
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.
create(
status
,detail
):ResponseObject
Creates a response object with the given status and detail.
import { reply } from '@modular-rest/server';
// inside the router
const response = reply.create("s", { message: "Hello, world!" });
ctx.body = response;
ctx.status = 200;
Parameter | Type | Description |
---|---|---|
status | ResponseStatus | The status of the response. Can be "s" for success, "f" for fail, or "e" for error. |
detail | Record <string , any > | The detail of the response. Can contain any additional information about the response. |
The response object with the given status and detail.
create(
count
,perPage
,page
):PaginationResult
Creates a pagination object based on the given parameters.
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,
// }
Parameter | Type | Description |
---|---|---|
count | number | The total number of items to paginate. |
perPage | number | The number of items to display per page. |
page | number | The current page number. |
An object containing pagination information.
auth(
ctx
,next
):Promise
<void
>
Authentication middleware that secures routes by validating user tokens and managing access control.
This middleware performs several key functions:
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:
// 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;
Parameter | Type | Description |
---|---|---|
ctx | Context | Koa Context object containing request/response data |
next | Next | Function to invoke next middleware |
Promise
<void
>
401 - If no authorization header is present
412 - If token validation fails
User manager class for handling user operations
This service provides functionality for managing users, including:
changePassword(
query
,newPass
):Promise
<void
>
Changes a user's password
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);
}
Parameter | Type | Description |
---|---|---|
query | Record <string , any > | Query to find the user |
newPass | string | The new password |
Promise
<void
>
Promise resolving when password is changed
If user is not found or password change fails
changePasswordForTemporaryID(
id
,password
,code
):Promise
<string
>
Changes password for a temporary ID
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);
}
Parameter | Type | Description |
---|---|---|
id | string | The temporary ID |
password | string | The new password |
code | string | The verification code |
Promise
<string
>
Promise resolving to the JWT token
If verification code is invalid or user is not found
generateVerificationCode(
id
,idType
):string
Generates a verification code for a user
import { userManager } from '@modular-rest/server';
const code = userManager.generateVerificationCode('user@example.com', 'email');
// Returns: '123' (default) or custom generated code
Parameter | Type | Description |
---|---|---|
id | string | User ID or identifier |
idType | string | Type of ID (email, phone) |
string
Verification code
getUserById(
id
):Promise
<User
>
Gets a user by their ID
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);
}
Parameter | Type | Description |
---|---|---|
id | string | The ID of the user |
Promise
<User
>
Promise resolving to the user
If user model is not found or user is not found
getUserByIdentity(
id
,idType
):Promise
<User
>
Gets a user by their identity (email or phone)
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');
Parameter | Type | Description |
---|---|---|
id | string | The identity of the user |
idType | string | The type of the identity (phone or email) |
Promise
<User
>
Promise resolving to the user
If user model is not found or user is not found
getUserByToken(
token
):Promise
<User
>
Gets a user by their JWT token
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);
}
Parameter | Type | Description |
---|---|---|
token | string | The JWT token of the user |
Promise
<User
>
Promise resolving to the user
If token is invalid or user is not found
isCodeValid(
id
,code
):boolean
Checks if a verification code is valid
import { userManager } from '@modular-rest/server';
const isValid = userManager.isCodeValid('user123', '123');
if (isValid) {
// Proceed with verification
}
Parameter | Type | Description |
---|---|---|
id | string | The ID of the user |
code | string | The verification code |
boolean
Whether the verification code is valid
issueTokenForUser(
Promise
<string
>
Issues a JWT token for a user by email
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);
}
Parameter | Type | Description |
---|---|---|
email | string | The email of the user |
Promise
<string
>
Promise resolving to the JWT token
If user is not found
loginAnonymous():
Promise
<string
>
Logs in an anonymous user and returns their JWT token
import { userManager } from '@modular-rest/server';
const token = await userManager.loginAnonymous();
console.log('Anonymous token:', token);
Promise
<string
>
Promise resolving to the JWT token
loginUser(
id
?,idType
?,password
?):Promise
<string
>
Logs in a user and returns their JWT token
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);
}
Parameter | Type | Default value | Description |
---|---|---|---|
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 |
Promise
<string
>
Promise resolving to the JWT token
If user is not found or credentials are invalid
registerTemporaryID(
id
,type
,code
):string
Registers a temporary ID for verification or password reset
import { userManager } from '@modular-rest/server';
const tempId = userManager.registerTemporaryID('user@example.com', 'password_reset', '123456');
Parameter | Type | Description |
---|---|---|
id | string | The ID to register |
type | string | The type of temporary ID |
code | string | The verification code |
string
The registered ID
registerUser(
detail
):Promise
<string
>
Registers a new user
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);
}
Parameter | Type | Description |
---|---|---|
detail | UserRegistrationDetail | User registration details |
Promise
<string
>
Promise resolving to the JWT token
If user model is not found or registration fails
setCustomVerificationCodeGeneratorMethod(
generatorMethod
):void
Sets a custom method for generating verification codes
import { userManager } from '@modular-rest/server';
userManager.setCustomVerificationCodeGeneratorMethod((id, type) => {
return Math.random().toString(36).substring(2, 8).toUpperCase();
});
Parameter | Type | Description |
---|---|---|
generatorMethod | (id , idType ) => string | Function that generates verification codes |
void
submitPasswordForTemporaryID(
id
,password
,code
):Promise
<string
>
Submits a password for a temporary ID
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);
}
Parameter | Type | Description |
---|---|---|
id | string | The temporary ID |
password | string | The new password |
code | string | The verification code |
Promise
<string
>
Promise resolving to the JWT token
If verification code is invalid or user is not found
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.
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:
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
.
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.
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.
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.
The @modular-rest/server
framework uses koa/cors
middleware to configure CORS policies. Here's how you can set it up:
Below is a detailed explanation of the CORS configuration options provided by koa/cors
in @modular-rest/server
:
Here's an example of how to configure CORS in your @modular-rest/server
application:
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:
https://www.example.com
.GET
and POST
methods are permitted.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.