express-controllers-decorators
v1.0.1
Published
A TypeScript library for creating express controllers using decorators
Readme
@syesy12/express-controllers
A TypeScript library for creating express controllers using decorators
Features
- TypeScript Support: Fully typed for better development experience.
- Modular Design: Organize your Express.js routes using controllers.
- Error Handling: Support for custom global error handler.
- Inversion of Control: Integrate with your own IoC framework or instantiate controller classes manually.
- Middleware Support: Apply middleware to specific routes or entire controllers.
Installation
Using npm:
npm install @syesy12/express-controllers reflect-metadataOr using yarn:
yarn add @syesy12/express-controllers reflect-metadataUsage
Basic Example
//./controllers/MyController.ts
import { Controller,
Get,
Post,
Put,
Delete,
Patch,
Options,
Head,
Body,
Query,
AbstractHttpResult,
HttpOkResult,
JsonPayload,
PathParam
} from '@syesy12/express-controllers';
@Controller("/my-controller")
export class MyController {
@Get("/example-get/:pathParam")
public async get(
@Query("param") param: string,
@PathParam("pathParam") pathParam: string,
): Promise<AbstractHttpResult<JsonPayload>> {
return new HttpOkResult(
new JsonPayload({ message: "Get", param, pathParam })
);
}
@Post("/example-post")
public async post(@Body() body: any): Promise<AbstractHttpResult<JsonPayload>> {
return new HttpOkResult(
new JsonPayload({ message: "Post", body })
);
}
@Put("/example-put")
public async put(): Promise<AbstractHttpResult<JsonPayload>> {
return new HttpOkResult(
new JsonPayload({ message: "Put" })
);
}
@Delete("/example-delete")
public async delete(): Promise<AbstractHttpResult<JsonPayload>> {
return new HttpOkResult(
new JsonPayload({ message: "Delete" })
);
}
@Patch("/example-patch")
public async patch(): Promise<AbstractHttpResult<JsonPayload>> {
return new HttpOkResult(
new JsonPayload({ message: "Patch" })
);
}
@Options("/example-options")
public async options(): Promise<AbstractHttpResult<JsonPayload>> {
return new HttpOkResult(
new JsonPayload({ message: "Options" })
);
}
@Head("/example-head")
public async head(): Promise<AbstractHttpResult<JsonPayload>> {
return new HttpOkResult(
new JsonPayload({ message: "Head" })
);
}
}//./ioc/MyControllerProvider.ts
import { IControllerProvider, ClassType } from '@syesy12/express-controllers';
export class MyControllerProvider implements IControllerProvider {
public getController(controllerClass: ClassType): unknown {
// Here you can implement your own logic to instantiate the controller
// For example, using a dependency injection framework or manually.
// Here we suppose this class has no dependencies and can be instantiated directly.
return new controllerClass();
}
}//index.ts
import "reflect-metadata"; // Required for decorators to work
import express, { json } from 'express';
import { attachControllers } from '@syesy12/express-controllers';
import { MyController } from './controllers/MyController';
import { MyControllerProvider } from './ioc/MyControllerProvider';
const app = express();
app.use(json());
const port = 3000;
attachControllers({
app,
controllers: [MyController],
controllerProvider: new MyControllerProvider(),
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});Global Error Handling
You can define a custom global error handler:
attachControllers({
app,
controllers: [MyController],
controllerProvider: new MyControllerProvider(),
globalErrorHandler: (err, req, res, next) => {
if (err instanceof CustomError) {
return res.status(400).json({ message: err.message });
}
next(err);
},
});Using With IoC Container
The only thing that changes is the MyControllerProvider implementation.
For example, here you can see an implementation using microsoft/tsyringe
import { container } from "tsyringe";
export class MyControllerProvider implements IControllerProvider {
public getController(controllerClass: ClassType): unknown {
return container.resolve(controllerClass);
}
}Accessing Request and Response
In some situations, you might need to access the raw request or response objects.
You can do this by using the @RequestObj() and @ResponseObj() decorators in your controller
import {
RequestObj,
ResponseObj,
} from '@syesy12/express-controllers';
import { Request, Response } from 'express';
@Controller("/my-controller")
export class MyController {
@Get("/example-request-response")
public async example(
@RequestObj() req: Request,
@ResponseObj() res: Response
): Promise<void> {
res.sendStatus(200).json({
queryParam: req.query.param,
})
}
}Validating Body
You can use zod to validate the request body
import { z } from "zod";
import { json } from "express"
import { Middlewares } from "./Middlewares";
const schema = z.object({
name: z.string(),
age: z.number().min(0),
});
@Controller("/body-validation")
@Middlewares([json()])
class BodyValidationController {
@Post("/")
async bodyValidation(
@Body(schema) body: z.infer<typeof schema>
) {
console.log(body.name);
console.log(body.age);
return new HttpOkResult(new JsonPayload({ message: "Body is valid" }));
}
}Middleware
You can use the @Middlewares decorator to apply middlewares to a specific route
import { Handler } from "express"
const myMiddleware: Handler = (req, res, next) => {
console.log("Middleware executed");
next();
};
@Controller("/my-controller")
export class MyController {
@Get("/example-middleware")
@Middlewares([myMiddleware])
public async exampleMiddleware(): Promise<AbstractHttpResult<JsonPayload>> {
return new HttpOkResult(
new JsonPayload({ message: "Middleware applied" })
);
}
}or to an entire controller
import { Handler } from "express"
const myMiddleware: Handler = (req, res, next) => {
console.log("Middleware executed");
next();
};
@Controller("/my-controller")
@Middlewares([myMiddleware])
export class MyController {
@Get("/example-middleware")
public async exampleMiddleware(): Promise<AbstractHttpResult<JsonPayload>> {
return new HttpOkResult(
new JsonPayload({ message: "Middleware applied" })
);
}
}License
This project is licensed under the GNU General Public License v3.0. See the LICENSE file for details.
Contributing
Contributions are welcome! Please follow the existing code style and ensure all tests pass before submitting a pull request
