Skip to content

Express

npm versionChangelog

npm install @feathersjs/express --save
npm install @feathersjs/express --save

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

import { feathers } from '@feathersjs/feathers'
import express from '@feathersjs/express'

const app = express(feathers())
import { feathers } from '@feathersjs/feathers'
import express from '@feathersjs/express'

const app = express(feathers())

Important

This chapter assumes that you are familiar with Express and describes how to set up an Express server and REST API. See the REST client chapter how to use this server on the client.

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.

import { feathers } from '@feathersjs/feathers'
import express from '@feathersjs/express'

const app = express(feathers())
import { feathers } from '@feathersjs/feathers'
import express from '@feathersjs/express'

const app = express(feathers())

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

  • json - A JSON body parser
  • urlencoded - A URL encoded form body parser
  • serveStatic - To statically host files in a folder
  • Router - Creates an Express router object
import { json, urlencoded, serveStatic, Router } from '@feathersjs/express'
import { json, urlencoded, serveStatic, Router } from '@feathersjs/express'

express(app, expressApp)

express(app, expressApp) -> app allows to extend an existing Express application with the Feathers application app.

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.

// Register a service
app.use('todos', {
  async get(id) {
    return { id }
  }
})

// Register an Express middleware
app.use('/test', (req, res) => {
  res.json({
    message: 'Hello world from Express middleware'
  })
})

// Register multiple Express middleware functions
app.use(
  '/test',
  (req, res, next) => {
    res.data = 'Step 1 worked'
    next()
  },
  (req, res) => {
    res.json({
      message: `Hello world from Express middleware ${res.data}`
    })
  }
)
// Register a service
app.use('todos', {
  async get(id) {
    return { id }
  }
})

// Register an Express middleware
app.use('/test', (req, res) => {
  res.json({
    message: 'Hello world from Express middleware'
  })
})

// Register multiple Express middleware functions
app.use(
  '/test',
  (req, res, next) => {
    res.data = 'Step 1 worked'
    next()
  },
  (req, res) => {
    res.json({
      message: `Hello world from Express middleware ${res.data}`
    })
  }
)

app.listen(port)

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

// Listen on port 3030
const server = await app.listen(3030)
// Listen on port 3030
const server = await app.listen(3030)

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.

import { feathers } from '@feathersjs/feathers'
import express from '@feathersjs/express'

const api = feathers()

api.use('service', myService)

const mainApp = express(feathers()).use('/api/v1', api)

const server = await mainApp.listen(3030)

// Now call setup on the Feathers app with the server
await api.setup(server)
import { feathers } from '@feathersjs/feathers'
import express from '@feathersjs/express'

const api = feathers()

api.use('service', myService)

const mainApp = express(feathers()).use('/api/v1', api)

const server = await mainApp.listen(3030)

// Now call setup on the Feathers app with the server
await api.setup(server)

HTTPS

HTTPS requires creating a separate server in which case app.setup(server) also has to be called explicitly. In a generated application src/index.js should look like this:

import https from 'https'
import { app } from './app'

const port = app.get('port')
const server = https
  .createServer(
    {
      key: fs.readFileSync('privatekey.pem'),
      cert: fs.readFileSync('certificate.pem')
    },
    app
  )
  .listen(443)

// Call app.setup to initialize all services and SocketIO
app.setup(server)

server.on('listening', () => logger.info('Feathers application started'))
import https from 'https'
import { app } from './app'

const port = app.get('port')
const server = https
  .createServer(
    {
      key: fs.readFileSync('privatekey.pem'),
      cert: fs.readFileSync('certificate.pem')
    },
    app
  )
  .listen(443)

// Call app.setup to initialize all services and SocketIO
app.setup(server)

server.on('listening', () => logger.info('Feathers application started'))

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.

import vhost from 'vhost'
import { feathers } from '@feathersjs/feathers'
import express from '@feathersjs/express'

const app = express(feathers())

app.use('/todos', todoService)

