REST
$ npm install feathers-rest --save
The feathers-rest module allows you to expose and consume services through a RESTful API. This means that you can call a service method through the GET
, POST
, PUT
, PATCH
and DELETE
HTTP methods:
Service method | HTTP method | Path |
---|---|---|
.find() | GET | /messages |
.get() | GET | /messages/1 |
.create() | POST | /messages |
.update() | PUT | /messages/1 |
.patch() | PATCH | /messages/1 |
.remove() | DELETE | /messages/1 |
Server
To expose services through a RESTful API we will have to configure the feathers-rest plugin and provide our own body parser middleware (usually the standard Express 4 body-parser) to make REST .create
, .update
and .patch
calls parse the data in the HTTP body. If you would like to add other middleware before the REST handler, call app.use(middleware)
before registering any services.
$ npm install body-parser --save
Important: For additional information about middleware, routing and how the REST module works with Express middleware see the Express chapter.
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.
app.configure(rest())
Configures the transport provider with a standard formatter sending JSON response via res.json.
const feathers = require('feathers');
const bodyParser = require('body-parser');
const rest = require('feathers-rest');
const app = feathers();
// Turn on JSON parser for REST services
app.use(bodyParser.json())
// Turn on URL-encoded parser for REST services
app.use(bodyParser.urlencoded({ extended: true }));
// Set up REST transport
app.configure(rest())
app.configure(rest(formatter))
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 pass a formatter(req, res)
function. This middleware will have access to res.data
which is the data returned by the service. res.format can be used for content negotiation.
const app = feathers();
const bodyParser = require('body-parser');
const rest = require('feathers-rest');
// Turn on JSON parser for REST services
app.use(bodyParser.json())
// Turn on URL-encoded parser for REST services
app.use(bodyParser.urlencoded({ extended: true }));
// Set up REST transport
app.configure(rest(function(req, res) {
// Format the message as text/plain
res.format({
'text/plain': function() {
res.end(`The Message is: "${res.data.text}"`);
}
});
}))
params.query
params.query
will contain the URL query parameters sent from the client. For the REST transport the query string is parsed using the qs module. For some query string examples see the database querying chapter.
Important: Only
params.query
is passed between the server and the client, other parts ofparams
are not. This is for security reasons so that a client can't set things likeparams.user
or the database options. You can always map fromparams.query
to otherparams
properties in a before hook.
params.provider
For any service method call made through REST params.provider
will be set to rest
. In a hook this can for example be used to prevent external users from making a service method call:
app.service('users').hooks({
before: {
remove(hook) {
// check for if(hook.params.provider) to prevent any external call
if(hook.params.provider === 'rest') {
throw new Error('You can not delete a user via REST');
}
}
}
});
Client
The client
module in feathers-rest
(require('feathers-rest/client')
) allows to connect to a service exposed through the REST server using jQuery, request, Superagent, Axios or Fetch as the AJAX library.
Very important: The examples below assume you are using Feathers either in Node or in the browser with a module loader like Webpack or Browserify. For using Feathers with a
<script>
tag, AMD modules or with React Native see the client chapter.
ProTip: REST client services do emit
created
,updated
,patched
andremoved
events but only locally for their own instance. Real-time events from other clients can only be received by using a websocket connection.
Note: A client application can only use a single transport (either REST, Socket.io or Primus). Using two transports in the same client application is normally not necessary.
rest([baseUrl])
REST client services can be initialized by loading feathers-rest/client
and initializing a client object with a base URL:
const feathers = require('feathers/client');
const rest = require('feathers-rest/client');
// Connect to REST endpoints
const restClient = rest();
// Connect to a different URL
const restClient = rest('http://feathers-api.com');
ProTip: The base URL is relative from where services are registered. That means that a service at
http://api.feathersjs.com/api/v1/messages
with a base URL ofhttp://api.feathersjs.com
would be available asapp.service('api/v1/messages')
. With a base URL ofhttp://api.feathersjs.com/api/v1
it would beapp.service('messages')
.
REST client wrappers are always initialized using a base URL:
app.configure(restClient.superagent(superagent [, options]));
Default headers can be set for all libaries (except request which has its own defaults mechanism) in the options like this:
app.configure(restClient.superagent(superagent, {
headers: { 'X-Requested-With': 'FeathersJS' }
}));
Then services that automatically connect to that remote URL can be retrieved as usual via app.service:
app.service('messages').create({
text: 'A message from a REST client'
});
Request specific headers can be through params.headers
in a service call:
app.service('messages').create({
text: 'A message from a REST client'
}, {
headers: { 'X-Requested-With': 'FeathersJS' }
});
The supported AJAX libraries can be initialized as follows.
jQuery
Pass the instance of jQuery ($
) to restClient.jquery
:
app.configure(restClient.jquery(window.jQuery));
Or with a module loader:
import $ from 'jquery';
app.configure(restClient.jquery($));
Request
The request object needs to be passed explicitly to feathers.request
. Using request.defaults - which creates a new request object - is a great way to set things like default headers or authentication information:
const request = require('request');
const client = request.defaults({
'auth': {
'user': 'username',
'pass': 'password',
'sendImmediately': false
}
});
app.configure(restClient.request(client));
Superagent
Superagent currently works with a default configuration:
const superagent = require('superagent');
app.configure(restClient.superagent(superagent));
Axios
Axios currently works with a default configuration:
const axios = require('axios');
app.configure(restClient.axios(axios));
Fetch
Fetch also uses a default configuration:
const fetch = require('node-fetch');
app.configure(restClient.fetch(fetch));
Direct connection
You can communicate with a Feathers server using any HTTP REST client. The following section describes what HTTP method, body and query parameters belong to which service method call.
All query parameters in a URL will be set as params.query
on the server. Other service parameters can be set through hooks and Express middleware. URL query parameter values will always be strings. Conversion (e.g. the string 'true'
to boolean true
) can be done in a hook as well.
The body type for POST
, PUT
and PATCH
requests is determined by the Express body-parser middleware which has to be registered before any service. You should also make sure you are setting your Accept
header to application/json
.
find
Retrieves a list of all matching resources from the service
GET /messages?status=read&user=10
Will call messages.find({ query: { status: 'read', user: '10' } })
on the server.
If you want to use any of the built-in find operands ($le, $lt, $ne, $eq, $in, etc.) the general format is as follows:
GET /messages?field[$operand]=value&field[$operand]=value2
For example, to find the records where field status is not equal to active you could do
GET /messages?status[$ne]=active
More information about the possible parameters for official database adapters can be found in the database querying section.
get
Retrieve a single resource from the service.
GET /messages/1
Will call messages.get(1, {})
on the server.
GET /messages/1?fetch=all
Will call messages.get(1, { query: { fetch: 'all' } })
on the server.
create
Create a new resource with data
which may also be an array.
POST /messages
{ "text": "I really have to iron" }
Will call messages.create({ "text": "I really have to iron" }, {})
on the server.
POST /messages
[
{ "text": "I really have to iron" },
{ "text": "Do laundry" }
]
update
Completely replace a single or multiple resources.
PUT /messages/2
{ "text": "I really have to do laundry" }
Will call messages.update(2, { "text": "I really have to do laundry" }, {})
on the server. When no id
is given by sending the request directly to the endpoint something like:
PUT /messages?complete=false
{ "complete": true }
Will call messages.update(null, { "complete": true }, { query: { complete: 'false' } })
on the server.
ProTip:
update
is normally expected to replace an entire resource which is why the database adapters only supportpatch
for multiple records.
patch
Merge the existing data of a single or multiple resources with the new data
.
PATCH /messages/2
{ "read": true }
Will call messages.patch(2, { "read": true }, {})
on the server. When no id
is given by sending the request directly to the endpoint something like:
PATCH /messages?complete=false
{ "complete": true }
Will call messages.patch(null, { complete: true }, { query: { complete: 'false' } })
on the server to change the status for all read messages.
This is supported out of the box by the Feathers database adapters
remove
Remove a single or multiple resources:
DELETE /messages/2?cascade=true
Will call messages.remove(2, { query: { cascade: 'true' } })
.
When no id
is given by sending the request directly to the endpoint something like:
DELETE /messages?read=true
Will call messages.remove(null, { query: { read: 'true' } })
to delete all read messages.