The REST API Provider
We have already seen in the services chapter that the feathers-rest module allows to expose services through a RESTful interface on the services path. This means that you can call a service method through the GET
, POST
, PUT
, PATCH
and DELETE
HTTP methods:
const messageService = {
// GET /messages
find(params [, callback]) {},
// GET /messages/<id>
get(id, params [, callback]) {},
// POST /messages
create(data, params [, callback]) {},
// PUT /messages[/<id>]
update(id, data, params [, callback]) {},
// PATCH /messages[/<id>]
patch(id, data, params [, callback]) {},
// DELETE /messages[/<id>]
remove(id, params [, callback]) {},
setup(app, path) {}
}
app.use('/messages', messageService);
A full overview of which HTTP method call belongs to which service method call and parameters can be found in the REST client use chapter. This chapter will talk about how to use and configure the provider module on the server.
Usage
Install the provider with:
$ npm install feathers-rest body-parser
We will have to provide our own body parser middleware (here the standard Express 4 body-parser) to make REST .create
, .update
and .patch
calls parse the data in the HTTP body.
ProTip: The body-parser middleware has to be registered before any service. Otherwise the service method will throw a
No data provided
orFirst parameter for 'create' must be an object
error.
If you would like to add other middleware before the REST handler, simply call app.use(middleware)
before registering any services. The following example creates a messages service that can save a new message and return all messages:
// app.js
'use strict';
const feathers = require('feathers');
const rest = require('feathers-rest');
const bodyParser = require('body-parser');
class MessageService {
constructor() {
this.messages = [];
}
find(params) {
return Promise.resolve(this.messages);
}
create(data, params) {
this.messages.push(data);
return Promise.resolve(data);
}
}
const app = feathers()
// Enable the REST provider
.configure(rest())
// Turn on JSON parser for REST services
.use(bodyParser.json())
// Turn on URL-encoded parser for REST services
.use(bodyParser.urlencoded({ extended: true }));
app.use('/messages', new MessageService());
// Log newly created messages on the server
app.service('messages').on('created', message =>
console.log('Created message', message)
);
app.listen(3030);
After starting the application with node app.js
, we can now use CURL to create a new message:
curl 'http://localhost:3030/messages/' -H 'Content-Type: application/json' --data-binary '{ "text": "Learning Feathers!" }'
And should see the created message logged on the console. When going to localhost:3030/messages/ we see the newly created message.
Query, route and middleware parameters
URL query parameters will be parsed and passed to the service as params.query
. For example:
GET /messages?read=true&$sort[createdAt]=-1
Will set params.query
to
{
"read": "true",
"$sort": { "createdAt": "-1" }
}
ProTip: Since the URL is just a string, there will be no type conversion. This can be done manually in a hook.
ProTip: For REST calls,
params.provider
will be set torest
so you know which provider the service call came in on.
ProTip: It is also possible to add information directly to the
params
object by registering an Express middleware that modifies thereq.feathers
property. It must be registered before your services are.
ProTip: Route params will automatically be added to the
params
object.
ProTip: To get extended query parsing set
app.set('query parser', 'extended')
which will use the qs instead of the built-in querystring module.
ProTip: If an array in your request consists of more than 20 items, the qs parser implicitly converts it to an object with indices as keys. To extend this limit, you can set a custom query parser:
app.set('query parser', str => qs.parse(str, {arrayLimit: 1000}))
const feathers = require('feathers');
const rest = require('feathers-rest');
const app = feathers();
app.configure(rest())
.use(function(req, res, next) {
req.feathers.fromMiddleware = 'Hello world';
next();
});
app.use('/users/:userId/messages', {
get(id, params) {
console.log(params.query); // -> ?query
console.log(params.provider); // -> 'rest'
console.log(params.fromMiddleware); // -> 'Hello world'
console.log(params.userId); // will be `1` for GET /users/1/messages
return Promise.resolve({
id,
params,
read: false,
text: `Feathers is great!`,
createdAt: new Date().getTime()
});
}
});
app.listen(3030);
You can see all the passed parameters by going to something like localhost:3030/users/213/messages/23?read=false&$sort[createdAt]=-1]
. More information on how services play with Express middleware, routing and versioning can be found in the middleware chapter.
Customizing The Response Format
The default REST response formatter is a middleware that formats the data retrieved by the service as JSON. If you would like to configure your own formatter
middleware just pass it to rest(formatter)
. This middleware will have access to res.data
which is the data returned by the service. res.format can be used for content negotiation. For example, a middleware that just renders plain text with the Message text:
const feathers = require('feathers');
const rest = require('feathers-rest');
const app = feathers();
function restFormatter(req, res) {
res.format({
'text/plain': function() {
res.end(`The Message is: "${res.data.text}"`);
}
});
}
app.configure(rest(restFormatter))
.use('/messages', {
get(id, params) {
return Promise.resolve({
text: `Feathers is great!`
});
}
});
app.listen(3030);
Now going to localhost:3030/messages/1 will print the plain text The message is: "Feathers is great!"
.