Express

GitHub stars
@feathersjs/express"">npm version
Changelog

  1. $ npm install @feathersjs/express --save

The @feathersjs/express module contains Express framework integrations for Feathers:

  1. const express = require('@feathersjs/express');

Very Important: This page describes how to set up an Express server and REST API. See the REST client chapter how to use this server on the client.

Important: This chapter assumes that you are familiar with Express.

express(app)

express(app) -> app is a function that turns a Feathers application into a fully Express (4+) compatible application that additionally to Feathers functionality also lets you use the Express API.

  1. const feathers = require('@feathersjs/feathers');
  2. const express = require('@feathersjs/express');
  3. // Create an app that is a Feathers AND Express application
  4. const app = express(feathers());

Note that @feathersjs/express (express) also exposes the standard Express middleware:

  • express.json - A JSON body parser
  • express.urlencoded - A URL encoded form body parser
  • express.static - To statically host files in a folder
  • express.Router - Creates an Express router object

express()

If no Feathers application is passed, express() -> app returns a plain Express application just like a normal call to Express would.

app.use(path, service|mw|[mw])

app.use(path, service|mw|[mw]) -> app registers either a service object, an Express middleware or an array of Express middleware on the given path. If a service object is passed it will use Feathers registration mechanism, for a middleware function Express.

  1. // Register a service
  2. app.use('/todos', {
  3. get(id) {
  4. return Promise.resolve({ id });
  5. }
  6. });
  7. // Register an Express middleware
  8. app.use('/test', (req, res) => {
  9. res.json({
  10. message: 'Hello world from Express middleware'
  11. });
  12. });
  13. // Register multiple Express middleware functions
  14. app.use('/test', (req, res, next) => {
  15. res.data = 'Step 1 worked';
  16. next();
  17. }, (req, res) => {
  18. res.json({
  19. message: 'Hello world from Express middleware ' + res.data
  20. });
  21. });

app.listen(port)

app.listen(port) -> HttpServer will first call Express app.listen and then internally also call the Feathers app.setup(server).

  1. // Listen on port 3030
  2. const server = app.listen(3030);
  3. server.on('listening', () => console.log('Feathers application started'));

app.setup(server)

app.setup(server) -> app is usually called internally by app.listen but in the cases described below needs to be called explicitly.

Sub-Apps

When registering an application as a sub-app, app.setup(server) has to be called to initialize the sub-apps services.

  1. const feathers = require('@feathersjs/feathers');
  2. const express = require('@feathersjs/express');
  3. const api = express(feathers())
  4. .configure(express.rest())
  5. .use('/service', myService);
  6. const mainApp = express().use('/api/v1', api);
  7. const server = mainApp.listen(3030);
  8. // Now call setup on the Feathers app with the server
  9. api.setup(server);

ProTip: We recommend avoiding complex sub-app setups because websockets and Feathers built in authentication are not fully sub-app aware at the moment.

HTTPS

HTTPS requires creating a separate server in which case app.setup(server) also has to be called explicitly.

  1. const fs = require('fs');
  2. const https = require('https');
  3. const feathers = require('@feathersjs/feathers');
  4. const express = require('@feathersjs/express');
  5. const app = express(feathers());
  6. const server = https.createServer({
  7. key: fs.readFileSync('privatekey.pem'),
  8. cert: fs.readFileSync('certificate.pem')
  9. }, app).listen(443);
  10. // Call app.setup to initialize all services and SocketIO
  11. app.setup(server);

Virtual Hosts

The vhost Express middleware can be used to run a Feathers application on a virtual host but again requires app.setup(server) to be called explicitly.

  1. const vhost = require('vhost');
  2. const feathers = require('@feathersjs/feathers');
  3. const express = require('@feathersjs/express');
  4. const app = express(feathers());
  5. app.use('/todos', todoService);
  6. const host = express().use(vhost('foo.com', app));
  7. const server = host.listen(8080);
  8. // Here we need to call app.setup because .listen on our virtal hosted
  9. // app is never called
  10. app.setup(server);

express.rest()

express.rest registers a Feathers transport mechanism that 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

To expose services through a RESTful API we will have to configure express.rest 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.

ProTip: The body-parser middleware has to be registered before any service. Otherwise the service method will throw a No data provided or First parameter for 'create' must be an object error.

app.configure(express.rest())

Configures the transport provider with a standard formatter sending JSON response via res.json.

  1. const feathers = require('@feathersjs/feathers');
  2. const express = require('@feathersjs/express');
  3. // Create an Express compatible Feathers application
  4. const app = express(feathers());
  5. // Turn on JSON parser for REST services
  6. app.use(express.json())
  7. // Turn on URL-encoded parser for REST services
  8. app.use(express.urlencoded({ extended: true }));
  9. // Set up REST transport
  10. app.configure(express.rest())

app.configure(express.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.

  1. const feathers = require('@feathersjs/feathers');
  2. const express = require('@feathersjs/express');
  3. const app = express(feathers());
  4. // Turn on JSON parser for REST services
  5. app.use(express.json())
  6. // Turn on URL-encoded parser for REST services
  7. app.use(express.urlencoded({ extended: true }));
  8. // Set up REST transport
  9. app.configure(express.rest(function(req, res) {
  10. // Format the message as text/plain
  11. res.format({
  12. 'text/plain': function() {
  13. res.end(`The Message is: "${res.data.text}"`);
  14. }
  15. });
  16. }))

Custom service middleware

Custom Express middleware that only should run before or after a specific service can be passed to app.use in the order it should run:

  1. const todoService = {
  2. get(id) {
  3. return Promise.resolve({
  4. id,
  5. description: `You have to do ${id}!`
  6. });
  7. }
  8. };
  9. app.use('/todos', ensureAuthenticated, logRequest, todoService, updateData);

Middleware that runs after the service has the service call information available as

  • res.data - The data that will be sent
  • res.hook - The hook context of the service method call

For example updateData could look like this:

  1. function updateData(req, res, next) {
  2. res.data.updateData = true;
  3. next();
  4. }

ProTip: If you run res.send in a custom middleware after the service and don’t call next, other middleware (like the REST formatter) will be skipped. This can be used to e.g. render different views for certain service method calls.

params

All middleware registered after the REST transport will have access to the req.feathers object to set properties on the service method params:

  1. const feathers = require('@feathersjs/feathers');
  2. const express = require('@feathersjs/express');
  3. const bodyParser = require('body-parser');
  4. const app = express(feathers());
  5. app.configure(express.rest())
  6. .use(bodyParser.json())
  7. .use(bodyParser.urlencoded({extended: true}))
  8. .use(function(req, res, next) {
  9. req.feathers.fromMiddleware = 'Hello world';
  10. next();
  11. });
  12. app.use('/todos', {
  13. get(id, params) {
  14. console.log(params.provider); // -> 'rest'
  15. console.log(params.fromMiddleware); // -> 'Hello world'
  16. return Promise.resolve({
  17. id, params,
  18. description: `You have to do ${id}!`
  19. });
  20. }
  21. });
  22. app.listen(3030);

You can see the parameters set by running the example and visiting http://localhost:3030/todos/test.

Avoid setting req.feathers = something directly since it may already contain information that other Feathers plugins rely on. Adding individual properties or using Object.assign(req.feathers, something) is the more reliable option.

Very important: Since the order of Express middleware matters, any middleware that sets service parameters has to be registered before your services (in a generated application before app.configure(services) or in middleware/index.js).

ProTip: Although it may be convenient to set req.feathers.req = req; to have access to the request object in the service, we recommend keeping your services as provider independent as possible. There usually is a way to pre-process your data in a middleware so that the service does not need to know about the HTTP request or response.

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 of params are not. This is for security reasons so that a client can’t set things like params.user or the database options. You can always map from params.query to other params properties in a before hook.

For example:

  1. GET /messages?read=true&$sort[createdAt]=-1

Will set params.query to

  1. {
  2. "read": "true",
  3. "$sort": { "createdAt": "-1" }
  4. }

ProTip: Since the URL is just a string, there will be no type conversion. This can be done manually in a hook.

Note: 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}))

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:

  1. app.service('users').hooks({
  2. before: {
  3. remove(context) {
  4. // check for if(context.params.provider) to prevent any external call
  5. if(context.params.provider === 'rest') {
  6. throw new Error('You can not delete a user via REST');
  7. }
  8. }
  9. }
  10. });

params.route

See the routing section.

express.notFound(options)

express.notFound() returns middleware that returns a NotFound (404) Feathers error. It should be used as the last middleware before the error handler. The following options are available:

  • verbose: Set to true if the URL should be included in the error message (default: false)
  1. // Return errors that include the URL
  2. app.use(express.notFound({ verbose: true });
  3. app.use(errorHandler());

express.errorHandler()

expres.errorHandler is an Express error handler middleware that formats any error response to a REST call as JSON (or HTML if e.g. someone hits our API directly in the browser) and sets the appropriate error code.

ProTip: You can still use any other Express compatible error middleware with Feathers. In fact, the express.errors is just a slightly customized one.
Very Important: Just as in Express, the error handler has to be registered after all middleware and services.

app.use(express.errorHandler())

Set up the error handler with the default configuration.

  1. const feathers = require('@feathersjs/feathers');
  2. const express = require('@feathersjs/express');
  3. const app = express(feathers());
  4. // before starting the app
  5. app.use(express.errorHandler())

app.use(express.errorHandler(options))

  1. const feathers = require('@feathersjs/feathers');
  2. const express = require('@feathersjs/express');
  3. const app = express(feathers());
  4. // Just like Express your error middleware needs to be
  5. // set up last in your middleware chain.
  6. app.use(express.errorHandler({
  7. html: function(error, req, res, next) {
  8. // render your error view with the error object
  9. res.render('error', error);
  10. }
  11. }));
  12. app.use(errorHandler({
  13. html: {
  14. 404: 'path/to/notFound.html',
  15. 500: 'there/will/be/robots.html'
  16. }
  17. }));

ProTip: If you want to have the response in json format be sure to set the Accept header in your request to application/json otherwise the default error handler will return HTML.

The following options can be passed when creating a new localstorage service:

  • html (Function|Object) [optional] - A custom formatter function or an object that contains the path to your custom html error pages.
  • logger (Function|false) (default: console) - Set a logger object to log the error (it will be logger with logger.error(error)

ProTip: html can also be set to false to disable html error pages altogether so that only JSON is returned.

Routing

Express route placeholders in a service URL will be added to the services params.route.

Important: See the FAQ entry on nested routes for more details on when and when not to use nested routes.

  1. const feathers = require('@feathersjs/feathers');
  2. const express = require('@feathersjs/express');
  3. const app = express(feathers());
  4. app.configure(express.rest())
  5. .use(function(req, res, next) {
  6. req.feathers.fromMiddleware = 'Hello world';
  7. next();
  8. });
  9. app.use('/users/:userId/messages', {
  10. get(id, params) {
  11. console.log(params.query); // -> ?query
  12. console.log(params.provider); // -> 'rest'
  13. console.log(params.fromMiddleware); // -> 'Hello world'
  14. console.log(params.route.userId); // will be `1` for GET /users/1/messages
  15. return Promise.resolve({
  16. id,
  17. params,
  18. read: false,
  19. text: `Feathers is great!`,
  20. createdAt: new Date().getTime()
  21. });
  22. }
  23. });
  24. app.listen(3030);