New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@egodigital/express-controllers

Package Overview
Dependencies
Maintainers
3
Versions
60
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@egodigital/express-controllers

Sets up controllers for Express framework.

  • 8.0.0
  • latest
  • Source
  • npm
  • Socket score

Version published
Maintainers
3
Created
Source

npm

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

# install modules
npm install

# build
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 { Request, Response } from 'express';
import { ControllerBase, GET, POST, schema } from '@egodigital/express-controllers';

interface INewUser {
    email?: string;
    password: string;
    username: string;
}

const NEW_USER_SCHEMA = schema.object({
    email: schema.string()
        .optional(),
    password: schema.string()
        .min(1)
        .required(),
    username: schema.string()
        .required(),
});

/**
 * /controllers/index.ts
 *
 * Base path: '/'
 */
export class Controller extends ControllerBase {
    /**
     * [GET] / endpoint
     *
     * 'index' will mapped to realtive '/' path by default.
     */
    @GET()
    public async index(req: Request, res: Response) {
        return res.status(200)
            .send('Hello, e.GO!');
    }

    /**
     * [GET] /foo endpoint
     *
     * Other method names than 'index', will always be mapped
     * to realtive '/{METHOD_NAME}' path
     */
    @GET()
    public async foo(req: Request, res: Response) {
        return res.status(200)
            .send('Hello, foo!');
    }

    /**
     * [POST] /foo/:foo_id endpoint
     *
     * Define relative path explicitly.
     */
    @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] /users endpoint
     *
     * Check JSON input via joi schema.
     */
    @POST({
        path: '/users',
        schema: NEW_USER_SCHEMA,
    })
    public async create_new_user(req: Request, res: Response) {
        const NEW_USER: INewUser = req.body;

        // TODO ...

        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, () => {
    // server now running
});

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';

/**
 * /controllers/foo/bar.ts
 *
 * Base path: '/foo/bar'
 */
export class Controller extends ControllerBase {
    /**
     * [GET] /foo/bar endpoint
     */
    @GET()
    public async index(req: Request, res: Response) {
        // TODO
    }

    /**
     * [GET] /foo/bar/xyz endpoint
     */
    @GET()
    public async xyz(req: Request, res: Response) {
        // TODO
    }

    /**
     * [GET] /foo/bar/tm endpoint
     */
    @GET('/tm')
    public async xyz(req: Request, res: Response) {
        // TODO
    }
}

Serialize

import { Request, Response } from 'express';
import { ControllerBase, GET, ResponseSerializerContext } from '@egodigital/express-controllers';

/**
 * /controllers/index.ts
 *
 * Base path: '/'
 */
export class Controller extends ControllerBase {
    // serialize the results of any
    // controller route method and
    // send each as response
    public async __serialize(context: ResponseSerializerContext) {
        return context.response
            .header('Content-Type', 'application/json')
            .send(JSON.stringify(
                context.result  // result of 'index()', e.g.
            ));
    }

    /**
     * [GET] / relative endpoint
     */
    @GET()
    public async index(req: Request, res: Response) {
        // this object is serialized and
        // send by '__serialize()' (s. above)
        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;
}

/**
 * /controllers/index.ts
 *
 * Base path: '/'
 */
export class Controller extends ControllerBase {
    // define one or more middlewares
    // for each route endpoint
    public __use = [
        express.urlencoded({ extended: true }),
    ];

    /**
     * [POST] /users endpoint
     */
    @POST('/users')
    public async new_user(req: express.Request, res: express.Response) {
        const NEW_USER: INewUser = req.body;

        // TODO ...

        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';

/**
 * /controllers/index.ts
 *
 * Base path: '/'
 */
export class Controller extends ControllerBase {
    // handle exceptions
    public async __error(context: RequestErrorHandlerContext) {
        return context.response
            .status(500)
            .send('SERVER ERROR: ' + context.error);
    }

    /**
     * [GET] / endpoint
     */
    @GET()
    public async index(req: express.Request, res: express.Response) {
        // all request error, like that
        // will be handled by
        // '__error()' method
        throw new Error('Test error!');
    }
}

Authorize

import * as express from 'express';
import { Authorize, AuthorizeFailedHandlerContext, AuthorizeHandlerContext, ControllerBase, GET, RequestErrorHandlerContext } from '@egodigital/express-controllers';

/**
 * /controllers/index.ts
 *
 * Base path: '/'
 */
export class Controller extends ControllerBase {
    // check if authorized
    public async __authorize(context: AuthorizeHandlerContext) {
        // return (true) or (false)
        // or a non empty string, which is returned as error message by default

        // default: (false)

        return 'The authorization has been failed';
    }

    // handle failed authorization
    public async __authorizeFailed(context: AuthorizeFailedHandlerContext) {
        // s. result of __authorize()
        const ERROR_MSG = context.result as string;

        return context.response
            .status(401)
            .send('AUTHORIZE FAILED: ' + ERROR_MSG);
    }


    /**
     * [GET] / endpoint
     */
    @Authorize()
    @GET()
    public async index(req: express.Request, res: express.Response) {
        // this will only be invoked
        // if __authorize() returns (true)
        // or nothing (null, undefined or empty string)

        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: {
        // s. https://swagger.io/docs/specification/2-0/describing-responses/
        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';


// update each path definition with default values (s. below)
function pathDefinitionUpdater(ctx: SwaggerPathDefinitionUpdaterContext) {
    // Bad Request
    ctx.definition['responses']['400'] = {
        "description": "Bad request!"
    };

    // Internal Server Error
    ctx.definition['responses']['500'] = {
        "description": "Operation has failed!"
    };
}


/**
 * /controllers/api/index.ts
 *
 * Base path: '/api'
 */
export class Controller extends ControllerBase {
    /**
     * [GET]  /api
     */
    @GET()
    // s. https://swagger.io/docs/specification/2-0/paths-and-operations/
    @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.

Keywords

FAQs

Package last updated on 07 Feb 2021

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc