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.
You can run the following to upgrade all Feathers core packages:
npx npm-check-updates --upgrade --filter /@feathersjs/
npm install
You can see the migration steps necessary for the Feathers chat here for Javascript and here for TypeScript.
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.
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.
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.
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>
Now import { HookContext } from './declarations' can be used as the context in hooks.
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
}
}
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.
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
}
You can revert to the previous behaviour by overriding he Params declaration:
declare module '@feathersjs/feathers/lib/declarations' {
interface Params {
[key: string]: any
}
}
The Express rest adapter now needs to be configured in the correct order, usually right after the json() middleware and before any services are registered. This is already the case in generated applications but it may have to be adjusted in a custom setup.
The import of feathers has changed from
const feathers = require('@feathersjs/feathers')
import feathers from '@feathersjs/feathers'
To
const { feathers } = require('@feathersjs/feathers')
import { feathers } from '@feathersjs/feathers'
The Express exports for TypeScript have changed from
import express from '@feathersjs/express'
app.use(express.json())
app.use(express.urlencoded())
app.use(express.notFound())
app.use(express.errorHandler())
To
import express, { json, urlencoded, notFound, errorHandler } from '@feathersjs/express'
app.use(json())
app.use(urlencoded())
app.use(notFound())
app.use(errorHandler())
We are exploring the best migration strategy to replace "whitelisting" options with a solution based on Feathers schema. We'll update this guide once the solution is in place.
The whitelist option is now split into two options: operators and filters. To migrate, you need to figure out how you're using each item from your old whitelist, then move them to the correct option. You can determine if each one is a filter or an operator based on where it is used in a query.
filters are top-level query properties.operators are positioned under an attribute.In the below example, $customFilter would be a filter, $regex and $options would be operators.
const query = {
$customFilter: 'value',
name: {
$regex: /pattern/,
$options: 'igm'
}
}
For v5 service adapters, split the whitelist options into the filters object or the operators array.
// 👎`whitelist` and `allow are unsupported.
const oldServiceOptions = {
whitelist: ['$customFilter', '$ignoreCase', '$regex', '$options']
}
// 👍 Separate items into `filters` and `operators` for v5 service adapters
const serviceOptions = {
filters: {
// Map a custom filter to a converter function
$ignoreCase: (value: any) => (value === 'true' ? true : false),
// Enable a custom param without converting
$customQueryOperator: true
} as const,
operators: ['$regex', '$options']
}
If you're using TypeScript, notice the as const after the filters object in the options, above. That will keep type errors from happening when passing the serviceOptions to the service.
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.
service.setup, app.setup and app.listen return a Promise:
// 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
The Socket.io and Grant (oAuth) dependencies have been updated to their latest versions. For more information on breaking changes see:
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:
../ and ./ have to be replaced with ./ and ./config/.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 like this:// config/custom-environment-variables.json
{
"hostname": "HOSTNAME",
"port": "PORT",
"someSetting": {
"apiKey": "MY_CUSTOM_API_KEY"
}
}
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:
import { feathers } from '@feathersjs/feathers'
import debug from 'debug'
feathers.setDebug(debug)
It is also possible to set a custom logger like this:
import { feathers } from '@feathersjs/feathers'
const customDebug =
(name) =>
(...args) => {
console.log(name, ...args)
}
feathers.setDebug(customDebug)
Setting the debugger will apply to all @feathersjs modules.
request library has been deprecated and request support has been removed from the REST client.@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.Due to low usage @feathersjs/primus and @feathers/primus-client have been removed from Feathers core.
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.
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.
app.serviceBy 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
}
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)
}
})
})
finally hookThe 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
}
}
])
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.app.providers has been removed since it provided the same functionality as app.mixinsapp.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.req.authentication property is no longer set on the express requests, use req.feathers.authentication instead.