express-mod
v1.3.1
Published
express-mod is an open source library for building API (Application programming interface)
Maintainers
Readme
About
express-mod is an open source library for building API (Application programming interface)
express-modis based on the express framework.
Features ✨
OOPandMVCbased routing ✔- Route validation ✔
- Error handling and global exception ✔
- Intercepting server and API crashes.
- Catch async error on all routes ✔
- No more trycatch blocks when dealing with async functions.
- Typescript support out of the box ✔
All these features are included by default, they can save you the time of setting them up from scratch.
👉 Note: some of these features are optional. You can either use or not to use.
express-mod is a production ready and it's lighter than Express itself
express-modaims to make Express more manageable using its decorator APIsexpress-moddoes not modify any functions of expressexpress-modaims to be fast, lighter, flexible and maintainable- Using
express-modwith TypeScript is recommended - Using
express-modwith JavaScript will gain less benifits or consider usingexpressinstead
Table of contents
Example
Attention: Using
express-modwith TypeScript projects is recommended. If you are using JavaScript see this.
Routing with decorator
./sevice.ts
import { Injectable } from 'express-mod'
@Injectable()
export class ExampleService {
public helloWorld(): string {
return 'Hello world!'
}
}./api.ts
import { Api, Get } from 'express-mod'
import { ExampleService } from './sevice'
@Api()
export class ExampleApi {
constructor(private readonly exampleService: ExampleService) {}
@Get()
public helloWorld(): string {
return this.exampleService.helloWorld()
}
}./route.ts
import express, { Route } from 'express-mod'
import { ExampleApi } from './api'
@Route([ExampleApi], { router: express.Router() })
export class ExampleRoute {}Attach and register decorated route
./index.ts
import express, { Router } from 'express-mod'
import http from 'node:http'
import { ExampleRoute } from './route'
// initialize express
const app = express()
// create http server
const server = http.createServer(app)
// router instance
const router = new Router({ initial: app })
// attach and register decorated route.
router.attach('/api/v1', [ExampleRoute])
async function __main__() {
// TODO: connect to database
// await connect({ uri: 'DB_URI' })
// listen for connections
server.listen(4000, '0.0.0.0', () => console.info(`⚡️ Server is up in ${process.env.NODE_ENV} mode. visit: http://localhost:${process.env.PORT}`))
}
// execute main
__main__()Installation
You need nodeJs installed on your OS.
# with npm
npm i express-mod
# installing typescript
1. npm i -D typescript
2. npx tsc --init - to create tsconfig.json fileAs we all know, the library uses @decorator without enabling some additional features. Typescript will complain. You need to enable these additional features of Typescript. In the file
'tsconfig.json' enable these:
{
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}That's it. well done! see more example
Apis
We provide all the Apis that you will need to create a flexible and maintainable application.
@Api
A class-level defined with methods for handling one or more requests.
- @param
urlurl path.
Example
import { Api } from 'express-mod'
@Api()
export class ExampleApi {}@Method
A specific endpoint for HTTP requests.
- @param
methodhttp method type. - @param
urlurl path. - @param
statusstatus code.
Possible methods
@Get(), @Post(), @Put(), @Patch(), @Delete()
Example
import { Get } from 'express-mod'
export class ExampleApi {
@Get() // => "/"
// or - @Get("/", 200) => "/"
// or - @Get(200) => "/"
public helloWorld(): string {
return 'hello world!'
}
}@Middleware
A function which is called before the route handler. there are 2 types of middleware Method-level and API-level
- @param
midsexecute any code.
Example
Method-level
import { Middleware } from 'express-mod'
export class ExampleApi {
@Middleware([
(req, res, next) => {
console.log('mid mounted before route bounds.')
next()
},
])
public helloWorld(): string {
return 'hello world!'
}
}API-level
import { Middleware } from 'express-mod'
@Middleware([
(req, res, next) => {
console.log('mid mounted before all routes bound.')
next()
},
])
export class ExampleRoute {}@Params
A named URL segments that are used to capture the values specified at their position in the URL.
- @param
namestring name.
Possible params
@Req(), @Res(), @Next(), @Params(), @Query(), @Body(), @Cookies(), @Headers(), @Ctx()
Example
import { Req, Request, Body } from 'express-mod'
export class ExampleApi {
public helloWorld(@Req() req: Request, @Body() body: object): string {
// `req.body` regular use.
// instead of `req.body` use `@Body()`
return 'hello world!'
}
}@Validation
Validation middleware. A function which is called before the route handler.
- @param
schemaschema object.
Supported library: zod
Note: For other libraries beside
zodcan also be integrated withexpress-mod, but you just have to set it up yourself.
Example
with zod
import { ValidateRequest, Validation } from 'express-mod'
import z from 'zod'
export class ExampleApi {
@Validation(z.object<ValidateRequest>({ body: z.object({ name: z.string().max(50) }) }))
public helloWorld(): string {
return 'hello world!'
}
}@Route
A crucial function use to attach api handlers for the next stage.
- @param
Apisapi handlers. - @param
routeOptionsroute options.
Example
import express, { Route } from 'express-mod'
import ExampleApi from './api'
@Route([ExampleApi, ...], { router: express.Router() })
export class ExampleRoute {}@Injectable
The @Injectable() decorator is used to define metadata object.
Example
./service.ts
import { Injectable } from 'express-mod'
@Injectable()
export class ExampleService {
public username(): string {
return 'Bob'
}
}@Inject
The @Inject() decorator is used to mark parameter as dependency. but usually we don't need to use this we inject one time using @Injectable() at the Service level and we're good to go.
Example
./api.ts
import { Inject } from 'express-mod'
import { ExampleService } from './injectable'
export class Example {
constructor(
@Inject(ExampleService) private readonly exampleService: ExampleService,
// short version:
private readonly exampleService: ExampleService // this will auto inject without using the @Inject() decorator.
) {}
public getName(): string {
return exampleService.username()
} // returns "Bob"
}@Injector
A top-level class used to resolve injector value.
Example
./injector.ts
import { Injector } from 'express-mod'
import { Example } from './inject'
// resolve Example injector value.
const value = Injector.get(Example)
value.username() // Returns "Bob"Customize
You can customize some Apis according to your needs.
Middleware
Most come with middleware. It has to be flexible. Sure, we got it!
Example
./mids.ts
import { Middleware, UnauthorizedError } from 'express-mod'
// check if user is logged in.
const Authenticated = () =>
Middleware([
(req, res, next) => {
if (req.isUnAuthenticated()) {
throw new UnauthorizedError('User unauthorized.')
}
},
])
// example usage:
export class ExampleApi {
@Authenticated() // TaaDaa!
public helloWorld(): string {
return 'hello world!'
}
}Method
In addition to the 5 common http methods
@Get(), @Post(), @Put(), @Patch(), @Delete()that we provided, there are some other http methods such asALL, TRACE, HEAD, OPTIONS, etc.that we didn't provided. you can customize it to your needs.
Example
./custom.method.ts
import { METHOD_DECORATOR_FACTORY, PathParams } from 'express-mod'
// head http method
const Head = (url?: PathParams, status: number = 200) => METHOD_DECORATOR_FACTORY('head', url, status)
// example usage:
export class ExampleApi {
@Head() // TaaDaa!
public helloWorld(): string {
return 'hello world!'
}
}Errors & Exceptions
Customize error response
Example
./custom.err.ts
import { CustomError } from 'express-mod'
export class BadRequestError extends CustomError {
public readonly status = 400
public readonly error = 'BAD_REQUEST'
constructor(public readonly message: string) {
super(message)
Object.setPrototypeOf(this, BadRequestError.prototype)
}
}
// example usage:
if (!req.user) throw new BadRequestError('User is not logged in.')
// response
response: { status: 400, error: 'BAD_REQUEST', message: 'User is not logged in.' }Router
The Router is a top-level class used to attach and register decorated route.
import express, { Router } from 'express-mod'
// initialize express
const app = express()
// router constance
const router = new Router({ initial: app })
// attach and register decorated route.
router.attach('/', [route, ...])Exception
Api error response message. Use inside the Method-level logic.
- @param
messageresponse message.
Possible errors
CustomError(), UnauthorizedError(), NotFoundError(), ConflictError(), ValidationError(), ForbiddenError()
Example
import { ConflictError } from 'express-mid'
if (user) throw new ConflictError('User is already exist')
// response
response: { status: 409, error: 'CONFLICT', message: 'User is already exist' }Define injector
The defineInjector function is used to define metadata object.
Example
import { defineInjector } from 'express-mod'
export class Example {}
// define injector
const example = defineInjector(Example)
console.log(example.name) // => 'Example'Using with Javascript
Attention: Using
express-modwith Javascript will gain less benefits. But you can still use it or consider usingexpressinstead.
Start the server
To start the server using
Javascript(CommonJs) you have to make some changes.
./index.js
// CommonJs
const { expressFn } = require('express-mod') // this would work! ✅
// const express = require('express-mod') this will not work!❌
// or
import { expressFn } from 'express-mod'
// initialize express
const app = expressFn()
// listen for connections
app.listen(4000, () => console.log('Server is up! visit: http://localhost:4000'))express-mod build everything Api (Application programming interface) lighter, easier and maintainable.
