Skip to content

Migrating to v5

This guide explains the new features and changes necessary to migrate to the Feathers v5 (Dove) release. It expects applications to be using the previous Feathers v4 (Crow). See the v4 (Crow) migration guide for upgrading to the previous version.

Status

The final v5 release is expected on October 31, 2022. Aside from the breaking changes and new features documented here. For more information

Testing the prerelease

You can run the following to test the latest Dove pre-release in your application:

npx npm-check-updates --upgrade --target newest --filter /@feathersjs/
npm install
npx npm-check-updates --upgrade --target newest --filter /@feathersjs/
npm install

You can see the migration steps necessary for the Feathers chat here for Javascript and here for TypeScript.

New Features

There are so many new features in this release that they got their own page! Read about the new features on the What's New in v5 page.

Core SQL and MongoDB

The new schemas and resolvers cover most use cases previously provided by higher level ORMs like Sequelize or Mongoose in a more flexible and Feathers friendly way. This allows for a better database integration into Feathers without the overhead of a full ORM which is why the more low level MongoDB and Knex (SQL) database adapters have been moved into Feathers core for first-class SQL and MongoDB database support.

TypeScript

The new version comes with major improvements in TypeScript support from improved service typings, fully typed hook context and typed configuration. You can see the changes necessary in the Feathers chat here.

Application and hook context

To get the typed hook context and application configuration update your declarations.ts as follows:

import '@feathersjs/transport-commons'
import { Application as ExpressFeathers } from '@feathersjs/express'
import { HookContext as FeathersHookContext } from '@feathersjs/feathers'

export interface Configuration {
  // Put types for app.get and app.set here
  port: number
}
// A mapping of service names to types. Will be extended in service files.
export interface ServiceTypes {}
// The application instance type that will be used everywhere else
export type Application = ExpressFeathers<ServiceTypes, Configuration>
export type HookContext = FeathersHookContext<Application>
import '@feathersjs/transport-commons'
import { Application as ExpressFeathers } from '@feathersjs/express'
import { HookContext as FeathersHookContext } from '@feathersjs/feathers'

export interface Configuration {
  // Put types for app.get and app.set here
  port: number
}
// A mapping of service names to types. Will be extended in service files.
export interface ServiceTypes {}
// The application instance type that will be used everywhere else
export type Application = ExpressFeathers<ServiceTypes, Configuration>
export type HookContext = FeathersHookContext<Application>

Now import { HookContext } from './declarations' can be used as the context in hooks.

Service types

Service types now only need the actual service class type and should no longer include the & ServiceAddons<any>. E.g. for the messages service like this:

// Add this service to the service type index
declare module '../../declarations' {
  interface ServiceTypes {
    messages: Messages
  }
}
// Add this service to the service type index
declare module '../../declarations' {
  interface ServiceTypes {
    messages: Messages
  }
}

Configuration types

A Feathers application can now also include types for the values of app.set and app.get. The configuration can also be validated and the type inferred from a Feathers schema.

Typed params and query

Service Params no longer include a catchall property type and need to be explicitly declared for services that use extended params. It is also possible to pass your own query type to use with params.query:

import { Params } from '@feathersjs/feathers'

export type MyQuery = {
  name: string
}

export interface MyServiceParams extends Params<MyQuery> {
  user: User
}
import { Params } from '@feathersjs/feathers'

export type MyQuery = {
  name: string
}

export interface MyServiceParams extends Params<MyQuery> {
  user: User
}

You can revert to the previous behaviour by overriding he Params declaration:

declare module '@feathersjs/feathers/lib/declarations' {
  interface Params {
    [key: string]: any
  }
}
declare module '@feathersjs/feathers/lib/declarations' {
  interface Params {
    [key: string]: any
  }
}

Deprecations and breaking changes

Core named export

The import of feathers has changed from

const feathers = require('@feathersjs/feathers')

import feathers from '@feathersjs/feathers'
const feathers = require('@feathersjs/feathers')

import feathers from '@feathersjs/feathers'

To

const { feathers } = require('@feathersjs/feathers')

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

import { feathers } from '@feathersjs/feathers'

Service Options

For v5 service adapters, when passing service options, the whitelist and allow options are no longer supported. If you're using whitelist or allow, rename them to operators.

// `whitelist` is no longer supported. Rename it to `operators`
{
  whitelist: ['$customQueryOperator']
}

// `allow` is no longer supported. Rename it to `operators`
{
  allow: ['$customQueryOperator']
}

// Use `operators` for v5 service adapters
{
  operators: ['$customQueryOperator']
}
// `whitelist` is no longer supported. Rename it to `operators`
{
  whitelist: ['$customQueryOperator']
}

// `allow` is no longer supported. Rename it to `operators`
{
  allow: ['$customQueryOperator']
}

// Use `operators` for v5 service adapters
{
  operators: ['$customQueryOperator']
}

tip

This change only affects service adapters that have been upgraded to v5, like @feathersjs/mongodb, @feathersjs/knex, and @feathersjs/memory. This also applies to any community-supported adapters which have been upgraded to v5. If you use a v4 adapter for a service in your v5 app, you do not need to make this change for that service.

Asynchronous setup

service.setup, app.setup and app.listen return a Promise:

// Before
const server = app.listen(3030)

// Now
app.listen(3030).then((server) => {})
// Before
const server = app.listen(3030)

// Now
app.listen(3030).then((server) => {})

