3.0.0 • Published 6 months ago

mvc-middleware v3.0.0

Weekly downloads
4
License
MIT
Repository
github
Last release
6 months ago

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';
decoratordescriptionvariants of using
apicollect method names and types (get/post/...) and concat prefix to is urls@api@api('api')@api('/api/domain')
GETmarks method as handler for GET request, can change handled url@GET@GET('')@GET('my-route')@GET('/my-route')
POSTmarks method as handler for GET request, can change handled url@POST@POST('')@POST('my-route')@POST('/my-route')
PUTmarks method as handler for PUT request, can change handled url@PUT@PUT('')@PUT('my-route')@PUT('/my-route')
PATCHmarks method as handler for PATCH request, can change handled url@PATCH@PATCH('')@PATCH('my-route')@PATCH('/my-route')
DELETEmarks method as handler for DELETE request, can change handled url@DELETE@DELETE('')@DELETE('my-route')@DELETE('/my-route')

MvcController methods

Method nameResponse status codeResponse typeArgumentsDescription
ok200text or jsonmodel?: anyreturns 200 status code with data
created201text or jsonmodel?: anyreturns 201 status code with data
accepted202text or jsonmodel?: anyreturns 202 status code with data
noContent204--returns 204 status code
found302texturl: stringreturns 302 status code
permanentRedirect308texturl: stringreturns 308 status code
redirect300 - 308textstatusCode: number, url: stringreturns redirection status code
badRequest400text or jsonmodel?: anyreturns 400 status code with data
unauthorized401text or jsonmodel?: anyreturns 401 status code with data
forbid403-model?: anyreturns 403 status code
notFound404text or jsonmodel?: anyreturns 404 status code with data
conflict409text or jsonmodel?: anyreturns 409 status code with data
serverError500textmessage?: anyreturns 500 status code with error message
sendResponseany http status codetextmodel: any, statusCode?: numberreturns 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);
// ...
3.0.0

6 months ago

3.0.0-rc.2

6 months ago

3.0.0-rc.1

6 months ago

3.0.0-alpha.2

6 months ago

3.0.0-alpha.1

7 months ago

2.0.0

1 year ago

1.1.4

4 years ago

1.1.3

4 years ago

1.1.2

4 years ago

1.1.1

4 years ago

1.1.0

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago

0.1.2

4 years ago

0.1.1

4 years ago

0.1.0

4 years ago