restful-node v1.6.4
RESTful-node
RESTful-node is a module built to simplify creation of CRUD based REST-APIs. By mimicing the MVC-pattern with controllers and models, we avoid duplications and weird project-structures.
Quick install
npm install --save restful-node
Read more about how RESTful-node works in our wiki
Table of Contents
- Summary
- Installation
- Creating Controllers
- Linking Routes and Controllers
- Creating Models
- Custom Filters
- More Examples
Summary
RESTful-node is built on top of the well established ExpressJS, with mongoose as the ODM. Making it cross-compatible with most other dependencies.
All controller methods used for http-requests,
are given the parameters req
, res
and next
,
such that you can inject middleware before or after the requests.
Installation
Minimum requirements
- NodeJS v6.9.0 (Built with v7.4.0. Must support ES2015 syntax)
- Recommends >= v7.6.0, which has support for
async-await
- Recommends >= v7.6.0, which has support for
- MongoDB (primary database client, planning to add support for Sequelize in the future)
Add to package.json
Add restful-node
to package.json
, or through shell
$ npm install --save restful-node
Folder structure
RESTful-node has no requirements for the structure.
However, we recommend having naming the folders after the file-types
it contains. I.e. folder of controllers, is named controllers
, and
a folder of filters, is named filters
.
Example
project-name
| bin # Generated by express-generator
| | www
|
| config
| | _config.js (secret configuration, untracked)
| | config.js (public configuration)
|
| controllers
| | name1.controller.js
| | name2.controller.js
| | ...
|
| filters
| | require-admin.filter.js
| | ...
|
| models
| | model1.js
| | ...
|
| library # if necessary
| | ...
|
| resources # Optional folder for media-files
| | image.jpeg
|
| app.js
| package.json
| README.md
Creating Controllers
Simply load the parent-controller you want to use features from:
RestController
- Simple controller with all CRUD operationsAuthController
- Introduces the possibility of authenticationJWTAuthController
- Uses theJWTFilter
to give quick access to Json Web Token Authentication for your routes
FileController
- Allows uploading and removal of files. InheritsAuthController
.
As AuthController
works exactly like RestController
, before any Filter
is added to authFilters
,
we will use it in the examples. But first create your class, and extend AuthController
.
If we have a model ready to go, we can register it in the constructor.
this.model = YourMongooseModel;
You should now have all the basic features you'll need. (We'll test the server after the routes are registered)
If no model is provided, and you haven't overridden any crud-methods the controller will respond with
HTTP 405 Method Not Allowed
CRUD-methods are:
list (req, res, next)
retrieve (req, res, next)
create (req, res, next)
update (req, res, next)
delete (req, res, next)
Example 1: Hello World
// hello-world.controller.js
const { AuthController } = require('restful-node`).controllers;
class HelloWorldController extends AuthController {
constructor(config = {}) {
super(config);
this.model = require('../models/hello-world');
}
}
module.exports = HelloWorldController;
Example 2: Override methods
// hello-two-world.controller.js
const { AuthController } = require('restful-node`).controllers;
class HelloTwoWorldController extends AuthController {
constructor(config = {}) {
super(config);
this.model = require('../models/hello-world');
this.disable.push('update'); // update will now respond with HTTP 405
}
// GET /:id (this.pk = 'id')
retrieve (req, res, next) {
// AuthController has already fetched the
// mongo-document. req.db.name is the model's name
// in lower-case
res.status(200).json(req.db.data[req.db.name]);
}
}
module.exports = HelloWorldController;
Linking routes and controllers
The controllers we have added, are ready to go. But before we can get any responses, we must register some routes.
RESTful-node has a function urls
which does it for us.
You only have to provide the express application app
, the prefix-url /api
, and a list of objects.
The list of objects has two elements, url
and controller
. url
is concated after /api
,
if it's omitted the controller will be linked to /api
. controller
explains itself.
Prefixes can also be added to the controller instances by adding config.prefix = '' as an argument to the constructor. These prefixes can also contain url-parameters
Example
// app.js
const restful = require('restful-node');
const HelloWorldController = require('./controller/hello-world.controller');
const HelloTwoWorldController = require('./controller/hello-two-world.controller');
restful.routes.urls(app, '/api', [
{ url: '/user', controller: new HelloWorlController({prefix: 'name'}) }, // Url becomes '/api/user/name'
{ controller: new HelloTwoWorldController() } // Uses only '/api' as prefix
]);
Creating models
I will not go into detail on how to create models,
because it is already well documented by mongoose
,
who created this schema.
The HelloWorld
-model in examples,
should give enough information to create your own.
To setup a connection, call setupMongoose
in require('restful-node').database
NOTE: RESTful-node expects
mongoose.Promise = global.Promise
to be set, both for the controllers and yours pleasure
Example
// app.js
const restful = require('restful-node');
const config = { database: 'hello-world' };
restful.database.setupMongoose(config)
.then((db) => {
// Connection successful
})
.catch(err => {
// Handle connection failures
});
// Routes registered bellow...
Checkout out the examples for more ways to interact with the models and controllers
Custom Filters
To provide general validation and authenticaion for requests, we introduce Filter
s.
Filters are simply classes containing the method canAccess
, which returns a Promise.
JWTAuthController
usesJWTFilter
to provide Authentication with Json Web Tokens (JWT).
Example 1: Validate Request headers
// require-auth-header.filter.js
const { Filter } = require('restful-node').auth;
const { ForbiddenError, UnauthorizedError } = require('restful-node').errors;
class RequireAuthHeaderFilter extends Filter {
constructor(config = {}) {
super();
}
canAccess(req, res) {
return new Promise((resolve, reject) => {
if (!req.headers['authorization']) {
return reject(
new UnauthorizedError('Missing "Authorization" header')
);
}
});
}
}
module.exports = RequireAuthHeaderFilter;
// -------------------------------------------
// hello-world.controller.js
const RequireAuthHeaderFilter = require('../filters/require-auth-header.filter');
class HelloWorldController extends AuthController {
constructor(config = {}) {
super(config);
this.model = require('../models/hello-world');
// Can also be added to `filters`,
// but `authFilters` is run first
this.authFilters.push(new RequireAuthHeaderFilter());
}
}
// ...
Example 2: Deny file uploading
In this example we will deny the possibility to upload files.
RESTful-node uses multer
as it's file-middleware, which attaches file
to req
.
Thus we will respond with HTTP 400
, if req.file
exists.
// disallow-files.filter.js
class DisallowFilesFilter extends Filter {
// Uses ES2016 async-await syntax,
async canAccess(req, res) {
if (req.file) {
throw new BadRequestError('File uploads are disallowed!');
}
}
}
More examples
Example 1: FileController
File-uploads are often a common practise when dealing with web-servers. RESTful-node tries to simplify this process by providing a controller for this process, with the usual CRUD operations.
Note: I recommend using Nginx or Apache to actually serve the static content, back to the users.
All methods list
, retrieve
, create
, update
and destroy
support file-uploads,
and each of them can disable this support by adding the method-name to denyUploadOn
.
As we see in the example bellow, most of the methods already has the basic operations we will need.
// example.controller.js
const { FileController } = require('restful-node').controllers;
class ExampleController extends FileController {
constructor(config = {}) {
config.root = 'resources/uploads'; // Root for the files
super(config);
this.disable.push('update'); // No need for this method
this.denyUploadOn.push('retrieve', 'destroy');
this.maxSize = 50000; // Measured in bytes
this.allowedMimeTypes.push('image/jpeg', 'image/png'); // Only allow web-friendly formats
}
// All information about the file, is available in `req.file`
async create(req, res, next) {
res.status(req.file ? 201 : 204).json(req.file);
}
// list will automatically respond with a list of the files in the `root`-folder
// Destroy will remove the file specified through the Primary Key (this.pk)
}
module.exports = ExampleController;
Example 2: Json Web Token
In this example we will create a
controller responsible for issuing jwt tokens,
which will can be used by JWTAuthController
s.
// token.controller.js
const { JWT } = require('restful-node').auth;
const { AuthController } = require('restful-node').controllers;
class UserController extends JWTAuthController {
constructor(prefix, secret) {
super(prefix, {secret, ttl: 5920392);
this.ignoreMethods.push('create');
}
create(req, res, next) {
res.status(200).json({
token: new JWT(this.secret).create({uid: 1})
});
}
}
module.exports = UserController;
Route registration
I normally use app.js
(generated by express-generator
) to call urls
.
// app.js
const { urls } = require('restful-node').routes;
const ExampleController = require('./example.controller');
const UserController = require('./user.controller');
urls(app, '/api', [
{ url: '/user', controller: new UserController('', 'bla bla bla') },
{ controller: new ExampleController() }
]);
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago