express
express copied to clipboard
Using express as a plugin?
I am finding very useful to make use of adapter functions so you can write plain TypeScript (JavaScript) controllers and use them in express without compromising them.
A simple example:
Define your own request and response objects in which you will add or retrieve data.
// ./web/types.ts
export type HttpRequest = {
headers: any,
params: any,
body: any,
}
export type HttpResponse = {
statusCode: number,
body: any,
}
export interface HttpController {
execute: Function
}
export enum HttpStatusCodes {
'OK' = 200,
'RESOURCE_CREATED' = 201,
'FORBIDDEN' = 401,
'INTERNAL_ERROR' = 500,
}
Define your express adapter that receives your controller and returns a compatible express handler, this functions calls the execute method on the controller and rely on it for all the logic that will come next, it will expect a status Code and a body which are defined in types.ts
// ./web/app.ts
import express, { Request, Response } from 'express';
import { HttpRequest, HttpResponse, HttpController } from './types';
function adaptRequest(controller: HttpController) {
return async (req: Request, res: Response) => {
try {
const httpRequest: HttpRequest = {
headers: req.headers,
params: req.params,
body: req.body,
}
const httpResponse: HttpResponse = await controller.execute(httpRequest);
res.status(httpResponse.statusCode).json(httpResponse.body);
} catch (error: any) {
res.status(500).json({
error: error.message
});
}
}
}
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.post('/jwt/sign', adaptRequest(signController));
An this is the controller you define implementing the interface, which you also define :). I believe the controller should also handle only plain request and response as JavaScript objects and probably receive a use case via constructor that will execute logic. (I didn't do it here but anyways).
import jwt from 'jsonwebtoken';
import { HttpRequest, HttpResponse, HttpController, HttpStatusCodes } from '../web/types';
export default class SignController implements HttpController {
private secretKey: string;
constructor(secretKey: string) {
this.secretKey = secretKey;
}
execute(httpRequest: HttpRequest): HttpResponse {
try {
const { body } = httpRequest;
const token = jwt.sign(body, this.secretKey);
return {
statusCode: HttpStatusCodes.OK,
body: {
token: token,
}
}
} catch (error: any) {
return {
statusCode: HttpStatusCodes.INTERNAL_ERROR,
body: {
error: error.message,
}
}
}
}
}
What do you think about this approach? I think about two cases such as redirect and middleware that at this point my solution is to create an expressMiddlewareAdapter function and a redirectAdapterFunction and use them depending on the case I got.