mvc-middleware
TypeScript icon, indicating that this package has built-in type declarations

3.0.0 • Public • Published

mvc-middleware

Mvc middleware for express like .Net Mvc

license npm latest package codecov

Installation

npm i mvc-middleware

How to use

// src/index.ts
import { json } from 'body-parser';
import cors from 'cors';
import express from 'express';
import http from 'http';
import { MvcMiddleware } from 'mvc-middleware';
import path from 'path';

// Path to folder where you have your controllers.
// Middleware will search controllers recursively.
// Each file with '.ts' extension and default export and 
//  typeof === 'function', decorated with '@api' will be
//  assumed as controller class.
const controllersPath = path.join(__dirname, 'api');

const expressApp = express();
expressApp.use(cors()).use(json());

const mvcMiddleware = new MvcMiddleware(expressApp);

(async () => {
  await mvcMiddleware.registerControllers(controllersPath);

  http
    .createServer(expressApp)
    .listen(80, 'localhost');
})();
// src/api/UsersApi.ts
import { MvcController } from 'mvc-middleware';

@api('/api')
export default class UsersApi extends MvcController {
  @GET // api/users
  users() {
    return this.ok(['user-1', 'user-2', 'user-3']);
  }
}
// src/api/ArticlesApi.ts
import { MvcController } from 'mvc-middleware';

@api('/api')
export default class ArticlesApi extends MvcController {
  @GET // articles
  async articles() {
    const articles = await Promise.resolve(['article-1', 'article-2']);
    return this.ok(articles);
  }
}

Dependency injection

We recommend to use cheap-di package to handle your dependency injection:

import { container } from 'cheap-di';
import express from 'express';

const expressApp = express();
const mvcMiddleware = new MvcMiddleware(expressApp, container);
// ...

You may pass any custom DI resolver that implements the following contract:

type AbstractConstructor<T = any> = abstract new (...args: any[]) => T;
type Constructor<T = any> = new (...args: any[]) => T;

type Resolver = <TInstance>(type: Constructor<TInstance> | AbstractConstructor<TInstance>, ...args: any[]) => TInstance | undefined;

interface DependencyResolver {
  /** instantiate by class */
  resolve: Resolver;
}

Decorators

We have two sets of decorators: for stage 2 (legacy) and stage 3 proposals. You may choose one of them with imports:

import { api, GET, POST, PUT, PATCH, DELETE } from 'mvc-middleware/stage2';
import { api, GET, POST, PUT, PATCH, DELETE } from 'mvc-middleware/stage3';
decorator description variants of using
api collect method names and types (get/post/...) and concat prefix to is urls @api
@api('api')
@api('/api/domain')
GET marks method as handler for GET request, can change handled url @GET
@GET('')
@GET('my-route')
@GET('/my-route')
POST marks method as handler for GET request, can change handled url @POST
@POST('')
@POST('my-route')
@POST('/my-route')
PUT marks method as handler for PUT request, can change handled url @PUT
@PUT('')
@PUT('my-route')
@PUT('/my-route')
PATCH marks method as handler for PATCH request, can change handled url @PATCH
@PATCH('')
@PATCH('my-route')
@PATCH('/my-route')
DELETE marks method as handler for DELETE request, can change handled url @DELETE
@DELETE('')
@DELETE('my-route')
@DELETE('/my-route')

MvcController methods

Method name Response status code Response type Arguments Description
ok 200 text or json model?: any returns 200 status code with data
created 201 text or json model?: any returns 201 status code with data
accepted 202 text or json model?: any returns 202 status code with data
noContent 204 - - returns 204 status code
found 302 text url: string returns 302 status code
permanentRedirect 308 text url: string returns 308 status code
redirect 300 - 308 text statusCode: number, url: string returns redirection status code
badRequest 400 text or json model?: any returns 400 status code with data
unauthorized 401 text or json model?: any returns 401 status code with data
forbid 403 - model?: any returns 403 status code
notFound 404 text or json model?: any returns 404 status code with data
conflict 409 text or json model?: any returns 409 status code with data
serverError 500 text message?: any returns 500 status code with error message
sendResponse any http status code text model: any, statusCode?: number returns status code with data. Default status code is 200

More examples

// src/api/UsersController.ts
import { Request, Response } from 'express';
import { api, GET, POST } from 'mvc-middleware/stage2';

@api('/api/users')
export default class UsersController extends MvcController {
  static users = [{
    id: 1,
    name: 'user 1',
  }]

  // GET: /api/users
  @GET('')
  list() {
    return this.ok(UsersController.users);
  }

  // GET: /api/users/:userId
  @GET(':userId')
  getById(stringId: string) {
    const userId = parseInt(stringId, 10);
    const user = UsersController.users.find(user => user.id === userId);
    return this.ok(user);
  }

  // POST: /api/users
  @POST('')
  add({ name }: { name: string }) {
    UsersController.users.push({
      id: UsersController.users.length + 1,
      name,
    });
    return this.ok('user is added');
  }
}

If you want to get query params and body content, you have to connect another middleware that will handle requests before MvcMiddleware like below.

// src/index.ts
import express, { Router } from 'express';
import bodyParser from 'body-parser';
import { MvcMiddleware } from 'mvc-middleware';

const expressApp = express();

expressApp.use(bodyParser.json());
expressApp.use(bodyParser.urlencoded());

const mvcMiddleware = new MvcMiddleware(expressApp, container);
// ...

Package Sidebar

Install

npm i mvc-middleware

Weekly Downloads

8

Version

3.0.0

License

MIT

Unpacked Size

196 kB

Total Files

140

Last publish

Collaborators

  • tomas-light