const host = express().use(vhost('foo.com', app))
const server = await host.listen(8080)

// Here we need to call app.setup because .listen on our virtual hosted
// app is never called
app.setup(server)
import vhost from 'vhost'
import { feathers } from '@feathersjs/feathers'
import express from '@feathersjs/express'

const app = express(feathers())

app.use('/todos', todoService)

const host = express().use(vhost('foo.com', app))
const server = await host.listen(8080)

// Here we need to call app.setup because .listen on our virtual hosted
// app is never called
app.setup(server)

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 methodHTTP methodPath
.find()GET/messages
.get()GET/messages/1
.create()POST/messages
.update()PUT/messages/1
.patch()PATCH/messages/1
.remove()DELETE/messages/1

app.configure(rest())

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

import { feathers } from '@feathersjs/feathers'
import express, { json, urlencoded, rest } from '@feathersjs/express'

// Create an Express compatible Feathers application
const app = express(feathers())

// Turn on JSON parser for REST services
app.use(json())
// Turn on URL-encoded parser for REST services
app.use(urlencoded({ extended: true }))
// Set up REST transport
app.configure(rest())
import { feathers } from '@feathersjs/feathers'
import express, { json, urlencoded, rest } from '@feathersjs/express'

// Create an Express compatible Feathers application
const app = express(feathers())

// Turn on JSON parser for REST services
app.use(json())
// Turn on URL-encoded parser for REST services
app.use(urlencoded({ extended: true }))
// Set up REST transport
app.configure(rest())

Important

The json and urlencoded body parser middleware has to be registered before any service. Otherwise the service method throw an error that data is undefined.

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.

import { feathers } from '@feathersjs/feathers'
import express, { json, urlencoded, rest } from '@feathersjs/express'

const app = express(feathers())

// Turn on JSON parser for REST services
app.use(json())
// Turn on URL-encoded parser for REST services
app.use(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}"`)
      }
    })
  })
)
import { feathers } from '@feathersjs/feathers'
import express, { json, urlencoded, rest } from '@feathersjs/express'

const app = express(feathers())

// Turn on JSON parser for REST services
app.use(json())
// Turn on URL-encoded parser for REST services
app.use(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}"`)
      }
    })
  })
)

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:

const todoService = {
  async get(id: Id) {
    return {
      id,
      description: `You have to do ${id}!`
    }
  }
}

app.use('todos', logRequest, todoService, updateData)
const todoService = {
  async get(id: Id) {
    return {
      id,
      description: `You have to do ${id}!`
    }
  }
}

app.use('todos', logRequest, todoService, updateData)

danger

Custom middleware will only run for REST requests and not when used with other transports (like Socket.io). If possible try to avoid custom middleware and use hooks or customized services instead which will work for all transports.

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:

function updateData(req, res, next) {
  res.data.updateData = true
  next()
}
function updateData(req, res, next) {
  res.data.updateData = true
  next()
}

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, for example to export a file as CSV:

import json2csv from 'json2csv'

const fields = ['done', 'description']

app.use('/todos', todoService, function (req, res) {
  const result = res.data
  const data = result.data // will be either `result` as an array or `data` if it is paginated
  const csv = json2csv({ data, fields })

  res.type('csv')
  res.end(csv)
})
import json2csv from 'json2csv'

const fields = ['done', 'description']

app.use('/todos', todoService, function (req, res) {
  const result = res.data
  const data = result.data // will be either `result` as an array or `data` if it is paginated
  const csv = json2csv({ data, fields })

  res.type('csv')
  res.end(csv)
})

params

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

import { feathers, type Id, type Params } from '@feathersjs/feathers'
import express, { json, urlencoded, rest } from '@feathersjs/express'

const app = express(feathers())

app
  .configure(rest())
  .use(json())
  .use(urlencoded({ extended: true }))
  .use(function (req, res, next) {
    req.feathers.fromMiddleware = 'Hello world'
    next()
  })

