1.6.4 • Published 7 years ago

restful-node v1.6.4

Weekly downloads
9
License
MIT
Repository
github
Last release
7 years ago

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.

Known Vulnerabilities Dependency Status

Quick install npm install --save restful-node

Read more about how RESTful-node works in our wiki

Table of Contents

  1. Summary
  2. Installation
  3. Creating Controllers
  4. Linking Routes and Controllers
  5. Creating Models
  6. Custom Filters
  7. 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
  • 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 operations
  • AuthController - Introduces the possibility of authentication
    • JWTAuthController - Uses the JWTFilter to give quick access to Json Web Token Authentication for your routes
  • FileController - Allows uploading and removal of files. Inherits AuthController.

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 Filters. Filters are simply classes containing the method canAccess, which returns a Promise.

JWTAuthController uses JWTFilter 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 JWTAuthControllers.

// 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() }
]);
1.6.4

7 years ago

1.6.3

7 years ago

1.6.2

7 years ago

1.6.1

7 years ago

1.6.0

7 years ago

1.5.1

7 years ago

1.5.0

7 years ago

1.4.4

7 years ago

1.4.3

7 years ago

1.4.2

7 years ago

1.4.1

7 years ago

1.4.0

7 years ago

1.3.1

7 years ago

1.3.0

7 years ago

1.1.2

7 years ago

1.1.1

7 years ago

1.1.0

7 years ago

1.0.1

7 years ago

1.0.0

7 years ago