.NET MVC-like controller structure for Express.js in Typescript
Version 0.9.x brings new features that break existing code, initially this version will be tagged with beta, so until more testing is done it wont be available on the latest
tag.
- Route method parameters are now populated. The logic is the following:
- if the parameter type is either
Request
orResponse
, the current instances are injected - if there is an annotation, act accordingly (inject query,form,header,router,body values)
- if the parameter type is a class, try to instantiate it with the help of the dependency manager
- otherwise try injecting the value from
- if the parameter type is either
This library depends on the experimentalDecorators
and emitDecoratorMetadata
typescript compiler option which are experimental and may change in the future rendering this library useless.
Allows you to write better structured, declaration-oriented and more concise code for your ExpressJS application. The library is mostly inspired by the ASP.NET MVC project with many names being identical.
- Add the library to your project:
npm install --save express-mvc-ts
- Grab the typescript definition file (
express-mvc-ts.d.ts
) from the github page and add it to your project. No definitelytyped/tsd/type/@types support yet. - Add the
experimentalDecorators
andemitDecoratorMetadata
compiler options to your typescript configuration with the valuetrue
. - Transform your existing ExpressJS code:
app.get('/', function (req, res) {
refererService.saveReferer(req.query.referer);
res.render('home');
});
app.get('/about', function (req, res) {
if (req.get('Content-Type') === 'application/json') {
res.json({ description: "This is my site." });
} else {
res.render('about');
}
});
app.get('/contact/form(/:reason)?', function (req, res) {
res.render('contact/index', { reason: req.params.reason });
});
app.post('/contact/api/save', function (req, res) {
var contactRequest = req.body;
myDataStore.saveContactRequest(contactRequest)
.then(() => res.json({ success: true }))
.catch(() => res.json({ success: false }));
});
to Controllers *
:
import { Controller, Inject, Route, HttpGet, HttpPost, FromHeader } from 'express-mvc-ts';
import { MyRefererDataService } from '../services/refererServices';
@Inject
@Route('')
export class HomeController extends Controller {
@HttpGet
public index(referer: string, refererService: MyRefererDataService) {
refererService.saveReferer(referer);
return this.view();
}
@HttpGet
public about( @FromHeader('Content-Type') contentType: string) {
if (contentType === 'application/json') {
return this.json({ description: "This is my site." });
} else {
return this.view('about');
}
}
}
@Inject
export class ContactController extends Controller {
public constructor(private dataStore: MyCoolDataStoreService) {
super();
}
@HttpGet('form(/:reason)?')
public form(reason: string) {
return this.view('contact', { reason });
}
@HttpPost('api/save')
public save( @FromBody contactRequest: IContactRequest) {
return this.dataStore.saveContactRequest(contactRequest)
.then((response) => this.json({ success: true, response }))
.catch(() => this.json({ success: false }));
}
}
- Invoke the
setup
method on your app*
:
// 1. Include the library
import * as mvc from 'express-mvc-ts';
// 2. Instantiate and configure your ExpressJS app
var app = Express();
// ... configure
// 3. Call mvc.setup on your app
mvc.setup(app);
// 4. Profit
app.listen();
*
these code samples were not tested, nor do they represent viable real-life scenarios. Detailed samples are coming soon
- The default value for the
view
response is alwaysindex
, this should be the name of the method, like in .NET MVC. There is currently no point specifying method parameters as they will not be populated. The plan is to ultimately have request parameters bound to method parameters like?name=John
=>public profile(name: string) { this.request.query.name === name; }
- If the resulting code is minified, route method parameters may not work since variable names will be mangled/shortened. To avoid this, specify the names explicitly (i.e.
method(@FromQuery('search') search:string)
). - Probably a bunch of other things...