app.use('todos', {
  async get(id: Id, params: Params) {
    console.log(params.provider) // -> 'rest'
    console.log(params.fromMiddleware) // -> 'Hello world'

    return {
      id,
      params,
      description: `You have to do ${id}!`
    }
  }
})

app.listen(3030)
import { feathers, type Id, type Params } from '@feathersjs/feathers'
import express, { json, urlencoded, rest } from '@feathersjs/express'

const app = express(feathers())

app
  .configure(rest())
  .use(json())
  .use(urlencoded({ extended: true }))
  .use(function (req, res, next) {
    req.feathers.fromMiddleware = 'Hello world'
    next()
  })

app.use('todos', {
  async get(id: Id, params: Params) {
    console.log(params.provider) // -> 'rest'
    console.log(params.fromMiddleware) // -> 'Hello world'

    return {
      id,
      params,
      description: `You have to do ${id}!`
    }
  }
})

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 { ...req.feathers, something } is the more reliable option.

warning

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).

tip

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.

warning

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 hook.

For example:

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

Will set params.query to

{
  "read": "true",
  "$sort": { "createdAt": "-1" }
}
{
  "read": "true",
  "$sort": { "createdAt": "-1" }
}

tip

Note that the URL is a string so type conversion may be necessary. This is usually done with query schemas and resolvers.

danger

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:

app.service('users').hooks({
  before: {
    remove(context) {
      // check for if(context.params.provider) to prevent any external call
      if (context.params.provider === 'rest') {
        throw new Error('You can not delete a user via REST')
      }
    }
  }
})
app.service('users').hooks({
  before: {
    remove(context) {
      // check for if(context.params.provider) to prevent any external call
      if (context.params.provider === 'rest') {
        throw new Error('You can not delete a user via REST')
      }
    }
  }
})

params.route

See the routing section.

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)
import { notFound, errorHandler } from '@feathersjs/express'

// Return errors that include the URL
app.use(notFound({ verbose: true }))
app.use(errorHandler())
import { notFound, errorHandler } from '@feathersjs/express'

// Return errors that include the URL
app.use(notFound({ verbose: true }))
app.use(errorHandler())

errorHandler()

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.

tip

You can still use any other Express compatible error middleware with Feathers.

Important

Just as in Express, the error handler has to be registered after all middleware and services.

app.use(errorHandler())

Set up the error handler with the default configuration.

import { feathers } from '@feathersjs/feathers'
import express from '@feathersjs/express'

const app = express(feathers())

// before starting the app
app.use(express.errorHandler())
import { feathers } from '@feathersjs/feathers'
import express from '@feathersjs/express'

const app = express(feathers())

// before starting the app
app.use(express.errorHandler())

app.use(errorHandler(options))

import { feathers } from '@feathersjs/feathers'
import express from '@feathersjs/express'

const app = express(feathers())

// Just like Express your error middleware needs to be
// set up last in your middleware chain.
app.use(express.errorHandler({
    html: function(error, req, res, next) {
      // render your error view with the error object
      res.render('error', error)
    }
}));

app.use(errorHandler({
    html: {
      404: 'path/to/notFound.html',
      500: 'there/will/be/robots.html'
    }
}))
import { feathers } from '@feathersjs/feathers'
import express from '@feathersjs/express'

const app = express(feathers())

// Just like Express your error middleware needs to be
// set up last in your middleware chain.
app.use(express.errorHandler({
    html: function(error, req, res, next) {
      // render your error view with the error object
      res.render('error', error)
    }
}));

app.use(errorHandler({
    html: {
      404: 'path/to/notFound.html',
      500: 'there/will/be/robots.html'
    }
}))

