arrest

Swagger REST framework for Node.js, with support for MongoDB and JSON-Schema

Showing:

Popularity

Downloads/wk

61

GitHub Stars

16

Maintenance

Last Commit

20d ago

Contributors

6

Package

Dependencies

18

License

MIT

Type Definitions

Built-In

Tree-Shakeable

No?

Categories

Readme

arrest

Swagger REST framework for Node.js, with support for MongoDB and JSON-Schema

travis build Coverage Status npm version

Arrest lets you write RESTful web services in minutes. It automatically generates a Swagger description of the API and support input validation using JSON-Schemas.

Highlight features:

  • Compatible with Express 4.x
  • Implements simple CRUD semantics on MongoDB
  • Supports querying object with the RQL syntax
  • Input validation with JSON-Schema
  • Oauth2 scope checks per operation

Note for 1.3 users: arrest 3.x is a complete rewrite of the old module and it's not backwards compatible.

How to Install

npm install arrest

Super Simple Example

The following sample application shows how to create a simple REST API, using a MongoDB collection as the data store. In the sample, the path /tests is linked to a tests collection on a MongoDB instance running on localhost:

const arrest = require('arrest');
const api = new arrest.API();

api.addResource(new arrest.MongoResource('mongodb://localhost:27017', { name: 'Test' }));

api.listen(3000);

The Swagger specification of the API you just created is available at http://localhost:3000/swagger.json

Now you can query your data collection like this:

curl "http://localhost:3000/tests"

You can add a new item:

curl "http://localhost:3000/tests" -H "Content-Type: application/json" -X POST -d '{ "name": "Jimbo", "surname": "Johnson" }'

You can query a specific item by appeding the identifier of the record (the _id attribute):

curl "http://localhost:3000/tests/51acc04f196573941f000002"

You can update an item:

curl "http://localhost:3000/tests/51acc04f196573941f000002" -H "Content-Type: application/json" -X PUT -d '{ "name": "Jimbo", "surname": "Smith" }'

And finally you can delete an item:

curl "http://localhost:3000/tests/51acc04f196573941f000002" -X DELETE

Creating an API

An API is a collection of Resources, each supporting one or more Operations.

In arrest you create an API by creating an instance of the base API class or of a derived class. You then add instances of the Resource class or a derived one. Each resource contains its supported Routes, that is a collection of instances of classes derived from the abstract Operation, which represents an operation to be executed when an HTTP method is called on a path.

The following code demonstrates this three level structure:

const arrest = require('arrest');
const api = new arrest.API();

const operation1 = function(req, res, next) {
  res.json({ data: 'this is operation 1' });
}
const operation2 = function(req, res, next) {
  res.json({ data: 'this is operation 2' });
}
const operation3 = function(req, res, next) {
  res.json({ data: 'this is operation 3' });
}
const resource1 = new arrest.Resource({
  name: 'SomeResource',
  routes: {
    '/': {
      get: operation1,
      post: operation2
    },
    '/some-path': {
      put: operation3
    }
  }
})

api.addResource(resource1);
api.listen(3000);

The API above supports the following operations:

  • GET on http://localhost/some-resources
  • POST on http://localhost/some-resources
  • PUT on http://localhost/some-resources/some-path

Please note how the some-resources path was automatically constructed using the name of the resource SomeResource, making it plural and converting the camelcase in a dash-separated name. This default behaviour can be changed specifying the namePlural and path when creating the resource (e.g. new Resource({ name: 'OneResource', namePlural: 'ManyResources', path: 'my_resources' }))

Another other way to produce the same result is:

const arrest = require('arrest');
const api = new arrest.API();

const resource1 = new arrest.Resource({ name: 'SomeResource' });

resource1.addOperation('/', 'get', function(req, res, next) {
  res.json({ data: 'this is operation 1' });
});
resource1.addOperation('/', 'post', function(req, res, next) {
  res.json({ data: 'this is operation 2' });
});
resource1.addOperation('/some-path', 'put', function(req, res, next) {
  res.json({ data: 'this is operation 3' });
});

api.addResource(resource1);
api.listen(3000);

In real world applications, where resources and operation are in fact more complex, you will want to create class that extend the basic classes in arrest, like in the next example:

const arrest = require('arrest');

class MyOperation extends arrest.Operation {
  constructor(resource, path, method) {
    super('op1', resource, path, method);
  }
  handler(req, res, next) {
    res.json({ data: 'this is a custom operation' });
  }
}

class MyResource extends arrest.Resource {
  constructor() {
    super();
    this.addOperation(new MyOperation(this, '/', 'get'));
  }
}

class MyAPI extends arrest.API {
  constructor() {
    super({
      info: {
        title: 'This is a custom API',
        version: '0.9.5'
      }
    });
    this.addResource(new MyResource());
  }
}

const api = new MyAPI();
api.listen(3000);

The API above supports GETs on http://localhost/my-resources (note how the path was in this case constructed automatically from the name of the class MyResource).

By the default, arrest APIs add a special route /swagger.json that returns the Swagger description of the API: the Swagger object is populated with the properties of the API object, Resources are converted into Swagger Tags and Operations are mapped to Swagger Operations.

Data validation

arrest supports JSON-Schema for data validation. Validation rules are set using the Swagger specification. For instance, the following code show how to validate the body of a POST and the query paramters of a GET:

class MyOperation1 extends arrest.Operation {
  constructor(resource, path, method) {
    super('op1', resource, path, method);
    this.setInfo({
      parameters: [
        {
          name: 'body',
          in: 'body',
          required: true,
          schema: {
            type: 'object',
            required: [ 'name' ],
            additionalProperties: false,
            properties: {
              name: {
                type: 'string'
              },
              surname: {
                type: 'string'
              }
            }
          }
        }
      ]
    });
  }
  handler(req, res, next) {
    res.json({ data: 'this is a op1' });
  }
}

class MyOperation2 extends arrest.Operation {
  constructor(resource, path, method) {
    super('op2', resource, path, method);
    this.setInfo({
      parameters: [
        {
          name: 'lang',
          in: 'query',
          type: 'string',
          required: true
        },
        {
          name: 'count',
          in: 'query',
          type: 'integer'
        }
      ]
    });
  }
  handler(req, res, next) {
    res.json({ data: 'this is a op2' });
  }
}

class MyResource extends arrest.Resource {
  constructor() {
    super();
    this.addOperation(new MyOperation1(this, '/', 'post'));
    this.addOperation(new MyOperation2(this, '/', 'get'));
  }
}

Omitting the body or passing an invalid body (e.g. an object without the name property) when POSTing to http://localhost/my-resources will return an error. Likewise GETting without a lang parameter or with a count set to anything other than a number will fail.

Scopes and security validators

TBA

Creating an API with a MongoDB data store

TBA (default api routes)

Using arrest with express

TBA

Debugging

TBA

API documentation

TBA

Rate & Review

Great Documentation0
Easy to Use0
Performant0
Highly Customizable0
Bleeding Edge0
Responsive Maintainers0
Poor Documentation0
Hard to Use0
Slow0
Buggy0
Abandoned0
Unwelcoming Community0
100
No reviews found
Be the first to rate

Alternatives

No alternatives found

Tutorials

No tutorials found
Add a tutorial