Usually you would call app.listen. In case you are calling app.setup instead (e.g. for internal jobs or seed scripts) it is now also asynchronous:

// Before
app.setup()
// Do something here

// Now
await app.setup()
// Do something here
// Before
app.setup()
// Do something here

// Now
await app.setup()
// Do something here

Socket.io 4 and Grant 5

The Socket.io and Grant (oAuth) dependencies have been updated to their latest versions. For more information on breaking changes see:

Configuration

The automatic environment variable substitution in @feathersjs/configuration was causing subtle and hard to debug issues. It has been removed to instead rely on the functionality already provided and battle tested by the underlying node-config. To update your configuration:

  • Relative paths are no longer relative to the configuration file, but instead to where the application runs. This normally (when running from the application folder) means that paths starting with ../ and ./ have to be replaced with ./ and ./config/.
  • Configuration through environment variables should be included via the NODE_CONFIG JSON string or as Custom Environment Variable support. To use existing environment variables add the following configuration file in config/custom-environment-variables.<json|yaml|js> like this:
// config/custom-environment-variables.json
{
  "hostname": "HOSTNAME",
  "port": "PORT",
  "someSetting": {
    "apiKey": "MY_CUSTOM_API_KEY"
  }
}
// config/custom-environment-variables.json
{
  "hostname": "HOSTNAME",
  "port": "PORT",
  "someSetting": {
    "apiKey": "MY_CUSTOM_API_KEY"
  }
}

Debugging

The debug module has been removed as a direct dependency. This reduces the the client bundle size and allows to support other platforms (like Deno). The original debug functionality can now be initialized as follows:

const feathers = require('@feathersjs/feathers')
const debug = require('debug')

feathers.setDebug(debug)
const feathers = require('@feathersjs/feathers')
const debug = require('debug')

feathers.setDebug(debug)

It is also possible to set a custom logger like this:

const feathers = require('@feathersjs/feathers')

const customDebug =
  (name) =>
  (...args) => {
    console.log(name, ...args)
  }

feathers.setDebug(customDebug)
const feathers = require('@feathersjs/feathers')

const customDebug =
  (name) =>
  (...args) => {
    console.log(name, ...args)
  }

feathers.setDebug(customDebug)

Setting the debugger will apply to all @feathersjs modules.

Client

  • The request library has been deprecated and request support has been removed from the REST client.
  • Since all modern browsers now support built-in fetch, the Angular and jQuery REST clients have been removed as well.
  • The @feathersjs/client package now only comes with a full (dist/feathers.js) and core (dist/core.js) browser build. Using Feathers with a module loader is recommended for all other use cases.

Removed Primus Transport

Due to low usage @feathersjs/primus and @feathers/primus-client have been removed from Feathers core.

Changes to WebSockets

Legacy Socket Format

The legacy servicename::method socket message format has been deprecated since Feathers 3 and has now been removed. Use a v3 or later Feathers client or the current Socket.io direct connection API.

Timeouts

The timeout setting for socket services has been removed. It was mainly intended as a fallback for the old message format and interfered with the underlying timeout and retry mechanism provided by the websocket libraries themselves.

NotFound for app.service

By default, when getting a non existing service via app.service('something') on the server, it will now throw a NotFound error instead of returning undefined. The previous behaviour can be restored by setting app.defaultService:

app.defaultService = () => {
  return null // undefined
}
app.defaultService = () => {
  return null // undefined
}

Removed service.mixin()

Services are no longer Uberproto (an ES5 inheritance utility) objects and instead rely on modern JavaScript classes and extension. This means app.service(name).mixin(data) is no longer available which can be replaced with a basic Object.assign(app.service(name), data):

// Before
app.mixins.push((service, path) => {
  service.mixin({
    create(data, params) {
      // do something here
      return this._super(data, params)
    }
  })
})

// Now
app.mixins.push((service, path) => {
  const { create } = service

  Object.assign(service, {
    create(data, params) {
      // do something here, then invoke the old method
      // through normal JavaScript functionality
      return create.call(this, data, params)
    }
  })
})
// Before
app.mixins.push((service, path) => {
  service.mixin({
    create(data, params) {
      // do something here
      return this._super(data, params)
    }
  })
})

// Now
app.mixins.push((service, path) => {
  const { create } = service

  Object.assign(service, {
    create(data, params) {
      // do something here, then invoke the old method
      // through normal JavaScript functionality
      return create.call(this, data, params)
    }
  })
})

finally hook

The undocumented finally hook type is no longer available and should be replaced by the new around hooks which offer the same functionality using plain JavaScript:

app.service('myservice').hooks([
  async (context, next) => {
    try {
      await next()
    } finally {
      // Do finally hook stuff here
    }
  }
])
app.service('myservice').hooks([
  async (context, next) => {
    try {
      await next()
    } finally {
      // Do finally hook stuff here
    }
  }
])

Other internal changes

  • The undocumented service._setup method introduced in v1 will no longer be called. It was used to circumvent middleware inconsistencies from Express 3 and is no longer necessary.
  • The undocumented app.providers has been removed since it provided the same functionality as app.mixins
  • app.disable, app.disabled, app.enable and app.enabled have been removed from basic Feathers applications. It will still be available in an Express-compatible Feathers application. app.get() and app.set() should be used instead.
  • The Express rest adapter now needs to be configured in the correct order, usually right after the json() middleware. This is already the case in generated applications but it may have to be adjusted in a custom setup.

Released under the MIT License.