important

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. Can also be set to false to disable html error pages altogether so that only JSON is returned.
  • logger (Function|false) (default: console) - Set a logger object to log the error (it will be logger with logger.error(error)

authenticate()

express.authenticate(...strategies) allows to protect an Express middleware with an authentication service that has strategies registered that can parse HTTP headers. It will set the authentication information on the req object (e.g. req.user). The following example protects the /hello endpoint with the JWT strategy (so the Authorization: Bearer <JWT> header needs to be set) and uses the user email to render the message:

import { authenticate } from '@feathersjs/express'

app.use('/hello', authenticate('jwt'), (req, res) => {
  const { user } = req

  res.render(`Hello ${user.email}`)
})

// When using with the non-default authentication service
app.use(
  '/hello',
  authenticate({
    service: 'v2/auth',
    strategies: ['jwt', 'api-key']
  }),
  (req, res) => {
    const { user } = req

    res.render(`Hello ${user.email}`)
  }
)
import { authenticate } from '@feathersjs/express'

app.use('/hello', authenticate('jwt'), (req, res) => {
  const { user } = req

  res.render(`Hello ${user.email}`)
})

// When using with the non-default authentication service
app.use(
  '/hello',
  authenticate({
    service: 'v2/auth',
    strategies: ['jwt', 'api-key']
  }),
  (req, res) => {
    const { user } = req

    res.render(`Hello ${user.email}`)
  }
)

When clicking a normal link, web browsers do not send the appropriate header. In order to initate an authenticated request to a middleware from a browser link, there are two options. One is using a session which is described in the Server Side rendering guide, another is to add the JWT access token to the query string and mapping it to an authentication request:

import { authenticate } from '@feathersjs/express'
const setQueryAuthentication = (req, res, next) => {
  const { access_token } = req.query

  if (access_token) {
    req.authentication = {
      strategy: 'jwt',
      accessToken: access_token
    }
  }

  next()
}

// Request this with `hello?access_token=<your jwt>`
app.use('/hello', setQueryAuthentication, authenticate('jwt'), (req, res) => {
  const { user } = req

  res.render(`Hello ${user.email}`)
})
import { authenticate } from '@feathersjs/express'
const setQueryAuthentication = (req, res, next) => {
  const { access_token } = req.query

  if (access_token) {
    req.authentication = {
      strategy: 'jwt',
      accessToken: access_token
    }
  }

  next()
}

// Request this with `hello?access_token=<your jwt>`
app.use('/hello', setQueryAuthentication, authenticate('jwt'), (req, res) => {
  const { user } = req

  res.render(`Hello ${user.email}`)
})

How to get the access token from the authentication client is described in the authentication client documentation.

warning

When using HTTPS URLs are safely encrypted but when using this method you have to make sure that access tokens are not logged through any of your logging mechanisms.

Routing

Express route placeholders in a service URL will be added to the services params.route. See the FAQ entry on nested routes for more details on when and when not to use nested routes.

import { feathers } from '@feathersjs/feathers'
import express, { rest } from '@feathersjs/express'

const app = express(feathers())

app.configure(rest()).use(function (req, res, next) {
  req.feathers.fromMiddleware = 'Hello world'
  next()
})

app.use('users/:userId/messages', {
  async get(id, params) {
    console.log(params.query) // -> ?query
    console.log(params.provider) // -> 'rest'
    console.log(params.fromMiddleware) // -> 'Hello world'
    console.log(params.route.userId) // will be `1` for GET /users/1/messages

    return {
      id,
      params,
      read: false,
      text: `Feathers is great!`,
      createdAt: new Date().getTime()
    }
  }
})

app.listen(3030)
import { feathers } from '@feathersjs/feathers'
import express, { rest } from '@feathersjs/express'

const app = express(feathers())

app.configure(rest()).use(function (req, res, next) {
  req.feathers.fromMiddleware = 'Hello world'
  next()
})

app.use('users/:userId/messages', {
  async get(id, params) {
    console.log(params.query) // -> ?query
    console.log(params.provider) // -> 'rest'
    console.log(params.fromMiddleware) // -> 'Hello world'
    console.log(params.route.userId) // will be `1` for GET /users/1/messages

    return {
      id,
      params,
      read: false,
      text: `Feathers is great!`,
      createdAt: new Date().getTime()
    }
  }
})

app.listen(3030)

Released under the MIT License.