![npm](https://img.shields.io/npm/v/@egodigital/express-controllers.svg)
express-controllers
Sets up controllers, which are running with Express.js.
Install
Execute the following command from your project folder, where your package.json
file is stored:
npm install --save @egodigital/express-controllers
Samples
Example code can be found in express-controllers-samples repository.
Usage
Build
npm install
npm run build
Requirements
TypeScript
You have to enable decorator feature in your tsconfig.json
file:
{
"compilerOptions": {
// ...
"experimentalDecorators": true
},
// ...
}
Examples
Quick start
First create a root directory, where all your controllers will be stored and implemented, lets say /controllers
.
Then create a /controllers/index.ts
(if using TypeScript) and implement an exported / public Controller
class with the following skeleton:
import * as joi from 'joi';
import { Request, Response } from 'express';
import { ControllerBase, GET, POST } from '@egodigital/express-controllers';
interface INewUser {
email?: string;
password: string;
username: string;
}
const NEW_USER_SCHEMA = joi.object({
email: joi.string()
.optional(),
password: joi.string()
.min(1)
.required(),
username: joi.string()
.required(),
});
export class Controller extends ControllerBase {
@GET()
public async index(req: Request, res: Response) {
return res.status(200)
.send('Hello, e.GO!');
}
@GET()
public async foo(req: Request, res: Response) {
return res.status(200)
.send('Hello, foo!');
}
@POST('/foo/:foo_id')
public async foo_with_post(req: Request, res: Response) {
return res.status(200)
.send('Hello, foo.POST: ' + req.params['foo_id']);
}
@POST({
path: '/users',
schema: NEW_USER_SCHEMA,
})
public async create_new_user(req: Request, res: Response) {
const NEW_USER: INewUser = req.body;
return res.status(200)
.send('Created new user: ' + NEW_USER.username);
}
}
For loading and initializing the controllers from /controllers
, simply create a /index.ts
file and use the following code snippet:
import * as express from 'express';
import { initControllers } from '@egodigital/express-controllers';
const app = express();
initControllers({
app,
cwd: __dirname + '/controllers',
files: ['**/*.ts', '**/*.js']
});
app.listen(8080, () => {
});
The library will scan the complete /controllers
folder structure and map the endpoints by that structure.
You can also use other filenames as index.ts
. For example, if you would like to implement a /foo/bar
endpoint, create a /controllers/foo/bar.ts
and use the following snippet:
import { Request, Response } from 'express';
import { ControllerBase, GET } from '@egodigital/express-controllers';
export class Controller extends ControllerBase {
@GET()
public async index(req: Request, res: Response) {
}
@GET()
public async xyz(req: Request, res: Response) {
}
@GET('/tm')
public async xyz(req: Request, res: Response) {
}
}
Serialize
import { Request, Response } from 'express';
import { ControllerBase, GET, ResponseSerializerContext } from '@egodigital/express-controllers';
export class Controller extends ControllerBase {
public async __serialize(context: ResponseSerializerContext) {
return context.response
.header('Content-Type', 'application/json')
.send(JSON.stringify(
context.result
));
}
@GET()
public async index(req: Request, res: Response) {
return {
success: true,
data: {
'TM': '1979-09-05 23:09'
},
};
}
}
Middlewares
import * as express from 'express';
import { ControllerBase, POST } from '@egodigital/express-controllers';
interface INewUser {
email?: string;
password: string;
username: string;
}
export class Controller extends ControllerBase {
public __use = [
express.urlencoded({ extended: true }),
];
@POST('/users')
public async new_user(req: express.Request, res: express.Response) {
const NEW_USER: INewUser = req.body;
return res.status(200)
.send('Created new user: ' + JSON.stringify(
NEW_USER, null, 2
));
}
}
Error handling
import * as express from 'express';
import { ControllerBase, GET, RequestErrorHandlerContext } from '@egodigital/express-controllers';
export class Controller extends ControllerBase {
public async __error(context: RequestErrorHandlerContext) {
return context.response
.status(500)
.send('SERVER ERROR: ' + context.error);
}
@GET()
public async index(req: express.Request, res: express.Response) {
throw new Error('Test error!');
}
}
Authorize
import * as express from 'express';
import { Authorize, AuthorizeFailedHandlerContext, AuthorizeHandlerContext, ControllerBase, GET, RequestErrorHandlerContext } from '@egodigital/express-controllers';
export class Controller extends ControllerBase {
public async __authorize(context: AuthorizeHandlerContext) {
return 'The authorization has been failed';
}
public async __authorizeFailed(context: AuthorizeFailedHandlerContext) {
const ERROR_MSG = context.result as string;
return context.response
.status(401)
.send('AUTHORIZE FAILED: ' + ERROR_MSG);
}
@Authorize()
@GET()
public async index(req: express.Request, res: express.Response) {
return res.status(204)
.send();
}
}
Swagger
First, define the main information of the document:
import * as express from 'express';
import { initControllers } from '@egodigital/express-controllers';
const app = express();
initControllers({
app,
cwd: __dirname + '/controllers',
swagger: {
definitions: {
'SuccessResponse': {
"type": "object",
"properties": {
"success": {
"description": "Indicates if operation was successful or not.",
"type": "boolean"
},
"data": {
"description": "The result data.",
"type": "string"
},
}
}
},
document: {
host: 'api.example.com',
info: {
contact: {
email: "hello@e-go-digital.com",
},
description: "Describes all API endpoints.",
title: "Test API",
version: "1.0.0",
},
schemes: ['http', 'https'],
tags: {
'test': 'A test tag',
},
},
title: 'Swagger Test',
},
});
app.listen(8080, () => {
console.log('Swagger document: http://localhost:8080/swagger');
});
Now use @Swagger
decorator for each of your method, to document your API (for more information, visit Paths and Operations
):
import { Request, Response } from 'express';
import { ControllerBase, GET, Swagger, SwaggerPathDefinitionUpdaterContext } from '@egodigital/express-controllers';
function pathDefinitionUpdater(ctx: SwaggerPathDefinitionUpdaterContext) {
ctx.definition['responses']['400'] = {
"description": "Bad request!"
};
ctx.definition['responses']['500'] = {
"description": "Operation has failed!"
};
}
export class Controller extends ControllerBase {
@GET()
@Swagger({
"tags": [
"test"
],
"summary": "A test.",
"produces": [
"application/json"
],
"parameters": [
{
"in": "header",
"name": "X-My-Header",
"required": false,
"type": "string"
},
],
"responses": {
"200": {
"description": "Operation was successful.",
"schema": {
"$ref": "#/definitions/SuccessResponse",
}
},
}
}, pathDefinitionUpdater)
public async index(req: Request, res: Response) {
return res.json({
success: true,
data: 'Swagger test: OK',
});
}
}
Documentation
The API documentation can be found here.