tenx96 / pretty-express

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Pretty Express

A typescript library to write express Controllers and Middlewares.

pretty-express also provides versatility by providing methods to write your own decorator middlewares which can be used to encapsulate complicated middlewares behind simple/readable decorators.

See this section on custom middlewares

Sections

Creating a controller
Route Decorators
Middleware Decorators
Returning a response
Parameter Decorators
Custom Middleware decorator
Custom Parameter decorator
Order of middleware execution

Setup your typescript project and install pretty express

1: In your tsconfig add the following,

"experimentalDecorators":  true,
"emitDecoratorMetadata":  true ,

2: Install reflect-metadata and pretty-express

npm install reflect-metadata pretty-express

3: On your entry file (eg : index.ts) initialize reflect-metadata

	import "reflect-metadata"
	.
	.
	... <reset of the code here>

Write your controller

@controller("/users")
export class UserController {
  @get("/")
  getUsers() {
    return { message: "Users data" };
  }
}

Generate a router and use it on your express application

const userController = new UserController();
const router = combineControllers([userController]);
app.use(router);

Your final Final code

import "reflect-metadata";
import express from "express";
import { combineControllers, controller, get } from "pretty-express";
// Controller class

@controller("/users")
export class UserController {
  @get("/")
  getUsers() {
    return { message: "Users data" };
  }
}
// express initialization
const app = express();
app.use(express.json());
app.use(
  express.urlencoded({
    extended: true,
  })
);

// INITIALIZE CONTROLLERS
const userController = new UserController();
const router = combineControllers([userController]);
app.use(router);

// listen on port
app.listen(7000, () => {
  console.log("Listening on http://localhost:7000");
});

Call your new users api

curl --location --request GET 'http://localhost:7000/users'
{"message":"Users data"}

Api Details

The @controller decorator

@controller(path : string)

Specifies a class as a controller and use route decorators inside it to create apis.

eg :

@controller("/users")
export class UserController {
  @get("/")
  getUsers() {
    return { message: "Users data" };
  }
}

Route Decorators

Decorators Function
@get(path : string) create a GET route on specified path
@post(path : string) create a POST route on specified path
@patch(path : string) create a PATCH route on specified path
@del(path : string) create a DELETE route on specified path

Middleware decorator

Adds a middleware/s to a route, accepts one or multiple middlewares as an argument

@middleware(middlewares : ...(req,res,next) => void)
>>>>>>
const  authMiddleware  = (req :Request, res:Response, next:NextFunction) => {
console.log("Authenticating...")
next()
}


>>>>>>

	@middleware(authMiddleware)
	@post("/new")
	createUser(){
	return {
	message  :  "Created new user;"
	}



}

You can also pass multiple middlewares on this decorator. The middlewares will be executed based on the order in the params.

Example :

@middleware(firstMiddleware,secondMiddleware)

here firstMiddleware will be executed first followed by secondMiddleware

Similary we can assign errorMiddlewares using the @errMiddleware decorator

eg

const  errorMiddleware  = (err:  Error,req  :  Request,res  :  Response,next  :  NextFunction) => {
	console.log("Handling error...")
	next()
}
>>>>
@errMiddleware(errorMiddleware)
@post("/delete")
deleteUser(){
	return {
	message  :  "Deleted user;"
	}
}

Like @middleware we can also pass multiple error handlers in order

@errMiddleware(firstMiddleware,secondMiddleware)

we can also use both @middleware and @errMiddleware on the class/controller level. This will assign this middlewares on all the routes inside it.

@middleware(sampleMiddleware)
@controller("/users")
export  class  UserController {
@get("/")
getUsers() {
return { message:  "Users data" };
}
>>>

returning a response

By default the returned object of a decorated fucntion is returned as a JSON object with a status : 200

  @del("/")
  async someFunction(req: Request, res: Response, next: NextFunction) {
    return { method: "delete" };
  }

To return a response with a custom status code. Simply return an object of type HttpResponse(status, object)

  @post("/")
  async someFunction(req: Request, res: Response, next: NextFunction) {
    return new HttpResponse(201, { message: "created" });
  }

to return an error response, throw an error of type HttpErrorResponse

  @patch("/")
  async someFunction(req: Request, res: Response, next: NextFunction) {
    throw new HttpErrorResponse(400, "Hello Error!");
  }

You can also automatically get the status codes from static methods provided in HttpErrorResponse and HttpResponse.

@post("/user")
  async someFunction() {
    throw HttpErrorResponse.NOT_IMPLEMENTED("This method is not implemented!");
  }

Similary for HttpResponse

@post("/")
  async addUser(req: Request, res: Response, next: NextFunction) {
    return HttpResponse.CREATED({data : "new data entered"})
  }

Parameter Decorators

Pretty express provides 3 parameter decorators for our decorated functions

decorators details
@reqBody returns request.body from the express request parameter
@reqParam returns request.params from the express request parameter
@reqQuery returns request.query from the express request parameter
@reqFile returns request.file from the express request parameter
@reqFiles returns request.files from the express request parameter
  @post("/")
  async addUsers(@reqBody data: any , @reqParam params : any) {
    return  {data};
  }

if no parameters are decorated the default arguments (Request, Response, NextFunction) from the express RouterHandler will be passed

  @get("/")
  async getUsers(req: Request, res: Response, next: NextFunction) {
    return { message: "hello pretty express" };
  }

Optionally You can also access the express routeHandler params as the final 3 arguments of the function in order.

  @get("/")
  async getUsers(@reqBody data : any, req: Request, res: Response, next: NextFunction) {
    return { message: "hello pretty express" };
  }

Writing your own Middleware Decorator

you can build your own custom middleware decorator instead of using @middleware, using the createMiddlewareDecorator method from pretty-express

// import { createMiddlewareDecorator } from "pretty-express";
const jwtAuth = createMiddlewareDecorator<[role: string]>((args) => {
  return (req, res, next) => {
    console.log("ARGS , ", args);
    next();
  };
});

And use it in your controller/functions

  @jwtAuth("user")
  @get("/auth")
  getUsers(req: any) {
    console.log("Fetching users...");

    return { msg: "Hello world"};
  }

Writing your own custom parameter decorator

You can also write your own parameter decorator to access the properties in a express request object.

// import { createParamDecorator } from "pretty-express";
const authData = createParamDecorator("authUser");

here authData is a decorator that will return request.authData (if present) in our decorated functions

 @get("/test")
  test(@authData authData: any, req: any) {
    console.log("AuthData : ", authData);

    return { msg: "Hello world", data: authData };
  }

You can use these two functions together to write your own middlewares using different libraries using this very simple format

const jwtAuth = createMiddlewareDecorator<[role: string]>((args) => {
  return (req : any, res, next) => {
    console.log("ARGS , ", args);
    req.authData = {email : "jugga@redoc.mh"}
    next();
  };
});

const authData = createParamDecorator("authData");

@controller("/")
export class SampleController {
  @jwtAuth("user")
  @get("/auth")
  getUsers(@authData authData: any, req: any) {
    console.log("REQ : ", req.authData);
    return { msg: "Hello world", data: authData };
  }
}

Order of middleware execution

The order in which middlewares are executed is from Bottom to Top in which they are placed.

For example , Here the middleware will be called in order
middleware1 >> jwtAuth >> middleware2

  @middleware(middleware2)
  @jwtAuth("user")
  @middleware(middleware1)
  @get("/auth")
  getUsers(@authData authData: any, req: any) {
    console.log("REQ : ", req.authData);
    return { msg: "Hello world", data: authData };
  }
}

About


Languages

Language:TypeScript 100.0%