---
[](https://github.com/feathersjs/feathers/actions?query=workflow%3ACI)
[](https://www.npmjs.com/package/@feathersjs/feathers)
[](https://discord.gg/qa8kez8QBx)
Feathers is a full-stack framework for creating web APIs and real-time applications with TypeScript or JavaScript.
Feathers can interact with any backend technology, supports many databases out of the box and works with any frontend like React, VueJS, Angular, React Native, Android or iOS.
# Getting started
Get started with just three commands:
```bash
$ npm create feathers my-new-app
$ cd my-new-app
$ npm run dev
```
To learn more about Feathers visit the website at [feathersjs.com](http://feathersjs.com) or jump right into [the Feathers guides](https://feathersjs.com/guides/).
# Contributing
To start developing, clone this repository, then run:
```
cd feathers
npm install
```
To run all tests run
```
npm test
```
Individual tests can be run in the module you are working on:
```
cd packages/feathers
npm test
```
# License
Copyright (c) 2024 [Feathers contributors](https://github.com/feathersjs/feathers/graphs/contributors)
Licensed under the [MIT license](LICENSE).
================================================
FILE: docs/.vitepress/components/Badges.vue
================================================
If the `methods` property is `undefined`, all standard methods will be enabled and accessible externally.## .unuse(path) `app.unuse(path)` unregisters an existing service on `path` and calls the services [.teardown method](./services.md#teardownapp-path) if it is implemented. ## .service(path) `app.service(path) -> service` returns the [service object](./services.md) for the given path. Feathers internally creates a new object from each registered service. This means that the object returned by `app.service(path)` will provide the same methods and functionality as your original service object but also functionality added by Feathers and its plugins like [service events](./events.md) and [additional methods](./services.md#feathers-functionality). ```ts const messageService = app.service('messages') const message = await messageService.get('test') console.log(message) messageService.on('created', (message: Message) => { console.log('Created a todo') }) ```
Note that a server side `app.service(path)` only allows the original service name (e.g. `app.service(':userId/messages')`) and does not parse placeholders. To get a service with route paramters use [.lookup](#lookuppath)## .lookup(path) `app.lookup(path)` allows to look up a full path and will return the `data` (route parameters) and `service` **on the server**. ```ts const lookup = app.lookup('messages/4321') // lookup.service -> app.service('messages') // lookup.data -> { __id: '4321' } // `lookup.dta` needs to be passed as `params.route` lookup.service.find({ route: lookup.data }) ``` Case insensitive lookups can be enabled in the `app` file like this: ```ts app.routes.caseSensitive = false ``` ## .hooks(hooks) `app.hooks(hooks) -> app` allows registration of application-level hooks. For more information see the [application hooks section in the hooks chapter](./hooks.md#application-hooks). ## .publish([event, ] publisher) `app.publish([event, ] publisher) -> app` registers a global event publisher. For more information see the [channels publishing](./channels.md#publishing) chapter. ## .configure(callback) `app.configure(callback) -> app` runs a `callback` function that gets passed the application object. It is used to initialize plugins and can be used to separate your application into different files. ```ts const setupService = (app: Application) => { app.use('/todos', todoService) } app.configure(setupService) ``` ## .setup([server]) `app.setup([server]) -> Promise
`app.set` is global to the application. It is used for storing application wide information like database connection strings etc. **Do not use it for storing request or service specific data.** This can be done by adding data to the [hook context](./hooks.md#hook-context).```ts import { feathers } from '@feathersjs/feathers' type ServiceTypes = { // Add services path to type mapping here } // app.get and app.set can be typed when initializing the app type Configuration = { port: number } const app = feathers
On the server, settings are usually initialized using [Feathers configuration](configuration.md).## .get(name) `app.get(name) -> value` retrieves the setting `name`. ## .on(eventname, listener) Provided by the core [NodeJS EventEmitter .on](https://nodejs.org/api/events.html#events_emitter_on_eventname_listener). Registers a `listener` method (`function(data) {}`) for the given `eventname`. ```js app.on('login', (user) => console.log('Logged in', user)) ``` ## .emit(eventname, data) Provided by the core [NodeJS EventEmitter .emit](https://nodejs.org/api/events.html#events_emitter_emit_eventname_args). ```ts type MyEventData = { message: string } app.emit('myevent', { message: 'Something happened' }) app.on('myevent', (data: MyEventData) => console.log('myevent happened', data)) ```
`app` can not receive or send events to or from clients. A [custom service](./services.md) should be used for that.## .removeListener(eventname) Provided by the core [NodeJS EventEmitter .removeListener](https://nodejs.org/api/events.html#events_emitter_removelistener_eventname_listener). Removes all or the given listener for `eventname`. ## .mixins `app.mixins` contains a list of service mixins. A mixin is a callback (`(service, path, options) => {}`) that gets run for every service that is being registered. Adding your own mixins allows to add functionality to every registered service. ```ts import type { Id } from '@feathersjs/feathers' // Mixins have to be added before registering any services app.mixins.push((service: any, path: string) => { service.sayHello = function () { return `Hello from service at '${path}'` } }) app.use('/todos', { async get(id: Id) { return { id } } }) app.service('todos').sayHello() // -> Hello from service at 'todos' ``` ## .services `app.services` contains an object of all [services](./services.md) keyed by the path they have been registered via `app.use(path, service)`. This allows to return a list of all available service names: ```ts const servicePaths = Object.keys(app.services) servicePaths.forEach((path) => { const service = app.service(path) }) ```
To retrieve services use [app.service(path)](#service-path), not `app.services[path]` directly.A Feathers [client](client.md) does not know anything about the server it is connected to. This means that `app.services` will _not_ automatically contain all services available on the server. Instead, the server has to provide the list of its services, e.g. through a [custom service](./services.md): ```ts class InfoService { constructor(public app: Application) {} async find() { return { service: Object.keys(this.app.services) } } } app.use('info', new InfoService(app)) ``` ## .defaultService `app.defaultService` can be a function that returns an instance of a new standard service for `app.service(path)` if there isn't one registered yet. By default it throws a `NotFound` error when you are trying to access a service that doesn't exist. ```ts import { MemoryService } from '@feathersjs/memory' // For every `path` that doesn't have a service // Automatically return a new in-memory service app.defaultService = function (path: string) { return new MemoryService() } ``` This is used by the [client transport adapters](./client.md) to automatically register client side services that talk to a Feathers server. ================================================ FILE: docs/api/authentication/client.md ================================================ --- outline: deep --- # Authentication Client
Verifying or parsing the token on the client usually isn't necessary since the server does that on JWT authentication and returns with the token information but it can still be done manually with the [jwt-decode](https://www.npmjs.com/package/jwt-decode) package.## app.reAuthenticate([force]) `app.reAuthenticate() -> Promise` will try to authenticate using the access token from the storage or the window location (e.g. after a successful [OAuth](./oauth.md) login). This is normally called to either show your application (when successful) or showing a login page or redirecting to the appropriate OAuth link. ```js try { await app.reAuthenticate() showDashboard() } catch (error) { showLoginPage() } ```
`app.reAuthenticate()` has to be called when you want to use the token from storage. **There is no need to call it more than once**, so you’d typically only do it once when the application initializes. When successful, all subsequent requests will send their authentication information automatically.In some rare cases, for example making sure the user object returned by `app.get('authentication')` is up-to-date after it was changed on the server, you may force reauthentication via `app.reAuthenticate(true)`. ## app.authenticate(data) `app.authenticate(data) -> Promise` will try to authenticate with a Feathers server by passing a `strategy` and other properties as credentials. ```ts try { // Authenticate with the local email/password strategy await app.authenticate({ strategy: 'local', email: 'my@email.com', password: 'my-password' }) // Show e.g. logged in dashboard page } catch (error: any) { // Show login page (potentially with `e.message`) console.error('Authentication error', e) } ``` - `data {Object}` - of the format `{strategy [, ...otherProps]}` - `strategy {String}` - the name of the strategy to be used to authenticate. Required. - `...otherProps {Properties} ` vary depending on the chosen strategy. Above is an example of using the `local` strategy. ## app.logout() Removes the access token from storage on the client. It also calls the `remove` method of the [authentication service](./service.md). ## app.get('authentication') `app.get('authentication') -> Promise` is a Promise that resolves with the current authentication information. For most strategies this is the best place to **get the currently authenticated user**: ```js // Returns the authenticated user const { user } = await app.get('authentication') // Gets the authenticated accessToken (JWT) const { accessToken } = await app.get('authentication') ``` ## app.authentication Returns the instance of the [AuthenticationClient](#authenticationclient). ## AuthenticationClient ### service `app.authentication.service` returns an instance of the authentication client service, normally `app.service('authentication')`. ### storage `app.authentication.storage` returns the storage instance (e.g. window.LocalStorage, React Native AsyncStorage or an in-memory store). ### handleSocket(socket) `app.authentication.handleSocket(socket) -> void` makes sure that a websocket real-time connection is always re-authenticated before making any other request. ### getFromLocation(location) `app.authentication.getFromLocation(location) -> Promise` tries to retrieve an access token from `window.location`. This usually means the `access_token` in the hash set by the [OAuth authentication strategy](./oauth.md). ### setAccessToken(token) `app.authentication.setAccessToken(token) -> Promise` sets the access token in the storage (normally `feathers-jwt` in `window.localStorage`). ### getAccessToken() `app.authentication.getAccessToken() -> Promise` returns the access token from `storage`. If not found it will try to get the access token via [getFromLocation()]() or return `null` if neither was successful. ### removeAccessToken() `app.authentication.removeAccessToken() -> Promise` removes the access token from the storage. ### reset() `app.authentication.reset()` resets the authentication state without explicitly logging out. Should not be called directly. ### handleError() `app.authentication.handleError(error, type: 'authenticate'|'logout') -> Promise` handles any error happening in the `authenticate` or `logout` method. By default it removes the access token if the error is a `NotAuthenticate` error. Otherwise it does nothing. ### reAuthenticate(force, strategy?) `app.authentication.reAuthenticate(force = false, strategy) -> Promise` will re-authenticate with the current access token from [app.authentication.getAccessToken()](). If `force` is set to `true` it will always re-authenticate, with the default `false` only when not already authenticated. `strategy` is an optional parameter which defaults to the configured `jwtStrategy`. ### authenticate() The internal method called when using [app.authenticate()](#app-authenticate-data). ### logout() The internal method called when using [app.logout()](#app-logout). ## Customization The [AuthenticationClient]() can be extended to provide custom functionality and then passed during initialization: ```ts import { feathers } from '@feathersjs/feathers' import socketio from '@feathersjs/socketio-client' import io from 'socket.io-client' import authentication, { AuthenticationClient } from '@feathersjs/authentication-client' const socket = io('http://api.feathersjs.com') const app = feathers() class MyAuthenticationClient extends AuthenticationClient { getFromLocation(location) { // Do custom location things here return super.getFromLocation(location) } } // Setup the transport (Rest, Socket, etc.) here app.configure(socketio(socket)) // Pass the custom authentication client class as the `Authentication` option app.configure( authentication({ Authentication: MyAuthenticationClient }) ) ``` ## Hooks The following hooks are added to the client side application automatically (when calling `app.configure(authentication())`). ### authentication Hook that ensures for every request that authentication is completed and successful. It also makes the authentication information available in the client side `params` (e.g. `params.user`). ### populateHeader Adds the appropriate `Authorization` header for any REST request. ================================================ FILE: docs/api/authentication/hook.md ================================================ --- outline: deep --- # Authenticate Hook The `authenticate` hook will use `params.authentication` of the service method call and run [authenticationService.authenticate()](./service.md#authenticate-data-params-strategies). The hook will - Throw an error if the strategy fails - Throw an error if no authentication information is set and it is an external call (`params.provider` is set) or do nothing if it is an internal call (`params.provider` is `undefined`) - If successful, merge `params` with the return value of the authentication strategy For example, a successful [JWT strategy](./jwt.md) authentication will set: ```js params.authentication.payload // The decoded payload params.authentication.strategy === 'jwt' // The strategy name params.user // or params[entity] if entity is not `null` ``` In the following hooks and for the service method call. It can be used as a `before` or `around` [hook](../hooks.md). ## authenticate(...strategies) Check `params.authentication` against a list of authentication strategy names. ```ts import { authenticate } from '@feathersjs/authentication' // Authenticate with `jwt` and `api-key` strategy // using app.service('authentication') as the authentication service app.service('messages').hooks({ around: { all: [authenticate('jwt', 'api-key')] } }) ``` ## authenticate(options) Check `params.authentication` against a list of strategies and specific authentication service. Available `options` are: - `service` - The path to the authentication service - `strategies` - A list of strategy names ```js import { authenticate } from '@feathersjs/authentication' // Authenticate with `jwt` and `api-key` strategy // using app.service('v1/authentication') as the authentication service app.service('messages').hooks({ before: { all: [ authenticate({ service: 'v1/authentication', strategies: ['jwt', 'api-key'] }) ] } }) ``` ================================================ FILE: docs/api/authentication/index.md ================================================ --- outline: deep --- # Authentication Overview The `@feathersjs/authentication` plugins provide a collection of tools for username/password, JWT and OAuth (GitHub, Facebook etc.) authentication as well as custom authentication mechanisms. It consists of the following core modules: - `@feathersjs/authentication` which includes - The [AuthenticationService](./service.md) that allows to register [authentication strategies](./strategy.md) and create and manage access tokens - The [JWTStrategy](./jwt.md) to use JWTs to make authenticated requests - The [authenticate hook](./hook.md) to limit service calls to an authentication strategy. - [Local authentication](./local.md) for local username/password authentication - [OAuth authentication](./oauth.md) for Google, GitHub, Facebook etc. authentication - [The authentication client](./client.md) to use Feathers authentication on the client.
`@feathersjs/authentication` is an abstraction for different authentication mechanisms. It does not handle things like user verification or password reset functionality etc. This can be implemented manually, with the help of libraries like [feathers-authentication-management](https://github.com/feathers-plus/feathers-authentication-management) or a platform like [Auth0](https://auth0.com/).================================================ FILE: docs/api/authentication/jwt.md ================================================ --- outline: deep --- # JWT Authentication
Since the default options are what most clients expect for JWT authentication they usually don't need to be customized.To change the settings for generating and validating a JWT see the [authentication service configuration](./service.md#configuration) ## JwtStrategy ### getEntity(id, params) `jwtStrategy.getEntity(id, params)` returns the entity instance for `id`, usually `entityService.get(id, params)`. It will _not_ be called if `entity` in the [authentication configuration](./service.md#configuration) is set to `null`. ### authenticate(data, params) `jwtStrategy.authenticate(data, params)` will try to verify `data.accessToken` by calling the strategies [authenticationService.verifyAccessToken](./service.md). Returns a promise that resolves with the following format: ```js { [entity], accessToken, authentication: { strategy: 'jwt', payload } } ```
Since the JWT strategy returns an `accessToken` property (the same as the token sent to this strategy), that access token will also be returned by [authenticationService.create](./service.md#create-data-params) instead of creating a new one.### getEntityQuery(params) Returns the `query` to use when calling `entityService.get` (default: `{}`). ### parse(req, res) Parse the HTTP request headers for JWT authentication information. By default in the `Authorization` header. Returns a promise that resolves with either `null` or data in the form of: ```js { strategy: '
The methods described in this section are intended for [customization](#customization) purposes and internal calls. They usually do not need to be called directly.### getEntityQuery(query, params) `localStrategy.getEntityQuery(query, params) -> Promise` returns the query for finding the entity. `query` includes the `usernameField` or `entityUsernameField` as `{ [field]: username }` and by default returns a promise that resolves with `{ $limit: 1 }` combined with `query`. ### findEntity(username, params) `localStrategy.findEntity(username, params) -> Promise` return the entity for a given username and service call parameters. It will use the query returned by `getEntityQuery` and call `.find` on the entity (usually `/users`) service. It will return a promise that resolves with the first result of the `.find` call or throw an error if nothing was found. ### getEntity(entity, params) `localStrategy.getEntity(authResult, params) -> Promise` returns the external representation for `entity` that will be sent back to the client. ### hashPassword(password) `localStrategy.hashPassword(password) -> Promise` creates a safe one-way hash of the given plain `password` string. By default [bCryptJS](https://www.npmjs.com/package/bcryptjs) is used. ### comparePassword(entity, password) `localStrategy.comparePassword(entity, password) -> Promise` compares a plain text `password` with the hashed password of the `entity` returned by `findEntity`. Returns the `entity` or throws an error if the passwords don't match. ### authenticate(authentication, params) `localStrategy.authenticate(authentication, params)` is the main endpoint implemented by any [authentication strategy](./strategy.md). It is usually called for authentication requests for this strategy by the [AuthenticationService](./service.md). ## Customization The `LocalStrategy` can be customized like any ES6 class and then registered on the [AuthenticationService](./service.md): ```ts import type { Application, Params, Query } from '@feathersjs/feathers' import { AuthenticationService, JWTStrategy } from '@feathersjs/authentication' import { LocalStrategy } from '@feathersjs/authentication-local' class MyLocalStrategy extends LocalStrategy { async getEntityQuery(query: Query, params: Params) { // Query for use but only include `active` users return { ...query, active: true, $limit: 1 } } } export default (app: Application) => { const authService = new AuthenticationService(app) authService.register('local', new MyLocalStrategy()) // ... app.use('/authentication', authService) } ``` ## Helpers ### Protecting fields As of Feathers v5, external [resolvers](../schema/resolvers.md) using the `schemaHooks.resolveExternal` hook are the preferred method to hide or change fields for external requests. The following will always hide the user password for external responses and events: ```ts import { resolve, schemaHooks } from '@feathersjs/schema' export const userExternalResolver = resolve
If you are attempting to authenticate using an existing oAuth access token, ensure that you have added the strategy (e.g. 'facebook') to the allowed [authStrategies](./service.md#configuration) configuration.### OAuth URLs There are two URLs and redirects that are important for OAuth authentication: - `http(s)://
The redirect is using a hash instead of a query string by default because it is not logged server side and can be easily read on the client. You can force query based redirect by adding a `?` to the end of the `redirect` option.If the `redirect` option is not set and no origin is available the authentication result data will be sent as JSON instead. Dynamic redirects to the same URL are possible by setting the `redirect` query parameter in the OAuth flow. For example, the following OAuth link: ```html Login with GitHub ``` With the above configuration will redirect to `https://app.mydomain.com/dashboard` after the OAuth flow. ## Options Options are usually set under the registered name via [Feathers configuration](../configuration.md) in `config/default.json` or `config/
Removing the `redirect` option allows to troubleshoot troubleshoot OAuth authentication errors as a JSON response by opening the oAuth URL directly in the browser.```json { "authentication": { "origins": ["localhost:3030"], "oauth": { "redirect": "/frontend", "google": { "key": "...", "secret": "...", "custom_params": { "access_type": "offline" } }, "twitter": { "key": "...", "secret": "..." } } } } ```
All OAuth strategies will by default always look for configuration under `authentication.oauth.Here is a [list of all Grant configuration options](https://github.com/simov/grant#all-available-options) that are available: | Key | Location | Description | | :------------------------- | :------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------- | | request_url | [oauth.json](https://github.com/simov/grant/blob/master/config/oauth.json) | OAuth1/step1 | | authorize_url | [oauth.json](https://github.com/simov/grant/blob/master/config/oauth.json) | OAuth1/step2 or OAuth2/step1 | | access_url | [oauth.json](https://github.com/simov/grant/blob/master/config/oauth.json) | OAuth1/step3 or OAuth2/step2 | | oauth | [oauth.json](https://github.com/simov/grant/blob/master/config/oauth.json) | OAuth version number | | scope_delimiter | [oauth.json](https://github.com/simov/grant/blob/master/config/oauth.json) | string delimiter used for concatenating multiple scopes | | protocol, host, path | `defaults` | used to generate `redirect_uri` | | transport | `defaults` | [transport](#response-data-transport) to use to deliver the response data in your final `callback` route | | state | `defaults` | toggle random `state` string generation for OAuth2 | | key | `[provider]` | OAuth app key, reserved aliases: `consumer_key` and `client_id` | | secret | `[provider]` | OAuth app secret, reserved aliases: `consumer_secret` and `client_secret` | | scope | `[provider]` | list of scopes to request | | custom_params | `[provider]` | custom authorization [parameters](#custom-parameters) and their values | | subdomain | `[provider]` | string to be [embedded](#subdomain-urls) in `request_url`, `authorize_url` and `access_url` | | nonce | `[provider]` | toggle random `nonce` string generation for [OpenID Connect](#openid-connect) providers | | callback | `[provider]` | final callback route on your server to receive the [response data](#response-data) | | dynamic | `[provider]` | allow [dynamic override](#dynamic-override) of configuration | | overrides | `[provider]` | [static overrides](#static-overrides) for a provider | | response | `[provider]` | [limit](#limit-response-data) the response data | | token_endpoint_auth_method | `[provider]` | authentication method for the [token endpoint](#token-endpoint-auth-method) | | name | generated | provider's [name](#grant), used to generate `redirect_uri` | | profile_url | [grant-profile](https://github.com/simov/grant-profile) | The URL to retrieve the user profile from | | [provider] | generated | provider's [name](#grant) as key | | redirect_uri | generated | OAuth app [redirect URI](#redirect-uri), generated using `protocol`, `host`, `path` and `name` | ## OAuthStrategy ### entityId `oauthStrategy.entityId -> string` returns the name of the id property of the entity. ### getEntityQuery(profile, params) `oauthStrategy.getEntityQuery(profile, params) -> Promise` returns the entity lookup query to find the entity for a profile. By default returns ```js { [`${this.name}Id`]: profile.sub || profile.id } ``` ### getEntityData(profile, entity, params) `oauthStrategy.getEntityData(profile, existing, params) -> Promise` returns the data to either create a new or update an existing entity. `entity` is either the existing entity or `null` when creating a new entity. ### getProfile(data, params) `oauthStrategy.getProfile(data, params) -> Promise` returns the user profile information from the OAuth provider that was used for the login. `data` is the OAuth callback information which normally contains e.g. the OAuth access token. ### getRedirect (data) `oauthStrategy.getRedirect(data) -> Promise` returns the URL to redirect to after a successful OAuth login and entity lookup or creation. By default it redirects to `authentication.oauth.redirect` from the configuration with `#access_token=`. If `authentication.oauth` is not set in the configuration, OAuth authentication will be disabled.
The authentication service can also be configured dynamically or without Feathers configuration by using [app.set](../application.md#set-name-value), e.g. `app.set('authentication', config)`.The following options are available: - `secret`: The JWT signing secret. - `service`: The path of the entity service - `authStrategies`: A list of authentication strategy names to allow on this authentication service to create access tokens. - `parseStrategies`: A list of authentication strategies that should be used to parse HTTP requests. Defaults to the same as `authStrategies`. - `entity`: The name of the field that will contain the entity after successful authentication. Will also be used to set `params[entity]` (usually `params.user`) when using the [authenticate hook](./hook). Can be `null` if no entity is used (see [stateless tokens](../../cookbook/authentication/stateless.md)). - `entityId`: The id property of an entity object. Only necessary if the entity service does not have an `id` property (e.g. when using a custom entity service). - `jwtOptions`: All options available for the [node-jsonwebtoken package](https://github.com/auth0/node-jsonwebtoken). An authentication service configuration in `config/default.json` can look like this: ```json { "authentication": { "secret": "CHANGE_ME", "entity": "user", "service": "users", "authStrategies": ["jwt", "local"], "jwtOptions": { "header": { "typ": "access" }, "audience": "https://yourdomain.com", "issuer": "feathers", "algorithm": "HS256", "expiresIn": "1d" } } } ```
`typ` in the `header` options is not a typo, it is part of the [JWT JOSE header specification](https://tools.ietf.org/html/rfc7519#section-5).Additionally to the above configuration, most [strategies](./strategy.md) will look for their own configuration under the name it was registered. An example can be found in the [local strategy configuration](./local.md#configuration). ## Authentication flows Below are the flows how the authentication service can be used. ### To _create a new JWT_ For any strategy allowed in `authStrategies`, a user can call `app.service('/authentication').create(data)` or `POST /authentication` with `data` as `{ strategy: name, ...loginData }`. Internally authentication will then - Call the strategy `.authenticate` method with `data` - Create a JWT for the entity returned by the strategy - Return the JWT (`accessToken`) and the additional information from the strategy For `local` strategy, the user has to be created before doing auth, otherwise, a 401 `NotAuthenticated` error will be sent. ### To _authenticate an external request_ For any HTTP request and strategy allowed in `parseStrategies` or - if not set - `authStrategies` authentication will: - Call [strategy.parse](./strategy.md#parse-req-res) and set the return value of the first strategy that does not return `null` as `params.authentication` - Verify `params.authentication` using the [authenticate hook](./hook.md) which calls the strategy `.authenticate` method with `params.authentication` as the data - Merge the return value of the strategy with `params` (e.g. setting `params.user`) ### To authenticate _your own service request_ For any service that uses the [authenticate hook](./hook.md) called internally you can set `params.authentication` in the service call which will then: - Verify `params.authentication` using the [authenticate hook](./hook.md) which calls the strategy `.authenticate` method with `params.authentication` as the data - Merge the return value of the strategy with `params` (e.g. setting `params.user`)
You can set `params.authentication` for internal requests on the server but usually setting the entity (`params.user` in most cases) if you already have it available should be preferred. This will avoid the overhead of running authentication again if it has already been done.## AuthenticationService ### constructor(app [, configKey]) `const authService = new AuthenticationService(app, configKey = 'authentication')` initializes a new authentication service with the [Feathers application](../application.md) instance and a `configKey` which is the name of the configuration property to use via [app.get()](../application.md#get-name) (default: `app.get('authentication')`). Upon initialization it will also update the configuration with the [default settings](#configuration). ### authenticate(data, params, ...strategies) `authService.authenticate(data, params, ...strategies) -> Promise` is the main authentication method and authenticates `data` and `params` against a list of strategies in `strategies`. `data` _must_ always contain a `strategy` property indicating the name of the strategy. If `data.strategy` is not available or not allowed (included in the `strategies` list) a `NotAuthenticated` error will be thrown. Otherwise the result of [strategy.authenticate()](./strategy.md#authenticate-authentication-params) will be returned. ### create(data, params) `authService.create(data, params) -> Promise` runs `authService.authenticate` with `data`, `params` and the list of `strategies` from `authStrategies` in the [configuration](#configuration). As with any other [Feathers service](../services.md), this method will be available to clients, e.g. running a `POST /authentication`. If successful it will create a JWT with the payload taken from [authService.getPayload](#getpayload-authresult-params) and the options from [authService.getTokenOptions](#gettokenoptions-authresult-params). `data` _must_ always contain a valid and allowed `strategy` name. Will emit the [`login` event](#app-on-login). ### remove(id, params) `authService.remove(id, params) -> Promise` should be called with `id` set to `null` or to the authenticated access token. Will verify `params.authentication` and emit the [`logout` event](#app-on-logout) if successful. ### configuration `authService.configuration` returns a copy of current value of `app.get(configKey)` (default: `app.get('authentication')`). This is a deep copy of the configuration and is not intended to be modified. In order to change the configuration, [app.set(configKey)](../application.md#set-name-value) should be used: ```ts const config = app.get('authentication') // Update configuration with a new entity app.set('authentication', { ...config, entity: 'some other entity name' }) ``` ### register(name, strategy) `authService.register(name, strategy)` registers an [authentication strategy](./strategy.md) under `name` and calls the strategy methods `setName`, `setApplication`, `setAuthentication` and `verifyConfiguration` if they are implemented. ### getStrategy(name) `service.getStrategy(name)` returns the authentication strategy registered under `name`. Usually authentication strategies do not need to be used directly. ### getStrategies(...names) `service.getStrategies(...names) -> AuthenticationStrategy[]` returns the [authentication strategies](./strategy.md) that exist for a list of names. The returned array may include `undefined` values if the strategy does not exist. Usually authentication strategies do not need to be used directly. ```js const [localStrategy] = authService.getStrategies('local') ``` ### createAccessToken(payload) `authService.createAccessToken(payload, [options, secret]) -> Promise` creates a new access token. By default it is a [JWT](https://jwt.io/) with `payload`, using [configuration.jwtOptions](#configuration) merged with `options` (optional). It will either use `authService.configuration.secret` or the optional `secret` to sign the JWT. Throws an error if the access token can not be created. ```ts const token = await app.service('authentication').createAccessToken({ permission: 'admin' }) ```
Normally, it is not necessary to call this method directly. Calling [authService.create(data, params)](#create-data-params) using an authentication strategy will take care of creating the correct access token.### verifyAccessToken(accessToken) `authService.verifyAccessToken(accessToken, [options, secret]) -> Promise` verifies the access token. By default it will try to verify a JWT using `configuration.jwtOptions` merged with `options` (optional). Will either use `configuration.secret` or the optional `secret` to verify the JWT. Returns the encoded payload or throws an error. ### getTokenOptions(authResult, params) `authService.getTokenOptions(authResult, params) -> Promise` returns the options for creating a new access token based on the return value from calling [authService.authenticate()](#authenticate-data-params-strategies). Called internally on [authService.create()](#create-data-params). It will try to set the JWT `subject` to the entity (user) id if it is available which will then be used by the [JWT strategy](./jwt.md) to populate `params[entity]` (usually `params.user`). ### getPayload(authResult, params) `authService.getPayload(authResult, params) -> Promise` returns the access token payload for an authentication result (the return value of [authService.create()](#create-data-params)) and [service call parameters](../services.md#params). Called internally on [.create](#create-data-params). Returns either `params.payload` or an empty object (`{}`). ### parse(req, res, ...strategies) `authService.parse(req, res, ...strategies) -> Promise` parses a [NodeJS HTTP request](https://nodejs.org/api/http.html#http_class_http_incomingmessage) and [HTTP response](https://nodejs.org/api/http.html#http_class_http_serverresponse) for authentication information using `strategies` calling [each strategies `.parse()` method](./strategy.md#parse-req-res) if it is implemented. Will return the value of the first strategy that didn't return `null`. This does _not_ authenticate the request, it will only return authentication information that can be used by `authService.authenticate` or `authService.create`. ### setup(path, app) `authService.setup(path, app)` verifies the [configuration](#configuration) and makes sure that - A `secret` has been set - If `entity` is not `null`, check if the entity service is available and make sure that either the `entityId` configuration or the `entityService.id` property is set. - Register internal hooks to send events and keep real-time connections up to date. All custom hooks should be registered at this time. ## app.get('defaultAuthentication') After registering an authentication service, it will set the `defaultAuthentication` property on the application to its configuration name (`configKey` set in the constructor) if it does not exist. `app.get('defaultAuthentication')` will be used by other parts of Feathers authentication to access the authentication service if it is not otherwise specified. Usually this will be `'authentication'`. ## Customization The `AuthenticationService` can be customized like any other class: ```ts import type { Params } from '@feathersjs/feathers' import type { AuthenticationResult } from '@feathersjs/authentication' import { AuthenticationService } from '@feathersjs/authentication' class MyAuthService extends AuthenticationService { async getPayload(authResult: AuthenticationResult, params: Params) { // Call original `getPayload` first const payload = await super.getPayload(authResult, params) const { user } = authResult if (user && user.permissions) { payload.permissions = user.permissions } return payload } } app.use('/authentication', new MyAuthService(app)) ``` Things to be aware of when extending the authentication service: - When implementing your own `constructor`, always call `super(app, configKey)` - When overriding a method, calling `super.method` and working with its return value is recommended unless you are certain your custom method behaves exactly the same way, otherwise things may no longer work as expected. - When extending `setup`, `super.setup(path, app)` should always be called, otherwise events and real-time connection authentication will no longer work. ## Events For both, `login` and `logout` the event data is `(authenticationResult, params, context) => {}` as follows: - `authResult` is the return value of the `authService.create` or `authService.remove` call. It usually contains the user and access token. - `params` is the service call parameters - `context` is the service methods [hook context](../hooks.md#hook-context) ### app.on('login') `app.on('login', (authenticationResult, params, context) => {})` will be sent after a user logs in. This means, after any successful external call to [authService.create](#create-data-params).
The `login` event is also sent for e.g. reconnections of websockets and may not always have a corresponding `logout` event. Use the [`disconnect` event](../channels.md#app-on-disconnect) for handling disconnection.### app.on('logout') `app.on('logout', (authenticationResult, params, context) => {})` will be sent after a user explicitly logs out. This means after any successful external call to [authService.remove](#remove-id-params). ================================================ FILE: docs/api/authentication/strategy.md ================================================ --- outline: deep --- # Authentication Strategies An authentication strategy is any object or class that implements at least an [authenticate(data, params)]() method. They can be registered with the AuthenticationService to authenticate service calls and other requests. The following strategies already come with Feathers: - [JWTStrategy](./jwt.md) in `@feathersjs/authentication` - [LocalStrategy](./local.md) in `@feathersjs/authentication-local` - [OAuthStrategy](./oauth.md) in `@feathersjs/authentication-oauth` More details on how to customize existing strategies can be found in their API documentation. This section describes the common methods for all authentication strategies and how a custom authentication strategy can be implemented. ## setName(name) Will be called with the `name` under which the strategy has been [registered on the authentication service](./service.md#register-name-strategy). Does not have to be implemented. ## setApplication(app) Will be called with the [Feathers application](../application.md) instance. Does not have to be implemented. ## setAuthentication(service) Will be called with the [Authentication service](./service.md) this strategy has been registered on. Does not have to be implemented. ## verifyConfiguration() Synchronously verify the configuration for this strategy and throw an error if e.g. required fields are not set. Does not have to be implemented. ## authenticate(authentication, params) Authenticate `authentication` data with additional `params`. `authenticate` should throw a `NotAuthenticated` if it failed or return an authentication result object. ## parse(req, res) Parse a given plain Node HTTP request and response and return `null` or the authentication information it provides. Does not have to be implemented. This is called by the authentication service. See [AuthService.parse](https://dove.feathersjs.com/api/authentication/service.html#parse-req-res-strategies) ## AuthenticationBaseStrategy The `AuthenticationBaseStrategy` class provides a base class that already implements some of the strategy methods below with some common functionality: - [setName](#setname-name) sets `this.name` - [setApplication](#setapplication-app) sets `this.app` - [setAuthentication](#setauthentication-service) sets `this.authentication` - `configuration` getter returns `this.authentication.configuration[this.name]` - `entityService` getter returns the entity (usually `/users`) service from `this.app` ## Examples Examples for authentication strategies can be found in the [Cookbook](../../cookbook/): - [Anonymous strategy](../../cookbook/authentication/anonymous.md) ================================================ FILE: docs/api/channels.md ================================================ --- outline: deep --- # Channels On a Feathers server with a real-time transport (like [Socket.io](./socketio.md)) configured, event channels determine which connected clients to send [real-time events](./events.md) to and how the sent data should look. This chapter describes: - [Concepts](#concepts) of real-time communication - [An example](#example) channels.js file - [Real-time Connections](#connections) and how to access them - [Channel usage](#channels) and how to retrieve, join and leave channels - [Publishing events](#publishing) to channels
Channels functionality will not be available in the following two scenarios: - When you're making a rest-only API, not using a real-time adapter. - When you're using Feathers on the client. Only server-side Feathers has channel management.Here are some example logic conditions where channels are useful: - Real-time events should only be sent to authenticated users - Users should only get updates about messages from chat rooms they joined - Only users in the same organization should receive real-time updates about their data changes - Only admins should be notified when new users are created - When a user is created, modified or removed, non-admins should only receive a "safe" version of the user object (e.g. only `email`, `id` and `avatar`) ## Concepts A **_channel_** is basically an array of **_connection_** objects. Each array is explicitly given a name. When using a real-time server transport and a new client connects, you can tell the server to explicitly add that client's connection object to any relevant channels. Any connection in a channel will receive all events that are sent to that channel. This allows clients to receive only their intended messages. When using a real-time transport, the server pushes events (such as "created", "removed" etc. for a particular service) down to its clients. Using channels allows customizing which clients should receive each event. The client doesn’t subscribe to individual channels, directly, but rather subscribes to specific events like `created`, `patched`, custom events, etc, in which they are interested. Those events will only fire for a client if the server pushes data to one a channel to which the client has been added. You can have any number of channels. This helps to organise how data is sent and to control the volume of data, by not sending things that aren't relevant. The server can also change connection channel membership from time to time, eg. before vs after login. The server needs to explicitly **publish** channels it is interested in sharing with clients before they become available. ## Example The example below shows a `channels.js` file illustrating how the different parts fit together: ```ts import type { RealTimeConnection, Params } from '@feathersjs/feathers' import type { Application, HookContext } from './declarations' export default function (app: any) { if (typeof app.channel !== 'function') { // If no real-time functionality has been configured just return return } app.on('connection', (connection: RealTimeConnection) => { // On a new real-time connection, add it to the anonymous channel app.channel('anonymous').join(connection) }) app.on('login', (AuthenticationResult: any, { connection }: Params) => { // connection can be undefined if there is no // real-time connection, e.g. when logging in via REST if (connection) { // The connection is no longer anonymous, remove it app.channel('anonymous').leave(connection) // Add it to the authenticated user channel app.channel('authenticated').join(connection) } }) // eslint-disable-next-line no-unused-vars app.publish((data: any, context: HookContext) => { // Here you can add event publishers to channels set up in `channels.js` // To publish only for a specific event use `app.publish(eventname, () => {})` console.log( 'Publishing all events to all authenticated users. See `channels.js` and https://docs.feathersjs.com/api/channels.html for more information.' ) // e.g. to publish all service events to all authenticated users use return app.channel('authenticated') }) } ``` ## Connections A connection is an object that represents a real-time connection. It is the same object as `socket.feathers` in a [Socket.io](./socketio.md#params) middleware. You can add any kind of information to it but most notably, when using [authentication](./authentication/service.md), it will contain the authenticated user. By default it is located in `connection.user` once the client has authenticated on the socket (usually by calling `app.authenticate()` on the [client](./client.md)). We can get access to the `connection` object by listening to `app.on('connection', connection => {})` or `app.on('login', (payload, { connection }) => {})`.
When a connection is terminated it will be automatically removed from all channels.### app.on('connection') `app.on('connection', connection => {})` is fired every time a new real-time connection is established. This is a good place to add the connection to a channel for anonymous users (in case we want to send any real-time updates to them): ```ts import type { RealTimeConnection } from '@feathersjs/feathers' app.on('connection', (connection: RealTimeConnection) => { // On a new real-time connection, add it to the // anonymous channel app.channel('anonymous').join(connection) }) ``` ### app.on('disconnect') `app.on('disconnect', connection => {})` is fired every time real-time connection is disconnected. This is a good place to to handle disconnections outside of a logout. A connection that is disconnected will always leave all its channels automatically. ### app.on('login') `app.on('login', (authenticationResult, params, context) => {})` is sent by the [AuthenticationService](./authentication/service.md#app-on-login) on successful login. This is a good place to add the connection to channels related to the user (e.g. chat rooms, admin status etc.) ```ts import type { Params } from '@feathersjs/feathers' import type { AuthenticationResult } from '@feathersjs/authentication' app.on('login', (payload: AuthenticationResult, { connection }: Params) => { // connection can be undefined if there is no // real-time connection, e.g. when logging in via REST if (connection) { // The user attached to this connection const { user } = connection // The connection is no longer anonymous, remove it app.channel('anonymous').leave(connection) // Add it to the authenticated user channel app.channel('authenticated').join(connection) // Channels can be named anything and joined on any condition ` // E.g. to send real-time events only to admins use if (user.isAdmin) { app.channel('admins').join(connection) } // If the user has joined e.g. chat rooms user.rooms.forEach((room) => { app.channel(`rooms/${room.id}`).join(connection) }) } }) ``` ### app.on('logout') `app.on('logout', (AuthenticationResult, params, context) => {})` is sent by the [AuthenticationService](./authentication/service.md) on successful logout: ```ts import type { Params } from '@feathersjs/feathers' import type { AuthenticationResult } from '@feathersjs/authentication' app.on('logout', (payload: AuthenticationResult, { connection }: Params) => { if (connection) { // Join the channels a logged out connection should be in app.channel('anonymous').join(connection) } }) ```
On `logout` the connection will be removed from all existing channels automatically.## Channels A channel is an object that contains a number of connections. It can be created via `app.channel` and allows a connection to join or leave it. ### app.channel(...names) `app.channel(name) -> Channel`, when given a single name, returns an existing or new named channel: ```ts app.channel('admins') // the admin channel app.channel('authenticated') // the authenticated channel ``` `app.channel(name1, name2, ... nameN) -> Channel`, when given multiples names, will return a combined channel. A combined channel contains a list of all connections (without duplicates) and re-directs `channel.join` and `channel.leave` calls to all its child channels. ```ts // Combine the anonymous and authenticated channel const combinedChannel = app.channel('anonymous', 'authenticated') // Join the `anonymous` and `authenticated` channel combinedChannel.join(connection) // Join the `admins` and `chat` channel app.channel('admins', 'chat').join(connection) // Leave the `admins` and `chat` channel app.channel('admins', 'chat').leave(connection) // Make user with `_id` 5 leave the admins and chat channel app.channel('admins', 'chat').leave((connection) => { return connection.user._id === 5 }) ``` ### app.channels `app.channels -> [string]` returns a list of all existing channel names. ```ts app.channel('authenticated') app.channel('admins', 'users') app.channels // [ 'authenticated', 'admins', 'users' ] app.channel(app.channels) // will return a channel with all connections ``` This is useful to e.g. remove a connection from all channels: ```ts import type { RealTimeConnection } from '@feathersjs/feathers' // When a user is removed, make all their connections leave every channel app.service('users').on('removed', (user: User) => { app.channel(app.channels).leave((connection: RealTimeConnection) => { return user._id === connection.user._id }) }) ``` ### channel.join(connection) `channel.join(connection) -> Channel` adds a connection to this channel. If the channel is a combined channel, add the connection to all its child channels. If the connection is already in the channel it does nothing. Returns the channel object. ```ts import type { Params } from '@feathersjs/feathers' import type { AuthenticationResult } from '@feathersjs/authentication' app.on('login', (payload: AuthenticationResult, { connection }: Params) => { if (connection && connection.user.isAdmin) { // Join the admins channel app.channel('admins').join(connection) // Calling a second time will do nothing app.channel('admins').join(connection) } }) ``` ### channel.leave(connection|fn) `channel.leave(connection|fn) -> Channel` removes a connection from this channel. If the channel is a combined channel, remove the connection from all its child channels. Also allows to pass a callback that is run for every connection and returns if the connection should be removed or not. Returns the channel object. ```ts import type { RealTimeConnection } from '@feathersjs/feathers' // Make the user with `_id` 5 leave the `admins` channel app.channel('admins').leave((connection: RealTimeConnection) => { return connection.user._id === 5 }) ``` ### channel.filter(fn) `channel.filter(fn) -> Channel` returns a new channel filtered by a given function which gets passed the connection. ```ts import type { RealTimeConnection } from '@feathersjs/feathers' // Returns a new channel with all connections of the user with `_id` 5 const userFive = app .channel(app.channels) .filter((connection: RealTimeConnection) => connection.user._id === 5) ``` ### channel.send(data) `channel.send(data) -> Channel` returns a copy of this channel with customized data that should be sent for this event. Usually this should be handled by modifying either the service method result or setting client "safe" data in `context.dispatch` but in some cases it might make sense to still change the event data for certain channels. What data will be sent as the event data will be determined by the first available in the following order: 1. `data` from `channel.send(data)` 2. `context.dispatch` 3. `context.result` ```ts import type { RealTimeConnection } from '@feathersjs/feathers' app.on('connection', (connection: RealTimeConnection) => { // On a new real-time connection, add it to the // anonymous channel app.channel('anonymous').join(connection) }) // Send the `users` `created` event to all anonymous // users but use only the name as the payload app.service('users').publish('created', (data: User) => { return app.channel('anonymous').send({ name: data.name }) }) ```
If a connection is in multiple channels (e.g. `users` and `admins`) it will get the data from the _first_ channel that it is in.### channel.connections `channel.connections -> [ object ]` contains a list of all connections in this channel. ### channel.length `channel.length -> integer` returns the total number of connections in this channel. ## Publishing Publishers are callback functions that return which channel(s) to send an event to. They can be registered at the application and the service level and for all or specific events. A publishing function gets the event data and context object (`(data, context) => {}`) and returns a named or combined channel, an array of channels or `null`. Only one publisher can be registered for one type. Besides the standard [service event names](./events.md#service-events) an event name can also be a [custom event](./events.md#custom-events). `context` is the [hook context object](./hooks.md) from the service call or an object containing `{ path, service, app, result }` for custom events. ### service.publish([event,] fn) `service.publish([event,] fn) -> service` registers a publishing function for a specific service for a specific event or all events if no event name was given. ```ts import { HookContext } from './declarations' import type { Params } from '@feathersjs/feathers' import type { AuthenticationResult } from '@feathersjs/authentication' app.on('login', (payload: AuthenticationResult, { connection }: Params) => { // connection can be undefined if there is no // real-time connection, e.g. when logging in via REST if (connection && connection.user.isAdmin) { app.channel('admins').join(connection) } }) // Publish all messages service events only to its room channel app.service('messages').publish((data: Message, context: HookContext) => { return app.channel(`rooms/${data.roomId}`) }) // Publish the `created` event to admins and the user that sent it app.service('users').publish('created', (data: User, context: HookContext) => { return [ app.channel('admins'), app.channel(app.channels).filter((connection) => connection.user._id === context.params.user._id) ] }) // Prevent all events in the `password-reset` service from being published app.service('password-reset').publish(() => null) ``` ### app.publish([event,] fn) `app.publish([event,] fn) -> app` registers a publishing function for all services for a specific event or all events if no event name was given. ```ts import type { Params } from '@feathersjs/feathers' import type { AuthenticationResult } from '@feathersjs/authentication' app.on('login', (payload: AuthenticationResult, { connection }: Params) => { // connection can be undefined if there is no // real-time connection, e.g. when logging in via REST if (connection) { app.channel('authenticated').join(connection) } }) // Publish all events to all authenticated users app.publish((data: any, context: HookContext) => { return app.channel('authenticated') }) // Publish the `log` custom event to all connections app.publish('log', (data: any, context: HookContext) => { return app.channel(app.channels) }) ``` ### Publisher precedence The first publisher callback found in the following order will be used: 1. Service publisher for a specific event 2. Service publisher for all events 3. App publishers for a specific event 4. App publishers for all events ## Keeping channels updated Since every application will be different, keeping the connections assigned to channels up to date (e.g. if a user joins/leaves a room) is up to you. In general, channels should reflect your persistent application data. This means that it normally isn't necessary for e.g. a user to request to directly join a channel. This is especially important when running multiple instances of an application since channels are only _local_ to the current instance. Instead, the relevant information (e.g. what rooms a user is currently in) should be stored in the database and then the active connections can be re-distributed into the appropriate channels listening to the proper [service events](./events.md). The following example updates all active connections for a given user when the user object (which is assumed to have a `rooms` array being a list of room ids the user has joined) is updated or removed: ```ts import type { RealTimeConnection } from '@feathersjs/feathers' import type { Params } from '@feathersjs/feathers' import type { AuthenticationResult } from '@feathersjs/authentication' // Join a channel given a user and connection const joinChannels = (user: User, connection: RealTimeConnection) => { app.channel('authenticated').join(connection) // Assuming that the chat room/user assignment is stored // on an array of the user user.rooms.forEach((room) => app.channel(`rooms/${roomId}`).join(connection)) } // Get a user to leave all channels const leaveChannels = (user: User) => { app.channel(app.channels).leave((connection) => connection.user._id === user._id) } // Leave and re-join all channels with new user information const updateChannels = (user: User) => { // Find all connections for this user const { connections } = app.channel(app.channels).filter((connection) => connection.user._id === user._id) // Leave all channels leaveChannels(user) // Re-join all channels with the updated user information connections.forEach((connection) => joinChannels(user, connection)) } app.on('login', (payload: AuthenticationResult, { connection }: Params) => { if (connection) { // Join all channels on login joinChannels(connection.user, connection) } }) // On `updated` and `patched`, leave and re-join with new room assignments app.service('users').on('updated', updateChannels) app.service('users').on('patched', updateChannels) // On `removed`, remove the connection from all channels app.service('users').on('removed', leaveChannels) ```
The number active connections is usually one (or none) but unless you prevent it explicitly Feathers is not preventing multiple logins of the same user (e.g. with two open browser windows or on a mobile device).================================================ FILE: docs/api/client/rest.md ================================================ --- outline: deep --- # REST Client The following chapter describes the use of - [@feathersjs/rest-client](#feathersjsrest-client) as a client side Feathers HTTP API integration - [Direct connection](#http-api) with any other HTTP client ## rest-client
For directly using a Feathers REST API (via HTTP) without using Feathers on the client see the [HTTP API](#http-api) section.
REST client services do emit `created`, `updated`, `patched` and `removed` events but only _locally for their own instance_. Real-time events from other clients can only be received by using a real-time transport like [Socket.io](./socketio.md).
A client application can only use **a single transport** (e.g. either REST or Socket.io). Using two transports in the same client application is not necessary.### rest([baseUrl]) REST client services can be initialized by loading `@feathersjs/rest-client` and initializing a client object with a base URL. ```ts import { feathers } from '@feathersjs/feathers' import rest from '@feathersjs/rest-client' const app = feathers() // Connect to the same as the browser URL (only in the browser) const restClient = rest() // Connect to a different URL const restClient = rest('http://feathers-api.com') // Configure an AJAX library (see below) with that client app.configure(restClient.fetch(window.fetch.bind(window))) // Connect to the `http://feathers-api.com/messages` service const messages = app.service('messages') ``` 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 of `http://api.feathersjs.com` would be available as `app.service('api/v1/messages')` - A base URL of `http://api.feathersjs.com/api/v1` would be `app.service('messages')`.
In the browser `window.fetch` (which the same as the global `fetch`) has to be passed as `window.fetch.bind(window)` otherwise it will be called with an incorrect context, causing a JavaScript error: `Failed to execute 'fetch' on 'Window': Illegal invocation`.### params.headers Request specific headers can be through `params.headers` in a service call: ```js app.service('messages').create( { text: 'A message from a REST client' }, { headers: { 'X-Requested-With': 'FeathersJS' } } ) ``` ### params.connection Allows to pass additional options specific to the AJAX library. `params.connection.headers` will be merged with `params.headers`: ```js app.configure(restClient.axios(axios)) app.service('messages').get(1, { connection: { // Axios specific options here } }) ``` ### app.rest `app.rest` contains a reference to the `connection` object passed to `rest().
Just like on the server _all_ methods you want to use have to be listed in the `methods` option.### Connecting to multiple servers It is possible to instantiate and use individual services pointing to different servers by calling `rest('server').
If the authentication information is different, it needs to be set as an option as shown above or via `params.headers` when making the request.### Extending rest clients This can be useful if you e.g. wish to override how the query is transformed before it is sent to the API. ```ts import type { Query } from '@feathersjs/feathers' import { FetchClient } from '@feathersjs/rest-client' import qs from 'qs' class CustomFetch extends FetchClient { getQuery(query: Query) { if (Object.keys(query).length !== 0) { const queryString = qs.stringify(query, { strictNullHandling: true }) return `?${queryString}` } return '' } } app.configure(restClient.fetch(fetch, CustomFetch)) ``` ## HTTP API You can communicate with a Feathers REST API using any other 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](../hooks.md) and [Express middleware](../express.md). URL query parameter values will always be strings. Conversion (e.g. the string `'true'` to boolean `true`) on the server is done via [schemas](../schema/index.md) or [hooks](../hooks.md). The body type for `POST`, `PUT` and `PATCH` requests is determined by the request type. You should also make sure you are setting your `Accept` header to `application/json`. Here is the mapping of service methods to REST API calls: | 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 | ### Authentication Authenticating HTTP (REST) requests is a two step process. First you have to obtain a JWT from the [authentication service](../authentication/service.md) by POSTing the strategy you want to use: ```json // POST /authentication the Content-Type header set to application/json { "strategy": "local", "email": "your email", "password": "your password" } ``` Here is what that looks like with curl: ```bash curl -H "Content-Type: application/json" -X POST -d '{"strategy":"local","email":"your email","password":"your password"}' http://localhost:3030/authentication ``` Then to authenticate subsequent requests, add the returned `accessToken` to the `Authorization` header as `Bearer
With a [database adapters](../databases/adapters.md) the [`multi` option](../databases/common.md) has to be set explicitly to support creating multiple entries.### 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?status=unread { "status": "read" } ``` Will call `messages.update(null, { status: 'read' }, { query: { status: 'unread' } })` on the server. ### patch Merge the existing data of a single or multiple resources with the new `data`. ``` PATCH /messages/2 { "status": "read" } ``` Will call `messages.patch(2, { status: 'read' }, {})` on the server. When no `id` is given by sending the request directly to the endpoint something like: ``` PATCH /messages?status=unread { "status": "read" } ``` Will call `messages.patch(null, { status: 'read' }, { query: { status: 'unread' } })` on the server to change the status for all read messages.
With a [database adapters](../databases/adapters.md) the [`multi` option](../databases/common.md) has to be set to support patching multiple entries.This is supported out of the box by the Feathers [database adapters](../databases/adapters.md) ### remove Remove a single or multiple resources: ``` DELETE /messages/2 ``` Will call `messages.remove(2, {} })`. When no `id` is given by sending the request directly to the endpoint something like: ``` DELETE /messages?status=archived ``` Will call `messages.remove(null, { query: { status: 'archived' } })` to delete all read messages.
With a [database adapters](../databases/adapters.md) the [`multi` option](../databases/common.md) has to be set to support patching multiple entries.### Custom methods [Custom service methods](../services.md#custom-methods) can be called directly via HTTP by sending a POST request and setting the `X-Service-Method` header to the method you want to call: ``` POST /messages X-Service-Method: myCustomMethod { "message": "Hello world" } ``` Via CURL: ```bash curl -H "Content-Type: application/json" -H "X-Service-Method: myCustomMethod" -X POST -d '{"message": "Hello world"}' http://localhost:3030/myservice ``` This will call `messages.myCustomMethod({ message: 'Hello world' }, {})`. ### Route placeholders Service URLs can have placeholders, e.g. `users/:userId/messages`. (see in [express](../express.md#params.route) or [koa](../koa.md#params.route)) You can call the client with route placeholders in the `params.route` property: ```ts import { feathers } from '@feathersjs/feathers' import rest from '@feathersjs/rest-client' const app = feathers() // Connect to the same as the browser URL (only in the browser) const restClient = rest() // Connect to a different URL const restClient = rest('http://feathers-api.com') // Configure an AJAX library (see below) with that client app.configure(restClient.fetch(window.fetch.bind(window))) // Connect to the `http://feathers-api.com/messages` service const messages = app.service('users/:userId/messages') // Call the `http://feathers-api.com/users/2/messages` URL messages.find({ route: { userId: 2 } }) ``` This can also be achieved by using the client bundled, sharing several `servicePath` variable exported in the [service shared file](../../guides/cli/service.shared.md#Variables) file. ```ts import rest from '@feathersjs/rest-client' // usersMessagesPath contains 'users/:userId/messages' import { createClient, usersMessagesPath } from 'my-app' const connection = rest('https://myapp.com').fetch(window.fetch.bind(window)) const client = createClient(connection) // Call the `https://myapp.com/users/2/messages` URL client.service(usersMessagesPath).find({ route: { userId: 2 } }) ``` ================================================ FILE: docs/api/client/socketio.md ================================================ --- outline: deep --- # Socket.io Client ## socketio-client
Socket.io is also used to _call_ service methods. Using sockets for both calling methods and receiving real-time events is generally faster than using [REST](./rest.md). There is therefore no need to use both REST and Socket.io in the same client application.### socketio(socket) Initialize the Socket.io client using a given socket and the default options. ```ts import { feathers } from '@feathersjs/feathers' import socketio from '@feathersjs/socketio-client' import io from 'socket.io-client' const socket = io('http://api.feathersjs.com') const app = feathers() // Set up Socket.io client with the socket app.configure(socketio(socket)) // Receive real-time events through Socket.io app.service('messages').on('created', (message) => console.log('New message created', message)) // Call the `messages` service app.service('messages').create({ text: 'A message from a REST client' }) ``` ### `app.io` `app.io` contains a reference to the `socket` object passed to `socketio(socket [, options])` ```ts app.io.on('disconnect', (reason: any) => { // Show offline message }) ``` ### Custom Methods On the client, [custom service methods](../services.md#custom-methods) are also registered using the `methods` option when registering the service via `socketClient.service()`: ```ts import { feathers } from '@feathersjs/feathers' import type { Params } from '@feathersjs/feathers' import socketio from '@feathersjs/socketio-client' import type { SocketService } from '@feathersjs/socketio-client' import io from 'socket.io-client' // `data` and return type of custom method type CustomMethodData = { name: string } type CustomMethodResponse = { acknowledged: boolean } type ServiceTypes = { // The type is a Socket service extended with custom methods myservice: SocketService & { myCustomMethod(data: CustomMethodData, params: Params): Promise
Just like on the server _all_ methods you want to use have to be listed in the `methods` option.### Route placeholders Service URLs can have placeholders, e.g. `users/:userId/messages`. (see in [express](../express.md#params.route) or [koa](../koa.md#params.route)) You can call the client with route placeholders in the `params.route` property: ```ts import { feathers } from '@feathersjs/feathers' import socketio from '@feathersjs/socketio-client' import io from 'socket.io-client' const socket = io('http://api.feathersjs.com') const app = feathers() // Set up Socket.io client with the socket app.configure(socketio(socket)) // Call `users/2/messages` app.service('users/:userId/messages').find({ route: { userId: 2 } }) ``` This can also be achieved by using the client bundled, sharing several `servicePath` variable exported in the [service shared file](../../guides/cli/service.shared.md#Variables) file. ```ts import rest from '@feathersjs/rest-client' const connection = rest('https://myapp.com').fetch(window.fetch.bind(window)) const client = createClient(connection) // Call the `https://myapp.com/users/2/messages` URL client.service(usersMyMessagesPath).find({ route: { userId: 2 } }) import io from 'socket.io-client' import socketio from '@feathersjs/socketio-client' import { createClient, usersMessagesPath } from 'my-app' const socket = io('http://api.my-feathers-server.com') const connection = socketio(socket) const client = createClient(connection) const messageService = client.service('users/:userId/messages') // Call `users/2/messages` app.service('users/:userId/messages').find({ route: { userId: 2 } }) ``` ## Direct connection Feathers sets up a normal Socket.io server that you can connect to with any Socket.io compatible client, usually the [Socket.io client](http://socket.io/docs/client-api/) either by loading the `socket.io-client` module or `/socket.io/socket.io.js` from the server. Query parameter types do not have to be converted from strings as they do for REST requests.
The socket connection URL has to point to the server root which is where Feathers will set up Socket.io.```html ``` Service methods can be called by emitting a `
When a socket disconnects and then reconnects, it has to be authenticated again before making any other request that requires authentication. This is usually done with the [jwt strategy](../authentication/jwt.md) using the `accessToken` from the `authResult`. The [authentication client](../authentication/client.md) handles this already automatically.```js socket.on('connect', () => { socket.emit( 'create', 'authentication', { strategy: 'jwt', accessToken: authResult.accessToken }, function (error, newAuthResult) { console.log(newAuthResult) } ) }) ``` #### Via handshake headers If the authentication strategy (e.g. JWT or API key) supports parsing headers, an authenticated websocket connection can be established by adding the information in the [extraHeaders option](https://socket.io/docs/client-api/#With-extraHeaders): ```ts import io from 'socket.io-client' const socket = io('http://localhost:3030', { extraHeaders: { Authorization: `Bearer
The authentication strategy needs to be included in the [`authStrategies` configuration](../authentication/service.md#configuration).### find Retrieves a list of all matching resources from the service ```js socket.emit('find', 'messages', { status: 'read', user: 10 }, (error, data) => { console.log('Found all messages', data) }) ``` Will call `app.service('messages').find({ query: { status: 'read', user: 10 } })` on the server. ### get Retrieve a single resource from the service. ```js socket.emit('get', 'messages', 1, (error, message) => { console.log('Found message', message) }) ``` Will call `app.service('messages').get(1, {})` on the server. ```js socket.emit('get', 'messages', 1, { status: 'read' }, (error, message) => { console.log('Found message', message) }) ``` Will call `app.service('messages').get(1, { query: { status: 'read' } })` on the server. ### create Create a new resource with `data` which may also be an array. ```js socket.emit( 'create', 'messages', { text: 'I really have to iron' }, (error, message) => { console.log('Todo created', message) } ) ``` Will call `app.service('messages').create({ text: 'I really have to iron' }, {})` on the server. ```js socket.emit('create', 'messages', [{ text: 'I really have to iron' }, { text: 'Do laundry' }]) ``` Will call `app.service('messages').create` with the array. ### update Completely replace a single or multiple resources. ```js socket.emit( 'update', 'messages', 2, { text: 'I really have to do laundry' }, (error, message) => { console.log('Todo updated', message) } ) ``` Will call `app.service('messages').update(2, { text: 'I really have to do laundry' }, {})` on the server. The `id` can also be `null` to update multiple resources: ```js socket.emit( 'update', 'messages', null, { status: 'unread' }, { status: 'read' } ) ``` Will call `app.service('messages').update(null, { status: 'read' }, { query: { satus: 'unread' } })` on the server. ### patch Merge the existing data of a single or multiple resources with the new `data`. ```js socket.emit( 'patch', 'messages', 2, { read: true }, (error, message) => { console.log('Patched message', message) } ) ``` Will call `app.service('messages').patch(2, { read: true }, {})` on the server. The `id` can also be `null` to update multiple resources: ```js socket.emit( 'patch', 'messages', null, { status: 'read' }, { status: 'unread' }, (error, message) => { console.log('Patched message', message) } ) ``` Will call `app.service('messages').patch(null, { status: 'read' }, { query: { status: 'unread' } })` on the server, to change the status for all read app.service('messages'). ### remove Remove a single or multiple resources: ```js socket.emit('remove', 'messages', 2, {}, (error, message) => { console.log('Removed a message', message) }) ``` Will call `app.service('messages').remove(2, {})` on the server. The `id` can also be `null` to remove multiple resources: ```js socket.emit('remove', 'messages', null, { status: 'archived' }) ``` Will call `app.service('messages').remove(null, { query: { status: 'archived' } })` on the server to delete all messages with status `archived`. ### Custom methods [Custom service methods](../services.md#custom-methods) can be called directly via Socket.io by sending a `socket.emit(methodName, serviceName, data, query)` message: ```js socket.emit('myCustomMethod', 'myservice', { message: 'Hello world' }, {}, (error, data) => { console.log('Called myCustomMethod', data) }) ``` ### Listening to events Listening to service events allows real-time behaviour in an application. [Service events](../events.md) are sent to the socket in the form of `servicepath eventname`. #### created The `created` event will be published with the callback data, when a service `create` returns successfully. ```ts const socket = io('http://localhost:3030/') socket.on('messages created', (message: Message) => { console.log('Got a new Todo!', message) }) ``` #### updated, patched The `updated` and `patched` events will be published with the callback data, when a service `update` or `patch` method calls back successfully. ```ts const socket = io('http://localhost:3030/') socket.on('my/messages updated', (message: Message) => { console.log('Got an updated Todo!', message) }) socket.emit( 'update', 'my/messages', 1, { text: 'Updated text' }, {}, (error, callback) => { // Do something here } ) ``` #### removed The `removed` event will be published with the callback data, when a service `remove` calls back successfully. ```js const socket = io('http://localhost:3030/') socket.on('messages removed', (message: Message) => { // Remove element showing the Todo from the page $('#message-' + message.id).remove() }) ``` #### Custom events [Custom events](../events.md#custom-events) can be listened to accordingly: ```ts const socket = io('http://localhost:3030/') socket.on('messages myevent', function (data: any) { console.log('Got myevent event', data) }) ``` ================================================ FILE: docs/api/client.md ================================================ --- outline: deep --- # Feathers Client One of the most notable features of Feathers is that it can also be used as the client. In contrast with most other frameworks, it isn't a separate library; instead you get the exact same functionality with a client and on a server. This means you can use [services](./services.md) and [hooks](./hooks.md) and configure plugins. By default, a Feathers client automatically creates services that talk to a Feathers server. In order to connect to a Feathers server, a client creates [Services](./services.md) that use a REST or websocket connection to relay method calls and - for a real-time transport like Socket.io - allow listening to [events](./events.md) on the server. This means the [Feathers application instance](./application.md) is usable the exact same way as on the server. Modules most relevant on the client are: - `@feathersjs/feathers` to initialize a new Feathers [application](./application.md) - [@feathersjs/rest-client](./client/rest.md) to connect to services through REST HTTP provided by [Koa](./koa.md) or [Express](./express.md). - [@feathersjs/socketio-client](./client/socketio.md) to connect to services through [Socket.io](./socketio.md). - [@feathersjs/authentication-client](./authentication/client.md) to authenticate a client
You do not have to use Feathers on the client to connect to a Feathers server. The client [REST client](./client/rest.md) and [Socket.io client](./client/socketio.md) chapters also describe how to use the connection directly without Feathers on the client side.This chapter describes how to set up Feathers as the client in Node, React Native and in the browser with a module loader like Webpack or Parcel or through a ` ``` ================================================ FILE: docs/api/configuration.md ================================================ --- outline: deep --- # Configuration
Direct access to nested config properties is not supported via `app.get()`. To access a nested config property (e.g. `Customer.dbConfig.host`, use `app.get('Customer').dbConfig.host`.## Configuration validation `app.configure(configuration(validator))` loads the configuration from `node-config`, makes it available via `app.get()` and validates the original configuration against a [Feathers schema](./schema/) validator when [app.setup](./application.md#setup-server) (or [app.listen](./application.md#listen-port)) is called. ```ts import { feathers } from '@feathersjs/feathers' import { Ajv } from '@feathersjs/schema' import { Type, getValidator } from '@feathersjs/typebox' import type { Static } from '@feathersjs/typebox' import configuration from '@feathersjs/configuration' const configurationSchema = Type.Object( { port: Type.Number(), host: Type.String() }, { $id: 'Configuration', additionalProperties: false } ) const configurationValidator = getValidator(configurationSchema, new Ajv()) type ServiceTypes = {} // Use the schema type for typed `app.get` and `app.set` calls type Configuration = Static
The NODE_CONFIG_DIR environment variable isn’t used directly by @feathersjs/configuration but by the [node-config](https://github.com/lorenwest/node-config) module that it uses. For more information on configuring node-config settings, see the [Configuration Files Wiki page](https://github.com/lorenwest/node-config/wiki/Configuration-Files).================================================ FILE: docs/api/databases/adapters.md ================================================ --- outline: deep --- # Overview Feathers database adapters are modules that provide [services](../services.md) that implement standard [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) functionality for a specific database. They use a [common API](./common.md) for initialization and settings, and they provide a [common query syntax](./querying.md).
[Services](../services.md) allow to implement access to _any_ database or API. The database adapters listed here are just convenience wrappers with a common API. See the community adapters section for support for other datastores.## Core Adapters The following data storage adapters are available in Feathers core | Core Package | Supported Data Stores | | -------------------- | -------------------------------------------------------------------------------------------------------------- | | [Memory](./memory) | Memory | | [MongoDB](./mongodb) | MongoDB | | [SQL (Knex)](./knex) | MySQL
Every database adapter is an implementation of the [Feathers service interface](../services.md). If there is no adapter available for your database of choice, you can still use it directly in a [custom service](../services.md). We recommend being familiar with Feathers services, service events and hooks and the database before using a database adapter.## Initialization ### `new
Disabling or changing the default pagination is not available in the client. Only `params.query` is passed to the server (also see a [workaround here](https://github.com/feathersjs/feathers/issues/382#issuecomment-288125825))## params.adapter Setting the `adapter` in the [service method `params`](../services.md#params) allows do dynamically modify the database adapter options based on the request. This e.g. allows to temporarily allow multiple entry creation/changes or the pagination settings. ```ts const messages = [ { text: 'message 1' }, { text: 'message 2' } ] // Enable multiple entry insertion for this request app.service('messages').create(messages, { adapter: { multi: true } }) ```
If the adapter has a `Model` option, `params.adapter.Model` can be used to point to different databases based on the request to e.g. allow multi-tenant systems. This is usually done by setting `context.params.adapter` in a [hook](../hooks.md).## params.paginate Setting `paginate` in the [service method `params`](../services.md#params) allows to change or disable the default pagination for a single request: ```ts // Get all messages as an array const allMessages = await app.service('messages').find({ paginate: false }) ``` ## Extending Adapters There are two ways to extend existing database adapters. Either by extending the base class or by adding functionality through hooks. ### Classes All modules also export an [ES6 class](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes) as `
These methods are only available internally on the server, not on the client side and only for the Feathers database adapters. They do _not_ send any events.### adapter.find(params) `adapter.find(params) -> Promise` returns a list of all records matching the query in `params.query` using the [common querying mechanism](./querying.md). Will either return an array with the results or a page object if [pagination is enabled](#pagination). ```ts // Find all messages for user with id 1 const messages = await app.service('messages').find({ query: { userId: 1 } }) console.log(messages) // Find all messages belonging to room 1 or 3 const roomMessages = await app.service('messages').find({ query: { roomId: { $in: [1, 3] } } }) console.log(roomMessages) ``` Find all messages for user with id 1 ``` GET /messages?userId=1 ``` Find all messages belonging to room 1 or 3 ``` GET /messages?roomId[$in]=1&roomId[$in]=3 ``` ### adapter.get(id, params) `adapter.get(id, params) -> Promise` retrieves a single record by its unique identifier (the field set in the `id` option during initialization). ```ts const message = await app.service('messages').get(1) console.log(message) ``` ``` GET /messages/1 ``` ### adapter.create(data, params) `adapter.create(data, params) -> Promise` creates a new record with `data`. `data` can also be an array to create multiple records. ```ts const message = await app.service('messages').create({ text: 'A test message' }) console.log(message) const messages = await app.service('messages').create([ { text: 'Hi' }, { text: 'How are you' } ]) console.log(messages) ``` ``` POST /messages { "text": "A test message" } ``` ### adapter.update(id, data, params) `adapter.update(id, data, params) -> Promise` completely replaces a single record identified by `id` with `data`. Does not allow replacing multiple records (`id` can't be `null`). `id` can not be changed. ```ts const updatedMessage = await app.service('messages').update(1, { text: 'Updates message' }) console.log(updatedMessage) ``` ``` PUT /messages/1 { "text": "Updated message" } ``` ### adapter.patch(id, data, params) `adapter.patch(id, data, params) -> Promise` merges a record identified by `id` with `data`. `id` can be `null` to allow replacing multiple records (all records that match `params.query` the same as in `.find`). `id` can not be changed. ```ts const patchedMessage = await app.service('messages').patch(1, { text: 'A patched message' }) console.log(patchedMessage) const params = { query: { read: false } } // Mark all unread messages as read const multiPatchedMessages = await app.service('messages').patch( null, { read: true }, params ) ``` ``` PATCH /messages/1 { "text": "A patched message" } ``` Mark all unread messages as read ``` PATCH /messages?read=false { "read": true } ``` ### adapter.remove(id, params) `adapter.remove(id, params) -> Promise` removes a record identified by `id`. `id` can be `null` to allow removing multiple records (all records that match `params.query` the same as in `.find`). ```ts const removedMessage = await app.service('messages').remove(1) console.log(removedMessage) const params = { query: { read: true } } // Remove all read messages const removedMessages = await app.service('messages').remove(null, params) ``` ``` DELETE /messages/1 ``` Remove all read messages ``` DELETE /messages?read=true ``` ================================================ FILE: docs/api/databases/knex.md ================================================ --- outline: deep --- # SQL Databases
The Knex adapter implements the [common database adapter API](./common) and [querying syntax](./querying).## API ### KnexService(options) `new KnexService(options)` returns a new service instance initialized with the given options. The following example extends the `KnexService` and then uses the `sqliteClient` (or relevant client for your SQL database type) from the app configuration and provides it to the `Model` option, which is passed to the new `MessagesService`. ```ts import type { Params } from '@feathersjs/feathers' import { KnexService } from '@feathersjs/knex' import type { KnexAdapterParams, KnexAdapterOptions } from '@feathersjs/knex' import type { Application } from '../../declarations' import type { Messages, MessagesData, MessagesQuery } from './messages.schema' export interface MessagesParams extends KnexAdapterParams
Since SQL does not have a concept of nested objects, joined properties will be dot separated strings, **not nested objects**. Conversion can be done by e.g. using Lodash `_.set` in a [resolver converter](../schema/resolvers.md#options).This works well for individual properties, however if you require the complete (and safe) representation of the entire related data, use a [resolver](../schema/resolvers.md) instead. ## Transactions The Knex adapter comes with three hooks that allows to run service method calls in a transaction. They can be used as application wide hooks or per service. To use the transactions feature, you must ensure that the three hooks (start, end and rollback) are being used. At the start of any request, a new transaction will be started. All the changes made during the request to the services that are using knex will use the transaction. At the end of the request, if successful, the changes will be commited. If an error occurs, the changes will be forfeit, all the `creates`, `patches`, `updates` and `deletes` are not going to be commited. The object that contains `transaction` is stored in the `params.transaction` of each request.
If you call another Knex service within a hook and want to share the transaction you will have to pass `context.params.transaction` in the parameters of the service call.Sometimes it can be important to know when the transaction has been completed (committed or rolled back). For example, we might want to wait for transaction to complete before we send out any realtime events. This can be done by awaiting on the `transaction.committed` promise which will always resolve to either `true` in case the transaction has been committed, or `false` in case the transaction has been rejected. ```ts app.service('messages').publish(async (data, context) => { const { transaction } = context.params if (transaction) { const success = await transaction.committed if (!success) { return [] } } return app.channel(`rooms/${data.roomId}`) }) ``` This also works with nested service calls and nested transactions. For example, if a service calls `transaction.start()` and passes the transaction param to a nested service call, which also calls `transaction.start()` in it's own hooks, they will share the top most `committed` promise that will resolve once all of the transactions have successfully committed. ### Example Transaction Setup We will be using TypeBox schemas throughout, but that is not a requirement. We will have two services `Order` and `ShippingOrder` When we create an `Order` we want to automatically create a `ShippingOrder`, but if `Order` or `ShippingOrder` fail to be created we want to roll everything back and not save either. #### Order Schema ```ts export const orderSchema = Type.Object( { id: Type.String({ format: 'uuid' }), item: Type.String(), address: Type.String(), quantity: Type.Number() }, { $id: 'Order', additionalProperties: false } ) ``` #### Shipping Order Schema ```ts export const shippingOrderSchema = Type.Object( { id: Type.String({ format: 'uuid' }), order_id: Type.String({ format: 'uuid', $schema: 'Order' }), expedited: Type.Boolean(), shipped: Type.Boolean() }, { $id: 'ShippingOrder', additionalProperties: false } ) ``` #### After hook Let's start by adding our logic to automatically create our `ShippingOrder`. In our `order.ts` file we can add this hook ```ts after: { create: [ async (context: HookContext
You can emulate an error by throwing an error in the before create hook of your `shipping-order.ts` file ```ts create: [ async () => { throw new Error('Fail') }, schemaHooks.validateData(shippingOrderDataValidator), schemaHooks.resolveData(shippingOrderDataResolver) ] ```#### Application wide wrapping transaction Using the global hooks in `src/app.ts` we are able to wrap all of our `create`, `update`, and `patch` hooks. ```ts import { transaction } from '@feathersjs/knex' const transactionHandler = async (context: HookContext
We have to use await here otherwise the transaction will close before the creation is finished. For something like sending an email, you can opt to not await. ```ts context.params.transaction?.committed.then((success: any) => { if (!success) return //Send Email }) ```### Service wide wrapping transaction The simplest way of doing this is - Add `transaction.start()` in the before create hook. - Add `transaction.end()` in the after create hook. - Add `transaction.rollback()` in the error all hook. ```ts app.service(orderPath).hooks({ around: { // ... }, before: { // ... create: [ schemaHooks.validateData(orderDataValidator), schemaHooks.resolveData(orderDataResolver), transaction.start() ] }, after: { create: [ async (context: HookContext
The memory adapter implements the [common database adapter API](./common) and [querying syntax](./querying).## API ### Usage ```ts import { MemoryService } from '@feathersjs/memory' type Message = { id: number text: string } type MessageData = Pick
The MongoDB adapter implements the [common database adapter API](./common) and [querying syntax](./querying).## API ### `MongoDBService(options)` `new MongoDBService(options)` returns a new service instance initialized with the given options. The following example extends the `MongoDBService` and then uses the `mongodbClient` from the app configuration and provides it to the `Model` option, which is passed to the new `MessagesService`. ```ts import type { Params } from '@feathersjs/feathers' import { MongoDBService } from '@feathersjs/mongodb' import type { MongoDBAdapterParams, MongoDBAdapterOptions } from '@feathersjs/mongodb' import type { Application } from '../../declarations' import type { Messages, MessagesData, MessagesQuery } from './messages.schema' export interface MessagesParams extends MongoDBAdapterParams
Note that creating indexes for an existing collection with many entries should be done as a separate operation instead. See the [MongoDB createIndex documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.createIndex/) for more information.## Querying Additionally to the [common querying mechanism](./querying.md) this adapter also supports [MongoDB's query syntax](https://www.mongodb.com/docs/manual/tutorial/query-documents/) and the `update` method also supports MongoDB [update operators](https://www.mongodb.com/docs/manual/reference/operator/update/). ## Securing update operators The `patch` method supports MongoDB [update operators](https://www.mongodb.com/docs/manual/reference/operator/update/) like `$push`, `$inc`, and `$unset` in the data payload. While this is powerful, it can be a security risk if patch data from the client is not properly validated. For example, an authenticated user who can patch their own profile could send: ```ts // Escalate privileges by pushing to a roles array await app.service('users').patch(userId, { $push: { roles: 'admin' } }) // Expose internal fields by renaming them await app.service('users').patch(userId, { $rename: { secretField: 'public' } }) ``` ### Schema validation The primary defense is to use [schema validation](../schema/validators.md) on your patch data. When your schema only allows known fields with known types, unexpected operators will be rejected before they reach the database. ### The `disabledOperators` option As an additional layer of defense, the `disabledOperators` option blocks specific update operators from being passed through to MongoDB. By default, `$rename` is blocked. To block additional operators on a service: ```ts new MongoDBService({ Model: app.get('mongodbClient').then((db) => db.collection('users')), disabledOperators: ['$rename', '$unset', '$inc'] }) ``` To override per-call via `params.adapter`: ```ts service.patch(id, data, { adapter: { disabledOperators: ['$rename', '$unset'] } }) ``` To allow all operators (not recommended without schema validation): ```ts new MongoDBService({ Model: app.get('mongodbClient').then((db) => db.collection('messages')), disabledOperators: [] }) ``` ## Search
Note that in a normal application all MongoDB specific operators have to explicitly be added to the [TypeBox query schema](../schema/typebox.md#query-schemas) or [JSON query schema](../schema/schema.md#querysyntax).There are two ways to perform search queries with MongoDB: - Perform basic Regular Expression matches using the `$regex` filter. - Perform full-text search using the `$search` filter. ### Basic Regex Search You can perform basic search using regular expressions with the `$regex` operator. Here's an example query. ```js { text: { $regex: 'feathersjs', $options: 'igm' }, } ``` ### Full-Text Search See the MongoDB documentation for instructions on performing full-text search using the `$search` operator: - Perform [full-text queries on self-hosted MongoDB](https://www.mongodb.com/docs/manual/core/link-text-indexes/). - Perform [full-text queries on MongoDB Atlas](https://www.mongodb.com/docs/atlas/atlas-search/) (MongoDB's first-party hosted database). - Perform [full-text queries with the MongoDB Pipeline](https://www.mongodb.com/docs/manual/tutorial/text-search-in-aggregation/) ## Aggregation Pipeline In Feathers v5 Dove, we added support for the full power of MongoDB's Aggregation Framework and blends it seamlessly with the familiar Feathers Query syntax. The `find` method automatically uses the aggregation pipeline when `params.pipeline` is set. The Aggregation Framework is accessed through the mongoClient's `model.aggregate` method, which accepts an array of "stages". Each stage contains an operator which describes an operation to apply to the previous step's data. Each stage applies the operation to the results of the previous step. It’s now possible to perform any of the [Aggregation Stages](https://www.mongodb.com/docs/upcoming/reference/operator/aggregation-pipeline/) like `$lookup` and `$unwind`, integration with the normal Feathers queries. Here's how it works with the operators that match the Feathers Query syntax. Let's convert the following Feathers query: ```ts const query = { text: { $regex: 'feathersjs', $options: 'igm' }, $sort: { createdAt: -1 }, $skip: 20, $limit: 10 } ``` The above query looks like this when converted to aggregation pipeline stages: ```ts ;[ // returns the set of records containing the word "feathersjs" { $match: { text: { $regex: 'feathersjs', $options: 'igm' } } }, // Sorts the results of the previous step by newest messages, first. { $sort: { createdAt: -1 } }, // Skips the first 20 records of the previous step { $skip: 20 }, // returns the next 10 records { $limit: 10 } ] ``` ### Pipeline Queries You can use the `params.pipeline` array to append additional stages to the query. This next example uses the `$lookup` operator together with the `$unwind` operator to populate a `user` attribute onto each message based on the message's `userId` property. ```ts const result = await app.service('messages').find({ query: { $sort: { name: 1 } }, pipeline: [ { $lookup: { from: 'users', localField: 'userId', foreignField: '_id', as: 'user' } }, { $unwind: { path: '$user' } } ], paginate: false }) ``` ### Aggregation Stages In the example, above, the `query` is added to the pipeline, first. Then additional stages are added in the `pipeline` option: - The `$lookup` stage creates an array called `user` which contains any matches in `message.userId`, so if `userId` were an array of ids, any matches would be in the `users` array. However, in this example, the `userId` is a single id, so... - The `$unwind` stage turns the array into a single `user` object. The above is like doing a join, but without the data transforming overhead like you'd get with an SQL JOIN. If you have properly applied index to your MongoDB collections, the operation will typically execute extremely fast for a reasonable amount of data. A couple of other notable query stages: - `$graphLookup` lets you recursively pull in a tree of data from a single collection. - `$search` lets you do full-text search on fields All stages of the pipeline happen directly on the MongoDB server. Read through the full list of supported stages [in the MongoDB documentation](https://www.mongodb.com/docs/upcoming/reference/operator/aggregation-pipeline/). ### The `$feathers` Stage The previous section showed how to append stages to a query using `params.pipeline`. Well, `params.pipeline` also supports a custom `$feathers` operator/stage which allows you to specify exactly where in the pipeline the Feathers Query gets injected. ### Example: Proxy Permissions Imagine a scenario where you want to query the `pages` a user can edit by referencing a `permissions` collection to find out which pages the user can actually edit. Each record in the `permissions` record has a `userId` and a `pageId`. So we need to find and return only the pages to which the user has access by calling `GET /pages` from the client. We could put the following query in a hook to pull the correct `pages` from the database in a single query THROUGH the permissions collection. Remember, the request is coming in on the `pages` service, but we're going to query for pages `through` the permissions collection. Assume we've already authenticated the user, so the user will be found at `context.params.user`. ```ts // Assume this query on the client const pages = await app.service('pages').find({ query: {} }) // And put this query in a hook to populate pages "through" the permissions collection const result = await app.service('permissions').find({ query: {}, pipeline: [ // query all permissions records which apply to the current user { $match: { userId: context.params.user._id } }, // populate the pageId onto each `permission` record, as an array containing one page { $lookup: { from: 'pages', localField: 'pageId', foreignField: '_id', as: 'page' } }, // convert the `page` array into an object, so now we have an array of permissions with permission.page on each. { $unwind: { path: '$page' } }, // Add a permissionId to each page { $addFields: { 'page.permissionId': '$_id' } }, // discard the permission and only keep the populated `page`, and bring it top level in the array { $replaceRoot: { newRoot: '$page' } }, // apply the feathers query stages to the aggregation pipeline. // now the query will apply to the pages, since we made the pages top level in the previous step. { $feathers: {} } ], paginate: false }) ``` Notice the `$feathers` stage in the above example. It will apply the query to that stage in the pipeline, which allows the query to apply to pages even though we had to make the query through the `permissions` service. If we were to express the above query with JavaScript, the final result would the same as with the following example: ```ts // perform a db query to get the permissions const permissions = await context.app.service('permissions').find({ query: { userId: context.params.user._id }, paginate: false }) // make a list of pageIds const pageIds = permissions.map((permission) => permission.pageId) // perform a db query to get the pages with matching `_id` const pages = await context.app.service('pages').find({ query: { _id: { $in: pageIds } }, paginate: false }) // key the permissions by pageId for easy lookup const permissionsByPageId = permissions.reduce((byId, current) => { byId[current.pageId] = current return byId }, {}) // Add the permissionId to each `page` record. const pagesWithPermissionId = pages.map((page) => { page.permissionId = permissionByPageId[page._id]._id return page }) // And now apply the original query, whatever the client may have sent, to the pages. // It might require another database query ``` Both examples look a bit complex, but te one using aggregation stages will be much quicker because all stages run in the database server. It will also be quicker because it all happens in a single database query! One more obstacle for using JavaScript this way is that if the user's query changed (from the front end), we would likely be required to edit multiple different parts of the JS logic in order to correctly display results. With the pipeline example, above, the query is very cleanly applied. ## Collation This adapter includes support for [collation and case insensitive indexes available in MongoDB v3.4](https://docs.mongodb.com/manual/release-notes/3.4/#collation-and-case-insensitive-indexes). Collation parameters may be passed using the special `collation` parameter to the `find()`, `remove()` and `patch()` methods. **Example: Patch records with case-insensitive alphabetical ordering** The example below would patch all student records with grades of `'c'` or `'C'` and above (a natural language ordering). Without collations this would not be as simple, since the comparison `{ $gt: 'c' }` would not include uppercase grades of `'C'` because the code point of `'C'` is less than that of `'c'`. ```ts const patch = { shouldStudyMore: true } const query = { grade: { $gte: 'c' } } const collation = { locale: 'en', strength: 1 } const patchedStudent = await students.patch(null, patch, { query, collation }) ``` **Example: Find records with a case-insensitive search** Similar to the above example, this would find students with a grade of `'c'` or greater, in a case-insensitive manner. ```ts const query = { grade: { $gte: 'c' } } const collation = { locale: 'en', strength: 1 } const collatedStudents = await students.find({ query, collation }) ``` For more information on MongoDB's collation feature, visit the [collation reference page](https://docs.mongodb.com/manual/reference/collation/). ## ObjectIds MongoDB uses [ObjectId](https://www.mongodb.com/docs/manual/reference/method/ObjectId/) object as primary keys. To store them in the right format they have to be converted from and to strings. ### AJV keyword To validate and convert strings to an object id using AJV, the `keywordObjectId` [AJV keyword](https://ajv.js.org/api.html#ajv-addkeyword-definition-string-object-ajv) helper can be used. It is set up automatically in a generated application using MongoDB. ```ts import { keywordObjectId } from '@feathersjs/mongodb' const validator = new Ajv() validator.addKeyword(keywordObjectId) ``` ### ObjectIdSchema Both, `@feathersjs/typebox` and `@feathersjs/schema` export an `ObjectIdSchema` helper that creates a schema which can be both, a MongoDB ObjectId or a string that will be converted with the `objectid` keyword: ```ts import { ObjectIdSchema } from '@feathersjs/typebox' // or '@feathersjs/schema' const typeboxSchema = Type.Object({ userId: ObjectIdSchema() }) const jsonSchema = { type: 'object', properties: { userId: ObjectIdSchema() } } ```
The `ObjectIdSchema` helper will only work when the [`objectid` AJV keyword](#ajv-keyword) is registered.### ObjectId resolvers While the AJV format checks if an object id is valid, it still needs to be converted to the right type. An alternative the the [AJV converter](#ajv-converter) is to use [Feathers resolvers](../schema/resolvers.md). The following [property resolver](../schema/resolvers.md) helpers can be used.
ObjectId resolvers do not need to be used when using the [AJV keyword](#ajv-keyword). They are useful however when using another JSON schema validation library.#### resolveObjectId `resolveObjectId` resolves a property as an object id. It can be used as a direct property resolver or called with the original value. ```ts import { resolveObjectId } from '@feathersjs/mongodb' export const messageDataResolver = resolve
When used via REST URLs all query values are strings and may need to be converted to the correct type. In a v5 application this is done automatically using [schemas](../schema/index.md).## Filters Filters are special properties (starting with a `$`) added at the top level of a query. They can determine page settings, the properties to select and more. ### $limit `$limit` will return only the number of results you specify: ```js // Retrieves the first two unread messages app.service('messages').find({ query: { $limit: 2, read: false } }) ``` ``` GET /messages?$limit=2&read=false ```
With [pagination enabled](common.md#pagination), to just get the number of available records set `$limit` to `0`. This will only run a (fast) counting query against the database and return a page object with the `total` and an empty `data` array.### $skip `$skip` will skip the specified number of results: ```js // Retrieves the next two unread messages app.service('messages').find({ query: { $limit: 2, $skip: 2, read: false } }) ``` ``` GET /messages?$limit=2&$skip=2&read=false ``` ### $sort `$sort` will sort based on the object you provide. It can contain a list of properties by which to sort mapped to the order (`1` ascending, `-1` descending). ```js // Find the 10 newest messages app.service('messages').find({ query: { $limit: 10, $sort: { createdAt: -1 } } }) ``` ``` /messages?$limit=10&$sort[createdAt]=-1 ``` ### $select `$select` allows to pick which fields to include in the result. This will work for any service method. ```js // Only return the `text` and `userId` field in a message app.service('messages').find({ query: { $select: ['text', 'userId'] } }) app.service('messages').get(1, { query: { $select: ['text'] } }) ``` ``` GET /messages?$select[]=text&$select[]=userId GET /messages/1?$select[]=text ``` ### $or Find all records that match any of the given criteria. ```js // Find all messages that are not marked as archived // or any message from room 2 app.service('messages').find({ query: { $or: [{ archived: { $ne: true } }, { roomId: 2 }] } }) ``` ``` GET /messages?$or[0][archived][$ne]=true&$or[1][roomId]=2 ``` ### $and Find all records that match all of the given criteria. ```js // Find all messages that are not marked as archived and in room 2 app.service('messages').find({ query: { $and: [{ archived: { $ne: true } }, { roomId: 2 }] } }) ``` ``` GET /messages?$and[0][archived][$ne]=true&$and[1][roomId]=2 ``` ## Operators Operators either query a property for a specific value or determine nested special properties (starting with a `$`) that allow querying the property for certain conditions. When multiple operators are set, all conditions have to apply for a property to match. ### Equality All fields that do not contain special query parameters are compared directly for equality. ```js // Find all unread messages in room #2 app.service('messages').find({ query: { read: false, roomId: 2 } }) ``` ``` GET /messages?read=false&roomId=2 ``` ### $in, $nin Find all records where the property does (`$in`) or does not (`$nin`) match any of the given values. ```js // Find all messages in room 2 or 5 app.service('messages').find({ query: { roomId: { $in: [2, 5] } } }) ``` ``` GET /messages?roomId[$in][]=2&roomId[$in][]=5 ``` ### $lt, $lte Find all records where the value is less (`$lt`) or less and equal (`$lte`) to a given value. ```js // Find all messages older than a day const DAY_MS = 24 * 60 * 60 * 1000 app.service('messages').find({ query: { createdAt: { $lt: new Date().getTime() - DAY_MS } } }) ``` ``` GET /messages?createdAt[$lt]=1479664146607 ``` ### $gt, $gte Find all records where the value is more (`$gt`) or more and equal (`$gte`) to a given value. ```js // Find all messages within the last day const DAY_MS = 24 * 60 * 60 * 1000 app.service('messages').find({ query: { createdAt: { $gt: new Date().getTime() - DAY_MS } } }) ``` ``` GET /messages?createdAt[$gt]=1479664146607 ``` ### $ne Find all records that do not equal the given property value. ```js // Find all messages that are not marked as archived app.service('messages').find({ query: { archived: { $ne: true } } }) ``` ``` GET /messages?archived[$ne]=true ``` ## Search Searching is not part of the common querying syntax since it is very specific to the database you are using. For built in databases, see the [SQL search](./knex.md#search) and [MongoDb search](./mongodb.md#search) documentation. If you are using [a community supported adapter](/ecosystem/?cat=Database&sort=lastPublish) their documentation may contain additional information on how to implement search functionality. ================================================ FILE: docs/api/errors.md ================================================ --- outline: deep --- # Errors
All of the Feathers core modules and most plugins and database adapters automatically emit the appropriate Feathers errors for you. For example, most of the database adapters will already send `Conflict` or `Unprocessable` errors on validation errors.Feathers errors contain the following fields: - `name` - The error name (e.g. "BadRequest", "ValidationError", etc.) - `message` - The error message string - `code` - The HTTP status code - `className` - A CSS class name that can be handy for styling errors based on the error type. (e.g. "bad-request" , etc.) - `data` - An object containing anything you passed to a Feathers error except for the `errors` object and `message`. - `errors` - An object containing whatever was passed to a Feathers error inside `errors`. This is typically validation errors or if you want to group multiple errors together.
To convert a Feathers error back to an object call `error.toJSON()`. A normal `console.log` of a JavaScript Error object will not automatically show those additional properties described above (even though they can be accessed directly).## Custom errors You can create custom errors by extending from the `FeathersError` class and calling its constructor with `(message, name, code, className, data)`: - `message` - The error message - `name` - The error name (e.g. `MyError`) - `code` - An [HTTP error code](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) - `className` - The full name of the error class (e.g. `my-error`) - `data` - Additional data to include in the error ```ts import { FeathersError } from '@feathersjs/errors' class UnsupportedMediaType extends FeathersError { constructor(message: string, data: any) { super(message, 'UnsupportedMediaType', 415, 'unsupported-media-type', data) } } const error = new UnsupportedMediaType('Not supported') console.log(error.toJSON()) ``` ## Error Handling It is important to make sure that errors get cleaned up before they go back to the client. [Express error handling middleware](https://docs.feathersjs.com/api/express.html#expresserrorhandler) works only for REST calls. If you want to make sure that ws errors are handled as well, you need to use [application error hooks](hooks.md#application-hooks) which are called on any service call error. Here is an example error handler you can add to app.hooks errors. ```js const errors = require('@feathersjs/errors') const errorHandler = (ctx) => { if (ctx.error) { const error = ctx.error if (!error.code) { const newError = new errors.GeneralError('server error') ctx.error = newError return ctx } if (error.code === 404 || process.env.NODE_ENV === 'production') { error.stack = null } return ctx } } ``` then add it as an [application level](./application.md#hooks-hooks) error hook ```ts app.hooks({ //... error: { all: [errorHandler] } }) ``` ================================================ FILE: docs/api/events.md ================================================ --- outline: deep --- # Events Events are the key part of Feathers real-time functionality. All events in Feathers are provided through the [NodeJS EventEmitter](https://nodejs.org/api/events.html) interface. This section describes - A quick overview of the [NodeJS EventEmitter interface](#eventemitters) - The standard [service events](#service-events) - How to allow sending [custom events](#custom-events) from the server to the client
For more information on how to safely send real-time events to clients, see the [Channels chapter](./channels.md).## EventEmitters Once registered, any [service](./services.md) gets turned into a standard [NodeJS EventEmitter](https://nodejs.org/api/events.html) and can be used accordingly. ```ts const messages = app.service('messages') // Listen to a normal service event messages.on('patched', (message: Message) => console.log('message patched', message)) // Only listen to an event once messsages.once('removed', (message: Message) => console.log('First time a message has been removed', message)) // A reference to a handler const onCreatedListener = (message: Message) => console.log('New message created', message) // Listen `created` with a handler reference messages.on('created', onCreatedListener) // Unbind the `created` event listener messages.removeListener('created', onCreatedListener) // Send a custom event messages.emit('customEvent', { anything: 'Data can be anything' }) ``` ## Service Events Any service automatically emits `created`, `updated`, `patched` and `removed` events when the respective service method returns successfully. This works on the client as well as on the server. Events are not fired until all [hooks](./hooks.md) have executed. When the client is using [Socket.io](socketio.md), events will be pushed automatically from the server to all connected clients. This is how Feathers does real-time.
To disable sending of events e.g. when updating a large amount of data, set [context.event](./hooks.md#context-event) to `null` in a hook.Additionally to the event `data`, all events also get the [hook context](./hooks.md) from their method call passed as the second parameter. ### created The `created` event will fire with the result data when a service `create` returns successfully. ```ts import { feathers, type Params, type HookContext } from '@feathersjs/feathers' type Message = { text: string } class MessageService { async create(data: Message) { return data } } const app = feathers<{ messages: MessageService }>() app.use('messages', new MessageService()) // Retrieve the wrapped service object which is also an EventEmitter const messages = app.service('messages') messages.on('created', (message: Message, contexHookContext) => console.log('created', message)) messages.create({ text: 'We have to do something!' }) ``` ### updated, patched The `updated` and `patched` events will fire with the callback data when a service `update` or `patch` method calls back successfully. ```ts import { feathers } from '@feathersjs/feathers' import type { Id, Params, HookContext } from '@feathersjs/feathers' type Message = { text: string } class MessageService { async update(id: Id, data: Message) { return data } async patch(id: Id, data: Message) { return data } } const app = feathers<{ messages: MessageService }>() app.use('messages', new MessageService()) const messages = app.service('my/messages') messages.on('updated', (message: Message, context: HookContext) => console.log('updated', message)) messages.on('patched', (message: Message) => console.log('patched', message)) messages.update(0, { text: 'updated message' }) messages.patch(0, { text: 'patched message' }) ``` ### removed The `removed` event will fire with the callback data when a service `remove` calls back successfully. ```ts import { feathers } from '@feathersjs/feathers' import type { Id, Params, HookContext } from '@feathersjs/feathers' type Message = { text: string } class MessageService { async remove(id: Id, params: Params) { return { id } } } const app = feathers<{ messages: MessageService }>() app.use('messages', new MessageService()) const messages = app.service('messages') messages.on('removed', (message: Message, context: HookContext) => console.log('removed', message)) messages.remove(1) ``` ## Custom events By default, real-time clients will only receive the [standard events](#service-events). However, it is possible to define a list of custom events that should also be sent to the client when registering the service with [app.use](./application.md##use-path-service-options), when `service.emit('customevent', data)` is called on the server. The `context` for custom events won't be a full hook context but just an object containing `{ app, service, path, result }`.
Custom events can only be sent from the server to the client, not the other way (client to server). A [custom service](./services.md) should be used for those cases.For example, a payment service that sends status events to the client while processing a payment could look like this: ```ts class PaymentService { async create(data: any, params: Params) { const customer = await createStripeCustomer(params.user) this.emit('status', { status: 'created' }) const payment = await createPayment(data) this.emit('status', { status: 'completed' }) return payment } } // Then register it like this: app.use('payments', new PaymentService(), { events: ['status'] }) ``` Using `service.emit` custom events can also be sent in a hook: ```js app.service('payments').hooks({ after: { create(context: HookContext) { context.service.emit('status', { status: 'completed' }) } } }) ``` Custom events can be [published through channels](./channels.md#publishing) just like standard events and listened to it in a [Feathers client](./client.md) or [directly on the socket connection](./client/socketio.md#listening-to-events): ```js client.service('payments').on('status', (data) => {}) // or socket.on('payments status', (data) => {}) ``` ================================================ FILE: docs/api/express.md ================================================ --- outline: deep --- # Express
As of Feathers v5, [Koa](./koa.md) is the recommended framework integration since it is more modern, faster and easier to use. When chosen explicitly, you should already be familiar with [Express](http://expressjs.com/en/guide/routing.html).## express(app) `express(app) -> app` is a function that turns a [Feathers application](./application.md) into a fully Express (4+) compatible application that additionally to Feathers functionality also lets you use the [Express API](http://expressjs.com/en/4x/api.html). ```ts import { feathers } from '@feathersjs/feathers' import express from '@feathersjs/express' const app = express(feathers()) ``` Note that `@feathersjs/express` also exposes the Express [built-in middleware](#built-ins) ## 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](./services.md), an [Express middleware](http://expressjs.com/en/guide/writing-middleware.html) or an array of [Express middleware](http://expressjs.com/en/guide/writing-middleware.html) on the given path. If [a service object](./services.md) is passed it will use Feathers registration mechanism, for a middleware function Express. ```ts // 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
The `json` and `urlencoded` body parser and [params middleware](#params) has to be registered **before** any service.### 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](http://expressjs.com/en/4x/api.html#res.format) can be used for content negotiation. ```ts 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: ```ts const todoService = { async get(id: Id) { return { id, description: `You have to do ${id}!` } } } app.use('todos', logRequest, todoService, updateData) ```
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](hooks.md) 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](./hooks.md) context of the service method call For example `updateData` could look like this: ```js 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: ```ts 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 Express middleware will have access to the `req.feathers` object to set properties on the service method `params`: ```ts import { feathers } from '@feathersjs/feathers' import type { Id, Params } from '@feathersjs/feathers' import express, { json, urlencoded, rest } from '@feathersjs/express' const app = express(feathers()) app.use(json()) app.use(urlencoded({ extended: true })) app.use(function (req, res, next) { req.feathers.fromMiddleware = 'Hello world' next() }) app.configure(rest()) 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) ``` 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.
Since the order of Express middleware matters, any middleware that sets service parameters has to be registered **before** `app.configure(rest())` or as a [custom service middleware](#custom-service-middleware)
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](https://github.com/ljharb/qs) module. For some query string examples see the [database querying](./databases/querying.md) chapter.
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](./hooks.md).For example: ``` GET /messages?read=true&$sort[createdAt]=-1 ``` Will set `params.query` to ```json { "read": "true", "$sort": { "createdAt": "-1" } } ```
Note that the URL is a string so type conversion may be necessary. This is usually done with [query schemas and resolvers](./schema/index.md).
If an array in your request consists of more than 20 items, the [qs](https://www.npmjs.com/package/qs) parser implicitly [converts](https://github.com/ljharb/qs#parsing-arrays) 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](./services.md) made through REST `params.provider` will be set to `rest`. In a [hook](./hooks.md) this can for example be used to prevent external users from making a service method call: ```ts import { HookContext } from 'declarations' app.service('users').hooks({ before: { remove: [ async (context: HookContext) => { // 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.headers `params.headers` will contain the original service request headers. ### params.route Express route placeholders in a service URL will be added to the services `params.route`. See the [FAQ entry on nested routes](../help/faq.md#how-do-i-do-nested-or-custom-routes) for more details on when and when not to use nested routes. ```ts import { feathers } from '@feathersjs/feathers' import express, { rest } from '@feathersjs/express' const app = express(feathers()) app.configure(rest()) app.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) ``` ## Middleware `@feathersjs/express` comes with the following middleware ### notFound(options) `notFound()` returns middleware that returns a `NotFound` (404) [Feathers error](./errors.md). 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`) ```ts 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](https://expressjs.com/en/guide/error-handling.html) 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.
You can still use any other Express compatible [error middleware](http://expressjs.com/en/guide/error-handling.html) with Feathers.
Just like 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. ```ts 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)) ```ts 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' } }) ) ```
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 error handler: - `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](./authentication/service.md) that has [strategies](./authentication/strategy.md) registered that can parse HTTP headers. It will set the authentication information on the `req.feathers` object (e.g. `req.feathers.user`). The following example protects the `/hello` endpoint with the JWT strategy (so the `Authorization: Bearer
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.### parseAuthentication The `parseAuthentication` middleware is registered automatically and will use the strategies of the default [authentication service](./authentication/service.md) to parse headers for authentication information. If you want to additionally parse authentication with a different authentication service this middleware can be registered again with that service configured. ```ts import { parseAuthentication } from '@feathersjs/express' app.use( parseAuthentication({ service: 'api/v1/authentication', strategies: ['jwt', 'local'] }) ) ``` ### cors A reference to the [cors](https://github.com/expressjs/cors) module. ### compression A reference to the [compression](https://github.com/expressjs/compression) module. ### Built ins Note that `@feathersjs/express` also exposes the standard [Express middleware](http://expressjs.com/en/4x/api.html#express): - `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 ================================================ FILE: docs/api/hooks.md ================================================ --- outline: deep --- # Hooks Hooks are pluggable middleware functions that can be registered **around**, **before**, **after** or on **error**(s) of a [service method](./services.md). Multiple hook functions can be chained to create complex work-flows. A hook is **transport independent**, which means it does not matter if it has been called internally on the server, through HTTP(S) (REST), websockets or any other transport Feathers supports. They are also service agnostic, meaning they can be used with **any** service regardless of whether they use a database or not. Hooks are commonly used to handle things like permissions, validation, logging, [authentication](./authentication/hook.md), [data schemas and resolvers](./schema/index.md), sending notifications and more. This pattern keeps your application logic flexible, composable, and easier to trace through and debug. For more information about the design patterns behind hooks see [this blog post](https://blog.feathersjs.com/api-service-composition-with-hooks-47af13aa6c01). ## Quick Example The following example logs the runtime of any service method on the `messages` service and adds `createdAt` property before saving the data to the database: ```ts import { feathers, type HookContext, type NextFunction } from '@feathersjs/feathers' const app = feathers() app.service('messages').hooks({ around: { all: [ // A hook that wraps around all other hooks and the service method // logging the total runtime of a successful call async (context: HookContext, next: NextFunction) => { const startTime = Date.now() await next() console.log(`Method ${context.method} on ${context.path} took ${Date.now() - startTime}ms`) } ] }, before: { create: [ async (context: HookContext) => { context.data = { ...context.data, createdAt: Date.now() } } ] } }) ```
While it is always possible to add properties like `createdAt` in the above example via hooks, the preferred way to make data modifications like this in Feathers 5 is via [schemas and resolvers](./schema/index.md).## Hook functions ### before, after and error `before`, `after` and `error` hook functions are functions that are `async` or return a promise and take the [hook context](#hook-context) as the parameter and return nothing or throw an error. ```ts import { HookContext } from '../declarations' export const hookFunction = async (context: HookContext) => { // Do things here } ``` For more information see the [hook flow](#hook-flow) section. ### around `around` hooks are a special kind of hook that allow to control the entire `before`, `after` and `error` flow in a single function. They are a Feathers specific version of the generic [@feathersjs/hooks](https://github.com/feathersjs/hooks). An `around` hook is an `async` function that accepts two arguments: - The [hook context](#hook-context) - An asynchronous `next` function. Somewhere in the body of the hook function, there is a call to `await next()`, which calls the `next` hooks OR the original function if all other hooks have run. In its simplest form, an around hook looks like this: ```js import { HookContext, NextFunction } from '../declarations' export const myAfoundHook = async (context: HookContext, next: NextFunction) => { try { // Code before `await next()` runs before the main function await next() // Code after `await next()` runs after the main function. } catch (error) { // Do things on error } finally { // Do things always } } ``` Any around hook can be wrapped around another function. Calling `await next()` will either call the next hook in the chain or the service method if all other hooks have run. ## Hook flow In general, hooks are executed in the order [they are registered](#registering-hooks) with `around` hooks running first: - `around` hooks (before `await next()`) - `before` hooks - service method - `after` hooks - `around` hooks (after `await next()`) Note that since `around` hooks wrap **around** everything, the first hook to run will be the last to execute its code after `await next()`. This is reverse of the order `after` hooks execute. The hook flow can be affected as follows. ### Throwing an error When an error is thrown (or the promise is rejected), all subsequent hooks - and the service method call if it didn't run already - will be skipped and only the error hooks will run. The following example throws an error when the text for creating a new message is empty. You can also create very similar hooks to use your Node validation library of choice. ```ts app.service('messages').hooks({ before: { create: [ async (context: HookContext) => { if (context.data.text.trim() === '') { throw new Error('Message text can not be empty') } } ] } }) ``` ### Setting `context.result` When `context.result` is set in an `around` hook before calling `await next()` or in a `before` hook, the original [service method](./services.md) call will be skipped. All other hooks will still execute in their normal order. The following example always returns the currently [authenticated user](./authentication/service.md) instead of the actual user for all `get` method calls: ```js app.service('users').hooks({ before: { get: [ async (context: HookContext) => { // Never call the actual users service // just use the authenticated user context.result = context.params.user } ] } }) ``` ## Hook context The hook `context` is passed to a hook function and contains information about the service method call. It has **read only** properties that should not be modified and **_writeable_** properties that can be changed for subsequent hooks.
The `context` object is the same throughout a service method call so it is possible to add properties and use them in other hooks at a later time.
If you want to inspect the hook context, e.g. via `console.log`, the object returned by [context.toJSON()](#contexttojson) should be used, otherwise you won't see all properties that are available.### `context.app` `context.app` is a _read only_ property that contains the [Feathers application object](./application.md). This can be used to retrieve other services (via `context.app.service('name')`) or configuration values. ### `context.service` `context.service` is a _read only_ property and contains the service this hook currently runs on. ### `context.path` `context.path` is a _read only_ property and contains the service name (or path) without leading or trailing slashes. ### `context.method` `context.method` is a _read only_ property with the name of the [service method](./services.md) (`find`, `get`, `create`, `update`, `patch`, `remove`). ### `context.type` `context.type` is a _read only_ property with the hook type (one of `around`, `before`, `after` or `error`). ### `context.params` `context.params` is a **writeable** property that contains the [service method](./services.md) parameters (including `params.query`). For more information see the [service params documentation](./services.md#params). ### `context.id` `context.id` is a **writeable** property and the `id` for a `get`, `remove`, `update` and `patch` service method call. For `remove`, `update` and `patch`, `context.id` can also be `null` when modifying multiple entries. In all other cases it will be `undefined`. ### `context.data` `context.data` is a **writeable** property containing the data of a `create`, `update` and `patch` service method call.
`context.data` will only be available for `create`, `update`, `patch` and [custom methods](./services.md#custom-methods).### `context.error` `context.error` is a **writeable** property with the error object that was thrown in a failed method call. It can be modified to change the error that is returned at the end.
`context.error` will only be available if `context.type` is `error`.### `context.result` `context.result` is a **writeable** property containing the result of the successful service method call. It is only available in `after` hooks. `context.result` can also be set in - An `around` or `before` hook to skip the actual service method (database) call - An `error` hook to swallow the error and return a result instead
`context.result` will only be available if `context.type` is `after` or if `context.result` has been set.### `context.dispatch` `context.dispatch` is a **writeable, optional** property and contains a "safe" version of the data that should be sent to any client. If `context.dispatch` has not been set `context.result` will be sent to the client instead. `context.dispatch` only affects the data sent through a Feathers Transport like [REST](./express.md) or [Socket.io](./socketio.md). An internal method call will still get the data set in `context.result`.
`context.dispatch` is used by the `schemaHooks.resolveDispatch` [resolver](./schema/resolvers.md). Use dispatch resolvers whenever possible to get safe representations external data.### `context.http` `context.http` is a **writeable, optional** property that allows customizing HTTP response specific properties. The following properties can be set: - `context.http.status` - Sets the [HTTP status code](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) that should be returned. Usually the most appropriate status code will be picked automatically but there are cases where it needs to be customized. - `context.http.headers` - An object with additional HTTP response headers - `context.http.location` - Setting this property will trigger a redirect for HTTP requests.
Setting `context.http` properties will have no effect when using a websocket real-time connection.### `context.event` `context.event` is a **writeable, optional** property that allows service events to be skipped by setting it to `null` ### `context.toJSON()` `context.toJSON()` returns a full object representation of the hook context and all its properties. ## Registering hooks Hook functions are registered on a service through the `app.service(
Hooks will only be available for the standard service methods or methods passed in `options.methods` to [app.use](application.md#usepath-service--options). See the [documentation for @feathersjs/hooks](https://github.com/feathersjs/hooks) how to use hooks on other methods.Since around hooks offer the same functionality as `before`, `after` and `error` hooks at the same time they can also be registered without a nested object: ```ts import { HookContext, NextFunction } from './declarations' // Passing an array of around hooks that run for every method app.service('servicename').hooks([ async (context: HookContext, next: NextFunction) => { console.log('around all hook ran') await next() } ]) // Passing an object with method names and a list of around hooks app.service('servicename').hooks({ get: [ async (context: HookContext, next: NextFunction) => { console.log('around get hook ran') await next() } ], create: [], update: [], patch: [], remove: [], myCustomMethod: [] }) ``` ## Application hooks ### Service hooks To add hooks to every service `app.hooks(hooks)` can be used. Application hooks are [registered in the same format as service hooks](#registering-hooks) and also work exactly the same. Note when application hooks will be executed: - `around` application hook will run around all other hooks - `before` application hooks will always run _before_ all service `before` hooks - `after` application hooks will always run _after_ all service `after` hooks - `error` application hooks will always run _after_ all service `error` hooks Here is an example for a very useful application hook that logs every service method error with the service and method name as well as the error stack. ```ts import { HookContext } from './declarations' app.hooks({ error: { all: [ async (context: HookContext) => { console.error(`Error in '${context.path}' service method '${context.method}'`, context.error.stack) } ] } }) ``` ### Setup and teardown A special kind of application hooks are [app.setup](./application.md#setupserver) and [app.teardown](./application.md#teardownserver) hooks. They are around hooks that can be used to initialize database connections etc. and only run once when the application starts or shuts down. Setup and teardown hooks only have `context.app` and `context.server` available in the hook context. ```ts import { MongoClient } from 'mongodb' app.hooks({ setup: [ async (context: HookContext, next: NextFunction) => { // E.g. wait for MongoDB connection to complete await context.app.get('mongoClient').connect() await next() } ], teardown: [ async (context: HookContext, next: NextFunction) => { // Close MongoDB connection await context.app.get('mongoClient').close() await next() } ] }) ``` ================================================ FILE: docs/api/index.md ================================================ --- outline: deep --- # API This section describes all the individual modules and APIs of Feathers. ## Core Feathers core functionality that works on the client and the server - [Application](./application.md) - The main Feathers application API - [Services](./services.md) - Service objects and their methods and Feathers specific functionality - [Hooks](./hooks.md) - Pluggable middleware for service methods - [Events](./events.md) - Events sent by Feathers service methods - [Errors](./errors.md) - A collection of error classes used throughout Feathers ## Transports Expose a Feathers application as an API server - [Configuration](./configuration.md) - A node-config wrapper to initialize configuration of a server side application. - [Koa](./koa.md) - Feathers KoaJS framework bindings, REST API provider and error middleware. - [Express](./express.md) - Feathers Express framework bindings, REST API provider and error middleware. - [Socket.io](./socketio.md) - The Socket.io real-time transport provider - [Channels](./channels.md) - Channels are used to send real-time events to clients ## Authentication Feathers authentication mechanism - [Service](./authentication/service.md) - The main authentication service configuration - [Hook](./authentication/hook.md) - The hook used to authenticate service method calls - [Strategies](./authentication/strategy.md) - More about authentication strategies - [Local](./authentication/local.md) - Local email/password authentication - [JWT](./authentication/jwt.md) - JWT authentication - [OAuth](./authentication/oauth.md) - Using OAuth logins (Facebook, Twitter etc.) ## Client More details on how to use Feathers on the client - [Usage](./client.md) - Feathers client usage in Node, React Native and the browser (also with Webpack and Browserify) - [REST](./client/rest.md) - Feathers client and direct REST API server usage - [Socket.io](./client/socketio.md) - Feathers client and direct Socket.io API server usage - [Authentication](authentication/client) - A client for Feathers authentication ## Schema Model definitions for validating and resolving data. - [TypeBox](./schema/typebox.md) - Integration for TypeBox, a JSON schema type builder - [JSON schema](./schema/schema.md) - JSON schema integration - [Validators](./schema/validators.md) - Schema validators and validation hooks - [Resolvers](./schema/resolvers.md) - Dynamic data resolvers ## Databases Feathers common database adapter API and querying mechanism - [Adapters](./databases/adapters.md) - A list of supported database adapters - [Common API](./databases/common.md) - Database adapter common initialization and configuration API - [Querying](./databases/querying.md) - The common querying mechanism - [MongoDB](./databases/querying.md) - The adapter for MongoDB databases - [SQL](./databases/knex.md) - The adapter for SQL databases using KnexJS - [Memory](./databases/memory.md) - The adapter for in-memory data storage ================================================ FILE: docs/api/koa.md ================================================ --- outline: deep --- # Koa
Note that `app.configure(rest())` has to happen **after** any custom middleware.### params.query `params.query` will contain the URL query parameters sent from the client parsed using [koa-qs](https://github.com/koajs/qs).
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](./hooks.md).To increase the array limit in query strings, `koa-qs` can be reinitalized with the options for the [qs](https://www.npmjs.com/package/qs) module: ```ts // app.ts import koaQs from 'koa-qs' // ... koaQs(app, 'extended', { arrayLimit: 200 }) ``` ### params.provider For any [service method call](./services.md) made through REST `params.provider` will be set to `rest`. ### params.route Route placeholders in a service URL will be added to the services `params.route`. See the [FAQ entry on nested routes](../help/faq.md#how-do-i-do-nested-or-custom-routes) for more details on when and when not to use nested routes. ```ts import { feathers } from '@feathersjs/feathers' import { koa, errorHandler, bodyParser, rest } from '@feathersjs/koa' const app = koa(feathers()) 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) // will be `{ userId: '1' }` for GET /users/1/messages return { id, params, read: false, text: `Feathers is great!`, createdAt: new Date().getTime() } } }) app.listen(3030) ``` ## Service middleware When registering a service, it is also possible to pass custom Koa middleware that should run `before` the specific service method in the `koa` [service option](./application.md#usepath-service--options): ```ts app.use('/todos', new TodoService(), { koa: { before: [ async (ctx, next) => { ctx.feathers // data that will be merged into sevice `params` // This will run all subsequent middleware and the service call await next() // Then we have additional properties available on the context ctx.hook // the hook context from the method call ctx.body // the return value } ] } }) ``` Note that the order of middleware will be `[...before, serviceMethod]`. ## Middleware ### rest ```ts import { rest } from '@feathersjs/koa' app.configure(rest()) ``` Configures the middleware for handling service calls via HTTP. It will also register authentication header parsing. The following (optional) options are available: - `formatter` - A middleware that formats the response body - `authentication` - The authentication `service` and `strategies` to use for parsing authentication information ### errorHandler ```ts import { errorHandler } from '@feathersjs/koa' app.use(errorHandler()) ``` A middleware that formats errors as a Feathers error and sets the proper status code. Needs to be the first middleware registered in order to catch all other errors. ### authenticate A middleware that allows to authenticate a user (or other authentication entity) using the [authentication service](./authentication/service.md) setting `ctx.feathers.user`. Not necessary for use with services but can be used in custom middleware. ```ts import { authenticate } from '@feathersjs/koa' // Authenticate other middleware with the JWT strategy app.use(authenticate('jwt')) // Authenticate a non default service app.use( authenticate({ service: 'api/v1', strategies: ['jwt'] }) ) ``` ### parseAuthentication The `parseAuthentication` middleware is registered automatically and will use the strategies of the default [authentication service](./authentication/service.md) to parse headers for authentication information. If you want to additionally parse authentication with a different authentication service this middleware can be registered again with that service configured. ```ts import { parseAuthentication } from '@feathersjs/koa' app.use( parseAuthentication({ service: 'api/v1/authentication', strategies: ['jwt', 'local'] }) ) ``` ### bodyParser A reference to the [koa-body](https://github.com/koajs/koa-body) module. ### cors A reference to the [@koa/cors](https://github.com/koajs/cors) module. ### serveStatic A reference to the [koa-static](https://github.com/koajs/static) module. ================================================ FILE: docs/api/schema/index.md ================================================ --- outline: deep --- # Schema Overview
Property resolver functions should only return a value and not have side effects. This means a property resolver **should not** do things like create new data or modify the `data` or `context` object. [Hooks](../hooks.md) should be used for side effects.## Virtual property resolvers Virtual resolvers are property resolvers that do not use the `value` but instead always return a value of their own. The parameters are (`(data, context, status)`). The above example can be written like this: ```ts import { resolve, virtual } from '@feathersjs/schema' const userResolver = resolve
Virtual resolvers should always be used when combined with a [database adapter](../databases/adapters.md) in order to make valid [$select queries](../databases/querying.md#select). Otherwise queries could try to select fields that do not exist in the database which will throw an error.## Options A resolver takes the following options as the second parameter: - `converter` (optional): A `(data, context) => {}` or `async (data, context) => {}` function that can return a completely new representation of the data. A `converter` runs before `properties` resolvers. ```ts const userResolver = resolve
`schemaHooks.resolveResult` must be used as an `around` hook. This is to ensure that the database adapters will be able to handle [$select queries](../databases/querying.md#select) properly when using [virtual properties](#virtual-property-resolvers).```ts import { hooks as schemaHooks, resolve, virtual } from '@feathersjs/schema' import { Type } from '@feathersjs/typebox' import type { Static } from '@feathersjs/typebox' import type { HookContext } from '../declarations' const userSchema = Type.Object( { id: Type.Number(), email: Type.String(), password: Type.String(), avatar: Type.Optional(Type.String()) }, { $id: 'User', additionalProperties: false } ) type User = Static
In order to get the safe data from resolved associations **all services** involved need the `schemaHooks.resolveExternal` hook registered even if it does not need a resolver (`schemaHooks.resolveExternal()`). `schemaHooks.resolveExternal` should be registered first when used as an `around` hook or last when used as an `after` hook so that it gets the final result data.### resolveQuery Query resolvers use the `hooks.resolveQuery(...resolvers)` hook to modify `params.query`. This is often used to set default values or limit the query so a user can only request data they are allowed to see. It is possible to pass multiple resolvers which will run in the order they are passed, using the previous data. `schemaHooks.resolveQuery` can be used as an `around` or `before` hook. In this example for a `User` schema we are first checking if a user is available in our request. In the case a user is available we are returning the user's ID. Otherwise we return whatever value was provided for `id`. `context.params.user` would only be set if the request contains a user. This is usually the case when an external request is made. In the case of an internal request we may not have a specific user we are dealing with, and we will just return `value`. If we were to receive an internal request, such as `app.service('users').get(123)`, `context.params.user` would be `undefined` and we would just return the `value` which is `123`. ```ts import { hooks as schemaHooks, resolve } from '@feathersjs/schema' import { Type } from '@feathersjs/typebox' import type { Static } from '@feathersjs/typebox' import type { HookContext } from '../declarations' const userSchema = Type.Object( { id: Type.Number(), email: Type.String(), password: Type.String(), avatar: Type.Optional(Type.String()) }, { $id: 'User', additionalProperties: false } ) type User = Static
You can find an introduction in the [JSON schema official getting started guide](https://json-schema.org/learn/getting-started-step-by-step) and a lot of type-specific JSON Schema examples in the [json-schema-to-ts docs](https://github.com/ThomasAribart/json-schema-to-ts).## Creating Schemas ### Definitions If you are not familiar with JSON schema have a look at the [official getting started guide](https://json-schema.org/learn/getting-started-step-by-step). Here is an example for a possible user schema: ```ts import type { FromSchema } from '@feathersjs/schema' export const userSchema = { $id: 'User', type: 'object', additionalProperties: false, required: ['email', 'password'], properties: { id: { type: 'number' }, email: { type: 'string' }, password: { type: 'string' } } } as const export type User = FromSchema
See the [validators](./validators.md) chapter for more information on validators and validator functions.### getDataValidator `getDataValidator(definition, validator)` returns validators for the data of `create`, `update` and `patch` service methods. You can either pass a single definition in which case all properties of the `patch` schema will be optional or individual validators for `create`, `update` and `patch`. ```ts import { getDataValidator, Ajv } from '@feathersjs/schema' import type { FromSchema } from '@feathersjs/schema' const userDataSchema = { $id: 'User', type: 'object', additionalProperties: false, required: ['email', 'password'], properties: { email: { type: 'string' }, password: { type: 'string' } } } as const type UserData = FromSchema
For additional information also see the [TypeBox documentation](https://github.com/sinclairzx81/typebox/blob/master/readme.md#contents).## Usage The module exports all of TypeBox functionality with additional support for [query schemas](#query-schemas) and [validators](#validators). The following is an example for defining the message schema [from the guide](../../guides/basics/schemas.md#handling-messages) using TypeBox: ```ts import { Type } from '@feathersjs/typebox' import type { Static } from '@feathersjs/typebox' const messageSchema = Type.Object( { id: Type.Number(), text: Type.String(), createdAt: Type.Number(), userId: Type.Number() }, { $id: 'Message', additionalProperties: false } ) type Message = Static
See the [validators](./validators.md) chapter for more information on validators and validator functions.### getDataValidator `getDataValidator(definition, validator)` returns validators for the data of `create`, `update` and `patch` service methods. You can either pass a single definition in which case all properties of the `patch` schema will be optional or individual validators for `create`, `update` and `patch`. ```ts import { Ajv } from '@feathersjs/schema' import { Type, getDataValidator } from '@feathersjs/typebox' import type { Static } from '@feathersjs/typebox' const userSchema = Type.Object( { id: Type.Number(), email: Type.String(), password: Type.String(), avatar: Type.Optional(Type.String()) }, { $id: 'User', additionalProperties: false } ) type User = Static
For string values, use [StringEnum](#stringenum).```js enum Foo { A, B, } const T = Type.Enum(Foo) ``` ```js enum Foo { A, B, } type T = Foo ``` ```js const T = { anyOf: [ { type: 'number', const: 0 }, { type: 'number', const: 1 } ] } ``` #### Utility Types The utility types create types which are derived from other types. ##### KeyOf Creates a schema for a string that can be any of the keys of a provided `Type.Object`. It's similar to TypeScript's [KeyOf](https://www.typescriptlang.org/docs/handbook/2/keyof-types.html#handbook-content) operator. ```js const T = Type.KeyOf( Type.Object({ x: Type.Number(), y: Type.Number() }) ) ``` ```js type T = keyof { x: number, y: number, } ``` ```js const T = { anyOf: [ { type: 'string', const: 'x' }, { type: 'string', const: 'y' } ] } ``` ##### Union Creates a type which can be one of the types in the provided array. It's the equivalent to using `|` to form a TypeScript [Union](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#unions). ```js const T = Type.Union([Type.String(), Type.Number()]) ``` ```js type T = string | number ``` ```js const T = { anyOf: [{ type: 'string' }, { type: 'number' }] } ``` ##### Intersect Creates an object type by combining two or more other object types. ```js const T = Type.Intersect([ Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() }) ]) ``` ```js type T = { x: number } & { y: number } ``` ```js const T = { type: 'object', properties: { x: { type: 'number' }, y: { type: 'number' } }, required: ['x', 'y'] } ``` ##### Never Creates a type that will never validate if the attribute is present. This is useful if you are allowing [additionalProperties](#additionalproperties) but need to prevent using specific keys. ```js const T = Type.Never() ``` ```js type T = never ``` ```js const T = { allOf: [ { type: 'boolean', const: false }, { type: 'boolean', const: true } ] } ``` ##### Record Creates the JSON Schema equivalent of TypeScript's [Record](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type) utility type. ```js const T = Type.Record(Type.String(), Type.Number()) ``` ```js type T = Record
Ajv and most other validation libraries are only used for ensuring data is valid and are not designed to convert data to different types. Type conversions and populating data can be done using [resolvers](./resolvers.md). This ensures a clean separation of concern between validating and populating data.## Usage The following is the standard `validators.ts` file that sets up a validator for data and queries (for which string types will be coerced automatically). It also sets up a collection of additional formats using [ajv-formats](https://ajv.js.org/packages/ajv-formats.html). The validators in this file can be customized according to the [Ajv documentation](https://ajv.js.org/) and [its plugins](https://ajv.js.org/packages/). You can find the available Ajv options in the [Ajv class API docs](https://ajv.js.org/options.html). ```ts import { Ajv, addFormats } from '@feathersjs/schema' import type { FormatsPluginOptions } from '@feathersjs/schema' const formats: FormatsPluginOptions = [ 'date-time', 'time', 'date', 'email', 'hostname', 'ipv4', 'ipv6', 'uri', 'uri-reference', 'uuid', 'uri-template', 'json-pointer', 'relative-json-pointer', 'regex' ] export const dataValidator = addFormats(new Ajv({}), formats) export const queryValidator = addFormats( new Ajv({ coerceTypes: true }), formats ) ``` ## Validation functions A validation function takes data and validates them against a schema using a validator. They can be used with any validation library. Currently the `getValidator` functions are available for: - [TypeBox schema](./typebox.md#validators) to validate a TypeBox definition using an Ajv validator instance - [JSON schema](./schema.md#validators) to validate a JSON schema object using an Ajv validator instance ## Hooks The following hooks take a [validation function](#validation-functions) and validate parts of the [hook context](../hooks.md#hook-context). ### validateData `schemaHooks.validateData` takes a [validation function](#validation-functions) and allows to validate the `data` in a `create`, `update` and `patch` request as well as [custom service methods](../services.md#custom-methods). It can be used as an `around` or `before` hook. ```ts import { Ajv, hooks as schemaHooks } from '@feathersjs/schema' import { Type, getValidator } from '@feathersjs/typebox' import type { Static } from '@feathersjs/typebox' import { dataValidator } from '../validators' const userSchema = Type.Object( { id: Type.Number(), email: Type.String(), password: Type.String(), avatar: Type.Optional(Type.String()) }, { $id: 'User', additionalProperties: false } ) type User = Static
Always use the service returned by `app.service(path)` not the service object or class directly or you will not get any of the [Feathers service functionality](services.md#feathers-functionality)
Methods are optional and if a method is not implemented Feathers will automatically emit a `NotImplemented` error. At least one standard service method must be implemented to be considered a service. If you used `methods` option when registering the service via [app.use](./application.md#usepath-service--options), all methods listed must be available.Service methods must use [async/await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) or return a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) and have the following parameters: - `id` — The identifier for the resource. A resource is the data identified by a unique id. - `data` — The resource data. - `params` - Additional parameters for the method call (see [params](#params)) Once registered, the service can be retrieved and used via [app.service()](./application.md#servicepath): ```js const myService = app.service('my-service') const items = await myService.find() const item = await app.service('my-service').get(1) console.log('.get(1)', item) ```
Although probably the most common use case, a service does not necessarily have to connect to a database. A custom service can implement any functionality like talking to another API or send an email etc.
This section describes the general usage of service methods and how to implement them. They are already implemented by the official Feathers database adapters. For specifics on how to use the database adapters, see the [database adapters documentation](./databases/common.md).### params `params` contain additional information for the service method call. Some properties in `params` can be set by Feathers already. Commonly used are: - `params.query` - the query parameters from the client, either passed as URL query parameters (see the [REST](./express.md) chapter) or through websockets (see [Socket.io](./socketio.md)). - `params.provider` - The transport (`rest` or `socketio`) used for this service call. Will be `undefined` for internal calls from the server (unless passed explicitly). - `params.authentication` - The authentication information to use for the [authentication service](./authentication/service.md) - `params.user` - The authenticated user, either set by [Feathers authentication](./authentication/) or passed explicitly. - `params.connection` - If the service call has been made by a real-time transport (e.g. through websockets), `params.connection` is the connection object that can be used with [channels](./channels.md). - `params.headers` - The HTTP headers connected to this service call if available. This is either the headers of the REST call or the headers passed when initializing a websocket connection.
For external calls only `params.query` will be sent between the client and server. This is because other parameters in `params` on the server often contain security critical information (like `params.user` or `params.authentication`).### .find(params) `service.find(params) -> Promise` - Retrieves a list of all resources from the service. `params.query` can be used to filter and limit the returned data. ```ts class MessageService { async find(params: Params) { return [ { id: 1, text: 'Message 1' }, { id: 2, text: 'Message 2' } ] } } app.use('messages', new MessageService()) ```
`find` does not have to return an array. It can also return an object. The database adapters already do this for [pagination](./databases/common.md#pagination).### .get(id, params) `service.get(id, params) -> Promise` - Retrieves a single resource with the given `id` from the service. ```ts import type { Id, Params } from '@feathersjs/feathers' class TodoService { async get(id: Id, params: Params) { return { id, text: `You have to do ${id}!` } } } app.use('todos', new TodoService()) ``` ### .create(data, params) `service.create(data, params) -> Promise` - Creates a new resource with `data`. The method should return with the newly created data. `data` may also be an array. A successful `create` method call emits the [`created` service event](./events.md#created) with the returned data or a separate event for every item if the returned data is an array. ```ts import type { Id, Params } from '@feathersjs/feathers' type Message = { text: string } class MessageService { messages: Message[] = [] async create(data: Message, params: Params) { this.messages.push(data) return data } } app.use('messages', new MessageService()) ```
Note that `data` may also be an array. When using a [database adapters](./databases/adapters.md) the [`multi` option](./databases/common.md) has to be set to allow arrays.### .update(id, data, params) `service.update(id, data, params) -> Promise` - Replaces the resource identified by `id` with `data`. The method should return with the complete, updated resource data. `id` can also be `null` when updating multiple records. A successful `update` method call emits the [`updated` service event](./events.md#updated-patched). If an array is returned, it will send an individual `updated` event for every item.
The [database adapters](./databases/adapters.md) do not support completely replacing multiple entries.### .patch(id, data, params) `patch(id, data, params) -> Promise` - Merges the existing data of the resource identified by `id` with the new `data`. `id` can also be `null` indicating that multiple resources should be patched with `params.query` containing the query criteria. A successful `patch` method call emits the [`patched` service event](./events.md#updated-patched) with the returned data. When an array is returned when patching mutiple items, it will send an individual `patched` event for every item in the array. The method should return with the complete, updated resource data. Implement `patch` additionally (or instead of) `update` if you want to distinguish between partial and full updates and support the `PATCH` HTTP method.
With [database adapters](./databases/adapters.md) the [`multi` option](./databases/common.md) has to be set explicitly to support patching multiple entries.### .remove(id, params) `service.remove(id, params) -> Promise` - Removes the resource with `id`. The method should return with the removed data. `id` can also be `null`, which indicates the deletion of multiple resources, with `params.query` containing the query criteria. A successful `remove` method call emits the [`removed` service event](./events.md#removed) with the returned data or a separate event for every item if the returned data is an array.
With [database adapters](./databases/adapters.md) the [`multi` option](./databases/common.md) has to be set explicitly to support removing multiple entries.### .setup(app, path) `service.setup(app, path) -> Promise` is a special method that initializes the service, passing an instance of the Feathers application and the path it has been registered on. When calling [app.listen](application.md#listenport) or [app.setup](application.md#setupserver) all registered services `setup` methods will be called. If a service is registered afterwards, the `setup` method will be called immediately. ### .teardown(app, path) `service.teardown(app, path) -> Promise` is a special method that shuts down the service, passing an instance of the Feathers application and the path it has been registered on. If a service implements a `teardown` method, it will be called during [app.teardown()](application.md#teardownserver) or when unregistering the service via [app.unuse](./application.md#unusepath). ## Custom Methods A custom method is any other service method you want to expose publicly. A custom method **must have** the signature of `(data, params)` with the same semantics as standard service methods (`data` is the payload, `params` is the service [params](#params)). They can be used with [hooks](./hooks.md) (including authentication) and must be `async` or return a Promise. In order to register a public custom method, the names of _all methods_ have to be passed as the `methods` option when registering the service with [app.use()](./application.md#usepath-service--options) ```ts import type { Id, Params } from '@feathersjs/feathers' type CustomData = { name: string } class MyService { async get(id: Id, params: Params) { return { id, message: `You have to do ${id}` } } async myCustomMethod(data: CustomData, params: Params) { return data } } type ServiceTypes = { 'my-service': MyService } const app = feathers
When passing the `methods` option **all methods** you want to expose, including standard service methods, must be listed. This allows to completely disable standard service method you might not want to expose. The `methods` option only applies to external access (via a transport like HTTP or websockets). All methods continue to be available internally on the server.## Feathers functionality When registering a service, Feathers (or its plugins) can also add its own methods to a service. Most notably, every service will automatically become an instance of a [NodeJS EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter). ### .hooks(hooks) Register [hooks](./hooks.md) for this service. ### .publish([event, ] publisher) Register an event publishing callback. For more information, see the [channels chapter](./channels.md). ### .on(eventname, listener) Provided by the core [NodeJS EventEmitter .on](https://nodejs.org/api/events.html#events_emitter_on_eventname_listener). Registers a `listener` method (`function(data) {}`) for the given `eventname`.
For more information about service events, see the [Events chapter](./events.md).### .emit(eventname, data) Provided by the core [NodeJS EventEmitter .emit](https://nodejs.org/api/events.html#events_emitter_emit_eventname_args). Emits the event `eventname` to all event listeners. ### .removeListener(eventname) Provided by the core [NodeJS EventEmitter .removeListener](https://nodejs.org/api/events.html#events_emitter_removelistener_eventname_listener). Removes all listeners, or the given listener, for `eventname`. ================================================ FILE: docs/api/socketio.md ================================================ --- outline: deep --- # Socket.io
This page describes how to set up a Socket.io server. The [Socket.io client chapter](./client/socketio.md) shows how to connect to this server on the client and the message format for service calls and real-time events.## Configuration `@feathersjs/socketio` can be used standalone or together with a Feathers framework integration like [Express](./express.md). ### socketio() `app.configure(socketio())` sets up the Socket.io transport with the default configuration using either the server provided by [app.listen](./application.md#listenport) or passed in [app.setup(server)](./application.md#setupserver). ```ts import { feathers } from '@feathersjs/feathers' import socketio from '@feathersjs/socketio' const app = feathers() app.configure(socketio()) app.listen(3030) ```
Once the server has been started with `app.listen()` or `app.setup(server)` the Socket.io object is available as `app.io`. Usually you should not have to send or listen to events on `app.io` directly.### socketio(callback) `app.configure(socketio(callback))` sets up the Socket.io transport with the default configuration and call `callback` with the [Socket.io server object](http://socket.io/docs/server-api/). ```ts import { feathers } from '@feathersjs/feathers' import socketio from '@feathersjs/socketio' const app = feathers() app.configure( socketio((io) => { io.on('connection', (socket) => { // Do something here }) // Registering Socket.io middleware io.use(function (socket, next) { // Exposing a request property to services and hooks socket.feathers.referrer = socket.request.referrer next() }) }) ) app.listen(3030) ```
Try to avoid listening and sending events on the `socket` directly since it circumvents Feathers secure dispatch mechanisms available through [channels](./channels.md) and [hooks](./hooks.md).#### Using uWebSockets.js uWS can be used as a drop in replacement for socket handling. As a result you'll see lower latencies, a better memory footprint and even slightly less overall resource usage. You will on the other hand need to install the following extra package to get things working. ``` npm install uNetworking/uWebSockets.js#20.31.0 --save ``` Now you can use the `io.attachApp` function to attach uWS as a replacement. ```ts import { feathers } from '@feathersjs/feathers' import socketio from '@feathersjs/socketio' import { App } from 'uWebSockets.js' const app = feathers() app.configure( socketio((io) => { io.attachApp(App()) }) ) app.listen(3030) ``` ### socketio(options [, callback]) `app.configure(socketio(options [, callback]))` sets up the Socket.io transport with the given [Socket.io options object](https://github.com/socketio/engine.io#methods-1) and optionally calls the callback described above. This can be used to e.g. configure the path where Socket.io is initialize (`socket.io/` by default). The following changes the path to `ws/`: ```ts import { feathers } from '@feathersjs/feathers' import socketio from '@feathersjs/socketio' const app = feathers() app.configure( socketio( { path: '/ws/' }, (io) => { // Do something here // This function is optional } ) ) app.listen(3030) ``` ### socketio(port, [options], [callback]) `app.configure(socketio(port, [options], [callback]))` creates a new Socket.io server on a separate port. Options and a callback are optional and work as described above. ```ts import { feathers } from '@feathersjs/feathers' import socketio from '@feathersjs/socketio' const app = feathers() app.configure(socketio(3031)) app.listen(3030) ``` ## params [Socket.io middleware](https://socket.io/docs/v4/middlewares/) can modify the `feathers` property on the `socket` which will then be used as the service call `params`: ```ts app.configure( socketio((io) => { io.use((socket, next) => { socket.feathers.user = { name: 'David' } next() }) }) ) app.use('messages', { async create(data, params, callback) { // When called via SocketIO: params.provider // -> socketio params.user // -> { name: 'David' } return data } }) ```
`socket.feathers` is the same object as the `connection` in a [channel](./channels.md). `socket.request` and `socket.handshake` contains information the HTTP request that initiated the connection (see the [Socket.io documentation](https://socket.io/docs/server-api/#socket-request)).### params.provider For any [service method call](./services.md) made through Socket.io `params.provider` will be set to `socketio`. In a [hook](./hooks.md) this can for example be used to prevent external users from making a service method call: ```js app.service('users').hooks({ before: { remove(context) { // check for if(context.params.provider) to prevent any external call if (context.params.provider === 'socketio') { throw new Error('You can not delete a user via Socket.io') } } } }) ``` ### params.query `params.query` will contain the query parameters sent from the client.
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 `params` in a before [hook](./hooks.md).### params.connection `params.connection` is the connection object that can be used with [channels](./channels.md). It is the same object as `socket.feathers` in a Socket.io middleware as [shown in the `params` section](#params). ### params.headers `params.headers` contains the headers from the original handshake. This is usually sent with the `extraHeaders` option when initialising the connection on the client: ```ts const socket = io('http://localhost:9777', { extraHeaders: { MyHeader: 'somevalue' } }) ``` ================================================ FILE: docs/auto-imports.d.ts ================================================ /* eslint-disable */ /* prettier-ignore */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // Generated by unplugin-auto-import // biome-ignore lint: disable export {} declare global { } ================================================ FILE: docs/comparison.md ================================================ # Feathers vs others The following sections compare Feathers to other software choices that seem similar or may overlap with the use cases of Feathers. Due to the bias of these comparisons being on the Feathers website, we attempt to only use facts. Below you can find a feature comparison table and in each section you can get more detailed comparisons. If you find something invalid or out of date in the comparisons, please create an issue and we'll address it as soon as possible. - [Feathers vs Firebase](/feathers-vs-firebase) - [Feathers vs Meteor](/feathers-vs-meteor) - [Feathers vs Sails](/feathers-vs-sails) - [Feathers vs Loopback](/feathers-vs-loopback) - [Feathers vs Nest](/feathers-vs-nest) ================================================ FILE: docs/components/CTAButton.vue ================================================
Check out the docs to learn more about Feathers.
Feel free say hello on Discord.
Feathers is a full-stack web-framework for creating APIs and real-time applications with TypeScript or JavaScript.
Feathers can interact with any backend technology, supports many databases out of the box and works with any frontend technology like React, VueJS, Angular, React Native, Android or iOS.
Build prototypes in minutes and production-ready apps in days.
Seriously.
{{ title }}
For TypeScript and JavaScript in Node.js, React Native and the browser
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
Since the generated application is using modern features like ES modules, the Feathers CLI requires __Node 16 or newer__.First, choose if you want to use JavaScript or TypeScript. When presented with the project name, just hit enter, or enter a name (no spaces). Next, write a short description for your application. Confirm the next questions with the default selection by pressing Enter. If you choose a database other than __SQLite__, make sure it is reachable at the connection string. For following this guide using MongoDB, change the database selection in the dropdown below.
`SQLite` creates an SQL database in a file so we don't need to have a database server running.
Most generated files have a page in the [CLI guide](../cli/index.md) which contains more information about the file and what it does.## Running the server and tests The server can be started by running
You can exit the running process by pressing **CTRL + C**The app also comes with a set of basic tests which can be run with ```sh npm test ``` There is also a handy development command that restarts the server automatically whenever we make a code change: ```sh npm run dev ```
Keep this command running throughout the rest of this guide so it will reload all our changes automatically.## What's next? In this chapter, we've created a new Feathers application. To learn more about the generated files and what you can do with the CLI, have a look at the [CLI guide](../cli/index.md) after finishing the Getting Started guide. In [the next chapter](./authentication.md) we will set up user authentication. ================================================ FILE: docs/guides/basics/hooks.md ================================================ --- outline: deep --- # Hooks When we created our messages service in [the services chapter](./services.md), we saw that Feathers services are a great way to implement data storage and modification. Technically, we could write our entire app with services but very often we need similar functionality across multiple services. For example, we might want to check for all services if a user is allowed to access it. With just services, we would have to write this every time. This is where Feathers hooks come in. Hooks are pluggable middleware functions that can be registered **around**, **before**, **after** or on **errors** of a service method without changing the original code. Just like services themselves, hooks are _transport independent_. They are usually also service independent, meaning they can be used with *any* service. This pattern keeps your application logic flexible, composable, and much easier to trace through and debug. Hooks are commonly used to handle things like validation, authorization, logging, sending emails and more.
A full overview of the hook API can be found in the [hooks API documentation](../../api/hooks.md). For the general design pattern behind hooks see [this blog post](https://blog.feathersjs.com/design-patterns-for-modern-web-apis-1f046635215).## Generating a hook Let's generate a hook that logs the total runtime of a service method to the console. ```sh npx feathers generate hook ``` We call our hook `log-runtime` and confirm the type with enter to make it an `around` hook. 
For more information about the hook context see the [hooks API documentation](../../api/hooks.md).## Registering hooks In a Feathers application, hooks are being registered in the [<servicename>](../cli/service.md) file. The hook registration object is an object with `{ around, before, after, error }` and a list of hooks per method like `{ all: [], find: [], create: [] }`.
`all` is a special keyword which means those hooks will run before the method specific hooks. Method specific hooks can be registered based on their name, e.g. to only log the runtime for `find` and `get`: ```ts app.service('messages').hooks({ around: { all: [authenticate('jwt')], find: [logRuntime], get: [logRuntime] } // ... }) ```## What's next? In this chapter we learned how Feathers hooks can be used as middleware for service method calls without having to change our service. Here we just logged the runtime of a service method to the console but you can imagine that hooks can be useful for many other things like more advanced logging, sending notifications or checking user permissions. You may also have noticed above that there are already some hooks like `schemaHooks.validateQuery` or `schemaHooks.resolveResult` registered on our service. This brings us to the next chapter on how to define our data model with [schemas and resolvers](./schemas.md). ================================================ FILE: docs/guides/basics/login.md ================================================ # Logging in We now have a fully functional chat application consisting of [services](./services.md) and [schemas](./schemas.md). In the [authentication chapter](./authentication.md) we generated everything necessary for using Feathers authentication so let's look at how to register users and add a log in with GitHub to our chat. ## Registering a user The HTTP REST API can be used directly to register a new user. We can do this by sending a POST request to `http://localhost:3030/users` with JSON data like this as the body: ```js // POST /users { "email": "hello@feathersjs.com", "password": "supersecret" } ``` Try it: ```sh curl 'http://localhost:3030/users/' \ -H 'Content-Type: application/json' \ --data-binary '{ "email": "hello@feathersjs.com", "password": "supersecret" }' ``` [](https://app.getpostman.com/run-collection/6bcea48aac6c7494c2ad)
For SQL databases, creating a user with the same email address will only work once, then fail since it already exists. With the default database selection, you can reset your database by removing the `feathers-chat.sqlite` file and running `npm run migrate` to initialise it again.This will return something like this:
The password has been hashed and stored securely in the database but will never be included in an external response.## Logging in By default, Feathers uses [JSON Web Tokens](https://jwt.io/) for authentication. It is an access token that is issued by the Feathers server for a limited time (one day by default) and needs to be sent with every API request that requires authentication. Usually a token is issued for a specific user. Let's see if we can issue a JWT for the user that we just created.
If you are wondering why Feathers is using JWT for authentication, have a look at [this FAQ](../../help/faq.md#why-are-you-using-jwt-for-sessions).Tokens can be created by sending a POST request to the `/authentication` endpoint (which is the same as calling the `create` method on the `authentication` service set up in `src/authentication`) and passing the authentication strategy you want to use along with any other relevant data. To get a JWT for an existing user through a username (email) and password login, we can use the built-in `local` authentication strategy with a request like this: ```js // POST /authentication { "strategy": "local", "email": "hello@feathersjs.com", "password": "supersecret" } ``` Try it: ```sh curl 'http://localhost:3030/authentication/' \ -H 'Content-Type: application/json' \ --data-binary '{ "strategy": "local", "email": "hello@feathersjs.com", "password": "supersecret" }' ``` [](https://app.getpostman.com/run-collection/6bcea48aac6c7494c2ad) This will return something like this:
Make sure to replace the `That's pretty neat, but emails and passwords are boring, let's spice things up a bit. ## Login with GitHub OAuth is an open authentication standard supported by almost every major social platform and what gets us the log in with Facebook, Google, GitHub etc. buttons. From the Feathers perspective, the authentication flow with OAuth is pretty similar. Instead of authenticating with the `local` strategy by sending a username and password, Feathers directs the user to authorize the application with the login provider. If it is successful, Feathers authentication finds or creates the user in the `users` service with the information it got back from the provider and then issues a token for them. To allow login with GitHub, we first have to [create a new OAuth application on GitHub](https://github.com/settings/applications/new). You can put anything in the name, homepage and description fields. The callback URL **must** be set to ```sh http://localhost:3030/oauth/github/callback ``` ` in the above request. For more information about the direct usage of the REST API see the [REST client API](../../api/client/rest.md) and for websockets the [Socket.io client API](../../api/client/socketio.md).
You can find your existing applications in the [GitHub OAuth apps developer settings](https://github.com/settings/developers).Once you've clicked "Register application", we need to update our Feathers app configuration with the client id and secret copied from the GitHub application settings. Find the `authentication` section in `config/default.json` replace the `
In a production environment you would set those values as [custom environment variables](../cli/custom-environment-variables.md).This tells the OAuth strategy to redirect back to our index page after a successful login and already makes a basic login with GitHub possible. Because of the changes we made in the `users` service in the [services chapter](./services.md) we do need a small customization though. Instead of only adding `githubId` to a new user when they log in with GitHub we also include their email and the avatar image from the profile we get back. We can do this by extending the standard OAuth strategy and registering it as a GitHub specific one and overwriting the `getEntityData` method: Update `src/authentication.ts` as follows: ```ts{1,5,14-26,33} import type { Params } from '@feathersjs/feathers' import { AuthenticationService, JWTStrategy } from '@feathersjs/authentication' import { LocalStrategy } from '@feathersjs/authentication-local' import { oauth, OAuthStrategy } from '@feathersjs/authentication-oauth' import type { OAuthProfile } from '@feathersjs/authentication-oauth' import type { Application } from './declarations' declare module './declarations' { interface ServiceTypes { authentication: AuthenticationService } } class GitHubStrategy extends OAuthStrategy { async getEntityData(profile: OAuthProfile, existing: any, params: Params) { const baseData = await super.getEntityData(profile, existing, params) return { ...baseData, // The GitHub profile image avatar: profile.avatar_url, // The user email address (if available) email: profile.email || profile.login } } } export const authentication = (app: Application) => { const authentication = new AuthenticationService(app) authentication.register('jwt', new JWTStrategy()) authentication.register('local', new LocalStrategy()) authentication.register('github', new GitHubStrategy()) app.use('authentication', authentication) app.configure(oauth()) } ```
For more information about the OAuth flow and strategy see the [OAuth API documentation](../../api/authentication/oauth.md).To log in with GitHub, visit ``` http://localhost:3030/oauth/github ``` It will redirect to GitHub and ask to authorize our application. If everything went well, we get redirected to our homepage with the Feathers logo with the token information in the location hash. This will be used by the Feathers authentication client to authenticate our user. ## What's next? Sweet! We now have an API that can register new users with email/password and GitHub. This means we have everything we need for a frontend for our chat application. See the [JavaScript frontend guide](../frontend/javascript.md) on how to create a plain JavaScript chat application. ================================================ FILE: docs/guides/basics/schemas.md ================================================ # Schemas and resolvers In Feathers, schemas and resolvers allow us to define, validate and secure our data model and types.
The `virtual()` in the `messageResolver` `user` property is a [virtual property](../../api/schema/resolvers.md#virtual-property-resolvers) and indicates that the value does not come from the messages database table.## Creating a migration
For MongoDB no migrations are necessary.
A service does not have to implement all those methods but must have at least one. For more information about services, service methods, and parameters see the [Service API documentation](../../api/services.md).When used as a REST API, incoming requests get mapped automatically to their corresponding service method like this: | Service method | HTTP method | Path | | ------------------------------------------- | ----------- | --------------------- | | `service.find({ query: {} })` | GET | /messages | | `service.find({ query: { unread: true } })` | GET | /messages?unread=true | | `service.get(123)` | GET | /messages/123 | | `service.create(body)` | POST | /messages | | `service.update(123, body)` | PUT | /messages/123 | | `service.patch(123, body)` | PATCH | /messages/123 | | `service.remove(123)` | DELETE | /messages/123 | ### Service events A registered service will automatically become a [NodeJS EventEmitter](https://nodejs.org/api/events.html) that sends events with the new data when a service method that modifies data (`create`, `update`, `patch` and `remove`) returns. Events can be listened to with `app.service('messages').on('eventName', data => {})`. Here is a list of the service methods and their corresponding events: | Service method | Service event | | ------------------ | ----------------------- | | `service.create()` | `service.on('created')` | | `service.update()` | `service.on('updated')` | | `service.patch()` | `service.on('patched')` | | `service.remove()` | `service.on('removed')` | This is how Feathers does real-time. ```js app.service('myservice').on('created', (data) => { console.log('Got created event', data) }) ``` ## Database adapters Now that we have all those service methods, we could go ahead and implement any kind of custom logic using any backend, similar to what we did in the [quick start guide](./starting.md). Very often, this means creating, reading, updating and removing data from a database. Writing all that code yourself for every service is pretty repetitive and cumbersome, which is why Feathers has a collection of pre-built services for different databases. They offer most of the basic functionality and can always be customized to your needs. Feathers database adapters support a common [usage API](../../api/databases/common.md), pagination and [querying syntax](../../api/databases/querying.md) for many popular databases. The following database adapters are maintained as part of Feathers core: - [SQL](../../api/databases/knex.md) for databases like PostgreSQL, SQLite, MySQL, MariaDB, MSSQL - [MongoDB](../../api/databases/mongodb.md) for MongoDB - [Memory](../../api/databases/memory.md) for in-memory data
There are also many other community maintained database integrations which you can explore on the [ecosystem page](/ecosystem/?cat=Database&sort=downloads). Since they are not part of Feathers core, they are outside the scope of these guides.If you went with the default selection, we will use **SQLite** which writes the database to a file and does not require any additional setup. The user service that was created when we [generated authentication](./authentication.md) is already using it. ## Generating a service In our new `feathers-chat` application, we can create database backed services with the following command: ```sh npx feathers generate service ``` The name for our service is `message` (this is used for variable names etc.) and for the path we use `messages`. Anything else we can confirm with the default:
You can follow this guide on your own computer in the terminal or try the steps out live without installing anything in the [Feathers Quick Start on Stackblitz](https://stackblitz.com/@daffl/collections/feathers-quick-start).After successful installation, the `node` and `npm` commands should be available on the terminal: ``` node --version ``` ``` npm --version ```
Running NodeJS and npm should not require admin or root privileges.Let's create a new folder for our application: ```sh mkdir feathers-basics cd feathers-basics ``` Since any Feathers application is a Node application, we can create a default [package.json](https://docs.npmjs.com/files/package.json) using `npm`:
All Feathers core modules are in the `@feathersjs` namespace.## Our first app Now we can create a Feathers application with a simple `messages` service that allows us to create new messages and find all existing ones.
The server will stay running until you stop it by pressing **Control + C** in the terminal.And in the browser visit ``` http://localhost:3030/messages ``` to see an array with the one message we created on the server.
The built-in [JSON viewer in Firefox](https://developer.mozilla.org/en-US/docs/Tools/JSON_viewer) or a browser plugin like [JSON viewer for Chrome](https://chrome.google.com/webstore/detail/json-viewer/gbmdgpbipfallnflgajpaliibnhdgobh) makes it nicer to view JSON responses in the browser.This is the basic setup of a Feathers API server. - The `app.use` calls probably look familiar if you have used something like Koa or Express before. - `app.configure` calls set up the Feathers transport to host the API. - `app.on('connection')` and `app.publish` are used to set up event channels, which send real-time events to the proper clients (everybody that is connected to our server in this case). You can learn [more about the channels API](../../api/channels.md) after finishing this guide. ## In the browser Now we can look at one of the really cool features of Feathers: **It works the same in a web browser!** This means that we could take [our first app example](#our-first-app) from above and run it just the same in a website. Since we already have a server running, however, let's go a step further and create a Feathers app that talks to our `messages` service on the server using a real-time Socket.io connection. In the same folder, add the following `index.html` page: ```html
When using Git for version control, the `data/` and `test/data` folders should be added to `.gitignore`.We also want to make sure that the database is cleaned up before every test run. To make that possible across platforms, first run: ```sh npm install shx --save-dev ``` Now we can update the `scripts` section of our `package.json` to the following:
When using Git for version control, the `.nyc_output/` folder should be added to `.gitignore`.## What's next? That’s it! Our chat guide is completed! We now have a fully-tested REST and real-time API, with a plain JavaScript frontend including login and signup. Follow up in the [Feathers API documentation](../../api/) for more details about using Feathers, or start building your own first Feathers application! ================================================ FILE: docs/guides/cli/app.md ================================================ --- outline: deep --- # Application The `src/app.ts` file is the main file where the [Feathers application](../../api/application.md) gets initialized and wired up with a Feathers transport. ## Transports The available transports are [Koa](../../api/koa.md) or [Express](../../api/express.md) for HTTP and [Socket.io](../../api/socketio.md) for real-time functionality. For both, Koa and Express, the Feathers application (`app` object) will also be fully compatible with the respective framework. For both frameworks, additional required middleware will be registered in the application file. More information can be found in the linked API documentation. ## Configure functions The Feathers application does not use a complicated dependency injection mechanism. Instead, the application is wired together using _configure functions_ to split things up into individual files. They are functions that are exported from a file and that take the Feathers [app object](../../api/application.md) and then use it to e.g. register services. Those functions are then passed to [app.configure](../../api/application.md#configurecallback). For example, have a look at the following files: `src/services/index.ts` looks like this: ```ts import type { Application } from '../declarations' import { user } from './users/users' export const services = (app: Application) => { app.configure(user) // All services will be registered here } ``` It uses another configure function exported from `src/services/users/users.ts`. The export from `src/services/index.js` is in turn used in `src/app.ts` as: ```ts // ... import { services } from './services' // ... app.configure(authentication) app.configure(services) // ... ``` This is how the generator splits things up into separate files and any documentation example that uses the `app` object can be used in a configure function. You can create your own files that export a configure function and `require`/`import` and `app.configure` them.
Keep in mind that the order in which configure functions are called might matter, e.g. if it is using a service, that service has to be registered first. Configure functions are not asynchronous. Any asynchronous operations should be done in [application setup hooks](#application-hooks).## Application hooks The application file also includes a section to set up [application hooks](../../api/hooks.md#application-hooks) which are hooks that run for every service. In our case, the `logErrorHook` to log any service errors has already been registered: ```ts // Register hooks that run on all service methods app.hooks({ around: { all: [logErrorHook] }, before: {}, after: {}, error: {} }) ``` Following that is the special [setup and teardown](../../api/hooks.md#setup-and-teardown) hook section to register hooks that run once when the application starts or shuts down. This can be used to e.g. set dynamic configuration values. ```ts // Register application setup and teardown hooks here app.hooks({ setup: [], teardown: [] }) ``` ## Tests, jobs and SSR The `app` file can be imported like any other Node module. This means it can be used directly in tests, scheduled jobs or server side rendering without having to start a separate server instance. For example, the unit tests import the application like this: ```ts import assert from 'assert' import { app } from '../../src/app' describe('messages service', () => { it('registered the service', () => { const service = app.service('messages') assert.ok(service, 'Registered the service') }) }) ``` ================================================ FILE: docs/guides/cli/app.test.md ================================================ --- outline: deep --- # Application tests The `app.test` file starts the server and then tests that it shows the index page and that 404 (Not Found) JSON errors are being returned. It uses the [Axios HTTP](https://axios-http.com/) library to make the calls. This file can e.g. be used to test application [setup](../../api/application.md#setupserver) and [teardown](../../api/application.md#teardownserver). All tests are using [MochaJS](https://mochajs.org/) but will be moving to the [NodeJS test runner](https://nodejs.org/api/test.html) in the future. ================================================ FILE: docs/guides/cli/authentication.md ================================================ --- outline: deep --- # Authentication The file in `src/authentication.ts` sets up an [authentication service](../../api/authentication/service.md) and registers [authentication strategies](../../api/authentication/strategy.md). Depending on the strategies you selected it looks similar to this: ```ts import { AuthenticationService, JWTStrategy } from '@feathersjs/authentication' import { LocalStrategy } from '@feathersjs/authentication-local' import type { Application } from './declarations' declare module './declarations' { interface ServiceTypes { authentication: AuthenticationService } } export const authentication = (app: Application) => { const authentication = new AuthenticationService(app) authentication.register('jwt', new JWTStrategy()) authentication.register('local', new LocalStrategy()) app.use('authentication', authentication) } ``` ## oAuth Note that when selecting oAuth logins (Google, Facebook, GitHub etc.), the standard registered oAuth strategy only uses the `
Note that you can use `client` for client side interactions and the server side [application](./app.md#application) `app` object for server side calls in the same file. For example, if the user required an email verification but you don't want to test sending out emails you can call something like `app.service('users').patch(user.id, { isVerified: true })` to enable the new user on the server.================================================ FILE: docs/guides/cli/configuration.md ================================================ --- outline: deep --- ### Configuration Schemas A generated application comes with a schema that validates the initial configuration when the application is started. This makes it much easier to catch configuration errors early which can otherwise be especially difficult to debug in remote environments. The configuration [schema definition](../../api/schema/index.md) can be found in `configuration.ts`. It is used as a [configuration schema](../../api/configuration.md#configuration-validation) and loads some default schemas for authentication and database connection configuration and adds values for `host`, `port` and the `public` hosted file folder. The types of this schema are also used for `app.get()` and `app.set()` [typings](./declarations.md). The initial configuration schema will be validated on application startup when calling [`app.listen()`](../../api/application.md#listenport) or [`app.setup()`](../../api/application.md#setupserver). ================================================ FILE: docs/guides/cli/custom-environment-variables.md ================================================ # Custom Environment Variables While `node-config` used for [application configuration](./default.json.md) recommends to pass environment based configuration as a JSON string in a single `NODE_CONFIG` environment variable, it is also possible to use other environment variables via the `config/custom-environment-variables.json` file which looks like this by default: ```json { "port": { "__name": "PORT", "__format": "number" }, "host": "HOSTNAME", "authentication": { "secret": "FEATHERS_SECRET" } } ``` This sets `app.get('port')` using the `PORT` environment variable (if it is available) parsing it as a number and `app.get('host')` from the `HOSTNAME` environment variable and the authentication secret to the `FEATHERS_SECRET` environment variable.
See the [node-config custom environment variable](https://github.com/node-config/node-config/wiki/Environment-Variables#custom-environment-variables) documentation for more information.## Dotenv To add support for [dotenv](https://www.dotenv.org/) `.env` files run ``` npm install dotenv --save ``` And update `src/app.ts` as follows: ```ts // dotenv replaces all environmental variables from ~/.env in ~/config/custom-environment-variables.json import * as dotenv from 'dotenv' dotenv.config() // or for ES6 import 'dotenv/config'; import configuration from '@feathersjs/configuration' ```
`dotenv.config()` needs to run _before_ `import configuration from '@feathersjs/configuration'`================================================ FILE: docs/guides/cli/databases.md ================================================ --- outline: deep --- # Databases
To get the latest types in the [client](./client.md) and any time before `npm start`, `npm run compile` needs to run.## Configuration Types The `Configuration` interface defines the types for [app.get](../../api/application.md#getname) and [app.set](../../api/application.md#setname-value). It is extended from the type inferred from the [configuration schema](./configuration.md#configuration-schemas). Since you can store anything global to the application in `app.get` and `app.set`, you can add additional types that are not part of the initial application configuration here. ```ts // The types for app.get(name) and app.set(name) // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface Configuration extends ApplicationConfiguration { startupTime: Date } ```
Both `Configuration` and `ServiceTypes` need to be declared as an `interface` (even if it is empty) so they can be extended via `declare module` in other files. Do not remove the `eslint-disable-next-line` comments.## Service Types The `ServiceTypes` interface contains a mapping of all service paths to their service type so that [app.use](../../api/application.md#usepath-service--options) and [app.service](../../api/application.md#servicepath) use the correct type. ```ts // A mapping of service names to types. Will be extended in service files. // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ServiceTypes {} ``` Usually the `ServiceTypes` interface is not modified directly in this file but instead extended via `declare module` in the files where the services are registered. This usually looks like this: ```ts // Add this service to the service type index declare module '../../declarations' { interface ServiceTypes { users: UserService } } ``` ## Application The `Application` interface is the type for the main [app object](./app.md) using the [ServiceTypes](#service-types) interface as the service index and [ConfigurationTypes](#configuration-types) for `app.get` and `app.set`. ```ts // The application instance type that will be used everywhere else export type Application = FeathersApplication
Always use `import { Application } from './declarations'` to get the proper service and configuration typings. You normally do **not** need to use `import { Application } from '@feathersjs/feathers'` directly.## Hook Context The `HookContext` type exports a [hook context](../../api/hooks.md) type with the `Application` and a generic service type `S`. ```ts // The context for hook functions - can be typed with a service class export type HookContext
Please pick **TypeScript** as the Code language in the main menu dropdown.
For more information on application configuration and schemas see the [configuration API documentation](../../api/configuration.md).## Environments The `NODE_ENV` environment variable determines which configuration file is used. For example, setting `NODE_ENV=development` (in a single command e.g. as `NODE_ENV=development npm run dev`) will first load `config/default.json` and then merge it with `config/development.json`. If no environment is set, `config/default.json` will be used. ## Default configuration The application uses the following configuration values. ### host, port, public These options are used directly in the generated application - `host` - Is the hostname of the API server - `port` - The port it listens on - `public` - The name of the folder static assets are hosted in ### paginate `paginate` sets the default and maximum page size when using [pagination](../../api/databases/common.md#pagination) with a [database service](../../api/databases/adapters.md). ```json { "paginate": { "default": 10, "max": 100 } } ``` ### origins `origins` contains a list of frontend URLs that requests can be made from. This is used to configure cross origin (CORS) policies and oAuth (Twitter, Facebook etc.) login redirects. For example to develop locally with a [create-react-app](https://create-react-app.dev/) frontend and deploy to `app.feathersjs.com`: ```json { "origins": ["http://localhost:3030", "http://localhost:5000", "https://app.feathersjs.com"] } ``` ### authentication `authentication` contains the configuration for the authentication service and strategies. See the [authentication service configuration](../../api/authentication/service.md#configuration) for more information. For strategy specific settings refer to the [jwt](../../api/authentication/jwt.md#options), [local](../../api/authentication/local.md#options) and [oAuth](../../api/authentication/oauth.md#options) API documentation. ### Databases
For more information see the [hooks API documentation](../../api/hooks.md).### Around hooks [Around hooks](../../api/hooks.md#around) allow to control the entire `before`, `after` and `error` flow in a single function. An `around` hook is an `async` function that accepts two arguments: - The [hook context](../../api/hooks.md#hook-context) - An asynchronous `next` function. Somewhere in the body of the hook function, there is a call to `await next()`, which calls the `next` hooks OR the original function if all other hooks have run. ```ts import type { HookContext, NextFunction } from '../declarations' export const myFancyHook = async (context: HookContext, next: NextFunction) => { console.log(`Running hook ${name} on ${context.path}.${context.method}`) await next() // Do things after here } ``` You can wrap the `await next()` in a `try/catch` block to also handle errors. ### Before, after, error [Before, after or error hooks](../../api/hooks.md#before-after-and-error) are `async` functions that take the [hook context](#hook-context) as the parameter. ```ts import type { HookContext } from '../declarations' export const myFancyHook = async (context: HookContext) => { console.log(`Running hook ${name} on ${context.path}.${context.method}`) } ``` ## Context types If the hook is for a specific service, you can pass the service as a generic to the [HookContext](./declarations.md#hook-context) type which will give you the correct types for [context.data](../../api/hooks.md#contextdata), [context.result](../../api/hooks.md#contextresult) and [context.params](../../api/hooks.md#contextparams): ```ts import type { UserService } from '../services/users/users' import type { HookContext } from '../declarations' export const myFancyUserHook = async (context: HookContext
For more information on what is available in migration files, see the [Knex migrations documentation](https://knexjs.org/guide/migrations.html).================================================ FILE: docs/guides/cli/log-error.md ================================================ # Error logging hook The `src/hooks/log-error.ts` file exports a `logError` hook that uses the [logger](./logger.md) to log any error for a service method, including validation error details (when they are available). It is registered as an [application hook](./app.md#application-hooks) `all` hook, meaning it will log errors for any service method. ================================================ FILE: docs/guides/cli/logger.md ================================================ --- outline: deep --- # Logging ## Logger The `src/logger.ts` file initialises the widely used [Winston logger](https://github.com/winstonjs/winston) library, by default with the `info` log level, logging to the console. ```ts import { createLogger, format, transports } from 'winston' // Configure the Winston logger. For the complete documentation see https://github.com/winstonjs/winston export const logger = createLogger({ // To see more detailed errors, change this to 'debug' level: 'info', format: format.combine(format.splat(), format.simple()), transports: [new transports.Console()] }) ``` You can import the logger directly in any file where you want to add logging information. ```ts import { logger } from './logger' logger.info('Log some information here') ``` ================================================ FILE: docs/guides/cli/package.md ================================================ # package.json ## Folders The source and test folders to which files are generated is set in the `package.json`. To change them, rename the `src/` or `test/` folder to what you want it to and then update `package.json` `directories` section accordingly: ```json { "directories": { "lib": "api/src", "test": "api/test" } } ``` ================================================ FILE: docs/guides/cli/prettierrc.md ================================================ # Prettier The Feathers CLI uses [Prettier](https://prettier.io/) for code formatting and generates a configuration for it in a new application. To change the options, like the use of semicolons, quotes etc, edit the `.prettierrc` file with the [options available](https://prettier.io/docs/en/options.html). To update all existing source files with the new code style run ``` npm run prettier ``` When new files are generated, they will use the current Prettier configuration. See the [Prettier Integration with Linters](https://prettier.io/docs/en/integrating-with-linters.html) documentation for how to integrate with tools like ESLint. ================================================ FILE: docs/guides/cli/service.class.md ================================================ --- outline: deep --- # Service classes The `
The generic types for a database service are always `AdapterService`. The `MessageService ` generic is used to change the parameter type when using this service interface as a [client side service](./client.md).
The examples on this page are using [TypeBox](../../api/schema/typebox.md). For more information on plain JSON schema see the [JSON schema API documentation](../../api/schema/schema.md).## Patterns There a four main types of schemas and resolvers. The schemas, resolvers and types are declared as follows: ```ts // The schema definition export const nameSchema = Type.Object({ text: Type.String() }) // The TypeScript type inferred from the schema export type Name = Static
Note that references (`Type.Ref`) can not be used in a query schema. Association querying is usually done by dot separated properties which have to be added manually in [MongoDB](../../api/databases/mongodb.md#querying) and [SQL](../../api/databases/knex.md#associations).================================================ FILE: docs/guides/cli/service.shared.md ================================================ --- outline: deep --- # Service Shared The `
If you want to test the full authentication and external access flow the [client.test](./client.test.md) can be used.================================================ FILE: docs/guides/cli/tsconfig.md ================================================ # tsconfig.json ================================================ FILE: docs/guides/cli/validators.md ================================================ --- outline: deep --- # Validators For all currently supported schema types, AJV is used as the default validator. See the [validators API documentation](../../api/schema/validators.md) for more information. ## AJV validators The `src/validators.ts` file sets up two Ajv instances for data and querys (for which string types will be coerced automatically). It also sets up a collection of additional formats using [ajv-formats](https://ajv.js.org/packages/ajv-formats.html). The validators in this file can be customized according to the [Ajv documentation](https://ajv.js.org/) and [its plugins](https://ajv.js.org/packages/). You can find the available Ajv options in the [Ajs class API docs](https://ajv.js.org/options.html). ```ts import { Ajv, addFormats } from '@feathersjs/schema' import type { FormatsPluginOptions } from '@feathersjs/schema' const formats: FormatsPluginOptions = [ 'date-time', 'time', 'date', 'email', 'hostname', 'ipv4', 'ipv6', 'uri', 'uri-reference', 'uuid', 'uri-template', 'json-pointer', 'relative-json-pointer', 'regex' ] export const dataValidator = addFormats(new Ajv({}), formats) export const queryValidator = addFormats( new Ajv({ coerceTypes: true }), formats ) ``` ## MongoDB ObjectIds When choosing MongoDB, the validators file will also register the [`objectid` keyword](../../api/databases/mongodb.md#ajv-keyword) to convert strings to MongoDB Object ids. ================================================ FILE: docs/guides/frameworks.md ================================================ # Frontend Frameworks Feathers works the same on the server and on the client and is front-end framework agnostic. You can use it with Vue, React, React Native, Angular, or whatever other front-end tech stack you choose. ## Client Side Docs If you want to learn how to use Feathers as a client in Node.js, React Native, or in the browser with a module loader like Webpack refer to the [client API docs](../api/client.md). ## The Feathers chat The [Feathers Chat application](../guides/) from guide gives a basic intro to using the Feathers Client in a vanilla JavaScript environment. That's a good place to start to see how things fit together. Framework specific repositories can be found at: - JavaScript + plain JS frontend: [feathersjs/feathers-chat](https://github.com/feathersjs/feathers-chat) - TypeScript + plain JS frontend: [feathersjs/feathers-chat-ts](https://github.com/feathersjs/feathers-chat-ts) - React: [feathersjs-ecosystem/feathers-chat-react](https://github.com/feathersjs-ecosystem/feathers-chat-react) - React Native: [feathersjs-ecosystem/feathers-react-native-chat](https://github.com/feathersjs-ecosystem/feathers-react-native-chat) - Angular: [feathersjs-ecosystem/feathers-chat-angular](https://github.com/feathersjs-ecosystem/feathers-chat-angular) - VueJS with Vuex: [feathersjs-ecosystem/feathers-chat-vuex](https://github.com/feathersjs-ecosystem/feathers-chat-vuex) ## Examples Beyond the basics, see [this list](https://github.com/feathersjs/awesome-feathersjs#examples) of Feathers examples in [awesome-feathersjs](https://github.com/feathersjs/awesome-feathersjs). ## Framework Integrations See [this list](https://github.com/feathersjs/awesome-feathersjs#frontend-frameworks) of Feathers front-end framework integrations if you are looking for something that makes Feathers even easier to use with things like React, Vue or others. ================================================ FILE: docs/guides/frontend/javascript.md ================================================ --- outline: deep --- # JavaScript web app As we have seen [in the quick start guide](../basics/starting.md), Feathers works great in the browser and comes with client services that allow it to easily connect to a Feathers server. In this chapter we will create a real-time chat web application with signup and login using modern plain JavaScript that connects to the API server we built in the [getting started guide](../basics/generator.md). It is mobile friendly and will work in the latest versions of Chrome, Firefox, Safari and Edge. We won't be be using a transpiler like Webpack or Babel which is also why there is no TypeScript option. The final version can be found in `public/` folder of the [feathers-chat repository](https://github.com/feathersjs/feathers-chat/tree/dove/public). 
We will not be using a frontend framework so we can focus on what Feathers is all about. Feathers is framework agnostic and can be used with any frontend framework like React, VueJS or Angular. For more information see the [frameworks section](../frameworks.md).## Set up the page First, let's update `public/index.html` to initialize everything we need for the chat frontend: ```html
We are exploring the best migration strategy to replace "whitelisting" options with a solution based on [Feathers schema](/api/schema/index). 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. ```ts 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. ```ts // 👎`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'] } ```
This change only affects service adapters that have been upgraded to v5, like [@feathersjs/mongodb](/api/databases/mongodb), [@feathersjs/knex](/api/databases/knex), and [@feathersjs/memory](/api/databases/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: ```js // 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: ```js // 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: - The Socket.io [version 3](https://socket.io/docs/v3/migrating-from-2-x-to-3-0/index.html#How-to-upgrade-an-existing-production-deployment) and [version 4](https://socket.io/docs/v3/migrating-from-3-x-to-4-0/) upgrade guide. Important points to note are a new improved [CORS policy](https://socket.io/docs/v3/migrating-from-2-x-to-3-0/index.html#CORS-handling) and an [explicit v2 client compatibility opt-in](https://socket.io/docs/v3/migrating-from-2-x-to-3-0/index.html#How-to-upgrade-an-existing-production-deployment) - For oAuth authentication the Grant standard configuration should continue to work as is. If you customized any other settings, see the [Grant v4 to v5 migration guide](https://github.com/simov/grant/blob/master/MIGRATION.md) for the changes necessary. ### 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](https://github.com/lorenwest/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](https://github.com/lorenwest/node-config/wiki/Environment-Variables#custom-environment-variables). To use existing environment variables add the following configuration file in `config/custom-environment-variables.json` like this: ```json // 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: ```ts import { feathers } from '@feathersjs/feathers' import debug from 'debug' feathers.setDebug(debug) ``` It is also possible to set a custom logger like this: ```ts 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. ### 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](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API), 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](../api/client.md#module-loaders) 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](../api/client.md) or the [current Socket.io direct connection API](../api/client/socketio.md). #### 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](../api/application.md#defaultservice): ```js 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)`: ```js // 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: ```js 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`](../api/application.md#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 `req.authentication` property is no longer set on the express requests, use `req.feathers.authentication` instead. ================================================ FILE: docs/guides/security.md ================================================ # Security We take security very seriously at Feathers. We welcome any peer review of our 100% open source code to ensure nobody's Feathers app is ever compromised or hacked. However, as a web application developer, you are responsible for the security of your application. We do our very best to make sure Feathers is as secure as possible. ## Reporting security issues In order to give the community time to respond and upgrade, we strongly urge you report all security issues to us. Send us a PM on [Discord](https://discord.gg/qa8kez8QBx) or email us at [hello@feathersjs.com](mailto:hello@feathersjs.com) with details, and we will respond ASAP. Security issues always take precedence over bug fixes and feature work; so, we'll work with you to come up with a resolution and plan and document the issue on Github in the appropriate repo. Issuing releases is typically very quick. Once an issue is resolved it is usually released immediately with the appropriate semantic version. ## Security considerations Here are some things that you should be aware of when writing your app to make sure it is secure. - Make sure to set up proper [event channels](../api/channels.md) so that only clients that are allowed to see them can see real-time updates - Use hooks to check security roles to make sure users can only access data they should be permitted to. You can find useful hook utilities in [feathers-hooks-common](https://hooks-common.feathersjs.com/) and [feathers-authentication-hooks](https://github.com/feathersjs-ecosystem/feathers-authentication-hooks/). - Restrict the [allowed database queries](../api/databases/querying.md) to only the use cases your application requires by sanitizing `params.query` in a hook. - When you explicitly allow multiple element changes, make sure queries are secured properly to limit the items that can be changed. - Escape any HTML and JavaScript to avoid XSS attacks. - Escape any SQL (typically done by the SQL library) to avoid SQL injection. - JSON Web Tokens (JWT's) are only signed. They are **not** encrypted. Therefore, the payload can be examined on the client. This is by design. **DO NOT** put anything that should be private in the JWT `payload` unless you encrypt it first. - Don't use a weak `secret` for your token service. The generator creates a strong one for you automatically. No need to change it. ## Technologies used - Password storage inside `@feathers/authentication-local` uses [bcrypt](https://github.com/dcodeIO/bcrypt.js). We don't store the salts separately since they are included in the bcrypt hashes. - By default, [JWT](https://jwt.io/)'s are stored in Local Storage (instead of cookies) to avoid CSRF attacks. For JWT, we use the `HS256` algorithm by default (HMAC using SHA-256 hash algorithm). If you choose to store JWT's in cookies, your app may have CSRF vulnerabilities. ## XSS attacks As with any web application **you** need to guard against XSS attacks. Since Feathers persists the JWT in localstorage in the browser, if your app falls victim to a XSS attack your JWT could be used by an attacker to make malicious requests on your behalf. This is far from ideal. Therefore you need to take extra care in preventing XSS attacks. Our stance on this particular attack vector is that if you are susceptible to XSS attacks, then a compromised JWT is the least of your worries because keystrokes could be logged and attackers can just steal passwords, credit card numbers, or anything else your users type directly. For more information see [this issue](https://github.com/feathersjs/authentication/issues/132) ================================================ FILE: docs/guides/whats-new.md ================================================ --- outline: deep --- # What's New in v5 Feathers Dove (v5) is a super-ambitious release which adds some really great tools and APIs. We don't have to mince words. This release is awesome with so many useful features. Here is an overview of each new feature, followed by links to learn more. ## New TypeScript Benefits Feathers has been a TypeScript-friendly framework for years, but TypeScript support took a huge leap forward with Feathers Dove. ### Complete TypeScript Rewrite We've completely rewritten all of Feathers in TypeScript. And we're not talking about a lightweight TypeScript implementation. It's TypeScript all the way down. Everything from the official database adapters, built-in hooks, and utilities, right down to Feathers core. The newly-rebuilt [v5 CLI and generator](#rebuilt-cli) even produces a TypeScript application, by default. You can find the shiny new TypeScript packages on GitHub, [here](https://github.com/feathersjs/feathers/tree/dove/packages). ### Typed Client Feathers has had an isomorphic API - working equally well in browser and server - since 2016. Now with Dove, our [new CLI](#rebuilt-cli) generates **shared types for the Feathers server and client**. We even integrated the types with our new [schemas feature](#official-schemas), so you define your types once, in a single location, and use them everywhere. Everything still uses Feathers tried and proven [standard HTTP and websocket](../api/client.md) communication mechanism. There is no custom protocol or the slow parsing of a domain specific language (like GraphQL) necessary and since it is all plain TypeScript with type information removed at compile time, there is also no bundle size overhead. Thanks to FeathersJS's clean, modular, loosely-coupled architecture, it was pleasantly simple to add this feature. This is another one of the benefits we get from building a pattern-driven framework. You can learn more about the Feathers Client, [here](../guides/cli/client.md). ## New Documentation The new docs are built on [Vitepress](https://vitepress.vuejs.org/). It had become difficult to maintain all of the examples in two languages: JavaScript and TypeScript. To simplify, we now ONLY write documentation examples in TypeScript. You can still switch languages in the sidebar thanks to our custom highlighter for Vitepress, which transpiles TypeScript examples to JavaScript. You can find the `docs` folder, [here](https://github.com/feathersjs/feathers/tree/dove/docs). ## Framework Agnostic API In 2016, we realized that we could decouple Feathers from the underlying HTTP transport, resulting in our first framework-agnostic, isomorphic build. As of Feathers v5, we now support three ways of using Feathers: - The Feathers client, which provides the same API in the browser, React Native and in Node.js. - The [KoaJS transport](../api/koa.md) (new in Feathers Dove) - The [ExpressJS transport](../api/express.md) ### KoaJS Support The CLI's official HTTP integration has always been built on the Express adapter. Lately, Express has been "showing its age", so we've made some inviting changes. - We've released the [`@feathersjs/koa`](../api/koa.md) adapter and utilities. Now Feathers apps can use [KoaJS](https://koajs.com/) as an API base. - The Feathers CLI now uses KoaJS as the default transport. - The Express adapter will continue to function alongside KoaJS. FeathersJS has its own, isomorphic middleware layer, based on [hooks](../api/hooks.md), so you likely won't need to tap into the middleware layer of any framework adapter. But, in those cases that you need framework middleware, it's available to you. Read about the KoaJS adapter, [here](../api/koa.md). ### Lightning-Fast Routing Feathers just got a huge speed upgrade, now including its own [Radix Trie](https://iq.opengenus.org/radix-tree/) router. This means that the algorithm behind Fastify's speed is now built into Feathers, and it works no matter which framework transport you use under the hood. The best part about the new router is that there's not another API you have to learn in order to use Feathers. It just works. For those who want to build a custom framework transport, there's a single `.lookup` method which routes requests through Feathers. Learn about the `lookup` method, [here](/api/application#lookup). ### Service De-Registration Now that Feathers comes with [its own router](#lightning-fast-routing), it's possible to de-register a service to completely remove it from the application. This allows you to build cleaner, dynamically generated applications. This is a coveted feature for those who want to build, for example, a dynamic CRUD admin application that's driven by some sort of schema. Learn about service de-registration [here](../api/application.md#unusepath). ## Custom Methods While Feathers [standard service methods](../api/services.md) cover 90% of the functionality you need to create and modify data, a service might also provide other custom functionality making custom Method creation one of the most-requested features for Feathers. Feathers Dove introduces an elegant solution for custom methods on top of the Feathers service interface. When you define your service class, you can specify additional methods on the class to create an internal-only method. To expose the method to the public API, add the method's name to the `methods` options when registering the service. Custom methods are similar to the `create` method, so you can POST to them when using HTTP-based adapters. Read more about custom methods, [here](../api/services#custom-methods). ## Official Schemas Feathers Dove (v5) introduces new, official tools for data validation in the new core package, [@feathersjs/schema](../api/schema/index.md). This same package includes schema-based resolvers, which you'll learn about in the next section. Schemas are powered by JSON Schema (an IETF standard) which makes them powerful and portable. ### Schema-Driven Types One of the problems we wanted to avoid was the need to define schemas and/or types in multiple places. If we had a motto/slogan for types, it would be **"Define it once, use it everywhere."** So in Feathers Dove, when you create a schema, it dynamically generates validation and proper TypeScript types. You can use the powerful and concise [TypeBox schema format](../api/schema/typebox.md) or [plain JSON schema](../api/schema/schema.md). ### Configuration Schemas If you've ever experienced pains of deploying to production, you'll appreciate this feature. When your app starts in production, all of your configuration and environment variables are checked against the configuration schema. The app won't start if the schema validation fails. This keeps bugs from missing environment variables from showing up in production days to weeks after deployment. Configuration schemas also produce TypeScript types, so the [TypeScript improvements](#new-typescript-benefits) in Feathers Dove include typed configuration lookup for `app.get()` and `app.set()`. It's really convenient. Read more about configuration schemas, [here](/api/configuration#configuration-schema) ## Resolvers Combined with the new [schemas](#official-schemas), resolvers allow to dynamically populate properties. It's a powerful tool that has many use cases from populating associations, securing queries or protecting secure data to easily setting values like the creation date or associated user. See the [chat guide](../guides/basics/generator.md) for an example on how to set the user avatar, populate the user associated with a message and let users only modify their own data. ### Resolver Utility Hooks Resolvers are powered by new hook utilities: - [resolveData](../api/schema/resolvers.md#data-resolvers) for incoming data. - [resolveResult](../api/schema/resolvers.md#result-resolvers) for results coming from databases or other services - [resolveDispatch](../api/schema/resolvers.md#safe-data-resolvers) for cleanly defining safe data for WebSocket / Real-time events. - [resolveQuery](../api/schema/resolvers.md#query-resolvers) for incoming query parameters Read about the new hooks using the links, above. These new hook utils allow you to - More cleanly manage properties on incoming records, query objects, and/or results - Perform advanced validation beyond what's possible with JSON Schema. - More efficiently write code for populating relational data (often faster than a normal ORM) - Save yourself a lot of boilerplate compared to writing the three features, above, manually with hooks. You can start using resolvers right away. The new [CLI](../guides/basics/services#generating-a-service) generates them with all new services. Resolvers are one of the new tools provided in the new core package: [@feathersjs/schema](../api/schema/index.md). You can read more about resolvers, [here](../api/schema/resolvers.md). ### Hooks vs Resolvers At first glance, choosing where to put logic might seem complex. Should the feature go into a hook or a resolver? Here are some general guidelines to assist you: - Data manipulation and **custom** validation probably fit best in a resolver. - Adding or pulling in data from other sources will likely fit best in a resolver. - Side effects that manipulate external data should go into a hook with few exceptions. ## More Powerful Hooks In Feathers Dove there are now two hook formats. One for `before`, `after`, and `error` hooks, and a new one for `around` hooks: - **Before, after, and error hooks** have been Feathers' most-used API for years. - Are registered as `before`, `after`, or `error` hooks in the hook object. - Continue to work fully in Feathers Dove. - Are less powerful than the new "around" hooks, but visually simpler. - **Around Hooks** are new in Feathers Dove. - Are registered in the `around` key of a hook object. - Are capable of handling before, after, and error logic, all within a single hook function. - Have a slightly different function definition than before, after, and error hooks - Are more powerful yet also more visually complex (The "after" part of each hook runs in reverse-registered order). Let's compare the signature of the two types of hooks. ### Before, After, & Error Hooks Let's look at an example of the before/after/error hook format. These hooks receive the `context` as their only argument. They return either the `context` object or `undefined`. ```ts import type { HookContext } from '../../declarations' export const myHook = async (context: HookContext) => { return context } ``` You can learn more about before/after/error hooks, [here](../api/hooks.md#before-after-and-error) ### Around Hooks Now let's see an around hook. An around hook receives two arguments: the `context` object and a `next` function. ```ts import type { HookContext, NextFunction } from '../../declarations' export const myHook = async (context: HookContext, next: NextFunction) => { await next() } ``` You can learn more about around hooks, [here](../api/hooks.md#around) ### Registering Hooks The hooks object now has a new `around` property, which is specifically for `around` hooks. Since around hooks have different function signatures, they are not interchangeable with before/after/error hooks. ```ts export const serviceHooks = { // `around` hook are new in Feathers Dove around: { all: [], find: [], get: [], create: [], update: [], patch: [], remove: [] }, // before/after/error hooks also continue to work before: {}, after: {}, error: {} } ``` Learn more about registering hooks, [here](../api/hooks.md#registering-hooks). ### When to use Around Hooks Using `around` hooks or `regular` hooks is mostly a matter of preference. There's no imminent need to rewrite all of your regular hooks into around hooks. Both hooks work together, as explained [here](/api/hooks#hook-flow). Starting with Dove, the CLI templates and new tooling features are written in `around` hooks. The around hooks simplify code in core tools because we can keep the logic for the entire hook flow (before, after, error) all in one file. Here are a couple of examples of where `around` hooks work really well:
One great use case for `around` hooks is data caching. A caching hook typically has the following responsibilities: - Check the cache for existing results. (before the service method executes) - Push new results into the cache, once received. (after the service method executes) - Handle and report errors which occur in the hook. With regular hooks, a cache hook has to be split into three parts, one for each responsibility. Instead, a single `around` hook can handle everything on its own. Below is an example of an overly-simple cache hook using JavaScript's `Map` API. Everything before `await next()` runs before the database call. Everything afterwards runs after the database call. You could also drop in a try/catch to handle possible errors. ```ts import type { HookContext, NextFunction } from '../../declarations' export const simpleCache = new Map() export const myHook = async (context: HookContext, next: NextFunction) => { // Check the cache for an existing record const existing = simpleCache.get(context.id) // If an existing record was found, set it as context.result to skip the database call. if (existing) { context.result = existing } await next() // Cache the latest record by its id simpleCache.set(context.result.id, context.result) } ```### Setup and Teardown Hooks Feathers v5 Dove adds built-in support for app-level `setup` and `teardown` hooks. They are special hooks that don't run on the service level but instead directly on [app.setup](../api/application.md#setupserver) or `app.listen` and [app.teardown](../api/application.md#teardownserver). They allow you to perform some async logic while starting and stopping the Feathers server. ```ts app.hooks({ setup: [connectMongoDB], teardown: [closeMongoDB] }) ``` Learn more about `setup` and `teardown` hooks, [here](../api/hooks.md#setup-and-teardown) ## Rebuilt CLI The new CLI is completely different under the hood, and very familiar on the surface. There are a few differences in file structure compared to apps generated with previous versions of the CLI. ### State of the Art When creating the new generator, we looked at open-source generators already available. We were very impressed with [Hygen](https://hygen.io). It's absolutely impressive work, for sure, so we even wrote a custom generator to try it out. Then [@fratzinger](https://github.com/fratzinger) came up with the idea of a 100% TypeScript generator based on JavaScript template strings. We couldn't find an existing project, so we made one! The new Feathers CLI is built on top of [Pinion](https://github.com/feathershq/pinion), our own generator with TypeScript-based templates. Instead of using some custom templating language, like Handlebars or EJS, Pinion uses Typed [Template Literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals), providing some wonderful benefits: - No more mystery context. You always know the exact context of the templates and what helpers are available. - It just works™ with existing npm packages. There's no need to make an EJS plugin for some custom helper using an obscure API. Just import the module and use it in your template. - Integrates with all existing TypeScript tooling. Hover over a variable and inspect the context like you would any TS code. Now that we have what we consider the best generator on the planet, we have some exciting plans for the Feathers CLI, which we will announce in the future. You can read more about Pinion, [here](https://github.com/feathershq/pinion) ### Fully TypeScript We have dramatically reduced the surface area for bugs to be introduced into the app. We've committed 100% to TypeScript, while making sure that you can still generate a JavaScript app. When you select `JavaScript` to generate an app, the CLI works some magic under the hood by - Compiling the `.ts` templates to JavaScript, in memory - Formatting the JavaScript code with Prettier - Writing clean `.js` to the file system. For Feathers Maintainers, committing to TypeScript means we only contribute to a single set of templates. And they get magically compiled - on the fly - to plain JavaScript when you want it. ### Shared Types We covered this [in more detail, earlier](#typed-client), but it's worth briefly mentioning again. The new generator powers Shared Types for both the Feathers server and client. You can make your public-facing API easier to use and give developers a typed client SDK. Read more about shared types, [here](#typed-client). ### New App Structure The file and folder structure of generated apps has changed a little bit. Here's an overview of the changes: - Each service has its own schema and resolver file - The service hooks are now found together with the service registration. - The `src/models` folder no longer gets created, since [Feathers schemas](#official-schemas) replace models. - Each service has its own folder inside the `tests` folder. You can learn more about the generated files in the [CLI guide](./cli/index.md). ## The Future We are self-funded and community powered. In every way, Feathers has a solid foundation for a steady, stable future. How did we ever manage to build such a great framework without millions of dollars? Really, we have a wonderful, active community of contributors who share values of good API design, boilerplate elimination, and making development fun. This is rewarding for us! We started in 2013 from a core architecture that's unique among frameworks - in **any** language. We offer the same API across multiple transports, which allows us all to build real-time, restful applications. The result is a robust, flexible framework that continues to be unique while showing its maturity. Feathers has made its way into enterprises that serve a large portion of the connected planet. With all of the new features in Feathers v5 (Dove), we're excited to build! And we're even more excited to see what you build! We have a few more things to show off in the coming months. Stay tuned! Enjoy the release! And come chat with us on [Discord](https://discord.gg/qa8kez8QBx) when you feel like it. ================================================ FILE: docs/help/faq.md ================================================ --- outline: deep --- # FAQ We've been collecting some commonly asked questions here. We'll either be updating the guide directly, providing answers here, or both. ## Why should I use Feathers? There are many other Frameworks that let you build web applications so this is definitely a justified question. The key part for Feathers is that it takes a different approach to both, traditional MVC frameworks like Rails, Sails or NestJS and low level HTTP frameworks like Sinatra, Express or Fastify. Instead of creating routes, controllers and HTTP request and response handlers, Feathers uses services and workflows (hooks) that let you focus on your application logic independently from how it is being accessed. This makes applications easier to understand and test and it allows Feathers to automatically provide REST APIs, websocket real-time APIs (which can often be quite a bit faster) and universal usage on the client and the server. It also makes it possible to add new communication protocols (like HTTP2 or GraphQL) without having to change anything in your application code. Feathers does all that while staying lightweight with a small API surface and codebase and flexible by letting you use it with the backend and frontend technology that best suits your needs. For more information - [Read about the philosophy behind Feathers and where it came from](https://blog.feathersjs.com/why-we-built-the-best-web-framework-you-ve-probably-never-heard-of-until-now-176afc5c6aac) - [Learn about the high level design patterns behind Feathers](https://blog.feathersjs.com/design-patterns-for-modern-web-apis-1f046635215) - [See how Feathers compares to others](https://feathersjs.com/comparison) ## Is Feathers production ready? Yes! Feathers had its first stable release in 2014 and is being used in production by a bunch of companies from startups to fortune 500s. For some more details see [this answer on Quora](https://www.quora.com/Is-FeathersJS-production-ready). ## What Node versions does Feathers support Feathers supports all NodeJS versions from the current Active LTS upwards. See [the Node.JS working group release schedule](https://github.com/nodejs/Release#release-schedule) for more information. ## How do I create custom methods? Feathers is built around the [REST architectural constraints](https://en.wikipedia.org/wiki/Representational_state_transfer#Architectural_constraints) reflected by its standard service methods. There are many benefits using those methods like security, predictability and sending pre-defined real-time events so you should try to structure your application with the [standard service methods](../api/services.md#service-methods). For example: > Send email action that does not store mail message in database. Resources (services) don't have to be database records. It can be any kind of resource (like the current weather for a city or creating an email which will send it). Sending emails is usually done with either a separate email [service](../api/services.md): ```ts class EmailService { async create(data: EmailData) { return sendEmail(data) } } app.use('/email', new EmailService()) ``` > Place an order in e-commerce web site. Behind the scenes, there are many records will be inserted in one transaction: order_item, order_header, voucher_tracking etc. This is what [Feathers hooks](../api/hooks.md) are used for. When creating a new order you also have a well defined hook chain: ```ts app.service('orders').hooks({ before: { create: [validateData(), checkStock(), checkVoucher()] }, after: { create: [ chargePayment(), // hook that calls `app.service('payment').create()` sendEmail(), // hook that calls `app.service('email').create()` updateStock() // Update product stock here ] } }) ``` However, there are some use cases where you might still want to allow additional methods for a client to call (like re-sending a verification email or resetting a password on the user service) and this is what [custom service method](../api/services.md#custom-methods) can be used for. ## How do I do nested or custom routes? Normally we find that they actually aren't needed and that it is much better to keep your routes as flat as possible. For example something like `users/:userId/posts` is - although nice to read for humans - actually not as easy to parse and process as the equivalent `/posts?userId=
URLs should never contain actions that change data (like `post/publish` or `post/delete`). This has always been an important part of the HTTP protocol and Feathers enforces this more strictly than most other frameworks. For example to publish a post you would call `.patch(id, { published: true })`.## Why are you using JWT for sessions Feathers is using [JSON web tokens (JWT)](https://jwt.io/) for its standard authentication mechanism. Some articles like [Stop using JWT for sessions](http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/) promotes using standard cookies and HTTP sessions. While it brings up some valid points, not all of them apply to Feathers and there are other good reasons why Feathers relies on JWT: - Feathers is designed to support many different transport mechanisms, most of which do not rely on HTTP but do work well with JWT as the authentication mechanism. This is even already the case for websockets where an established connection normally is not secured by a traditional HTTP session. - By default the only thing that Feathers stored in the JWT payload is the user id. It is a stateful token. You can change this and make the token stateless by putting more data into the JWT payload but this is at your discretion. Currently the user is looked up on every request after the JWT is verified to not be expired or tampered with. - You need to make sure that you revoke JWT tokens or set a low expiration date or add custom logic to verify that a user’s account is still valid/active. Currently the default expiration is 1 day. We chose a reasonable default for most apps but depending on your application this might be too long or too short. Additionally, it is still possible to use Feathers with existing _traditional_ Express session mechanism by using [custom Express middleware](../api/express.md). For example, `params.user` for all service calls from a traditional Express session can be passed like this: ```ts app.use(function (req, res, next) { // Set service call `param.user` from `session.user` req.feathers.user = req.session.user }) ``` ## Can you support another database? Feathers [database adapters](../api/databases/adapters.md) implement 90% of the functionality you may need to use Feathers with certain databases. You can also find a wide variety of community maintained database adapters [in the ecosystem](/ecosystem/?cat=Database&sort=lastPublish). However, even if your favourite database or ORM is not on the list or the adapter does not support specific functionality you are looking for, Feathers can still accommodate all your needs by [writing your own services](../api/services.md).
To use Feathers properly it is very important to understand how services work and that all existing database adapters are just services that talk to the database themselves.The why and how to write your own services is covered [in the Feathers guide](../guides/). A custom service can be generated by running `npx feathers generate service`, choosing "A custom service" and then editing the `
Make sure to validate and sanitize any client side values to prevent security issues.## My queries with null values aren't working
Query values will be converted to the correct type automatically when using a [query schema](../api/schema/index.md). This issue also does not happen when using websockets since it retains all type information.When making a request using REST (HTTP) query _string_ values don't have any type information and will always be strings. Some database adapters that have a schema (like `feathers-mongoose` or `feathers-sequelize`) will try to convert values to the correct type but others (like `feathers-mongodb`) can't. Additionally, `null` will always be a string and always has to be converted if you want to query for `null`. This can be done in a `before` [hook](../api/hooks.md): ```ts app.service('myservice').hooks({ before: { find: [ async (context: HookContext) => { const { params: { query = {} } } = context if (query.phone === 'null') { query.phone = null } context.params.query = query return context } ] } }) ``` Also see [this issue](https://github.com/feathersjs/feathers/issues/894). ## Why are queries with arrays failing? If you are using REST and queries with larger arrays (more than 21 items to be exact) are failing, you are probably running into an issue with the [querystring](https://github.com/ljharb/qs) module which [limits the size of arrays to 21 items](https://github.com/ljharb/qs#parsing-arrays) by default. The recommended solution is to implement a custom query string parser function via `app.set('query parser', parserFunction)` with the `arrayLimit` option set to a higher value: ```js var qs = require('qs') app.set('query parser', function (str) { return qs.parse(str, { arrayLimit: 100 }) }) ``` For more information see the [Express application settings](http://expressjs.com/en/4x/api.html#app.set) [@feathersjs/rest#88](https://github.com/feathersjs/feathers-rest/issues/88) and [feathers-mongoose#205](https://github.com/feathersjs-ecosystem/feathers-mongoose/issues/205). ## I always get a 404 for my custom middleware Just like in Express itself, the order of middleware matters. If you registered a custom middleware outside of the generator, you have to make sure that it runs before the `notFound()` error midlleware. ## My configuration isn't loaded If you are running or requiring the Feathers app from a different folder [Feathers configuration](../api/configuration.md) needs to be instructed where the configuration files for the app are located. Since it uses [node-config](https://github.com/lorenwest/node-config) this can be done by setting the [NODE_CONFIG_DIR envorinment variable](https://github.com/lorenwest/node-config/wiki/Environment-Variables#node_config_dir). ## How do I set up HTTPS? In most production environments your Feathers application should be behind an NginX proxy that handles HTTPS. It is also possible to add SSL directly to your Feathers application which is described in the [Express HTTPS docs](../api/express.md#https). ================================================ FILE: docs/help/index.md ================================================ --- outline: deep --- # Getting Help There are many ways that you can get help if you are stuck or have a question about Feathers. ## Resources Existing resources may already have an answer to your question, so it always makes sense checking them first: - [API Docs >](../api/) - [FAQ >](./faq.md) - [GitHub issues >](https://github.com/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+user%3Afeathersjs+) - [GitHub discussions](https://github.com/feathersjs/feathers/discussions) - [Cookbook](/cookbook/) - [Blog >](https://blog.feathersjs.com/) - [Stack Overflow >](http://stackoverflow.com/questions/tagged/feathersjs) ## Help channels If none of those work it's a very real possibility that we screwed something up or it's just not clear. We're sorry :disappointed_relieved: We want to hear about it and are very friendly so feel free to come talk to us: [Join our Discord server >](https://discord.gg/qa8kez8QBx) [Submit an issue to GitHub >](https://github.com/feathersjs/feathers/issues/new) [Ask on StackOverflow using the `feathersjs` tag >](http://stackoverflow.com) ## Consulting and app development [Feathers Cloud](https://feathers.cloud/) specialize in app development and consulting to get your app on the right track. [Contact us](https://feathers.cloud/consulting.html) to see how we can help. ## Support Feathers, get help By [becoming a sponsor](https://github.com/sponsors/daffl/) you support Feathers continued development and get access to a Feathers newsletter and tiers with 30 or 60 minute monthly office hour sessions to walk you through any issues you may be facing using FeathersJS. This may include architecture discussions, debug sessions, patterns, or more. ================================================ FILE: docs/index.md ================================================ --- layout: page sidebar: false title: Feathers titleTemplate: The API and Real-time Application Framework ---
{ adapter?: A paginate?: PaginationParams } /** * Hook-less (internal) service methods. Directly call database adapter service methods * without running any service-level hooks or sanitization. This can be useful if you need the raw data * from the service and don't want to trigger any of its hooks. * * Important: These methods are only available internally on the server, not on the client * side and only for the Feathers database adapters. * * These methods do not trigger events. * * @see {@link https://docs.feathersjs.com/guides/migrating.html#hook-less-service-methods} */ export interface InternalServiceMethods< Result = any, Data = Result, PatchData = Partial, Params extends AdapterParams = AdapterParams, IdType = Id > { /** * Retrieve all resources from this service. * Does not sanitize the query and should only be used on the server. * * @param _params - Service call parameters {@link Params} */ _find(_params?: Params & { paginate?: PaginationOptions }): Promise> _find(_params?: Params & { paginate: false }): Promise _find(params?: Params): Promise > /** * Retrieve a single resource matching the given ID, skipping any service-level hooks. * Does not sanitize the query and should only be used on the server. * * @param id - ID of the resource to locate * @param params - Service call parameters {@link Params} * @see {@link HookLessServiceMethods} * @see {@link https://docs.feathersjs.com/api/services.html#get-id-params|Feathers API Documentation: .get(id, params)} */ _get(id: IdType, params?: Params): Promise /** * Create a new resource for this service, skipping any service-level hooks. * Does not sanitize data or checks if multiple updates are allowed and should only be used on the server. * * @param data - Data to insert into this service. * @param params - Service call parameters {@link Params} * @see {@link HookLessServiceMethods} * @see {@link https://docs.feathersjs.com/api/services.html#create-data-params|Feathers API Documentation: .create(data, params)} */ _create(data: Data, params?: Params): Promise _create(data: Data[], params?: Params): Promise _create(data: Data | Data[], params?: Params): Promise /** * Completely replace the resource identified by id, skipping any service-level hooks. * Does not sanitize data or query and should only be used on the server. * * @param id - ID of the resource to be updated * @param data - Data to be put in place of the current resource. * @param params - Service call parameters {@link Params} * @see {@link HookLessServiceMethods} * @see {@link https://docs.feathersjs.com/api/services.html#update-id-data-params|Feathers API Documentation: .update(id, data, params)} */ _update(id: IdType, data: Data, params?: Params): Promise /** * Merge any resources matching the given ID with the given data, skipping any service-level hooks. * Does not sanitize the data or query and should only be used on the server. * * @param id - ID of the resource to be patched * @param data - Data to merge with the current resource. * @param params - Service call parameters {@link Params} * @see {@link HookLessServiceMethods} * @see {@link https://docs.feathersjs.com/api/services.html#patch-id-data-params|Feathers API Documentation: .patch(id, data, params)} */ _patch(id: null, data: PatchData, params?: Params): Promise _patch(id: IdType, data: PatchData, params?: Params): Promise _patch(id: IdType | null, data: PatchData, params?: Params): Promise /** * Remove resources matching the given ID from the this service, skipping any service-level hooks. * Does not sanitize query and should only be used on the server. * * @param id - ID of the resource to be removed * @param params - Service call parameters {@link Params} * @see {@link HookLessServiceMethods} * @see {@link https://docs.feathersjs.com/api/services.html#remove-id-params|Feathers API Documentation: .remove(id, params)} */ _remove(id: null, params?: Params): Promise _remove(id: IdType, params?: Params): Promise _remove(id: IdType | null, params?: Params): Promise } ================================================ FILE: packages/adapter-commons/src/index.ts ================================================ import { _ } from '@feathersjs/commons' import { Params } from '@feathersjs/feathers' export * from './declarations' export * from './service' export * from './query' export * from './sort' // Return a function that filters a result object or array // and picks only the fields passed as `params.query.$select` // and additional `otherFields` export function select(params: Params, ...otherFields: string[]) { const queryFields: string[] | undefined = params?.query?.$select if (!queryFields) { return (result: any) => result } const resultFields = queryFields.concat(otherFields) const convert = (result: any) => _.pick(result, ...resultFields) return (result: any) => { if (Array.isArray(result)) { return result.map(convert) } return convert(result) } } ================================================ FILE: packages/adapter-commons/src/query.ts ================================================ import { _ } from '@feathersjs/commons' import { BadRequest } from '@feathersjs/errors' import { Query } from '@feathersjs/feathers' import { FilterQueryOptions, FilterSettings, PaginationParams } from './declarations' const parse = (value: any) => (typeof value !== 'undefined' ? parseInt(value, 10) : value) const isPlainObject = (value: any) => _.isObject(value) && value.constructor === {}.constructor const validateQueryProperty = (query: any, operators: string[] = []): Query => { if (!isPlainObject(query)) { return query } for (const key of Object.keys(query)) { if (key.startsWith('$') && !operators.includes(key)) { throw new BadRequest(`Invalid query parameter ${key}`, query) } const value = query[key] if (isPlainObject(value)) { query[key] = validateQueryProperty(value, operators) } } return { ...query } } const getFilters = (query: Query, settings: FilterQueryOptions) => { const filterNames = Object.keys(settings.filters) return filterNames.reduce( (current, key) => { const queryValue = query[key] const filter = settings.filters[key] if (filter) { const value = typeof filter === 'function' ? filter(queryValue, settings) : queryValue if (value !== undefined) { current[key] = value } } return current }, {} as { [key: string]: any } ) } const getQuery = (query: Query, settings: FilterQueryOptions) => { const keys = Object.keys(query).concat(Object.getOwnPropertySymbols(query) as any as string[]) return keys.reduce((result, key) => { if (typeof key === 'string' && key.startsWith('$')) { if (settings.filters[key] === undefined) { throw new BadRequest(`Invalid filter value ${key}`) } } else { result[key] = validateQueryProperty(query[key], settings.operators) } return result }, {} as Query) } /** * Returns the converted `$limit` value based on the `paginate` configuration. * @param _limit The limit value * @param paginate The pagination options * @returns The converted $limit value */ export const getLimit = (_limit: any, paginate?: PaginationParams) => { const limit = parse(_limit) if (paginate && (paginate.default || paginate.max)) { const base = paginate.default || 0 const lower = typeof limit === 'number' && !isNaN(limit) && limit >= 0 ? limit : base const upper = typeof paginate.max === 'number' ? paginate.max : Number.MAX_VALUE return Math.min(lower, upper) } return limit } export const OPERATORS = ['$in', '$nin', '$lt', '$lte', '$gt', '$gte', '$ne', '$or'] export const FILTERS: FilterSettings = { $skip: (value: any) => parse(value), $sort: (sort: any): { [key: string]: number } => { if (typeof sort !== 'object' || Array.isArray(sort)) { return sort } return Object.keys(sort).reduce( (result, key) => { result[key] = typeof sort[key] === 'object' ? sort[key] : parse(sort[key]) return result }, {} as { [key: string]: number } ) }, $limit: (_limit: any, { paginate }: FilterQueryOptions) => getLimit(_limit, paginate), $select: (select: any) => { if (Array.isArray(select)) { return select.map((current) => `${current}`) } return select }, $or: (or: any, { operators }: FilterQueryOptions) => { if (Array.isArray(or)) { return or.map((current) => validateQueryProperty(current, operators)) } return or }, $and: (and: any, { operators }: FilterQueryOptions) => { if (Array.isArray(and)) { return and.map((current) => validateQueryProperty(current, operators)) } return and } } /** * Converts Feathers special query parameters and pagination settings * and returns them separately as `filters` and the rest of the query * as `query`. `options` also gets passed the pagination settings and * a list of additional `operators` to allow when querying properties. * * @param query The initial query * @param options Options for filtering the query * @returns An object with `query` which contains the query without `filters` * and `filters` which contains the converted values for each filter. */ export function filterQuery(_query: Query, options: FilterQueryOptions = {}) { const query = _query || {} const settings = { ...options, filters: { ...FILTERS, ...options.filters }, operators: OPERATORS.concat(options.operators || []) } return { filters: getFilters(query, settings), query: getQuery(query, settings) } } ================================================ FILE: packages/adapter-commons/src/service.ts ================================================ import { Id, Paginated, Query } from '@feathersjs/feathers' import { AdapterParams, AdapterServiceOptions, InternalServiceMethods, PaginationOptions } from './declarations' import { filterQuery } from './query' export const VALIDATED = Symbol.for('@feathersjs/adapter/sanitized') const alwaysMulti: { [key: string]: boolean } = { find: true, get: false, update: false } /** * An abstract base class that a database adapter can extend from to implement the * `__find`, `__get`, `__update`, `__patch` and `__remove` methods. */ export abstract class AdapterBase< Result = any, Data = Result, PatchData = Partial, ServiceParams extends AdapterParams = AdapterParams, Options extends AdapterServiceOptions = AdapterServiceOptions, IdType = Id > implements InternalServiceMethods { options: Options constructor(options: Options) { this.options = { id: 'id', events: [], paginate: false, multi: false, filters: {}, operators: [], ...options } } get id() { return this.options.id } get events() { return this.options.events } /** * Check if this adapter allows multiple updates for a method. * @param method The method name to check. * @param params The service call params. * @returns Wether or not multiple updates are allowed. */ allowsMulti(method: string, params: ServiceParams = {} as ServiceParams) { const always = alwaysMulti[method] if (typeof always !== 'undefined') { return always } const { multi } = this.getOptions(params) if (multi === true || !multi) { return multi } return multi.includes(method) } /** * Returns the combined options for a service call. Options will be merged * with `this.options` and `params.adapter` for dynamic overrides. * * @param params The parameters for the service method call * @returns The actual options for this call */ getOptions(params: ServiceParams): Options { const paginate = params.paginate !== undefined ? params.paginate : this.options.paginate return { ...this.options, paginate, ...params.adapter } } /** * Returns a sanitized version of `params.query`, converting filter values * (like $limit and $skip) into the expected type. Will throw an error if * a `$` prefixed filter or operator value that is not allowed in `filters` * or `operators` is encountered. * * @param params The service call parameter. * @returns A new object containing the sanitized query. */ async sanitizeQuery(params: ServiceParams = {} as ServiceParams): Promise { // We don't need legacy query sanitisation if the query has been validated by a schema already if (params.query && (params.query as any)[VALIDATED]) { return params.query || {} } const options = this.getOptions(params) const { query, filters } = filterQuery(params.query, options) return { ...filters, ...query } } /** * Retrieve all resources from this service. * Does not sanitize the query and should only be used on the server. * * @param _params - Service call parameters {@link ServiceParams} */ abstract _find(_params?: ServiceParams & { paginate?: PaginationOptions }): Promise > abstract _find(_params?: ServiceParams & { paginate: false }): Promise abstract _find(params?: ServiceParams): Promise > /** * Retrieve a single resource matching the given ID, skipping any service-level hooks. * Does not sanitize the query and should only be used on the server. * * @param id - ID of the resource to locate * @param params - Service call parameters {@link ServiceParams} * @see {@link HookLessServiceMethods} * @see {@link https://docs.feathersjs.com/api/services.html#get-id-params|Feathers API Documentation: .get(id, params)} */ abstract _get(id: IdType, params?: ServiceParams): Promise /** * Create a new resource for this service, skipping any service-level hooks. * Does not check if multiple updates are allowed and should only be used on the server. * * @param data - Data to insert into this service. * @param params - Service call parameters {@link ServiceParams} * @see {@link HookLessServiceMethods} * @see {@link https://docs.feathersjs.com/api/services.html#create-data-params|Feathers API Documentation: .create(data, params)} */ abstract _create(data: Data, params?: ServiceParams): Promise abstract _create(data: Data[], params?: ServiceParams): Promise abstract _create(data: Data | Data[], params?: ServiceParams): Promise /** * Completely replace the resource identified by id, skipping any service-level hooks. * Does not sanitize the query and should only be used on the server. * * @param id - ID of the resource to be updated * @param data - Data to be put in place of the current resource. * @param params - Service call parameters {@link ServiceParams} * @see {@link HookLessServiceMethods} * @see {@link https://docs.feathersjs.com/api/services.html#update-id-data-params|Feathers API Documentation: .update(id, data, params)} */ abstract _update(id: IdType, data: Data, params?: ServiceParams): Promise /** * Merge any resources matching the given ID with the given data, skipping any service-level hooks. * Does not sanitize the query and should only be used on the server. * * @param id - ID of the resource to be patched * @param data - Data to merge with the current resource. * @param params - Service call parameters {@link ServiceParams} * @see {@link HookLessServiceMethods} * @see {@link https://docs.feathersjs.com/api/services.html#patch-id-data-params|Feathers API Documentation: .patch(id, data, params)} */ abstract _patch(id: null, data: PatchData, params?: ServiceParams): Promise abstract _patch(id: IdType, data: PatchData, params?: ServiceParams): Promise abstract _patch(id: IdType | null, data: PatchData, params?: ServiceParams): Promise /** * Remove resources matching the given ID from the this service, skipping any service-level hooks. * Does not sanitize query and should only be used on the server. * * @param id - ID of the resource to be removed * @param params - Service call parameters {@link ServiceParams} * @see {@link HookLessServiceMethods} * @see {@link https://docs.feathersjs.com/api/services.html#remove-id-params|Feathers API Documentation: .remove(id, params)} */ abstract _remove(id: null, params?: ServiceParams): Promise abstract _remove(id: IdType, params?: ServiceParams): Promise abstract _remove(id: IdType | null, params?: ServiceParams): Promise } ================================================ FILE: packages/adapter-commons/src/sort.ts ================================================ // Sorting algorithm taken from NeDB (https://github.com/louischatriot/nedb) // See https://github.com/louischatriot/nedb/blob/e3f0078499aa1005a59d0c2372e425ab789145c1/lib/model.js#L189 export function compareNSB(a: number | string | boolean, b: number | string | boolean): 0 | 1 | -1 { if (a === b) { return 0 } return a < b ? -1 : 1 } export function compareArrays(a: any[], b: any[]): 0 | 1 | -1 { for (let i = 0, l = Math.min(a.length, b.length); i < l; i++) { const comparison = compare(a[i], b[i]) if (comparison !== 0) { return comparison } } // Common section was identical, longest one wins return compareNSB(a.length, b.length) } export function compare( a: any, b: any, compareStrings: (a: any, b: any) => 0 | 1 | -1 = compareNSB ): 0 | 1 | -1 { if (a === b) { return 0 } // null or undefined if (a == null) { return -1 } if (b == null) { return 1 } // detect typeof once const typeofA = typeof a const typeofB = typeof b // Numbers if (typeofA === 'number') { return typeofB === 'number' ? compareNSB(a, b) : -1 } if (typeofB === 'number') { return 1 } // Strings if (typeofA === 'string') { return typeofB === 'string' ? compareStrings(a, b) : -1 } if (typeofB === 'string') { return 1 } // Booleans if (typeofA === 'boolean') { return typeofB === 'boolean' ? compareNSB(a, b) : -1 } if (typeofB === 'boolean') { return 1 } // Dates if (a instanceof Date) { return b instanceof Date ? compareNSB(a.getTime(), b.getTime()) : -1 } if (b instanceof Date) { return 1 } // Arrays (first element is most significant and so on) if (Array.isArray(a)) { return Array.isArray(b) ? compareArrays(a, b) : -1 } if (Array.isArray(b)) { return 1 } // Objects const aKeys = Object.keys(a).sort() const bKeys = Object.keys(b).sort() for (let i = 0, l = Math.min(aKeys.length, bKeys.length); i < l; i++) { const comparison = compare(a[aKeys[i]], b[bKeys[i]]) if (comparison !== 0) { return comparison } } return compareNSB(aKeys.length, bKeys.length) } // lodash-y get - probably want to use lodash get instead const get = (value: any, path: string[]) => path.reduce((value, key) => value[key], value) // An in-memory sorting function according to the // $sort special query parameter export function sorter($sort: { [key: string]: -1 | 1 }) { const compares = Object.keys($sort).map((key) => { const direction = $sort[key] if (!key.includes('.')) { return (a: any, b: any) => direction * compare(a[key], b[key]) } else { const path = key.split('.') return (a: any, b: any) => direction * compare(get(a, path), get(b, path)) } }) return function (a: any, b: any) { for (const compare of compares) { const comparison = compare(a, b) if (comparison !== 0) { return comparison } } return 0 } } ================================================ FILE: packages/adapter-commons/test/commons.test.ts ================================================ import assert from 'assert' import { select } from '../src' describe('@feathersjs/adapter-commons', () => { describe('select', () => { it('select', () => { const selector = select({ query: { $select: ['name', 'age'] } }) return Promise.resolve({ name: 'David', age: 3, test: 'me' }) .then(selector) .then((result) => assert.deepStrictEqual(result, { name: 'David', age: 3 }) ) }) it('select with arrays', () => { const selector = select({ query: { $select: ['name', 'age'] } }) return Promise.resolve([ { name: 'David', age: 3, test: 'me' }, { name: 'D', age: 4, test: 'you' } ]) .then(selector) .then((result) => assert.deepStrictEqual(result, [ { name: 'David', age: 3 }, { name: 'D', age: 4 } ]) ) }) it('select with no query', () => { const selector = select({}) const data = { name: 'David' } return Promise.resolve(data) .then(selector) .then((result) => assert.deepStrictEqual(result, data)) }) it('select with other fields', () => { const selector = select( { query: { $select: ['name'] } }, 'id' ) const data = { id: 'me', name: 'David', age: 10 } return Promise.resolve(data) .then(selector) .then((result) => assert.deepStrictEqual(result, { id: 'me', name: 'David' }) ) }) }) }) ================================================ FILE: packages/adapter-commons/test/fixture.ts ================================================ import { AdapterBase, AdapterParams, PaginationOptions } from '../src' import { Id, NullableId, Paginated } from '@feathersjs/feathers' import { BadRequest, MethodNotAllowed } from '@feathersjs/errors/lib' export type Data = { id: Id } export class MethodBase extends AdapterBase, AdapterParams> { async _find(_params?: AdapterParams & { paginate?: PaginationOptions }): Promise > async _find(_params?: AdapterParams & { paginate: false }): Promise async _find(params?: AdapterParams): Promise> { if (params && params.paginate === false) { return [] } return { total: 0, limit: 10, skip: 0, data: [] } } async _get(id: Id, _params?: AdapterParams): Promise { return { id } } async _create(data: Data, _params?: AdapterParams): Promise async _create(data: Data[], _params?: AdapterParams): Promise async _create(data: Data | Data[], _params?: AdapterParams): Promise async _create(data: Data | Data[], _params?: AdapterParams): Promise { if (Array.isArray(data)) { return [ { id: 'something' } ] } return { id: 'something' } } async _update(id: Id, _data: Data, _params?: AdapterParams) { return Promise.resolve({ id: id ?? _data.id }) } async _patch(id: null, _data: Partial, _params?: AdapterParams): Promise async _patch(id: Id, _data: Partial, _params?: AdapterParams): Promise async _patch(id: NullableId, _data: Partial, _params?: AdapterParams): Promise async _patch(id: NullableId, _data: Partial, _params?: AdapterParams): Promise { if (id === null) { return [] } return { id } } async _remove(id: null, _params?: AdapterParams): Promise async _remove(id: Id, _params?: AdapterParams): Promise async _remove(id: NullableId, _params?: AdapterParams): Promise async _remove(id: NullableId, _params?: AdapterParams) { if (id === null) { return [] as Data[] } return { id } } } export class MethodService extends MethodBase { find(params?: AdapterParams): Promise> { return this._find(params) } get(id: Id, params?: AdapterParams): Promise { return this._get(id, params) } async create(data: Data[], _params?: AdapterParams): Promise async create(data: Data, _params?: AdapterParams): Promise async create(data: Data | Data[], params?: AdapterParams): Promise { if (Array.isArray(data) && !this.allowsMulti('create', params)) { throw new MethodNotAllowed('Can not create multiple entries') } return this._create(data, params) } async update(id: Id, data: Data, params?: AdapterParams) { if (id === null || Array.isArray(data)) { throw new BadRequest("You can not replace multiple instances. Did you mean 'patch'?") } return this._update(id, data, params) } async patch(id: NullableId, data: Partial, params?: AdapterParams) { if (id === null && !this.allowsMulti('patch', params)) { throw new MethodNotAllowed('Can not patch multiple entries') } return this._patch(id, data, params) } async remove(id: NullableId, params?: AdapterParams) { if (id === null && !this.allowsMulti('remove', params)) { throw new MethodNotAllowed('Can not remove multiple entries') } return this._remove(id, params) } } ================================================ FILE: packages/adapter-commons/test/query.test.ts ================================================ import assert from 'assert' import { ObjectId } from 'mongodb' import { filterQuery } from '../src' describe('@feathersjs/adapter-commons/filterQuery', () => { describe('$sort', () => { it('returns $sort when present in query', () => { const originalQuery = { $sort: { name: 1 } } const { filters, query } = filterQuery(originalQuery) assert.strictEqual(filters.$sort.name, 1) assert.deepStrictEqual(query, {}) assert.deepStrictEqual( originalQuery, { $sort: { name: 1 } }, 'does not modify original query' ) }) it('returns $sort when present in query as an object', () => { const { filters, query } = filterQuery({ $sort: { name: { something: 10 } } }) assert.strictEqual(filters.$sort.name.something, 10) assert.deepStrictEqual(query, {}) }) it('converts strings in $sort', () => { const { filters, query } = filterQuery({ $sort: { test: '-1' } }) assert.strictEqual(filters.$sort.test, -1) assert.deepStrictEqual(query, {}) }) it('does not convert $sort arrays', () => { const $sort = [ ['test', '-1'], ['a', '1'] ] const { filters, query } = filterQuery({ $sort }) assert.strictEqual(filters.$sort, $sort) assert.deepStrictEqual(query, {}) }) it('throws an error when special parameter is not known', () => { try { const query = { $foo: 1 } filterQuery(query) assert.ok(false, 'Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'BadRequest') assert.strictEqual(error.message, 'Invalid filter value $foo') } }) it('returns undefined when not present in query', () => { const query = { foo: 1 } const { filters } = filterQuery(query) assert.strictEqual(filters.$sort, undefined) }) }) describe('$limit', () => { let testQuery: any beforeEach(() => { testQuery = { $limit: 1 } }) it('returns $limit when present in query', () => { const { filters, query } = filterQuery(testQuery) assert.strictEqual(filters.$limit, 1) assert.deepStrictEqual(query, {}) }) it('returns undefined when not present in query', () => { const query = { foo: 1 } const { filters } = filterQuery(query) assert.strictEqual(filters.$limit, undefined) }) it('removes $limit from query when present', () => { assert.deepStrictEqual(filterQuery(testQuery).query, {}) }) it('parses $limit strings into integers (#4)', () => { const { filters } = filterQuery({ $limit: '2' }) assert.strictEqual(filters.$limit, 2) }) it('allows $limit 0', () => { const { filters } = filterQuery({ $limit: 0 }, { paginate: { default: 10 } }) assert.strictEqual(filters.$limit, 0) }) describe('pagination', () => { it('limits with default pagination', () => { const { filters } = filterQuery({}, { paginate: { default: 10 } }) const { filters: filtersNeg } = filterQuery({ $limit: -20 }, { paginate: { default: 5, max: 10 } }) assert.strictEqual(filters.$limit, 10) assert.strictEqual(filtersNeg.$limit, 5) }) it('limits with max pagination', () => { const { filters } = filterQuery({ $limit: 20 }, { paginate: { default: 5, max: 10 } }) assert.strictEqual(filters.$limit, 10) }) it('limits with default pagination when not a number', () => { const { filters } = filterQuery({ $limit: 'something' }, { paginate: { default: 5, max: 10 } }) assert.strictEqual(filters.$limit, 5) }) it('limits to 0 when no paginate.default and not a number', () => { const { filters } = filterQuery({ $limit: 'something' }, { paginate: { max: 10 } }) assert.strictEqual(filters.$limit, 0) }) it('still uses paginate.max when there is no paginate.default (#2104)', () => { const { filters } = filterQuery({ $limit: 100 }, { paginate: { max: 10 } }) assert.strictEqual(filters.$limit, 10) }) }) }) describe('$skip', () => { let testQuery: any beforeEach(() => { testQuery = { $skip: 1 } }) it('returns $skip when present in query', () => { const { filters } = filterQuery(testQuery) assert.strictEqual(filters.$skip, 1) }) it('removes $skip from query when present', () => { assert.deepStrictEqual(filterQuery(testQuery).query, {}) }) it('returns undefined when not present in query', () => { const query = { foo: 1 } const { filters } = filterQuery(query) assert.strictEqual(filters.$skip, undefined) }) it('parses $skip strings into integers (#4)', () => { const { filters } = filterQuery({ $skip: '33' }) assert.strictEqual(filters.$skip, 33) }) }) describe('$select', () => { let testQuery: any beforeEach(() => { testQuery = { $select: 1 } }) it('returns $select when present in query', () => { const { filters } = filterQuery(testQuery) assert.strictEqual(filters.$select, 1) }) it('removes $select from query when present', () => { assert.deepStrictEqual(filterQuery(testQuery).query, {}) }) it('returns undefined when not present in query', () => { const query = { foo: 1 } const { filters } = filterQuery(query) assert.strictEqual(filters.$select, undefined) }) it('includes Symbols', () => { const TEST = Symbol('testing') const original = { [TEST]: 'message', other: true, sub: { [TEST]: 'othermessage' } } const { query } = filterQuery(original) assert.deepStrictEqual(query, { [TEST]: 'message', other: true, sub: { [TEST]: 'othermessage' } }) }) it('only converts plain objects', () => { const userId = new ObjectId() const original = { userId } const { query } = filterQuery(original) assert.deepStrictEqual(query, original) }) }) describe('arrays', () => { it('validates queries in arrays', () => { assert.throws( () => { filterQuery({ $or: [{ $exists: false }] }) }, { name: 'BadRequest', message: 'Invalid query parameter $exists' } ) }) it('allows default operators in $or', () => { const { filters } = filterQuery({ $or: [{ value: { $gte: 10 } }] }) assert.deepStrictEqual(filters, { $or: [{ value: { $gte: 10 } }] }) }) }) describe('additional filters', () => { it('throw error when not set as additionals', () => { try { filterQuery({ $select: 1, $known: 1 }) assert.ok(false, 'Should never get here') } catch (error: any) { assert.strictEqual(error.message, 'Invalid filter value $known') } }) it('returns default and known additional filters (array)', () => { const query = { $select: ['a', 'b'], $known: 1, $unknown: 1 } const { filters } = filterQuery(query, { filters: { $known: true, $unknown: true } }) assert.strictEqual(filters.$unknown, 1) assert.strictEqual(filters.$known, 1) assert.deepStrictEqual(filters.$select, ['a', 'b']) }) it('returns default and known additional filters (object)', () => { const { filters } = filterQuery( { $known: 1, $select: 1 }, { filters: { $known: (value: any) => value.toString() } } ) assert.strictEqual(filters.$unknown, undefined) assert.strictEqual(filters.$known, '1') assert.strictEqual(filters.$select, 1) }) }) describe('additional operators', () => { it('returns query with default and known additional operators', () => { const { query } = filterQuery( { prop: { $ne: 1, $known: 1 } }, { operators: ['$known'] } ) assert.deepStrictEqual(query, { prop: { $ne: 1, $known: 1 } }) }) it('throws an error with unknown query operator', () => { assert.throws( () => filterQuery({ prop: { $unknown: 'something' } }), { message: 'Invalid query parameter $unknown' } ) }) }) }) ================================================ FILE: packages/adapter-commons/test/service.test.ts ================================================ /* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/ban-ts-comment */ import assert from 'assert' import { VALIDATED } from '../src' import { MethodService } from './fixture' const METHODS: ['find', 'get', 'create', 'update', 'patch', 'remove'] = [ 'find', 'get', 'create', 'update', 'patch', 'remove' ] describe('@feathersjs/adapter-commons/service', () => { describe('works when methods exist', () => { METHODS.forEach((method) => { it(`${method}`, () => { const service = new MethodService({}) const args: any[] = [] if (method !== 'find') { args.push('test') } if (method === 'update' || method === 'patch') { args.push({}) } // @ts-ignore return service[method](...args) }) }) it('does not allow multi patch', async () => { const service = new MethodService({}) await assert.rejects(() => service.patch(null, {}), { name: 'MethodNotAllowed', message: 'Can not patch multiple entries' }) }) it('does not allow multi remove', async () => { const service = new MethodService({}) await assert.rejects(() => service.remove(null, {}), { name: 'MethodNotAllowed', message: 'Can not remove multiple entries' }) }) it('does not allow multi create', async () => { const service = new MethodService({}) await assert.rejects(() => service.create([], {}), { name: 'MethodNotAllowed', message: 'Can not create multiple entries' }) }) it('multi can be set to true', async () => { const service = new MethodService({}) service.options.multi = true await service.create([]) }) }) it('sanitizeQuery', async () => { const service = new MethodService({ filters: { $something: true }, operators: ['$test'] }) assert.deepStrictEqual( await service.sanitizeQuery({ query: { $limit: '10', test: 'me' } as any }), { $limit: 10, test: 'me' } ) assert.deepStrictEqual( await service.sanitizeQuery({ adapter: { paginate: { max: 2 } }, query: { $limit: '10', test: 'me' } as any }), { $limit: 2, test: 'me' } ) await assert.rejects( () => service.sanitizeQuery({ query: { name: { $bla: 'me' } } }), { message: 'Invalid query parameter $bla' } ) assert.deepStrictEqual( await service.sanitizeQuery({ adapter: { operators: ['$bla'] }, query: { name: { $bla: 'Dave' } } }), { name: { $bla: 'Dave' } } ) const validatedQuery = { name: { $bla: 'me' } } Object.defineProperty(validatedQuery, VALIDATED, { value: true }) assert.deepStrictEqual( await service.sanitizeQuery({ query: validatedQuery }), validatedQuery, 'validated queries are not sanitized' ) }) it('getOptions', () => { const service = new MethodService({ multi: true, paginate: { default: 1, max: 10 } }) const opts = service.getOptions({ adapter: { multi: ['create'], paginate: { default: 10, max: 100 } } }) assert.deepStrictEqual(opts, { id: 'id', events: [], paginate: { default: 10, max: 100 }, multi: ['create'], filters: {}, operators: [] }) const notPaginated = service.getOptions({ paginate: false }) assert.deepStrictEqual(notPaginated, { id: 'id', events: [], paginate: false, multi: true, filters: {}, operators: [] }) }) it('allowsMulti', () => { context('with true', () => { const service = new MethodService({ multi: true }) it('does return true for multiple methodes', () => { assert.equal(service.allowsMulti('patch'), true) }) it('does return false for always non-multiple methodes', () => { assert.equal(service.allowsMulti('update'), false) }) it('does return true for unknown methods', () => { assert.equal(service.allowsMulti('other'), true) }) }) context('with false', () => { const service = new MethodService({ multi: false }) it('does return false for multiple methodes', () => { assert.equal(service.allowsMulti('remove'), false) }) it('does return true for always multiple methodes', () => { assert.equal(service.allowsMulti('find'), true) }) it('does return false for unknown methods', () => { assert.equal(service.allowsMulti('other'), false) }) }) context('with array', () => { const service = new MethodService({ multi: ['create', 'get', 'other'] }) it('does return true for specified multiple methodes', () => { assert.equal(service.allowsMulti('create'), true) }) it('does return false for non-specified multiple methodes', () => { assert.equal(service.allowsMulti('patch'), false) }) it('does return false for specified always multiple methodes', () => { assert.equal(service.allowsMulti('get'), false) }) it('does return true for specified unknown methodes', () => { assert.equal(service.allowsMulti('other'), true) }) it('does return false for non-specified unknown methodes', () => { assert.equal(service.allowsMulti('another'), false) }) }) }) }) ================================================ FILE: packages/adapter-commons/test/sort.test.ts ================================================ import assert from 'assert' import { sorter } from '../src' describe('@feathersjs/adapter-commons', () => { describe('sorter', () => { it('simple sorter', () => { const array = [ { name: 'David' }, { name: 'Eric' } ] const sort = sorter({ name: -1 }) assert.deepStrictEqual(array.sort(sort), [ { name: 'Eric' }, { name: 'David' } ]) }) it('simple sorter with arrays', () => { const array = [ { names: ['a', 'b'] }, { names: ['c', 'd'] } ] const sort = sorter({ names: -1 }) assert.deepStrictEqual(array.sort(sort), [ { names: ['c', 'd'] }, { names: ['a', 'b'] } ]) }) it('simple sorter with objects', () => { const array = [ { names: { first: 'Dave', last: 'L' } }, { names: { first: 'A', last: 'B' } } ] const sort = sorter({ names: 1 }) assert.deepStrictEqual(array.sort(sort), [ { names: { first: 'A', last: 'B' } }, { names: { first: 'Dave', last: 'L' } } ]) }) it('two property sorter', () => { const array = [ { name: 'David', counter: 0 }, { name: 'Eric', counter: 1 }, { name: 'David', counter: 1 }, { name: 'Eric', counter: 0 } ] const sort = sorter({ name: -1, counter: 1 }) assert.deepStrictEqual(array.sort(sort), [ { name: 'Eric', counter: 0 }, { name: 'Eric', counter: 1 }, { name: 'David', counter: 0 }, { name: 'David', counter: 1 } ]) }) it('two property sorter with names', () => { const array = [ { name: 'David', counter: 0 }, { name: 'Eric', counter: 1 }, { name: 'Andrew', counter: 1 }, { name: 'David', counter: 1 }, { name: 'Andrew', counter: 0 }, { name: 'Eric', counter: 0 } ] const sort = sorter({ name: -1, counter: 1 }) assert.deepStrictEqual(array.sort(sort), [ { name: 'Eric', counter: 0 }, { name: 'Eric', counter: 1 }, { name: 'David', counter: 0 }, { name: 'David', counter: 1 }, { name: 'Andrew', counter: 0 }, { name: 'Andrew', counter: 1 } ]) }) it('three property sorter with names', () => { const array = [ { name: 'David', counter: 0, age: 2 }, { name: 'Eric', counter: 1, age: 2 }, { name: 'David', counter: 1, age: 1 }, { name: 'Eric', counter: 0, age: 1 }, { name: 'Andrew', counter: 0, age: 2 }, { name: 'Andrew', counter: 0, age: 1 } ] const sort = sorter({ name: -1, counter: 1, age: -1 }) assert.deepStrictEqual(array.sort(sort), [ { name: 'Eric', counter: 0, age: 1 }, { name: 'Eric', counter: 1, age: 2 }, { name: 'David', counter: 0, age: 2 }, { name: 'David', counter: 1, age: 1 }, { name: 'Andrew', counter: 0, age: 2 }, { name: 'Andrew', counter: 0, age: 1 } ]) }) }) describe('sorter mongoDB-like sorting on embedded objects', () => { let data: any[] = [] beforeEach(() => { data = [ { _id: 1, item: { category: 'cake', type: 'chiffon' }, amount: 10 }, { _id: 2, item: { category: 'cookies', type: 'chocolate chip' }, amount: 50 }, { _id: 3, item: { category: 'cookies', type: 'chocolate chip' }, amount: 15 }, { _id: 4, item: { category: 'cake', type: 'lemon' }, amount: 30 }, { _id: 5, item: { category: 'cake', type: 'carrot' }, amount: 20 }, { _id: 6, item: { category: 'brownies', type: 'blondie' }, amount: 10 } ] }) it('straight test', () => { const sort = sorter({ amount: -1 }) assert.deepStrictEqual(data.sort(sort), [ { _id: 2, item: { category: 'cookies', type: 'chocolate chip' }, amount: 50 }, { _id: 4, item: { category: 'cake', type: 'lemon' }, amount: 30 }, { _id: 5, item: { category: 'cake', type: 'carrot' }, amount: 20 }, { _id: 3, item: { category: 'cookies', type: 'chocolate chip' }, amount: 15 }, { _id: 1, item: { category: 'cake', type: 'chiffon' }, amount: 10 }, { _id: 6, item: { category: 'brownies', type: 'blondie' }, amount: 10 } ]) }) it('embedded sort 1', () => { const sort = sorter({ 'item.category': 1, 'item.type': 1 }) assert.deepStrictEqual(data.sort(sort), [ { _id: 6, item: { category: 'brownies', type: 'blondie' }, amount: 10 }, { _id: 5, item: { category: 'cake', type: 'carrot' }, amount: 20 }, { _id: 1, item: { category: 'cake', type: 'chiffon' }, amount: 10 }, { _id: 4, item: { category: 'cake', type: 'lemon' }, amount: 30 }, { _id: 2, item: { category: 'cookies', type: 'chocolate chip' }, amount: 50 }, { _id: 3, item: { category: 'cookies', type: 'chocolate chip' }, amount: 15 } ]) }) it('embedded sort 2', () => { const sort = sorter({ 'item.category': 1, 'item.type': 1, amount: 1 }) assert.deepStrictEqual(data.sort(sort), [ { _id: 6, item: { category: 'brownies', type: 'blondie' }, amount: 10 }, { _id: 5, item: { category: 'cake', type: 'carrot' }, amount: 20 }, { _id: 1, item: { category: 'cake', type: 'chiffon' }, amount: 10 }, { _id: 4, item: { category: 'cake', type: 'lemon' }, amount: 30 }, { _id: 3, item: { category: 'cookies', type: 'chocolate chip' }, amount: 15 }, { _id: 2, item: { category: 'cookies', type: 'chocolate chip' }, amount: 50 } ]) }) it('embedded sort 3', () => { const sort = sorter({ 'item.category': 1, 'item.type': 1, amount: -1 }) assert.deepStrictEqual(data.sort(sort), [ { _id: 6, item: { category: 'brownies', type: 'blondie' }, amount: 10 }, { _id: 5, item: { category: 'cake', type: 'carrot' }, amount: 20 }, { _id: 1, item: { category: 'cake', type: 'chiffon' }, amount: 10 }, { _id: 4, item: { category: 'cake', type: 'lemon' }, amount: 30 }, { _id: 2, item: { category: 'cookies', type: 'chocolate chip' }, amount: 50 }, { _id: 3, item: { category: 'cookies', type: 'chocolate chip' }, amount: 15 } ]) }) it('embedded sort 4', () => { const sort = sorter({ amount: -1, 'item.category': 1 }) assert.deepStrictEqual(data.sort(sort), [ { _id: 2, item: { category: 'cookies', type: 'chocolate chip' }, amount: 50 }, { _id: 4, item: { category: 'cake', type: 'lemon' }, amount: 30 }, { _id: 5, item: { category: 'cake', type: 'carrot' }, amount: 20 }, { _id: 3, item: { category: 'cookies', type: 'chocolate chip' }, amount: 15 }, { _id: 6, item: { category: 'brownies', type: 'blondie' }, amount: 10 }, { _id: 1, item: { category: 'cake', type: 'chiffon' }, amount: 10 } ]) }) it('embedded sort 5', () => { const sort = sorter({ 'item.category': 1, amount: 1 }) assert.deepStrictEqual(data.sort(sort), [ { _id: 6, item: { category: 'brownies', type: 'blondie' }, amount: 10 }, { _id: 1, item: { category: 'cake', type: 'chiffon' }, amount: 10 }, { _id: 5, item: { category: 'cake', type: 'carrot' }, amount: 20 }, { _id: 4, item: { category: 'cake', type: 'lemon' }, amount: 30 }, { _id: 3, item: { category: 'cookies', type: 'chocolate chip' }, amount: 15 }, { _id: 2, item: { category: 'cookies', type: 'chocolate chip' }, amount: 50 } ]) }) }) }) ================================================ FILE: packages/adapter-commons/tsconfig.json ================================================ { "extends": "../../tsconfig", "include": [ "src/**/*.ts" ], "compilerOptions": { "outDir": "lib" } } ================================================ FILE: packages/adapter-tests/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [5.0.42](https://github.com/feathersjs/feathers/compare/v5.0.41...v5.0.42) (2026-03-04) ### Bug Fixes - Update dependencies ([#3666](https://github.com/feathersjs/feathers/issues/3666)) ([477bf45](https://github.com/feathersjs/feathers/commit/477bf45f9c9dbde77a14a07828aa02300de23ae7)) ## [5.0.41](https://github.com/feathersjs/feathers/compare/v5.0.40...v5.0.41) (2026-02-19) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.40](https://github.com/feathersjs/feathers/compare/v5.0.39...v5.0.40) (2026-02-03) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.39](https://github.com/feathersjs/feathers/compare/v5.0.38...v5.0.39) (2026-01-31) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.38](https://github.com/feathersjs/feathers/compare/v5.0.37...v5.0.38) (2026-01-31) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.37](https://github.com/feathersjs/feathers/compare/v5.0.36...v5.0.37) (2025-11-10) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.36](https://github.com/feathersjs/feathers/compare/v5.0.35...v5.0.36) (2025-11-08) ### Bug Fixes - @feathersjs/memory update with query ([#3617](https://github.com/feathersjs/feathers/issues/3617)) ([4c6caa2](https://github.com/feathersjs/feathers/commit/4c6caa27e9af1312718d0c233a0c35f7739ac553)) - **dependencies:** Update all dependencies ([#3625](https://github.com/feathersjs/feathers/issues/3625)) ([2698e4e](https://github.com/feathersjs/feathers/commit/2698e4e2996fbf479d82435938d907bc3d5b583a)) ## [5.0.35](https://github.com/feathersjs/feathers/compare/v5.0.34...v5.0.35) (2025-09-09) ### Bug Fixes - Update all dependencies ([#3613](https://github.com/feathersjs/feathers/issues/3613)) ([5136bbd](https://github.com/feathersjs/feathers/commit/5136bbd2e2eeb4e6579e07c9e914006629542363)) ## [5.0.34](https://github.com/feathersjs/feathers/compare/v5.0.33...v5.0.34) (2025-05-03) ### Bug Fixes - Update dependencies ([#3584](https://github.com/feathersjs/feathers/issues/3584)) ([119fa4e](https://github.com/feathersjs/feathers/commit/119fa4e1ade8b0078aa235083d566e2538b3a084)) ## [5.0.33](https://github.com/feathersjs/feathers/compare/v5.0.32...v5.0.33) (2025-02-24) ### Bug Fixes - **dependencies:** Update dependencies ([#3571](https://github.com/feathersjs/feathers/issues/3571)) ([ad611cb](https://github.com/feathersjs/feathers/commit/ad611cb6ffb1dc31d603ba5817331318c5a23217)) ## [5.0.32](https://github.com/feathersjs/feathers/compare/v5.0.31...v5.0.32) (2025-02-01) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.31](https://github.com/feathersjs/feathers/compare/v5.0.30...v5.0.31) (2024-10-31) ### Bug Fixes - **dependencies:** Update all dependencies ([#3545](https://github.com/feathersjs/feathers/issues/3545)) ([221b92b](https://github.com/feathersjs/feathers/commit/221b92bb0ee5d54fb1036742968797cb02e56da2)) ## [5.0.30](https://github.com/feathersjs/feathers/compare/v5.0.29...v5.0.30) (2024-09-02) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.29](https://github.com/feathersjs/feathers/compare/v5.0.28...v5.0.29) (2024-07-10) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.28](https://github.com/feathersjs/feathers/compare/v5.0.27...v5.0.28) (2024-07-10) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.27](https://github.com/feathersjs/feathers/compare/v5.0.26...v5.0.27) (2024-06-18) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.26](https://github.com/feathersjs/feathers/compare/v5.0.25...v5.0.26) (2024-06-09) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.25](https://github.com/feathersjs/feathers/compare/v5.0.24...v5.0.25) (2024-05-03) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.24](https://github.com/feathersjs/feathers/compare/v5.0.23...v5.0.24) (2024-03-13) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.23](https://github.com/feathersjs/feathers/compare/v5.0.22...v5.0.23) (2024-02-25) ### Bug Fixes - **core:** Update to latest feathersjs/hooks ([#3434](https://github.com/feathersjs/feathers/issues/3434)) ([1499ccc](https://github.com/feathersjs/feathers/commit/1499ccc41fb3ebba97b2c84e0cb19bc48ad3c651)) ## [5.0.22](https://github.com/feathersjs/feathers/compare/v5.0.21...v5.0.22) (2024-02-15) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.21](https://github.com/feathersjs/feathers/compare/v5.0.20...v5.0.21) (2024-01-25) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.20](https://github.com/feathersjs/feathers/compare/v5.0.19...v5.0.20) (2024-01-24) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.19](https://github.com/feathersjs/feathers/compare/v5.0.18...v5.0.19) (2024-01-23) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.18](https://github.com/feathersjs/feathers/compare/v5.0.17...v5.0.18) (2024-01-22) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.17](https://github.com/feathersjs/feathers/compare/v5.0.16...v5.0.17) (2024-01-22) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.16](https://github.com/feathersjs/feathers/compare/v5.0.15...v5.0.16) (2024-01-22) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.15](https://github.com/feathersjs/feathers/compare/v5.0.14...v5.0.15) (2024-01-22) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.14](https://github.com/feathersjs/feathers/compare/v5.0.13...v5.0.14) (2024-01-05) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.13](https://github.com/feathersjs/feathers/compare/v5.0.12...v5.0.13) (2023-12-29) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.12](https://github.com/feathersjs/feathers/compare/v5.0.11...v5.0.12) (2023-11-28) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.11](https://github.com/feathersjs/feathers/compare/v5.0.10...v5.0.11) (2023-10-11) ### Bug Fixes - **knex:** Update all dependencies and Knex peer ([#3308](https://github.com/feathersjs/feathers/issues/3308)) ([d2f9860](https://github.com/feathersjs/feathers/commit/d2f986036c4741cce2339d8abbcc6b2eb037a12a)) ## [5.0.10](https://github.com/feathersjs/feathers/compare/v5.0.9...v5.0.10) (2023-10-03) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.9](https://github.com/feathersjs/feathers/compare/v5.0.8...v5.0.9) (2023-09-27) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.8](https://github.com/feathersjs/feathers/compare/v5.0.7...v5.0.8) (2023-07-19) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.7](https://github.com/feathersjs/feathers/compare/v5.0.6...v5.0.7) (2023-07-14) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.6](https://github.com/feathersjs/feathers/compare/v5.0.5...v5.0.6) (2023-06-15) **Note:** Version bump only for package @feathersjs/adapter-tests ## [5.0.5](https://github.com/feathersjs/feathers/compare/v5.0.4...v5.0.5) (2023-04-28) ### Bug Fixes - **typebox:** Revert to TypeBox 0.25 ([#3183](https://github.com/feathersjs/feathers/issues/3183)) ([cacedf5](https://github.com/feathersjs/feathers/commit/cacedf59e3d2df836777f0cd06ab1b2484ed87c5)) ## [5.0.4](https://github.com/feathersjs/feathers/compare/v5.0.3...v5.0.4) (2023-04-12) ### Bug Fixes - **adapter-commons:** Support non-default import to ease use with ESM projects ([d06f2cf](https://github.com/feathersjs/feathers/commit/d06f2cfcadda7dc23f0e2bec44f64e6be8500d02)) ## [5.0.3](https://github.com/feathersjs/feathers/compare/v5.0.2...v5.0.3) (2023-04-05) ### Bug Fixes - **dependencies:** Update all dependencies ([#3139](https://github.com/feathersjs/feathers/issues/3139)) ([f24276e](https://github.com/feathersjs/feathers/commit/f24276e9a909e2e58a0730c730258ce1f70f4028)) ## [5.0.1](https://github.com/feathersjs/feathers/compare/v5.0.0...v5.0.1) (2023-03-15) ### Bug Fixes - **memory/mongodb:** $select as only property & force 'id' in '$select' ([#3081](https://github.com/feathersjs/feathers/issues/3081)) ([fbe3cf5](https://github.com/feathersjs/feathers/commit/fbe3cf5199e102b5aeda2ae33828d5034df3d105)) # [5.0.0](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.38...v5.0.0) (2023-02-24) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.38](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.37...v5.0.0-pre.38) (2023-02-17) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.37](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.36...v5.0.0-pre.37) (2023-02-09) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.36](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.35...v5.0.0-pre.36) (2023-01-29) ### Bug Fixes - **databases:** Improve documentation for adapters and allow dynamic Knex adapter options ([#3019](https://github.com/feathersjs/feathers/issues/3019)) ([66c4b5e](https://github.com/feathersjs/feathers/commit/66c4b5e72000dd03acb57fca1cad4737c85c9c9e)) ### Features - **database:** Add and to the query syntax ([#3021](https://github.com/feathersjs/feathers/issues/3021)) ([00cb0d9](https://github.com/feathersjs/feathers/commit/00cb0d9c302ae951ae007d3d6ceba33e254edd9c)) # [5.0.0-pre.35](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.34...v5.0.0-pre.35) (2023-01-12) ### Features - **generators:** Move core code generators to shared generators package ([#2982](https://github.com/feathersjs/feathers/issues/2982)) ([0328d22](https://github.com/feathersjs/feathers/commit/0328d2292153870bc43958f73d2c6f288a8cec17)) # [5.0.0-pre.34](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.33...v5.0.0-pre.34) (2022-12-14) ### Features - **adapter:** Add patch data type to adapters and refactor AdapterBase usage ([#2906](https://github.com/feathersjs/feathers/issues/2906)) ([9ddc2e6](https://github.com/feathersjs/feathers/commit/9ddc2e6b028f026f939d6af68125847e5c6734b4)) # [5.0.0-pre.33](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.32...v5.0.0-pre.33) (2022-11-08) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.32](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.31...v5.0.0-pre.32) (2022-10-26) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.31](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.30...v5.0.0-pre.31) (2022-10-12) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.30](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.29...v5.0.0-pre.30) (2022-10-07) ### Features - **core:** Allow to unregister services at runtime ([#2756](https://github.com/feathersjs/feathers/issues/2756)) ([d16601f](https://github.com/feathersjs/feathers/commit/d16601f2277dca5357866ffdefba2a611f6dc7fa)) # [5.0.0-pre.29](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.28...v5.0.0-pre.29) (2022-09-16) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.28](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.27...v5.0.0-pre.28) (2022-08-03) ### Bug Fixes - **cli:** Improve generated application and client ([#2701](https://github.com/feathersjs/feathers/issues/2701)) ([bd55ffb](https://github.com/feathersjs/feathers/commit/bd55ffb812e89bf215f4515e7f137656ea888c3f)) # [5.0.0-pre.27](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.26...v5.0.0-pre.27) (2022-07-13) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.26](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.25...v5.0.0-pre.26) (2022-06-22) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.25](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.24...v5.0.0-pre.25) (2022-06-22) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.24](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.23...v5.0.0-pre.24) (2022-06-21) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.23](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.22...v5.0.0-pre.23) (2022-06-06) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.22](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.21...v5.0.0-pre.22) (2022-05-24) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.20](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.19...v5.0.0-pre.20) (2022-05-04) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.19](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.18...v5.0.0-pre.19) (2022-05-01) ### Bug Fixes - **adapter-commons:** Clarify adapter query filtering ([#2607](https://github.com/feathersjs/feathers/issues/2607)) ([2dac771](https://github.com/feathersjs/feathers/commit/2dac771b0a3298d6dd25994d05186701b0617718)) - **adapter-tests:** Ensure multi tests can run standalone ([#2608](https://github.com/feathersjs/feathers/issues/2608)) ([d7243f2](https://github.com/feathersjs/feathers/commit/d7243f20e84d9dde428ad8dfc7f48388ca569e6e)) ### BREAKING CHANGES - **adapter-commons:** Changes the common adapter base class to use `sanitizeQuery` and `sanitizeData` # [5.0.0-pre.18](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.17...v5.0.0-pre.18) (2022-04-11) ### Bug Fixes - **adapter-tests:** Add tests for pagination in multi updates ([#2472](https://github.com/feathersjs/feathers/issues/2472)) ([98a811a](https://github.com/feathersjs/feathers/commit/98a811ac605575ff812a08d0504729a5efe7a69c)) # [5.0.0-pre.17](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.16...v5.0.0-pre.17) (2022-02-15) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.16](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.15...v5.0.0-pre.16) (2022-01-12) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.15](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.14...v5.0.0-pre.15) (2021-11-27) ### Bug Fixes - **typescript:** Overall typing improvements ([#2478](https://github.com/feathersjs/feathers/issues/2478)) ([b8eb804](https://github.com/feathersjs/feathers/commit/b8eb804158556d9651a8607e3c3fda15e0bfd110)) # [5.0.0-pre.14](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.13...v5.0.0-pre.14) (2021-10-13) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.13](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.12...v5.0.0-pre.13) (2021-10-13) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.12](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.11...v5.0.0-pre.12) (2021-10-12) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.11](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.10...v5.0.0-pre.11) (2021-10-06) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.10](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.9...v5.0.0-pre.10) (2021-09-19) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.9](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.8...v5.0.0-pre.9) (2021-08-09) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.8](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.7...v5.0.0-pre.8) (2021-08-09) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.7](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.6...v5.0.0-pre.7) (2021-08-09) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.6](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.5...v5.0.0-pre.6) (2021-08-08) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.5](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.4...v5.0.0-pre.5) (2021-06-23) ### Bug Fixes - Update database adapter common repository urls ([#2380](https://github.com/feathersjs/feathers/issues/2380)) ([3f4db68](https://github.com/feathersjs/feathers/commit/3f4db68d6700c7d9023ecd17d0d39893f75a19fd)) ### Features - **adapter-commons:** Add support for params.adapter option and move memory adapter to @feathersjs/memory ([#2367](https://github.com/feathersjs/feathers/issues/2367)) ([a43e7da](https://github.com/feathersjs/feathers/commit/a43e7da22b6b981a96d1321736ea9a0cb924fb4f)) # [5.0.0-pre.4](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.3...v5.0.0-pre.4) (2021-05-13) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-pre.3](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.2...v5.0.0-pre.3) (2021-04-21) ### Bug Fixes - **typescript:** Improve TypeScript backwards compatibility ([#2310](https://github.com/feathersjs/feathers/issues/2310)) ([f33be73](https://github.com/feathersjs/feathers/commit/f33be73fc46a533efb15df9aab0658e3240d3897)) # [5.0.0-pre.2](https://github.com/feathersjs/feathers/compare/v5.0.0-beta.1...v5.0.0-pre.2) (2021-04-06) **Note:** Version bump only for package @feathersjs/adapter-tests # [5.0.0-beta.1](https://github.com/feathersjs/feathers/compare/v5.0.0-beta.0...v5.0.0-beta.1) (2021-04-03) ### Bug Fixes - **adapter-tests:** Add test that verified paginated total ([#2273](https://github.com/feathersjs/feathers/issues/2273)) ([879bd6b](https://github.com/feathersjs/feathers/commit/879bd6b24f42e04eeeeba110ddddda3e1e1dea34)) # [5.0.0-beta.0](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.1...v5.0.0-beta.0) (2021-03-28) ### Features - **core:** Remove Uberproto ([#2178](https://github.com/feathersjs/feathers/issues/2178)) ([ddf8821](https://github.com/feathersjs/feathers/commit/ddf8821f53317e6a378657f7d66acb03a037ee47)) ### BREAKING CHANGES - **core:** Services no longer extend Uberproto objects and `service.mixin()` is no longer available. # [5.0.0-pre.1](https://github.com/feathersjs/feathers/compare/v4.5.11...v5.0.0-pre.1) (2020-12-17) ### Features - **memory:** Move feathers-memory into @feathersjs/memory ([#2153](https://github.com/feathersjs/feathers/issues/2153)) ([dd61fe3](https://github.com/feathersjs/feathers/commit/dd61fe371fb0502f78b8ccbe1f45a030e31ecff6)) ## [4.5.11](https://github.com/feathersjs/feathers/compare/v4.5.10...v4.5.11) (2020-12-05) **Note:** Version bump only for package @feathersjs/adapter-tests ## [4.5.4](https://github.com/feathersjs/feathers/compare/v4.5.3...v4.5.4) (2020-09-27) **Note:** Version bump only for package @feathersjs/adapter-tests ## 4.5.3 (2020-09-24) ### Bug Fixes - **adapter-tests:** Update multi patch + query tests ([#5](https://github.com/feathersjs/databases/issues/5)) ([84f1fe4](https://github.com/feathersjs/databases/commit/84f1fe4f13dc3a26891e43b965f75d08243f6c6f)) ## [4.5.2](https://github.com/feathersjs/feathers/compare/v4.5.1...v4.5.2) (2020-03-04) **Note:** Version bump only for package @feathersjs/adapter-tests ## [4.5.1](https://github.com/feathersjs/feathers/compare/v4.5.0...v4.5.1) (2020-01-24) **Note:** Version bump only for package @feathersjs/adapter-tests # [4.5.0](https://github.com/feathersjs/feathers/compare/v4.4.3...v4.5.0) (2020-01-18) **Note:** Version bump only for package @feathersjs/adapter-tests ## [4.4.3](https://github.com/feathersjs/feathers/compare/v4.4.1...v4.4.3) (2019-12-06) **Note:** Version bump only for package @feathersjs/adapter-tests ## [4.4.1](https://github.com/feathersjs/feathers/compare/v4.4.0...v4.4.1) (2019-11-27) **Note:** Version bump only for package @feathersjs/adapter-tests # [4.4.0](https://github.com/feathersjs/feathers/compare/v4.3.11...v4.4.0) (2019-11-27) **Note:** Version bump only for package @feathersjs/adapter-tests ## [4.3.11](https://github.com/feathersjs/feathers/compare/v4.3.10...v4.3.11) (2019-11-11) **Note:** Version bump only for package @feathersjs/adapter-tests ## [4.3.10](https://github.com/feathersjs/feathers/compare/v4.3.9...v4.3.10) (2019-10-26) **Note:** Version bump only for package @feathersjs/adapter-tests ## [4.3.9](https://github.com/feathersjs/feathers/compare/v4.3.8...v4.3.9) (2019-10-26) **Note:** Version bump only for package @feathersjs/adapter-tests ## [4.3.7](https://github.com/feathersjs/feathers/compare/v4.3.6...v4.3.7) (2019-10-14) **Note:** Version bump only for package @feathersjs/adapter-tests ## [4.3.4](https://github.com/feathersjs/feathers/compare/v4.3.3...v4.3.4) (2019-10-03) **Note:** Version bump only for package @feathersjs/adapter-tests ## [4.3.3](https://github.com/feathersjs/feathers/compare/v4.3.2...v4.3.3) (2019-09-21) **Note:** Version bump only for package @feathersjs/adapter-tests ## [4.3.2](https://github.com/feathersjs/feathers/compare/v4.3.1...v4.3.2) (2019-09-16) **Note:** Version bump only for package @feathersjs/adapter-tests ## [4.3.1](https://github.com/feathersjs/feathers/compare/v4.3.0...v4.3.1) (2019-09-09) **Note:** Version bump only for package @feathersjs/adapter-tests # [4.3.0](https://github.com/feathersjs/feathers/compare/v4.3.0-pre.4...v4.3.0) (2019-08-27) **Note:** Version bump only for package @feathersjs/adapter-tests # [4.3.0-pre.4](https://github.com/feathersjs/feathers/compare/v4.3.0-pre.3...v4.3.0-pre.4) (2019-08-22) **Note:** Version bump only for package @feathersjs/adapter-tests # [4.3.0-pre.3](https://github.com/feathersjs/feathers/compare/v4.3.0-pre.2...v4.3.0-pre.3) (2019-08-19) **Note:** Version bump only for package @feathersjs/adapter-tests # [4.3.0-pre.2](https://github.com/feathersjs/feathers/compare/v4.3.0-pre.1...v4.3.0-pre.2) (2019-08-02) **Note:** Version bump only for package @feathersjs/adapter-tests # [4.3.0-pre.1](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.5...v4.3.0-pre.1) (2019-07-11) **Note:** Version bump only for package @feathersjs/adapter-tests # [4.0.0-pre.5](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.4...v4.0.0-pre.5) (2019-07-10) ### Bug Fixes - Fix feathers-memory dependency that did not get updated ([9422b13](https://github.com/feathersjs/feathers/commit/9422b13)) # [4.0.0-pre.4](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.3...v4.0.0-pre.4) (2019-07-05) **Note:** Version bump only for package @feathersjs/adapter-tests # [4.0.0-pre.3](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.2...v4.0.0-pre.3) (2019-06-01) ### Bug Fixes - Update dependencies and fix tests ([#1373](https://github.com/feathersjs/feathers/issues/1373)) ([d743a7f](https://github.com/feathersjs/feathers/commit/d743a7f)) # [4.0.0-pre.2](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.1...v4.0.0-pre.2) (2019-05-15) **Note:** Version bump only for package @feathersjs/adapter-tests # [4.0.0-pre.1](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.0...v4.0.0-pre.1) (2019-05-08) **Note:** Version bump only for package @feathersjs/adapter-tests # [4.0.0-pre.0](https://github.com/feathersjs/feathers/compare/v3.2.0-pre.1...v4.0.0-pre.0) (2019-04-21) ### Bug Fixes - Add test to make sure different id in adapter query works ([#1165](https://github.com/feathersjs/feathers/issues/1165)) ([0ba4580](https://github.com/feathersjs/feathers/commit/0ba4580)) - Update adapter tests to not rely on error instance ([#1202](https://github.com/feathersjs/feathers/issues/1202)) ([6885e0e](https://github.com/feathersjs/feathers/commit/6885e0e)) - Update all dependencies to latest ([#1206](https://github.com/feathersjs/feathers/issues/1206)) ([e51e0f6](https://github.com/feathersjs/feathers/commit/e51e0f6)) ### chore - **package:** Move adapter tests into their own module ([#1164](https://github.com/feathersjs/feathers/issues/1164)) ([dcc1e6b](https://github.com/feathersjs/feathers/commit/dcc1e6b)) ### Features - Add TypeScript definitions ([#1275](https://github.com/feathersjs/feathers/issues/1275)) ([9dd6713](https://github.com/feathersjs/feathers/commit/9dd6713)) - Authentication v3 core server implementation ([#1205](https://github.com/feathersjs/feathers/issues/1205)) ([1bd7591](https://github.com/feathersjs/feathers/commit/1bd7591)) ### BREAKING CHANGES - **package:** Removes adapter tests from @feathersjs/adapter-commons ## [1.0.1](https://github.com/feathersjs/feathers/compare/@feathersjs/adapter-tests@1.0.0...@feathersjs/adapter-tests@1.0.1) (2019-01-10) ### Bug Fixes - Add test to make sure different id in adapter query works ([#1165](https://github.com/feathersjs/feathers/issues/1165)) ([0ba4580](https://github.com/feathersjs/feathers/commit/0ba4580)) # 1.0.0 (2019-01-10) ### chore - **package:** Move adapter tests into their own module ([#1164](https://github.com/feathersjs/feathers/issues/1164)) ([dcc1e6b](https://github.com/feathersjs/feathers/commit/dcc1e6b)) ### BREAKING CHANGES - **package:** Removes adapter tests from @feathersjs/adapter-commons ================================================ FILE: packages/adapter-tests/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2024 Feathers Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: packages/adapter-tests/README.md ================================================ # Feathers Adapter Tests [](https://github.com/feathersjs/feathers/actions?query=workflow%3A%22Node.js+CI%22) [](https://www.npmjs.com/package/@feathersjs/adapter-commons) [](https://discord.gg/qa8kez8QBx) > Feathers shared database adapter test suite ## About This is a repository that contains the test suite for the common database adapter syntax. See the [API documentation](https://docs.feathersjs.com/api/databases/common.html) for more information. ## Authors [Feathers contributors](https://github.com/feathersjs/adapter-tests/graphs/contributors) ## License Copyright (c) 2024 [Feathers contributors](https://github.com/feathersjs/feathers/graphs/contributors) Licensed under the [MIT license](LICENSE). ================================================ FILE: packages/adapter-tests/package.json ================================================ { "name": "@feathersjs/adapter-tests", "version": "5.0.42", "description": "Feathers shared database adapter test suite", "homepage": "https://feathersjs.com", "keywords": [ "feathers" ], "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/feathers" }, "repository": { "type": "git", "url": "git://github.com/feathersjs/feathers.git", "directory": "packages/adapter-tests" }, "author": { "name": "Feathers contributor", "email": "hello@feathersjs.com", "url": "https://feathersjs.com" }, "contributors": [], "bugs": { "url": "https://github.com/feathersjs/feathers/issues" }, "engines": { "node": ">= 12" }, "main": "lib/", "types": "lib/", "scripts": { "prepublish": "npm run compile", "pack": "npm pack --pack-destination ../generators/test/build", "compile": "shx rm -rf lib/ && tsc && npm run pack", "test": "mocha --config ../../.mocharc.json --recursive test/**.test.ts test/**/*.test.ts" }, "directories": { "lib": "lib" }, "files": [ "CHANGELOG.md", "LICENSE", "README.md", "src/**", "lib/**" ], "publishConfig": { "access": "public" }, "devDependencies": { "@types/mocha": "^10.0.10", "@types/node": "^25.3.3", "mocha": "^11.7.5", "shx": "^0.4.0", "ts-node": "^10.9.2", "typescript": "^5.9.3" }, "gitHead": "90caf635aec850550b9d37bea2762af959d9e8d5" } ================================================ FILE: packages/adapter-tests/src/basic.ts ================================================ import assert from 'assert' import { AdapterBasicTest } from './declarations' export default (test: AdapterBasicTest, app: any, _errors: any, serviceName: string, idProp: string) => { describe('Basic Functionality', () => { let service: any beforeEach(() => { service = app.service(serviceName) }) it('.id', () => { assert.strictEqual(service.id, idProp, 'id property is set to expected name') }) test('.options', () => { assert.ok(service.options, 'Options are available in service.options') }) test('.events', () => { assert.ok(service.events.includes('testing'), 'service.events is set and includes "testing"') }) describe('Raw Methods', () => { test('._get', () => { assert.strictEqual(typeof service._get, 'function') }) test('._find', () => { assert.strictEqual(typeof service._find, 'function') }) test('._create', () => { assert.strictEqual(typeof service._create, 'function') }) test('._update', () => { assert.strictEqual(typeof service._update, 'function') }) test('._patch', () => { assert.strictEqual(typeof service._patch, 'function') }) test('._remove', () => { assert.strictEqual(typeof service._remove, 'function') }) }) }) } ================================================ FILE: packages/adapter-tests/src/declarations.ts ================================================ export type AdapterTest = (name: AdapterTestName, runner: any) => void export type AdapterBasicTest = (name: AdapterBasicTestName, runner: any) => void export type AdapterMethodsTest = (name: AdapterMethodsTestName, runner: any) => void export type AdapterSyntaxTest = (name: AdapterSyntaxTestName, runner: any) => void export type AdapterTestName = AdapterBasicTestName | AdapterMethodsTestName | AdapterSyntaxTestName export type AdapterBasicTestName = | '.id' | '.options' | '.events' | '._get' | '._find' | '._create' | '._update' | '._patch' | '._remove' | '.$get' | '.$find' | '.$create' | '.$update' | '.$patch' | '.$remove' export type AdapterMethodsTestName = | '.get' | '.get + $select' | '.get + id + query' | '.get + NotFound' | '.get + NotFound (integer)' | '.get + id + query id' | '.find' | '.remove' | '.remove + $select' | '.remove + id + query' | '.remove + NotFound' | '.remove + NotFound (integer)' | '.remove + multi' | '.remove + multi no pagination' | '.remove + id + query id' | '.update' | '.update + $select' | '.update + id + query' | '.update + NotFound' | '.update + NotFound (integer)' | '.update + query + NotFound' | '.update + id + query id' | '.patch' | '.patch + $select' | '.patch + id + query' | '.patch multiple' | '.patch multiple no pagination' | '.patch multi query same' | '.patch multi query changed' | '.patch + NotFound' | '.patch + NotFound (integer)' | '.patch + query + NotFound' | '.patch + id + query id' | '.create' | '.create + $select' | '.create multi' | '.create ignores query' | 'internal .find' | 'internal .get' | 'internal .create' | 'internal .update' | 'internal .patch' | 'internal .remove' export type AdapterSyntaxTestName = | '.find + equal' | '.find + equal multiple' | '.find + $sort' | '.find + $sort + string' | '.find + $limit' | '.find + $limit 0' | '.find + $skip' | '.find + $select' | '.find + $or' | '.find + $in' | '.find + $nin' | '.find + $lt' | '.find + $lte' | '.find + $gt' | '.find + $gte' | '.find + $ne' | '.find + $gt + $lt + $sort' | '.find + $or nested + $sort' | '.find + $and' | '.find + $and + $or' | 'params.adapter + paginate' | 'params.adapter + multi' | '.find + paginate' | '.find + paginate + query' | '.find + paginate + $limit + $skip' | '.find + paginate + $limit 0' | '.find + paginate + params' ================================================ FILE: packages/adapter-tests/src/index.ts ================================================ /* eslint-disable no-console */ import basicTests from './basic' import { AdapterTestName } from './declarations' import methodTests from './methods' import syntaxTests from './syntax' export const adapterTests = (testNames: AdapterTestName[]) => { return (app: any, errors: any, serviceName: any, idProp = 'id') => { if (!serviceName) { throw new Error('You must pass a service name') } const skippedTests: AdapterTestName[] = [] const allTests: AdapterTestName[] = [] const test = (name: AdapterTestName, runner: any) => { const skip = !testNames.includes(name) const its = skip ? it.skip : it if (skip) { skippedTests.push(name) } allTests.push(name) its(name, runner) } describe(`Adapter tests for '${serviceName}' service with '${idProp}' id property`, () => { after(() => { testNames.forEach((name) => { if (!allTests.includes(name)) { console.error(`WARNING: '${name}' test is not part of the test suite`) } }) if (skippedTests.length) { console.log( `\nSkipped the following ${skippedTests.length} Feathers adapter test(s) out of ${allTests.length} total:` ) console.log(JSON.stringify(skippedTests, null, ' ')) } }) basicTests(test, app, errors, serviceName, idProp) methodTests(test, app, errors, serviceName, idProp) syntaxTests(test, app, errors, serviceName, idProp) }) } } export * from './declarations' export default adapterTests if (typeof module !== 'undefined') { module.exports = Object.assign(adapterTests, module.exports) } ================================================ FILE: packages/adapter-tests/src/methods.ts ================================================ import assert from 'assert' import { AdapterMethodsTest } from './declarations' export default (test: AdapterMethodsTest, app: any, _errors: any, serviceName: string, idProp: string) => { describe(' Methods', () => { let doug: any let service: any beforeEach(async () => { service = app.service(serviceName) doug = await app.service(serviceName).create({ name: 'Doug', age: 32 }) }) afterEach(async () => { try { await app.service(serviceName).remove(doug[idProp]) } catch (error: any) {} }) describe('get', () => { test('.get', async () => { const data = await service.get(doug[idProp]) assert.strictEqual(data[idProp].toString(), doug[idProp].toString(), `${idProp} id matches`) assert.strictEqual(data.name, 'Doug', 'data.name matches') assert.strictEqual(data.age, 32, 'data.age matches') }) test('.get + $select', async () => { const data = await service.get(doug[idProp], { query: { $select: ['name'] } }) assert.strictEqual(data[idProp].toString(), doug[idProp].toString(), `${idProp} id property matches`) assert.strictEqual(data.name, 'Doug', 'data.name matches') assert.ok(!data.age, 'data.age is falsy') }) test('.get + id + query', async () => { try { await service.get(doug[idProp], { query: { name: 'Tester' } }) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Got a NotFound Feathers error') } }) test('.get + NotFound', async () => { try { await service.get('568225fbfe21222432e836ff') throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Error is a NotFound Feathers error') } }) test('.get + NotFound (integer)', async () => { try { await service.get(123456789) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Error is a NotFound Feathers error') } }) test('.get + id + query id', async () => { const alice = await service.create({ name: 'Alice', age: 12 }) try { await service.get(doug[idProp], { query: { [idProp]: alice[idProp] } }) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Got a NotFound Feathers error') } await service.remove(alice[idProp]) }) }) describe('find', () => { test('.find', async () => { const data = await service.find() assert.ok(Array.isArray(data), 'Data is an array') assert.strictEqual(data.length, 1, 'Got one entry') }) }) describe('remove', () => { test('.remove', async () => { const data = await service.remove(doug[idProp]) assert.strictEqual(data.name, 'Doug', 'data.name matches') }) test('.remove + $select', async () => { const data = await service.remove(doug[idProp], { query: { $select: ['name'] } }) assert.strictEqual(data[idProp].toString(), doug[idProp].toString(), `${idProp} id property matches`) assert.strictEqual(data.name, 'Doug', 'data.name matches') assert.ok(!data.age, 'data.age is falsy') }) test('.remove + id + query', async () => { try { await service.remove(doug[idProp], { query: { name: 'Tester' } }) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Got a NotFound Feathers error') } }) test('.remove + NotFound', async () => { try { await service.remove('568225fbfe21222432e836ff') throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Error is a NotFound Feathers error') } }) test('.remove + NotFound (integer)', async () => { try { await service.remove(123456789) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Error is a NotFound Feathers error') } }) test('.remove + multi', async () => { try { await service.remove(null) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual( error.name, 'MethodNotAllowed', 'Removing multiple without option set throws MethodNotAllowed' ) } service.options.multi = ['remove'] await service.create({ name: 'Dave', age: 29, created: true }) await service.create({ name: 'David', age: 3, created: true }) const data = await service.remove(null, { query: { created: true } }) assert.strictEqual(data.length, 2) const names = data.map((person: any) => person.name) assert.ok(names.includes('Dave'), 'Dave removed') assert.ok(names.includes('David'), 'David removed') }) test('.remove + multi no pagination', async () => { try { await service.remove(doug[idProp]) } catch (error: any) {} const count = 14 const defaultPaginate = 10 assert.ok(count > defaultPaginate, 'count is bigger than default pagination') const multiBefore = service.options.multi const paginateBefore = service.options.paginate try { service.options.multi = true service.options.paginate = { default: defaultPaginate, max: 100 } const emptyItems = await service.find({ paginate: false }) assert.strictEqual(emptyItems.length, 0, 'no items before') const createdItems = await service.create( Array.from(Array(count)).map((_, i) => ({ name: `name-${i}`, age: 3, created: true })) ) assert.strictEqual(createdItems.length, count, `created ${count} items`) const foundItems = await service.find({ paginate: false }) assert.strictEqual(foundItems.length, count, `created ${count} items`) const foundPaginatedItems = await service.find({}) assert.strictEqual(foundPaginatedItems.data.length, defaultPaginate, 'found paginated items') const allItems = await service.remove(null, { query: { created: true } }) assert.strictEqual(allItems.length, count, `removed all ${count} items`) } finally { await service.remove(null, { query: { created: true }, paginate: false }) service.options.multi = multiBefore service.options.paginate = paginateBefore } }) test('.remove + id + query id', async () => { const alice = await service.create({ name: 'Alice', age: 12 }) try { await service.remove(doug[idProp], { query: { [idProp]: alice[idProp] } }) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Got a NotFound Feathers error') } await service.remove(alice[idProp]) }) }) describe('update', () => { test('.update', async () => { const originalData = { [idProp]: doug[idProp], name: 'Dougler' } const originalCopy = Object.assign({}, originalData) const data = await service.update(doug[idProp], originalData) assert.deepStrictEqual(originalData, originalCopy, 'data was not modified') assert.strictEqual(data[idProp].toString(), doug[idProp].toString(), `${idProp} id matches`) assert.strictEqual(data.name, 'Dougler', 'data.name matches') assert.ok(!data.age, 'data.age is falsy') }) test('.update + $select', async () => { const originalData = { [idProp]: doug[idProp], name: 'Dougler', age: 10 } const data = await service.update(doug[idProp], originalData, { query: { $select: ['name'] } }) assert.strictEqual(data[idProp].toString(), doug[idProp].toString(), `${idProp} id property matches`) assert.strictEqual(data.name, 'Dougler', 'data.name matches') assert.ok(!data.age, 'data.age is falsy') }) test('.update + id + query', async () => { try { await service.update( doug[idProp], { name: 'Dougler' }, { query: { name: 'Tester' } } ) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Got a NotFound Feathers error') } const updatedDoug = await service.get(doug[idProp]) assert.strictEqual(updatedDoug.name, 'Doug', 'Doug was not updated') }) test('.update + NotFound', async () => { try { await service.update('568225fbfe21222432e836ff', { name: 'NotFound' }) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Error is a NotFound Feathers error') } }) test('.update + NotFound (integer)', async () => { try { await service.update(123456789, { name: 'NotFound' }) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Error is a NotFound Feathers error') } }) test('.update + query + NotFound', async () => { const dave = await service.create({ name: 'Dave' }) try { await service.update(dave[idProp], { name: 'UpdatedDave' }, { query: { name: 'NotDave' } }) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Error is a NotFound Feathers error') } await service.remove(dave[idProp]) }) test('.update + id + query id', async () => { const alice = await service.create({ name: 'Alice', age: 12 }) try { await service.update( doug[idProp], { name: 'Dougler', age: 33 }, { query: { [idProp]: alice[idProp] } } ) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Got a NotFound Feathers error') } await service.remove(alice[idProp]) }) }) describe('patch', () => { test('.patch', async () => { const originalData = { [idProp]: doug[idProp], name: 'PatchDoug' } const originalCopy = Object.assign({}, originalData) const data = await service.patch(doug[idProp], originalData) assert.deepStrictEqual(originalData, originalCopy, 'original data was not modified') assert.strictEqual(data[idProp].toString(), doug[idProp].toString(), `${idProp} id matches`) assert.strictEqual(data.name, 'PatchDoug', 'data.name matches') assert.strictEqual(data.age, 32, 'data.age matches') }) test('.patch + $select', async () => { const originalData = { [idProp]: doug[idProp], name: 'PatchDoug' } const data = await service.patch(doug[idProp], originalData, { query: { $select: ['name'] } }) assert.strictEqual(data[idProp].toString(), doug[idProp].toString(), `${idProp} id property matches`) assert.strictEqual(data.name, 'PatchDoug', 'data.name matches') assert.ok(!data.age, 'data.age is falsy') }) test('.patch + id + query', async () => { try { await service.patch( doug[idProp], { name: 'id patched doug' }, { query: { name: 'Tester' } } ) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Got a NotFound Feathers error') } }) test('.patch multiple', async () => { try { await service.patch(null, {}) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual( error.name, 'MethodNotAllowed', 'Removing multiple without option set throws MethodNotAllowed' ) } const params = { query: { created: true } } const dave = await service.create({ name: 'Dave', age: 29, created: true }) const david = await service.create({ name: 'David', age: 3, created: true }) service.options.multi = ['patch'] const data = await service.patch( null, { age: 2 }, params ) assert.strictEqual(data.length, 2, 'returned two entries') assert.strictEqual(data[0].age, 2, 'First entry age was updated') assert.strictEqual(data[1].age, 2, 'Second entry age was updated') await service.remove(dave[idProp]) await service.remove(david[idProp]) }) test('.patch multiple no pagination', async () => { try { await service.remove(doug[idProp]) } catch (error: any) {} const count = 14 const defaultPaginate = 10 assert.ok(count > defaultPaginate, 'count is bigger than default pagination') const multiBefore = service.options.multi const paginateBefore = service.options.paginate let ids: any[] try { service.options.multi = true service.options.paginate = { default: defaultPaginate, max: 100 } const emptyItems = await service.find({ paginate: false }) assert.strictEqual(emptyItems.length, 0, 'no items before') const createdItems = await service.create( Array.from(Array(count)).map((_, i) => ({ name: `name-${i}`, age: 3, created: true })) ) assert.strictEqual(createdItems.length, count, `created ${count} items`) ids = createdItems.map((item: any) => item[idProp]) const foundItems = await service.find({ paginate: false }) assert.strictEqual(foundItems.length, count, `created ${count} items`) const foundPaginatedItems = await service.find({}) assert.strictEqual(foundPaginatedItems.data.length, defaultPaginate, 'found paginated data') const allItems = await service.patch(null, { age: 4 }, { query: { created: true } }) assert.strictEqual(allItems.length, count, `patched all ${count} items`) } finally { service.options.multi = multiBefore service.options.paginate = paginateBefore if (ids) { await Promise.all(ids.map((id) => service.remove(id))) } } }) test('.patch multi query same', async () => { const service = app.service(serviceName) const multiBefore = service.options.multi service.options.multi = true const params = { query: { age: { $lt: 10 } } } const dave = await service.create({ name: 'Dave', age: 8, created: true }) const david = await service.create({ name: 'David', age: 4, created: true }) const data = await service.patch( null, { age: 2 }, params ) assert.strictEqual(data.length, 2, 'returned two entries') assert.strictEqual(data[0].age, 2, 'First entry age was updated') assert.strictEqual(data[1].age, 2, 'Second entry age was updated') await service.remove(dave[idProp]) await service.remove(david[idProp]) service.options.multi = multiBefore }) test('.patch multi query changed', async () => { const service = app.service(serviceName) const multiBefore = service.options.multi service.options.multi = true const params = { query: { age: 10 } } const dave = await service.create({ name: 'Dave', age: 10, created: true }) const david = await service.create({ name: 'David', age: 10, created: true }) const data = await service.patch( null, { age: 2 }, params ) assert.strictEqual(data.length, 2, 'returned two entries') assert.strictEqual(data[0].age, 2, 'First entry age was updated') assert.strictEqual(data[1].age, 2, 'Second entry age was updated') await service.remove(dave[idProp]) await service.remove(david[idProp]) service.options.multi = multiBefore }) test('.patch + NotFound', async () => { try { await service.patch('568225fbfe21222432e836ff', { name: 'PatchDoug' }) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Error is a NotFound Feathers error') } }) test('.patch + NotFound (integer)', async () => { try { await service.patch(123456789, { name: 'PatchDoug' }) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Error is a NotFound Feathers error') } }) test('.patch + query + NotFound', async () => { const dave = await service.create({ name: 'Dave' }) try { await service.patch(dave[idProp], { name: 'PatchedDave' }, { query: { name: 'NotDave' } }) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Error is a NotFound Feathers error') } await service.remove(dave[idProp]) }) test('.patch + id + query id', async () => { const alice = await service.create({ name: 'Alice', age: 12 }) try { await service.patch( doug[idProp], { age: 33 }, { query: { [idProp]: alice[idProp] } } ) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual(error.name, 'NotFound', 'Got a NotFound Feathers error') } await service.remove(alice[idProp]) }) }) describe('create', () => { test('.create', async () => { const originalData = { name: 'Bill', age: 40 } const originalCopy = Object.assign({}, originalData) const data = await service.create(originalData) assert.deepStrictEqual(originalData, originalCopy, 'original data was not modified') assert.ok(data instanceof Object, 'data is an object') assert.strictEqual(data.name, 'Bill', 'data.name matches') await service.remove(data[idProp]) }) test('.create ignores query', async () => { const originalData = { name: 'Billy', age: 42 } const data = await service.create(originalData, { query: { name: 'Dave' } }) assert.strictEqual(data.name, 'Billy', 'data.name matches') await service.remove(data[idProp]) }) test('.create + $select', async () => { const originalData = { name: 'William', age: 23 } const data = await service.create(originalData, { query: { $select: ['name'] } }) assert.ok(idProp in data, 'data has id') assert.strictEqual(data.name, 'William', 'data.name matches') assert.ok(!data.age, 'data.age is falsy') await service.remove(data[idProp]) }) test('.create multi', async () => { try { await service.create([], {}) throw new Error('Should never get here') } catch (error: any) { assert.strictEqual( error.name, 'MethodNotAllowed', 'Removing multiple without option set throws MethodNotAllowed' ) } const items = [ { name: 'Gerald', age: 18 }, { name: 'Herald', age: 18 } ] service.options.multi = ['create', 'patch'] const data = await service.create(items) assert.ok(Array.isArray(data), 'data is an array') assert.ok(typeof data[0][idProp] !== 'undefined', 'id is set') assert.strictEqual(data[0].name, 'Gerald', 'first name matches') assert.ok(typeof data[1][idProp] !== 'undefined', 'id is set') assert.strictEqual(data[1].name, 'Herald', 'second name macthes') await service.remove(data[0][idProp]) await service.remove(data[1][idProp]) }) }) describe("doesn't call public methods internally", () => { let throwing: any before(() => { throwing = Object.assign(Object.create(app.service(serviceName)), { get store() { return app.service(serviceName).store }, find() { throw new Error('find method called') }, get() { throw new Error('get method called') }, create() { throw new Error('create method called') }, update() { throw new Error('update method called') }, patch() { throw new Error('patch method called') }, remove() { throw new Error('remove method called') } }) }) test('internal .find', () => app.service(serviceName).find.call(throwing)) test('internal .get', () => service.get.call(throwing, doug[idProp])) test('internal .create', async () => { const bob = await service.create.call(throwing, { name: 'Bob', age: 25 }) await service.remove(bob[idProp]) }) test('internal .update', () => service.update.call(throwing, doug[idProp], { name: 'Dougler' })) test('internal .patch', () => service.patch.call(throwing, doug[idProp], { name: 'PatchDoug' })) test('internal .remove', () => service.remove.call(throwing, doug[idProp])) }) }) } ================================================ FILE: packages/adapter-tests/src/syntax.ts ================================================ import assert from 'assert' import { AdapterSyntaxTest } from './declarations' export default (test: AdapterSyntaxTest, app: any, _errors: any, serviceName: string, idProp: string) => { describe('Query Syntax', () => { let bob: any let alice: any let doug: any let service: any beforeEach(async () => { service = app.service(serviceName) bob = await app.service(serviceName).create({ name: 'Bob', age: 25 }) doug = await app.service(serviceName).create({ name: 'Doug', age: 32 }) alice = await app.service(serviceName).create({ name: 'Alice', age: 19 }) }) afterEach(async () => { await service.remove(bob[idProp]) await service.remove(alice[idProp]) await service.remove(doug[idProp]) }) test('.find + equal', async () => { const params = { query: { name: 'Alice' } } const data = await service.find(params) assert.ok(Array.isArray(data)) assert.strictEqual(data.length, 1) assert.strictEqual(data[0].name, 'Alice') }) test('.find + equal multiple', async () => { const data = await service.find({ query: { name: 'Alice', age: 20 } }) assert.strictEqual(data.length, 0) }) describe('special filters', () => { test('.find + $sort', async () => { let data = await service.find({ query: { $sort: { name: 1 } } }) assert.strictEqual(data.length, 3) assert.strictEqual(data[0].name, 'Alice') assert.strictEqual(data[1].name, 'Bob') assert.strictEqual(data[2].name, 'Doug') data = await service.find({ query: { $sort: { name: -1 } } }) assert.strictEqual(data.length, 3) assert.strictEqual(data[0].name, 'Doug') assert.strictEqual(data[1].name, 'Bob') assert.strictEqual(data[2].name, 'Alice') }) test('.find + $sort + string', async () => { const data = await service.find({ query: { $sort: { name: '1' } } }) assert.strictEqual(data.length, 3) assert.strictEqual(data[0].name, 'Alice') assert.strictEqual(data[1].name, 'Bob') assert.strictEqual(data[2].name, 'Doug') }) test('.find + $limit', async () => { const data = await service.find({ query: { $limit: 2 } }) assert.strictEqual(data.length, 2) }) test('.find + $limit 0', async () => { const data = await service.find({ query: { $limit: 0 } }) assert.strictEqual(data.length, 0) }) test('.find + $skip', async () => { const data = await service.find({ query: { $sort: { name: 1 }, $skip: 1 } }) assert.strictEqual(data.length, 2) assert.strictEqual(data[0].name, 'Bob') assert.strictEqual(data[1].name, 'Doug') }) test('.find + $select', async () => { const data = await service.find({ query: { name: 'Alice', $select: ['name'] } }) assert.strictEqual(data.length, 1) assert.ok(idProp in data[0], 'data has id') assert.strictEqual(data[0].name, 'Alice') assert.strictEqual(data[0].age, undefined) }) test('.find + $or', async () => { const data = await service.find({ query: { $or: [{ name: 'Alice' }, { name: 'Bob' }], $sort: { name: 1 } } }) assert.strictEqual(data.length, 2) assert.strictEqual(data[0].name, 'Alice') assert.strictEqual(data[1].name, 'Bob') }) test('.find + $in', async () => { const data = await service.find({ query: { name: { $in: ['Alice', 'Bob'] }, $sort: { name: 1 } } }) assert.strictEqual(data.length, 2) assert.strictEqual(data[0].name, 'Alice') assert.strictEqual(data[1].name, 'Bob') }) test('.find + $nin', async () => { const data = await service.find({ query: { name: { $nin: ['Alice', 'Bob'] } } }) assert.strictEqual(data.length, 1) assert.strictEqual(data[0].name, 'Doug') }) test('.find + $lt', async () => { const data = await service.find({ query: { age: { $lt: 30 } } }) assert.strictEqual(data.length, 2) }) test('.find + $lte', async () => { const data = await service.find({ query: { age: { $lte: 25 } } }) assert.strictEqual(data.length, 2) }) test('.find + $gt', async () => { const data = await service.find({ query: { age: { $gt: 30 } } }) assert.strictEqual(data.length, 1) }) test('.find + $gte', async () => { const data = await service.find({ query: { age: { $gte: 25 } } }) assert.strictEqual(data.length, 2) }) test('.find + $ne', async () => { const data = await service.find({ query: { age: { $ne: 25 } } }) assert.strictEqual(data.length, 2) }) }) test('.find + $gt + $lt + $sort', async () => { const params = { query: { age: { $gt: 18, $lt: 30 }, $sort: { name: 1 } } } const data = await service.find(params) assert.strictEqual(data.length, 2) assert.strictEqual(data[0].name, 'Alice') assert.strictEqual(data[1].name, 'Bob') }) test('.find + $or nested + $sort', async () => { const params = { query: { $or: [ { name: 'Doug' }, { age: { $gte: 18, $lt: 25 } } ], $sort: { name: 1 } } } const data = await service.find(params) assert.strictEqual(data.length, 2) assert.strictEqual(data[0].name, 'Alice') assert.strictEqual(data[1].name, 'Doug') }) test('.find + $and', async () => { const params = { query: { $and: [{ age: 19 }], $sort: { name: 1 } } } const data = await service.find(params) assert.strictEqual(data.length, 1) assert.strictEqual(data[0].name, 'Alice') }) test('.find + $and + $or', async () => { const params = { query: { $and: [{ $or: [{ name: 'Alice' }] }], $sort: { name: 1 } } } const data = await service.find(params) assert.strictEqual(data.length, 1) assert.strictEqual(data[0].name, 'Alice') }) describe('params.adapter', () => { test('params.adapter + paginate', async () => { const page = await service.find({ adapter: { paginate: { default: 3 } } }) assert.strictEqual(page.limit, 3) assert.strictEqual(page.skip, 0) }) test('params.adapter + multi', async () => { const items = [ { name: 'Garald', age: 200 }, { name: 'Harald', age: 24 } ] const multiParams = { adapter: { multi: ['create'] } } const users = await service.create(items, multiParams) assert.strictEqual(users.length, 2) await service.remove(users[0][idProp]) await service.remove(users[1][idProp]) await assert.rejects(() => service.patch(null, { age: 2 }, multiParams), { message: 'Can not patch multiple entries' }) }) }) describe('paginate', function () { beforeEach(() => { service.options.paginate = { default: 1, max: 2 } }) afterEach(() => { service.options.paginate = {} }) test('.find + paginate', async () => { const page = await service.find({ query: { $sort: { name: -1 } } }) assert.strictEqual(page.total, 3) assert.strictEqual(page.limit, 1) assert.strictEqual(page.skip, 0) assert.strictEqual(page.data[0].name, 'Doug') }) test('.find + paginate + query', async () => { const page = await service.find({ query: { $sort: { name: -1 }, name: 'Doug' } }) assert.strictEqual(page.total, 1) assert.strictEqual(page.limit, 1) assert.strictEqual(page.skip, 0) assert.strictEqual(page.data[0].name, 'Doug') }) test('.find + paginate + $limit + $skip', async () => { const params = { query: { $skip: 1, $limit: 4, $sort: { name: -1 } } } const page = await service.find(params) assert.strictEqual(page.total, 3) assert.strictEqual(page.limit, 2) assert.strictEqual(page.skip, 1) assert.strictEqual(page.data[0].name, 'Bob') assert.strictEqual(page.data[1].name, 'Alice') }) test('.find + paginate + $limit 0', async () => { const page = await service.find({ query: { $limit: 0 } }) assert.strictEqual(page.total, 3) assert.strictEqual(page.data.length, 0) }) test('.find + paginate + params', async () => { const page = await service.find({ paginate: { default: 3 } }) assert.strictEqual(page.limit, 3) assert.strictEqual(page.skip, 0) const results = await service.find({ paginate: false }) assert.ok(Array.isArray(results)) assert.strictEqual(results.length, 3) }) }) }) } ================================================ FILE: packages/adapter-tests/test/index.test.ts ================================================ import { strict as assert } from 'assert' import adapterTests from '../src' const testSuite = adapterTests([ '.events', '._get', '._find', '._create', '._update', '._patch', '._remove', '.$get', '.$find', '.$create', '.$update', '.$patch', '.$remove', '.get', '.get + $select', '.get + id + query', '.get + NotFound', '.find', '.remove', '.remove + $select', '.remove + id + query', '.remove + multi', '.remove + multi no pagination', '.update', '.update + $select', '.update + id + query', '.update + NotFound', '.patch', '.patch + $select', '.patch + id + query', '.patch multiple', '.patch multiple no pagination', '.patch multi query changed', '.patch multi query same', '.patch + NotFound', '.create', '.create + $select', '.create multi', 'internal .find', 'internal .get', 'internal .create', 'internal .update', 'internal .patch', 'internal .remove', '.find + equal', '.find + equal multiple', '.find + $sort', '.find + $sort + string', '.find + $limit', '.find + $limit 0', '.find + $skip', '.find + $select', '.find + $or', '.find + $in', '.find + $nin', '.find + $lt', '.find + $lte', '.find + $gt', '.find + $gte', '.find + $ne', '.find + $gt + $lt + $sort', '.find + $or nested + $sort', '.find + paginate', '.find + paginate + $limit + $skip', '.find + paginate + $limit 0', '.find + paginate + params', '.get + id + query id', '.remove + id + query id', '.update + id + query id', '.patch + id + query id' ]) describe('Feathers Memory Service', () => { it('loads the test suite', () => { assert.ok(typeof testSuite === 'function') }) it('exports as CommonJS', () => { assert.equal(typeof require('../lib'), 'function') }) }) ================================================ FILE: packages/adapter-tests/tsconfig.json ================================================ { "extends": "../../tsconfig", "include": [ "src/**/*.ts" ], "compilerOptions": { "outDir": "lib" } } ================================================ FILE: packages/authentication/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [5.0.42](https://github.com/feathersjs/feathers/compare/v5.0.41...v5.0.42) (2026-03-04) ### Bug Fixes - Update dependencies ([#3666](https://github.com/feathersjs/feathers/issues/3666)) ([477bf45](https://github.com/feathersjs/feathers/commit/477bf45f9c9dbde77a14a07828aa02300de23ae7)) ## [5.0.41](https://github.com/feathersjs/feathers/compare/v5.0.40...v5.0.41) (2026-02-19) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.40](https://github.com/feathersjs/feathers/compare/v5.0.39...v5.0.40) (2026-02-03) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.39](https://github.com/feathersjs/feathers/compare/v5.0.38...v5.0.39) (2026-01-31) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.38](https://github.com/feathersjs/feathers/compare/v5.0.37...v5.0.38) (2026-01-31) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.37](https://github.com/feathersjs/feathers/compare/v5.0.36...v5.0.37) (2025-11-10) ### Bug Fixes - Revert to compatible UUID package ([#3630](https://github.com/feathersjs/feathers/issues/3630)) ([5c8c9e3](https://github.com/feathersjs/feathers/commit/5c8c9e36efbbf695eccd6d8822e36e1ea75c1516)) ## [5.0.36](https://github.com/feathersjs/feathers/compare/v5.0.35...v5.0.36) (2025-11-08) ### Bug Fixes - **dependencies:** Update all dependencies ([#3625](https://github.com/feathersjs/feathers/issues/3625)) ([2698e4e](https://github.com/feathersjs/feathers/commit/2698e4e2996fbf479d82435938d907bc3d5b583a)) ## [5.0.35](https://github.com/feathersjs/feathers/compare/v5.0.34...v5.0.35) (2025-09-09) ### Bug Fixes - Update all dependencies ([#3613](https://github.com/feathersjs/feathers/issues/3613)) ([5136bbd](https://github.com/feathersjs/feathers/commit/5136bbd2e2eeb4e6579e07c9e914006629542363)) ## [5.0.34](https://github.com/feathersjs/feathers/compare/v5.0.33...v5.0.34) (2025-05-03) ### Bug Fixes - Update dependencies ([#3584](https://github.com/feathersjs/feathers/issues/3584)) ([119fa4e](https://github.com/feathersjs/feathers/commit/119fa4e1ade8b0078aa235083d566e2538b3a084)) ## [5.0.33](https://github.com/feathersjs/feathers/compare/v5.0.32...v5.0.33) (2025-02-24) ### Bug Fixes - **dependencies:** Update dependencies ([#3571](https://github.com/feathersjs/feathers/issues/3571)) ([ad611cb](https://github.com/feathersjs/feathers/commit/ad611cb6ffb1dc31d603ba5817331318c5a23217)) ## [5.0.32](https://github.com/feathersjs/feathers/compare/v5.0.31...v5.0.32) (2025-02-01) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.31](https://github.com/feathersjs/feathers/compare/v5.0.30...v5.0.31) (2024-10-31) ### Bug Fixes - **dependencies:** Update all dependencies ([#3545](https://github.com/feathersjs/feathers/issues/3545)) ([221b92b](https://github.com/feathersjs/feathers/commit/221b92bb0ee5d54fb1036742968797cb02e56da2)) ## [5.0.30](https://github.com/feathersjs/feathers/compare/v5.0.29...v5.0.30) (2024-09-02) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.29](https://github.com/feathersjs/feathers/compare/v5.0.28...v5.0.29) (2024-07-10) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.28](https://github.com/feathersjs/feathers/compare/v5.0.27...v5.0.28) (2024-07-10) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.27](https://github.com/feathersjs/feathers/compare/v5.0.26...v5.0.27) (2024-06-18) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.26](https://github.com/feathersjs/feathers/compare/v5.0.25...v5.0.26) (2024-06-09) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.25](https://github.com/feathersjs/feathers/compare/v5.0.24...v5.0.25) (2024-05-03) ### Bug Fixes - Reduce usage of lodash ([#3455](https://github.com/feathersjs/feathers/issues/3455)) ([8ce807a](https://github.com/feathersjs/feathers/commit/8ce807a5ca53ff5b8d5107a0656c6329404e6e6c)) ## [5.0.24](https://github.com/feathersjs/feathers/compare/v5.0.23...v5.0.24) (2024-03-13) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.23](https://github.com/feathersjs/feathers/compare/v5.0.22...v5.0.23) (2024-02-25) ### Bug Fixes - **core:** Update to latest feathersjs/hooks ([#3434](https://github.com/feathersjs/feathers/issues/3434)) ([1499ccc](https://github.com/feathersjs/feathers/commit/1499ccc41fb3ebba97b2c84e0cb19bc48ad3c651)) ## [5.0.22](https://github.com/feathersjs/feathers/compare/v5.0.21...v5.0.22) (2024-02-15) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.21](https://github.com/feathersjs/feathers/compare/v5.0.20...v5.0.21) (2024-01-25) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.20](https://github.com/feathersjs/feathers/compare/v5.0.19...v5.0.20) (2024-01-24) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.19](https://github.com/feathersjs/feathers/compare/v5.0.18...v5.0.19) (2024-01-23) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.18](https://github.com/feathersjs/feathers/compare/v5.0.17...v5.0.18) (2024-01-22) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.17](https://github.com/feathersjs/feathers/compare/v5.0.16...v5.0.17) (2024-01-22) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.16](https://github.com/feathersjs/feathers/compare/v5.0.15...v5.0.16) (2024-01-22) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.15](https://github.com/feathersjs/feathers/compare/v5.0.14...v5.0.15) (2024-01-22) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.14](https://github.com/feathersjs/feathers/compare/v5.0.13...v5.0.14) (2024-01-05) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.13](https://github.com/feathersjs/feathers/compare/v5.0.12...v5.0.13) (2023-12-29) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.12](https://github.com/feathersjs/feathers/compare/v5.0.11...v5.0.12) (2023-11-28) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.11](https://github.com/feathersjs/feathers/compare/v5.0.10...v5.0.11) (2023-10-11) ### Bug Fixes - **knex:** Update all dependencies and Knex peer ([#3308](https://github.com/feathersjs/feathers/issues/3308)) ([d2f9860](https://github.com/feathersjs/feathers/commit/d2f986036c4741cce2339d8abbcc6b2eb037a12a)) ## [5.0.10](https://github.com/feathersjs/feathers/compare/v5.0.9...v5.0.10) (2023-10-03) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.9](https://github.com/feathersjs/feathers/compare/v5.0.8...v5.0.9) (2023-09-27) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.8](https://github.com/feathersjs/feathers/compare/v5.0.7...v5.0.8) (2023-07-19) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.7](https://github.com/feathersjs/feathers/compare/v5.0.6...v5.0.7) (2023-07-14) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.6](https://github.com/feathersjs/feathers/compare/v5.0.5...v5.0.6) (2023-06-15) ### Bug Fixes - **authentication:** Export JwtVerifyOptions ([#3214](https://github.com/feathersjs/feathers/issues/3214)) ([d59896e](https://github.com/feathersjs/feathers/commit/d59896eb0229f1490c712f19cf84eb2bcf123698)) ## [5.0.5](https://github.com/feathersjs/feathers/compare/v5.0.4...v5.0.5) (2023-04-28) **Note:** Version bump only for package @feathersjs/authentication ## [5.0.4](https://github.com/feathersjs/feathers/compare/v5.0.3...v5.0.4) (2023-04-12) ### Bug Fixes - Make sure all Readme files are up to date ([#3154](https://github.com/feathersjs/feathers/issues/3154)) ([a5f0b38](https://github.com/feathersjs/feathers/commit/a5f0b38bbf2a11486415a39533bcc6c67fb51e3e)) ## [5.0.3](https://github.com/feathersjs/feathers/compare/v5.0.2...v5.0.3) (2023-04-05) ### Bug Fixes - **dependencies:** Update all dependencies ([#3139](https://github.com/feathersjs/feathers/issues/3139)) ([f24276e](https://github.com/feathersjs/feathers/commit/f24276e9a909e2e58a0730c730258ce1f70f4028)) ## [5.0.1](https://github.com/feathersjs/feathers/compare/v5.0.0...v5.0.1) (2023-03-15) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.38...v5.0.0) (2023-02-24) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.38](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.37...v5.0.0-pre.38) (2023-02-17) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.37](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.36...v5.0.0-pre.37) (2023-02-09) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.36](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.35...v5.0.0-pre.36) (2023-01-29) ### Bug Fixes - Update all dependencies ([#3024](https://github.com/feathersjs/feathers/issues/3024)) ([283dc47](https://github.com/feathersjs/feathers/commit/283dc4798d85584bc031e6e54b83b4ea77d1edd0)) # [5.0.0-pre.35](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.34...v5.0.0-pre.35) (2023-01-12) ### Features - **generators:** Move core code generators to shared generators package ([#2982](https://github.com/feathersjs/feathers/issues/2982)) ([0328d22](https://github.com/feathersjs/feathers/commit/0328d2292153870bc43958f73d2c6f288a8cec17)) # [5.0.0-pre.34](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.33...v5.0.0-pre.34) (2022-12-14) ### Bug Fixes - **authentication:** Fix order of connection and login event handling ([#2909](https://github.com/feathersjs/feathers/issues/2909)) ([801a503](https://github.com/feathersjs/feathers/commit/801a503425062e27f2a32b91493b6ffae3822626)) - **core:** `context.type` for around hooks ([#2890](https://github.com/feathersjs/feathers/issues/2890)) ([d606ac6](https://github.com/feathersjs/feathers/commit/d606ac660fd5335c95206784fea36530dd2e851a)) ### Features - **schema:** Virtual property resolvers ([#2900](https://github.com/feathersjs/feathers/issues/2900)) ([7d03b57](https://github.com/feathersjs/feathers/commit/7d03b57ae2f633bdd4a368e0d5955011fbd6c329)) # [5.0.0-pre.33](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.32...v5.0.0-pre.33) (2022-11-08) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.32](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.31...v5.0.0-pre.32) (2022-10-26) ### Bug Fixes - **authentication:** Improve logout and disconnect connection handling ([#2813](https://github.com/feathersjs/feathers/issues/2813)) ([dd77379](https://github.com/feathersjs/feathers/commit/dd77379d8bdcd32d529bef912e672639e4899823)) # [5.0.0-pre.31](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.30...v5.0.0-pre.31) (2022-10-12) ### Features - **cli:** Generate full client test suite and improve typed client ([#2788](https://github.com/feathersjs/feathers/issues/2788)) ([57119b6](https://github.com/feathersjs/feathers/commit/57119b6bb2797f7297cf054268a248c093ecd538)) # [5.0.0-pre.30](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.29...v5.0.0-pre.30) (2022-10-07) ### Features - **core:** Allow to unregister services at runtime ([#2756](https://github.com/feathersjs/feathers/issues/2756)) ([d16601f](https://github.com/feathersjs/feathers/commit/d16601f2277dca5357866ffdefba2a611f6dc7fa)) - **schema:** Make schemas validation library independent and add TypeBox support ([#2772](https://github.com/feathersjs/feathers/issues/2772)) ([44172d9](https://github.com/feathersjs/feathers/commit/44172d99b566d11d9ceda04f1d0bf72b6d05ce76)) # [5.0.0-pre.29](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.28...v5.0.0-pre.29) (2022-09-16) ### Features - **authentication-oauth:** Koa and transport independent oAuth authentication ([#2737](https://github.com/feathersjs/feathers/issues/2737)) ([9231525](https://github.com/feathersjs/feathers/commit/9231525a24bb790ba9c5d940f2867a9c727691c9)) # [5.0.0-pre.28](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.27...v5.0.0-pre.28) (2022-08-03) ### Bug Fixes - **cli:** Improve generated application and client ([#2701](https://github.com/feathersjs/feathers/issues/2701)) ([bd55ffb](https://github.com/feathersjs/feathers/commit/bd55ffb812e89bf215f4515e7f137656ea888c3f)) # [5.0.0-pre.27](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.26...v5.0.0-pre.27) (2022-07-13) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.26](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.25...v5.0.0-pre.26) (2022-06-22) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.25](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.24...v5.0.0-pre.25) (2022-06-22) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.24](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.23...v5.0.0-pre.24) (2022-06-21) ### Bug Fixes - **authentication:** Add safe dispatch data for authentication requests ([#2662](https://github.com/feathersjs/feathers/issues/2662)) ([d8104a1](https://github.com/feathersjs/feathers/commit/d8104a19ee9181e6a5ea81014af29ff9a3c28a8a)) # [5.0.0-pre.23](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.22...v5.0.0-pre.23) (2022-06-06) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.22](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.21...v5.0.0-pre.22) (2022-05-24) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.21](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.20...v5.0.0-pre.21) (2022-05-23) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.20](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.19...v5.0.0-pre.20) (2022-05-04) ### Bug Fixes - **dependencies:** Lock monorepo package version numbers ([#2623](https://github.com/feathersjs/feathers/issues/2623)) ([5640c10](https://github.com/feathersjs/feathers/commit/5640c1020cc139994e695d658c08bad3494db507)) # [5.0.0-pre.19](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.18...v5.0.0-pre.19) (2022-05-01) ### Features - **typescript:** Improve adapter typings ([#2605](https://github.com/feathersjs/feathers/issues/2605)) ([3b2ca0a](https://github.com/feathersjs/feathers/commit/3b2ca0a6a8e03e8390272c4d7e930b4bffdaacf5)) - **typescript:** Improve params and query typeability ([#2600](https://github.com/feathersjs/feathers/issues/2600)) ([df28b76](https://github.com/feathersjs/feathers/commit/df28b7619161f1df5e700326f52cca1a92dc5d28)) # [5.0.0-pre.18](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.17...v5.0.0-pre.18) (2022-04-11) ### Features - **authentication:** Add setup method for auth strategies ([#1611](https://github.com/feathersjs/feathers/issues/1611)) ([a3c3581](https://github.com/feathersjs/feathers/commit/a3c35814dccdbbf6de96f04f60b226ce206c6dbe)) - **configuration:** Allow app configuration to be validated against a schema ([#2590](https://github.com/feathersjs/feathers/issues/2590)) ([a268f86](https://github.com/feathersjs/feathers/commit/a268f86da92a8ada14ed11ab456aac0a4bba5bb0)) # [5.0.0-pre.17](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.16...v5.0.0-pre.17) (2022-02-15) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.16](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.15...v5.0.0-pre.16) (2022-01-12) ### Features - **express, koa:** make transports similar ([#2486](https://github.com/feathersjs/feathers/issues/2486)) ([26aa937](https://github.com/feathersjs/feathers/commit/26aa937c114fb8596dfefc599b1f53cead69c159)) # [5.0.0-pre.15](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.14...v5.0.0-pre.15) (2021-11-27) ### Bug Fixes - **typescript:** Overall typing improvements ([#2478](https://github.com/feathersjs/feathers/issues/2478)) ([b8eb804](https://github.com/feathersjs/feathers/commit/b8eb804158556d9651a8607e3c3fda15e0bfd110)) ### Features - **authentication-oauth:** Allow dynamic oAuth redirect ([#2469](https://github.com/feathersjs/feathers/issues/2469)) ([b7143d4](https://github.com/feathersjs/feathers/commit/b7143d4c0fbe961e714f79512be04449b9bbd7d9)) # [5.0.0-pre.14](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.13...v5.0.0-pre.14) (2021-10-13) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.13](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.12...v5.0.0-pre.13) (2021-10-13) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.12](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.11...v5.0.0-pre.12) (2021-10-12) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.11](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.10...v5.0.0-pre.11) (2021-10-06) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.10](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.9...v5.0.0-pre.10) (2021-09-19) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.9](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.8...v5.0.0-pre.9) (2021-08-09) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.8](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.7...v5.0.0-pre.8) (2021-08-09) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.7](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.6...v5.0.0-pre.7) (2021-08-09) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.6](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.5...v5.0.0-pre.6) (2021-08-08) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.5](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.4...v5.0.0-pre.5) (2021-06-23) ### Bug Fixes - **hooks:** Migrate built-in hooks and allow backwards compatibility ([#2358](https://github.com/feathersjs/feathers/issues/2358)) ([759c5a1](https://github.com/feathersjs/feathers/commit/759c5a19327a731af965c3604119393b3d09a406)) - **koa:** Use extended query parser for compatibility ([#2397](https://github.com/feathersjs/feathers/issues/2397)) ([b2944ba](https://github.com/feathersjs/feathers/commit/b2944bac3ec6d5ecc80dc518cd4e58093692db74)) ### Features - **adapter-commons:** Add support for params.adapter option and move memory adapter to @feathersjs/memory ([#2367](https://github.com/feathersjs/feathers/issues/2367)) ([a43e7da](https://github.com/feathersjs/feathers/commit/a43e7da22b6b981a96d1321736ea9a0cb924fb4f)) # [5.0.0-pre.4](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.3...v5.0.0-pre.4) (2021-05-13) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.3](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.2...v5.0.0-pre.3) (2021-04-21) ### Bug Fixes - **typescript:** Improve TypeScript backwards compatibility ([#2310](https://github.com/feathersjs/feathers/issues/2310)) ([f33be73](https://github.com/feathersjs/feathers/commit/f33be73fc46a533efb15df9aab0658e3240d3897)) ### Features - **dependencies:** Remove direct debug dependency ([#2296](https://github.com/feathersjs/feathers/issues/2296)) ([501d416](https://github.com/feathersjs/feathers/commit/501d4164d30c6a126906dc640cdfdc82207ba34a)) # [5.0.0-pre.2](https://github.com/feathersjs/feathers/compare/v5.0.0-beta.1...v5.0.0-pre.2) (2021-04-06) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-beta.1](https://github.com/feathersjs/feathers/compare/v5.0.0-beta.0...v5.0.0-beta.1) (2021-04-03) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-beta.0](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.1...v5.0.0-beta.0) (2021-03-28) ### Bug Fixes - Update Grant usage and other dependencies ([#2264](https://github.com/feathersjs/feathers/issues/2264)) ([7b0f8fa](https://github.com/feathersjs/feathers/commit/7b0f8fad252419ed0ad0bf259cdf3104d322ab60)) ### Features - Application service types default to any ([#1566](https://github.com/feathersjs/feathers/issues/1566)) ([d93ba9a](https://github.com/feathersjs/feathers/commit/d93ba9a17edd20d3397bb00f4f6e82e804e42ed6)) - Feathers v5 core refactoring and features ([#2255](https://github.com/feathersjs/feathers/issues/2255)) ([2dafb7c](https://github.com/feathersjs/feathers/commit/2dafb7ce14ba57406aeec13d10ca45b1e709bee9)) # [5.0.0-pre.1](https://github.com/feathersjs/feathers/compare/v4.5.11...v5.0.0-pre.1) (2020-12-17) # [5.0.0-pre.0](https://github.com/feathersjs/feathers/compare/v4.5.4...v5.0.0-pre.0) (2020-05-19) **Note:** Version bump only for package @feathersjs/authentication # [5.0.0-pre.0](https://github.com/feathersjs/feathers/compare/v4.5.4...v5.0.0-pre.0) (2020-05-19) ## [4.5.11](https://github.com/feathersjs/feathers/compare/v4.5.10...v4.5.11) (2020-12-05) **Note:** Version bump only for package @feathersjs/authentication ## [4.5.10](https://github.com/feathersjs/feathers/compare/v4.5.9...v4.5.10) (2020-11-08) ### Bug Fixes - **authentication:** consistent response return between local and jwt strategy ([#2042](https://github.com/feathersjs/feathers/issues/2042)) ([8d25be1](https://github.com/feathersjs/feathers/commit/8d25be101a2593a9e789375c928a07780b9e28cf)) ## [4.5.9](https://github.com/feathersjs/feathers/compare/v4.5.8...v4.5.9) (2020-10-09) **Note:** Version bump only for package @feathersjs/authentication ## [4.5.8](https://github.com/feathersjs/feathers/compare/v4.5.7...v4.5.8) (2020-08-12) **Note:** Version bump only for package @feathersjs/authentication ## [4.5.7](https://github.com/feathersjs/feathers/compare/v4.5.6...v4.5.7) (2020-07-24) ### Bug Fixes - **authentication:** Add JWT getEntityQuery ([#2013](https://github.com/feathersjs/feathers/issues/2013)) ([e0e7fb5](https://github.com/feathersjs/feathers/commit/e0e7fb5162940fe776731283b40026c61d9c8a33)) ## [4.5.6](https://github.com/feathersjs/feathers/compare/v4.5.5...v4.5.6) (2020-07-12) ### Bug Fixes - **authentication:** Omit query in JWT strategy ([#2011](https://github.com/feathersjs/feathers/issues/2011)) ([04ce7e9](https://github.com/feathersjs/feathers/commit/04ce7e98515fe9d495cd0e83e0da097e9bcd7382)) ## [4.5.5](https://github.com/feathersjs/feathers/compare/v4.5.4...v4.5.5) (2020-07-11) ### Bug Fixes - **authentication:** Include query params when authenticating via authenticate hook [#2009](https://github.com/feathersjs/feathers/issues/2009) ([4cdb7bf](https://github.com/feathersjs/feathers/commit/4cdb7bf2898385ddac7a1692bc9ac2f6cf5ad446)) ## [4.5.3](https://github.com/feathersjs/feathers/compare/v4.5.2...v4.5.3) (2020-04-17) ### Bug Fixes - **authentication:** Remove entity from connection information on logout ([#1889](https://github.com/feathersjs/feathers/issues/1889)) ([b062753](https://github.com/feathersjs/feathers/commit/b0627530d61babe15dd84369d3093ccae4b780ca)) ## [4.5.2](https://github.com/feathersjs/feathers/compare/v4.5.1...v4.5.2) (2020-03-04) ### Bug Fixes - **authentication:** Improve JWT strategy configuration error message ([#1844](https://github.com/feathersjs/feathers/issues/1844)) ([2c771db](https://github.com/feathersjs/feathers/commit/2c771dbb22d53d4f7de3c3f514e57afa1a186322)) ## [4.5.1](https://github.com/feathersjs/feathers/compare/v4.5.0...v4.5.1) (2020-01-24) **Note:** Version bump only for package @feathersjs/authentication # [4.5.0](https://github.com/feathersjs/feathers/compare/v4.4.3...v4.5.0) (2020-01-18) ### Bug Fixes - Add `params.authentication` type, remove `hook.connection` type ([#1732](https://github.com/feathersjs/feathers/issues/1732)) ([d46b7b2](https://github.com/feathersjs/feathers/commit/d46b7b2abac8862c0e4dbfce20d71b8b8a96692f)) ## [4.4.3](https://github.com/feathersjs/feathers/compare/v4.4.1...v4.4.3) (2019-12-06) **Note:** Version bump only for package @feathersjs/authentication ## [4.4.1](https://github.com/feathersjs/feathers/compare/v4.4.0...v4.4.1) (2019-11-27) **Note:** Version bump only for package @feathersjs/authentication # [4.4.0](https://github.com/feathersjs/feathers/compare/v4.3.11...v4.4.0) (2019-11-27) **Note:** Version bump only for package @feathersjs/authentication ## [4.3.11](https://github.com/feathersjs/feathers/compare/v4.3.10...v4.3.11) (2019-11-11) ### Bug Fixes - **authentication:** Retain object references in authenticate hook ([#1675](https://github.com/feathersjs/feathers/issues/1675)) ([e1939be](https://github.com/feathersjs/feathers/commit/e1939be19d4e79d3f5e2fe69ba894a11c627ae99)) ## [4.3.10](https://github.com/feathersjs/feathers/compare/v4.3.9...v4.3.10) (2019-10-26) **Note:** Version bump only for package @feathersjs/authentication ## [4.3.9](https://github.com/feathersjs/feathers/compare/v4.3.8...v4.3.9) (2019-10-26) ### Bug Fixes - Add jsonwebtoken TypeScript type dependency ([317c80a](https://github.com/feathersjs/feathers/commit/317c80a9205e8853bb830a12c3aa1a19e95f9abc)) - Small type improvements ([#1624](https://github.com/feathersjs/feathers/issues/1624)) ([50162c6](https://github.com/feathersjs/feathers/commit/50162c6e562f0a47c6a280c4f01fff7c3afee293)) ## [4.3.7](https://github.com/feathersjs/feathers/compare/v4.3.6...v4.3.7) (2019-10-14) **Note:** Version bump only for package @feathersjs/authentication ## [4.3.5](https://github.com/feathersjs/feathers/compare/v4.3.4...v4.3.5) (2019-10-07) ### Bug Fixes - Authentication type improvements and timeout fix ([#1605](https://github.com/feathersjs/feathers/issues/1605)) ([19854d3](https://github.com/feathersjs/feathers/commit/19854d3)) - Improve error message when authentication strategy is not allowed ([#1600](https://github.com/feathersjs/feathers/issues/1600)) ([317a312](https://github.com/feathersjs/feathers/commit/317a312)) ## [4.3.4](https://github.com/feathersjs/feathers/compare/v4.3.3...v4.3.4) (2019-10-03) **Note:** Version bump only for package @feathersjs/authentication ## [4.3.3](https://github.com/feathersjs/feathers/compare/v4.3.2...v4.3.3) (2019-09-21) ### Bug Fixes - check for undefined access token ([#1571](https://github.com/feathersjs/feathers/issues/1571)) ([976369d](https://github.com/feathersjs/feathers/commit/976369d)) - Small improvements in dependencies and code sturcture ([#1562](https://github.com/feathersjs/feathers/issues/1562)) ([42c13e2](https://github.com/feathersjs/feathers/commit/42c13e2)) ## [4.3.2](https://github.com/feathersjs/feathers/compare/v4.3.1...v4.3.2) (2019-09-16) **Note:** Version bump only for package @feathersjs/authentication ## [4.3.1](https://github.com/feathersjs/feathers/compare/v4.3.0...v4.3.1) (2019-09-09) ### Bug Fixes - Use long-timeout for JWT expiration timers ([#1552](https://github.com/feathersjs/feathers/issues/1552)) ([65637ec](https://github.com/feathersjs/feathers/commit/65637ec)) # [4.3.0](https://github.com/feathersjs/feathers/compare/v4.3.0-pre.4...v4.3.0) (2019-08-27) **Note:** Version bump only for package @feathersjs/authentication # [4.3.0-pre.4](https://github.com/feathersjs/feathers/compare/v4.3.0-pre.3...v4.3.0-pre.4) (2019-08-22) ### Bug Fixes - Fix auth publisher mistake ([08bad61](https://github.com/feathersjs/feathers/commit/08bad61)) # [4.3.0-pre.3](https://github.com/feathersjs/feathers/compare/v4.3.0-pre.2...v4.3.0-pre.3) (2019-08-19) ### Bug Fixes - Expire and remove authenticated real-time connections ([#1512](https://github.com/feathersjs/feathers/issues/1512)) ([2707c33](https://github.com/feathersjs/feathers/commit/2707c33)) - Update all dependencies ([7d53a00](https://github.com/feathersjs/feathers/commit/7d53a00)) ### Features - Let strategies handle the connection ([#1510](https://github.com/feathersjs/feathers/issues/1510)) ([4329feb](https://github.com/feathersjs/feathers/commit/4329feb)) # [4.3.0-pre.2](https://github.com/feathersjs/feathers/compare/v4.3.0-pre.1...v4.3.0-pre.2) (2019-08-02) ### Bug Fixes - Add getEntityId to JWT strategy and fix legacy Socket authentication ([#1488](https://github.com/feathersjs/feathers/issues/1488)) ([9a3b324](https://github.com/feathersjs/feathers/commit/9a3b324)) - Add method to reliably get default authentication service ([#1470](https://github.com/feathersjs/feathers/issues/1470)) ([e542cb3](https://github.com/feathersjs/feathers/commit/e542cb3)) # [4.3.0-pre.1](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.5...v4.3.0-pre.1) (2019-07-11) **Note:** Version bump only for package @feathersjs/authentication # [4.0.0-pre.5](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.4...v4.0.0-pre.5) (2019-07-10) **Note:** Version bump only for package @feathersjs/authentication # [4.0.0-pre.4](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.3...v4.0.0-pre.4) (2019-07-05) ### Bug Fixes - Updated typings for ServiceMethods ([#1409](https://github.com/feathersjs/feathers/issues/1409)) ([b5ee7e2](https://github.com/feathersjs/feathers/commit/b5ee7e2)) # [4.0.0-pre.3](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.2...v4.0.0-pre.3) (2019-06-01) ### Bug Fixes - Make oAuth paths more consistent and improve authentication client ([#1377](https://github.com/feathersjs/feathers/issues/1377)) ([adb2543](https://github.com/feathersjs/feathers/commit/adb2543)) - Set authenticated: true after successful authentication ([#1367](https://github.com/feathersjs/feathers/issues/1367)) ([9918cff](https://github.com/feathersjs/feathers/commit/9918cff)) - Typings fix and improvements. ([#1364](https://github.com/feathersjs/feathers/issues/1364)) ([515b916](https://github.com/feathersjs/feathers/commit/515b916)) - Update dependencies and fix tests ([#1373](https://github.com/feathersjs/feathers/issues/1373)) ([d743a7f](https://github.com/feathersjs/feathers/commit/d743a7f)) # [4.0.0-pre.2](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.1...v4.0.0-pre.2) (2019-05-15) ### Bug Fixes - Throw NotAuthenticated on token verification errors ([#1357](https://github.com/feathersjs/feathers/issues/1357)) ([e0120df](https://github.com/feathersjs/feathers/commit/e0120df)) # [4.0.0-pre.1](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.0...v4.0.0-pre.1) (2019-05-08) ### Bug Fixes - Always require strategy parameter in authentication ([#1327](https://github.com/feathersjs/feathers/issues/1327)) ([d4a8021](https://github.com/feathersjs/feathers/commit/d4a8021)) - Bring back params.authenticated ([#1317](https://github.com/feathersjs/feathers/issues/1317)) ([a0ffd5e](https://github.com/feathersjs/feathers/commit/a0ffd5e)) - Improve authentication parameter handling ([#1333](https://github.com/feathersjs/feathers/issues/1333)) ([6e77204](https://github.com/feathersjs/feathers/commit/6e77204)) - Merge httpStrategies and authStrategies option ([#1308](https://github.com/feathersjs/feathers/issues/1308)) ([afa4d55](https://github.com/feathersjs/feathers/commit/afa4d55)) - Rename jwtStrategies option to authStrategies ([#1305](https://github.com/feathersjs/feathers/issues/1305)) ([4aee151](https://github.com/feathersjs/feathers/commit/4aee151)) ### Features - Change and *JWT methods to *accessToken ([#1304](https://github.com/feathersjs/feathers/issues/1304)) ([5ac826b](https://github.com/feathersjs/feathers/commit/5ac826b)) # [4.0.0-pre.0](https://github.com/feathersjs/feathers/compare/v3.2.0-pre.1...v4.0.0-pre.0) (2019-04-21) ### Bug Fixes - Added path and method in to express request for passport ([#1112](https://github.com/feathersjs/feathers/issues/1112)) ([afa1cb4](https://github.com/feathersjs/feathers/commit/afa1cb4)) - Authentication core improvements ([#1260](https://github.com/feathersjs/feathers/issues/1260)) ([c5dc7a2](https://github.com/feathersjs/feathers/commit/c5dc7a2)) - Improve JWT authentication option handling ([#1261](https://github.com/feathersjs/feathers/issues/1261)) ([31b956b](https://github.com/feathersjs/feathers/commit/31b956b)) - Make Mocha a proper devDependency for every repository ([#1053](https://github.com/feathersjs/feathers/issues/1053)) ([9974803](https://github.com/feathersjs/feathers/commit/9974803)) - Only merge authenticated property on update ([8a564f7](https://github.com/feathersjs/feathers/commit/8a564f7)) - reduce authentication connection hook complexity and remove unnecessary checks ([fa94b2f](https://github.com/feathersjs/feathers/commit/fa94b2f)) - Update all dependencies to latest ([#1206](https://github.com/feathersjs/feathers/issues/1206)) ([e51e0f6](https://github.com/feathersjs/feathers/commit/e51e0f6)) - **authentication:** Fall back when req.app is not the application when emitting events ([#1185](https://github.com/feathersjs/feathers/issues/1185)) ([6a534f0](https://github.com/feathersjs/feathers/commit/6a534f0)) - Update adapter common tests ([#1135](https://github.com/feathersjs/feathers/issues/1135)) ([8166dda](https://github.com/feathersjs/feathers/commit/8166dda)) - **docs/new-features:** syntax highlighting ([#347](https://github.com/feathersjs/feathers/issues/347)) ([4ab7c95](https://github.com/feathersjs/feathers/commit/4ab7c95)) - **package:** update @feathersjs/commons to version 2.0.0 ([#692](https://github.com/feathersjs/feathers/issues/692)) ([ca665ab](https://github.com/feathersjs/feathers/commit/ca665ab)) - **package:** update debug to version 3.0.0 ([#555](https://github.com/feathersjs/feathers/issues/555)) ([f788804](https://github.com/feathersjs/feathers/commit/f788804)) - **package:** update jsonwebtoken to version 8.0.0 ([#567](https://github.com/feathersjs/feathers/issues/567)) ([6811626](https://github.com/feathersjs/feathers/commit/6811626)) - **package:** update ms to version 2.0.0 ([#509](https://github.com/feathersjs/feathers/issues/509)) ([7e4b0b6](https://github.com/feathersjs/feathers/commit/7e4b0b6)) - **package:** update passport to version 0.4.0 ([#558](https://github.com/feathersjs/feathers/issues/558)) ([dcb14a5](https://github.com/feathersjs/feathers/commit/dcb14a5)) ### Features - @feathersjs/authentication-oauth ([#1299](https://github.com/feathersjs/feathers/issues/1299)) ([656bae7](https://github.com/feathersjs/feathers/commit/656bae7)) - Add AuthenticationBaseStrategy and make authentication option handling more explicit ([#1284](https://github.com/feathersjs/feathers/issues/1284)) ([2667d92](https://github.com/feathersjs/feathers/commit/2667d92)) - Add TypeScript definitions ([#1275](https://github.com/feathersjs/feathers/issues/1275)) ([9dd6713](https://github.com/feathersjs/feathers/commit/9dd6713)) - Authentication v3 core server implementation ([#1205](https://github.com/feathersjs/feathers/issues/1205)) ([1bd7591](https://github.com/feathersjs/feathers/commit/1bd7591)) - Authentication v3 local authentication ([#1211](https://github.com/feathersjs/feathers/issues/1211)) ([0fa5f7c](https://github.com/feathersjs/feathers/commit/0fa5f7c)) - Remove (hook, next) signature and SKIP support ([#1269](https://github.com/feathersjs/feathers/issues/1269)) ([211c0f8](https://github.com/feathersjs/feathers/commit/211c0f8)) - Support params symbol to skip authenticate hook ([#1296](https://github.com/feathersjs/feathers/issues/1296)) ([d16cf4d](https://github.com/feathersjs/feathers/commit/d16cf4d)) ### BREAKING CHANGES - Update authentication strategies for @feathersjs/authentication v3 ## [2.1.16](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication@2.1.15...@feathersjs/authentication@2.1.16) (2019-01-26) ### Bug Fixes - **authentication:** Fall back when req.app is not the application when emitting events ([#1185](https://github.com/feathersjs/feathers/issues/1185)) ([6a534f0](https://github.com/feathersjs/feathers/commit/6a534f0)) ## [2.1.15](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication@2.1.14...@feathersjs/authentication@2.1.15) (2019-01-02) ### Bug Fixes - Update adapter common tests ([#1135](https://github.com/feathersjs/feathers/issues/1135)) ([8166dda](https://github.com/feathersjs/feathers/commit/8166dda)) ## [2.1.14](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication@2.1.13...@feathersjs/authentication@2.1.14) (2018-12-16) ### Bug Fixes - Added path and method in to express request for passport ([#1112](https://github.com/feathersjs/feathers/issues/1112)) ([afa1cb4](https://github.com/feathersjs/feathers/commit/afa1cb4)) ## [2.1.13](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication@2.1.12...@feathersjs/authentication@2.1.13) (2018-10-26) **Note:** Version bump only for package @feathersjs/authentication ## [2.1.12](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication@2.1.11...@feathersjs/authentication@2.1.12) (2018-10-25) ### Bug Fixes - Make Mocha a proper devDependency for every repository ([#1053](https://github.com/feathersjs/feathers/issues/1053)) ([9974803](https://github.com/feathersjs/feathers/commit/9974803)) - Only merge authenticated property on update ([8a564f7](https://github.com/feathersjs/feathers/commit/8a564f7)) ## [2.1.11](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication@2.1.10...@feathersjs/authentication@2.1.11) (2018-09-21) **Note:** Version bump only for package @feathersjs/authentication ## [2.1.10](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication@2.1.9...@feathersjs/authentication@2.1.10) (2018-09-17) **Note:** Version bump only for package @feathersjs/authentication ## [2.1.9](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication@2.1.8...@feathersjs/authentication@2.1.9) (2018-09-02) **Note:** Version bump only for package @feathersjs/authentication ## 2.1.8 - Migrate to Monorepo ([feathers#462](https://github.com/feathersjs/feathers/issues/462)) ## [v2.1.7](https://github.com/feathersjs/authentication/tree/v2.1.7) (2018-06-29) [Full Changelog](https://github.com/feathersjs/authentication/compare/v2.1.6...v2.1.7) **Fixed bugs:** - XXXOrRestrict undermines provider \(security\) logic [\#395](https://github.com/feathersjs/authentication/issues/395) **Closed issues:** - Customize response of authentication service [\#679](https://github.com/feathersjs/authentication/issues/679) - hook.params.user is null using REST [\#678](https://github.com/feathersjs/authentication/issues/678) - Can't store JWT token to cookie on REST client [\#676](https://github.com/feathersjs/authentication/issues/676) - Is there a way to get req.user without using the authentication middleware? [\#675](https://github.com/feathersjs/authentication/issues/675) **Merged pull requests:** - Remove subject from the JWT verification options [\#686](https://github.com/feathersjs/authentication/pull/686) ([rasendubi](https://github.com/rasendubi)) - Replaced feathers.static with express.static [\#685](https://github.com/feathersjs/authentication/pull/685) ([georgehorrell](https://github.com/georgehorrell)) - Remove dependency on Express and Express middleware [\#683](https://github.com/feathersjs/authentication/pull/683) ([daffl](https://github.com/daffl)) - Update sinon to the latest version 🚀 [\#681](https://github.com/feathersjs/authentication/pull/681) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) ## [v2.1.6](https://github.com/feathersjs/authentication/tree/v2.1.6) (2018-06-01) [Full Changelog](https://github.com/feathersjs/authentication/compare/v2.1.5...v2.1.6) **Closed issues:** - Authentication local strategy not working with a Custom User service [\#672](https://github.com/feathersjs/authentication/issues/672) - CLI command bug: 'Feathers generate authentication' produces bad working 'users' service [\#670](https://github.com/feathersjs/authentication/issues/670) - config\default.json generated without callbackURL config needed to set redirect URL for Google Outh2 [\#669](https://github.com/feathersjs/authentication/issues/669) - HELP WANTED: Authentication strategy 'jwt' is not registered. [\#668](https://github.com/feathersjs/authentication/issues/668) - Authenticate shows error: No auth token [\#667](https://github.com/feathersjs/authentication/issues/667) - authentication - Method: remove [\#662](https://github.com/feathersjs/authentication/issues/662) - NotAuthenticated: jwt expired [\#633](https://github.com/feathersjs/authentication/issues/633) - Authentication via phone number [\#616](https://github.com/feathersjs/authentication/issues/616) - Persist auth tokens on db [\#569](https://github.com/feathersjs/authentication/issues/569) - Tighter integration with feathers-authentication-management [\#393](https://github.com/feathersjs/authentication/issues/393) **Merged pull requests:** - Fix tests to work with latest Sinon [\#674](https://github.com/feathersjs/authentication/pull/674) ([daffl](https://github.com/daffl)) - add option to allowUnauthenticated [\#599](https://github.com/feathersjs/authentication/pull/599) ([MichaelErmer](https://github.com/MichaelErmer)) ## [v2.1.5](https://github.com/feathersjs/authentication/tree/v2.1.5) (2018-04-16) [Full Changelog](https://github.com/feathersjs/authentication/compare/v2.1.4...v2.1.5) **Closed issues:** - feathersjs Invalid token: expired [\#661](https://github.com/feathersjs/authentication/issues/661) - Safari and iOS facebook login can't redirect back, but others can. [\#651](https://github.com/feathersjs/authentication/issues/651) **Merged pull requests:** - Remove payload and user entity on logout. [\#665](https://github.com/feathersjs/authentication/pull/665) ([bertho-zero](https://github.com/bertho-zero)) ## [v2.1.4](https://github.com/feathersjs/authentication/tree/v2.1.4) (2018-04-12) [Full Changelog](https://github.com/feathersjs/authentication/compare/v2.1.3...v2.1.4) **Closed issues:** - Column "createdAt" does not exist" in Autentication [\#660](https://github.com/feathersjs/authentication/issues/660) - How to make a user automatically logined on server side? [\#659](https://github.com/feathersjs/authentication/issues/659) - authentication-jwt functional example [\#657](https://github.com/feathersjs/authentication/issues/657) - "No auth token" with auth0 when following the guide [\#655](https://github.com/feathersjs/authentication/issues/655) - Service returns \[No Auth Token\] same by passing Authorization Token on HEADER [\#641](https://github.com/feathersjs/authentication/issues/641) **Merged pull requests:** - Throw an error for unavailable strategy [\#663](https://github.com/feathersjs/authentication/pull/663) ([daffl](https://github.com/daffl)) - Update sinon to the latest version 🚀 [\#656](https://github.com/feathersjs/authentication/pull/656) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) ## [v2.1.3](https://github.com/feathersjs/authentication/tree/v2.1.3) (2018-03-16) [Full Changelog](https://github.com/feathersjs/authentication/compare/v2.1.2...v2.1.3) **Closed issues:** - ts [\#647](https://github.com/feathersjs/authentication/issues/647) - Using /auth/facebook gives a 404 from vue-router [\#643](https://github.com/feathersjs/authentication/issues/643) - Crash after upgrade to feathersjs v3 [\#642](https://github.com/feathersjs/authentication/issues/642) - SameSite cookie option [\#640](https://github.com/feathersjs/authentication/issues/640) - context.params.user is empty object [\#635](https://github.com/feathersjs/authentication/issues/635) - Token is undefined for authenticated user [\#500](https://github.com/feathersjs/authentication/issues/500) - 1.x: logout timers need to be moved [\#467](https://github.com/feathersjs/authentication/issues/467) **Merged pull requests:** - Merge auk to master [\#653](https://github.com/feathersjs/authentication/pull/653) ([wnxhaja](https://github.com/wnxhaja)) - Update ws to the latest version 🚀 [\#645](https://github.com/feathersjs/authentication/pull/645) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - Update sinon-chai to the latest version 🚀 [\#644](https://github.com/feathersjs/authentication/pull/644) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) ## [v2.1.2](https://github.com/feathersjs/authentication/tree/v2.1.2) (2018-02-14) [Full Changelog](https://github.com/feathersjs/authentication/compare/v2.1.1...v2.1.2) **Fixed bugs:** - hook failed with auth & sync [\#540](https://github.com/feathersjs/authentication/issues/540) - JWT Cookie [\#389](https://github.com/feathersjs/authentication/issues/389) **Closed issues:** - forgot password [\#638](https://github.com/feathersjs/authentication/issues/638) - registered many authentication services [\#634](https://github.com/feathersjs/authentication/issues/634) - TypeError: Cannot read property '\_strategy' of undefined [\#632](https://github.com/feathersjs/authentication/issues/632) - How to change 5000ms timeout? [\#628](https://github.com/feathersjs/authentication/issues/628) - cookie reused from server in SSR app [\#619](https://github.com/feathersjs/authentication/issues/619) - Express middleware not setCookie [\#617](https://github.com/feathersjs/authentication/issues/617) - Server to Server Authentication Question [\#612](https://github.com/feathersjs/authentication/issues/612) - No way to share token between socket-rest-express [\#607](https://github.com/feathersjs/authentication/issues/607) - 404 when accessing route using customer authentication [\#579](https://github.com/feathersjs/authentication/issues/579) - \[question\] is it possible to protect by role a create method? [\#564](https://github.com/feathersjs/authentication/issues/564) - Authentication with server-side rendering [\#560](https://github.com/feathersjs/authentication/issues/560) - Problem authenticating using REST middleware [\#495](https://github.com/feathersjs/authentication/issues/495) - A supposed way to auth requests from SSR to Feathers API [\#469](https://github.com/feathersjs/authentication/issues/469) - rename `app.authenticate\(\)` to `app.\_authenticate\(\)` [\#468](https://github.com/feathersjs/authentication/issues/468) **Merged pull requests:** - Delete slack link [\#637](https://github.com/feathersjs/authentication/pull/637) ([vodniciarv](https://github.com/vodniciarv)) - Update @feathersjs/authentication-jwt to the latest version 🚀 [\#631](https://github.com/feathersjs/authentication/pull/631) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - Update mocha to the latest version 🚀 [\#629](https://github.com/feathersjs/authentication/pull/629) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - Update ws to the latest version 🚀 [\#625](https://github.com/feathersjs/authentication/pull/625) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - Options merged [\#611](https://github.com/feathersjs/authentication/pull/611) ([Makingweb](https://github.com/Makingweb)) ## [v2.1.1](https://github.com/feathersjs/authentication/tree/v2.1.1) (2018-01-03) [Full Changelog](https://github.com/feathersjs/authentication/compare/v2.1.0...v2.1.1) **Closed issues:** - Deleted user successfully signs in using JWT [\#615](https://github.com/feathersjs/authentication/issues/615) - Feathers.authenticate gives window undefined \(server-rendered\) [\#573](https://github.com/feathersjs/authentication/issues/573) - Be careful with discard\('password'\) in user [\#434](https://github.com/feathersjs/authentication/issues/434) **Merged pull requests:** - Update readme to correspond with latest release [\#621](https://github.com/feathersjs/authentication/pull/621) ([daffl](https://github.com/daffl)) - Update semistandard to the latest version 🚀 [\#620](https://github.com/feathersjs/authentication/pull/620) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - Update mongodb to the latest version 🚀 [\#618](https://github.com/feathersjs/authentication/pull/618) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) ## [v2.1.0](https://github.com/feathersjs/authentication/tree/v2.1.0) (2017-12-06) [Full Changelog](https://github.com/feathersjs/authentication/compare/v2.0.1...v2.1.0) **Closed issues:** - Method "Remove" from Authentication Service gives Internal Server Error when using JWT Authentication with Cookies. [\#606](https://github.com/feathersjs/authentication/issues/606) - Anonymous Authentication fails over Socket.io [\#457](https://github.com/feathersjs/authentication/issues/457) **Merged pull requests:** - Always prevent publishing of authentication events [\#614](https://github.com/feathersjs/authentication/pull/614) ([daffl](https://github.com/daffl)) - Update feathers-memory to the latest version 🚀 [\#613](https://github.com/feathersjs/authentication/pull/613) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) ## [v2.0.1](https://github.com/feathersjs/authentication/tree/v2.0.1) (2017-11-16) [Full Changelog](https://github.com/feathersjs/authentication/compare/v2.0.0...v2.0.1) **Merged pull requests:** - Add default export for better ES module \(TypeScript\) compatibility [\#605](https://github.com/feathersjs/authentication/pull/605) ([daffl](https://github.com/daffl)) ## [v2.0.0](https://github.com/feathersjs/authentication/tree/v2.0.0) (2017-11-09) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.3.1...v2.0.0) **Closed issues:** - is there a way to detect if the token used is correct or not ? [\#601](https://github.com/feathersjs/authentication/issues/601) - option for non-JWT based session [\#597](https://github.com/feathersjs/authentication/issues/597) **Merged pull requests:** - Update nsp to the latest version 🚀 [\#603](https://github.com/feathersjs/authentication/pull/603) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) ## [v1.3.1](https://github.com/feathersjs/authentication/tree/v1.3.1) (2017-11-03) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.4.1...v1.3.1) **Merged pull requests:** - Only set the JWT UUID if it is not already set [\#600](https://github.com/feathersjs/authentication/pull/600) ([daffl](https://github.com/daffl)) ## [v1.4.1](https://github.com/feathersjs/authentication/tree/v1.4.1) (2017-11-01) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.4.0...v1.4.1) **Merged pull requests:** - Update dependencies for release [\#598](https://github.com/feathersjs/authentication/pull/598) ([daffl](https://github.com/daffl)) - Finalize v3 dependency updates [\#596](https://github.com/feathersjs/authentication/pull/596) ([daffl](https://github.com/daffl)) - Update Codeclimate coverage token [\#595](https://github.com/feathersjs/authentication/pull/595) ([daffl](https://github.com/daffl)) ## [v1.4.0](https://github.com/feathersjs/authentication/tree/v1.4.0) (2017-10-25) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.3.0...v1.4.0) **Closed issues:** - An in-range update of socket.io-client is breaking the build 🚨 [\#588](https://github.com/feathersjs/authentication/issues/588) - An in-range update of feathers-hooks is breaking the build 🚨 [\#587](https://github.com/feathersjs/authentication/issues/587) **Merged pull requests:** - Move to npm scope [\#594](https://github.com/feathersjs/authentication/pull/594) ([daffl](https://github.com/daffl)) - Update to Feathers v3 \(Buzzard\) [\#592](https://github.com/feathersjs/authentication/pull/592) ([daffl](https://github.com/daffl)) - Update to new plugin infrastructure [\#591](https://github.com/feathersjs/authentication/pull/591) ([daffl](https://github.com/daffl)) ## [v1.3.0](https://github.com/feathersjs/authentication/tree/v1.3.0) (2017-10-24) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.7.13...v1.3.0) **Merged pull requests:** - updating the codeclimate setup [\#589](https://github.com/feathersjs/authentication/pull/589) ([ekryski](https://github.com/ekryski)) ## [v0.7.13](https://github.com/feathersjs/authentication/tree/v0.7.13) (2017-10-23) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.2.7...v0.7.13) **Closed issues:** - Error authenticating! Error: Token provided to verifyJWT is missing or not a string ? [\#584](https://github.com/feathersjs/authentication/issues/584) - Visual Studio Code Debug no authentication [\#583](https://github.com/feathersjs/authentication/issues/583) - \[Feature Request\] Cloud DB's [\#581](https://github.com/feathersjs/authentication/issues/581) - Request doesn't contain any headers when user service requested [\#578](https://github.com/feathersjs/authentication/issues/578) - No way to pass Options to auth.express.authenticate. Needed for Google API refreshToken [\#576](https://github.com/feathersjs/authentication/issues/576) - /auth/google 404 Not Found [\#574](https://github.com/feathersjs/authentication/issues/574) - unique email not working while create [\#572](https://github.com/feathersjs/authentication/issues/572) - authentication service not return token jwt [\#571](https://github.com/feathersjs/authentication/issues/571) - typo in jwt default options [\#570](https://github.com/feathersjs/authentication/issues/570) - Generate new app, Google-only auth, throws error [\#568](https://github.com/feathersjs/authentication/issues/568) - An in-range update of feathers is breaking the build 🚨 [\#565](https://github.com/feathersjs/authentication/issues/565) - Documentation not understanding [\#563](https://github.com/feathersjs/authentication/issues/563) - Checking hook.params.headers.authorization [\#552](https://github.com/feathersjs/authentication/issues/552) - Ability to send token as part of URL [\#546](https://github.com/feathersjs/authentication/issues/546) - Anonymous Authentication [\#544](https://github.com/feathersjs/authentication/issues/544) - Quote Error [\#519](https://github.com/feathersjs/authentication/issues/519) - \[example\] CustomStrategy using passport-custom [\#516](https://github.com/feathersjs/authentication/issues/516) - \[Epic\] Auth 2.0.0 [\#513](https://github.com/feathersjs/authentication/issues/513) - ID set to null - Unable to delete with customer ID field. [\#422](https://github.com/feathersjs/authentication/issues/422) - Prefixing socket events [\#418](https://github.com/feathersjs/authentication/issues/418) - Passwordless auth [\#409](https://github.com/feathersjs/authentication/issues/409) - How to authenticate the application client? not only the users [\#405](https://github.com/feathersjs/authentication/issues/405) - Multi-factor Local Auth [\#5](https://github.com/feathersjs/authentication/issues/5) **Merged pull requests:** - Features/typescript fix [\#585](https://github.com/feathersjs/authentication/pull/585) ([TimMensch](https://github.com/TimMensch)) - Update mocha to the latest version 🚀 [\#582](https://github.com/feathersjs/authentication/pull/582) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - Update sinon to the latest version 🚀 [\#580](https://github.com/feathersjs/authentication/pull/580) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - Update jsonwebtoken to the latest version 🚀 [\#567](https://github.com/feathersjs/authentication/pull/567) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - Include Babel Polyfill for Node 4 [\#566](https://github.com/feathersjs/authentication/pull/566) ([daffl](https://github.com/daffl)) - Update passport to the latest version 🚀 [\#558](https://github.com/feathersjs/authentication/pull/558) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - Revert "Make feathers-authentication match security documents" [\#556](https://github.com/feathersjs/authentication/pull/556) ([ekryski](https://github.com/ekryski)) - Update debug to the latest version 🚀 [\#555](https://github.com/feathersjs/authentication/pull/555) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - Make feathers-authentication match security documents [\#554](https://github.com/feathersjs/authentication/pull/554) ([micaksica2](https://github.com/micaksica2)) - Update sinon to the latest version 🚀 [\#551](https://github.com/feathersjs/authentication/pull/551) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - Update ws to the latest version 🚀 [\#549](https://github.com/feathersjs/authentication/pull/549) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - Update chai to the latest version 🚀 [\#543](https://github.com/feathersjs/authentication/pull/543) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - adding a default jwt uuid. Refs \#513 [\#539](https://github.com/feathersjs/authentication/pull/539) ([ekryski](https://github.com/ekryski)) - Refresh token must have a user ID [\#419](https://github.com/feathersjs/authentication/pull/419) ([francisco-sanchez-molina](https://github.com/francisco-sanchez-molina)) ## [v1.2.7](https://github.com/feathersjs/authentication/tree/v1.2.7) (2017-07-11) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.2.6...v1.2.7) **Closed issues:** - Connection without password [\#541](https://github.com/feathersjs/authentication/issues/541) - email in lower case ? [\#538](https://github.com/feathersjs/authentication/issues/538) - Im unable to ping feathers server from react native. [\#537](https://github.com/feathersjs/authentication/issues/537) - whats the official way to open cors in feather ? [\#536](https://github.com/feathersjs/authentication/issues/536) - Error options.service does not exist after initial auth setup [\#535](https://github.com/feathersjs/authentication/issues/535) - LogoutTimer not being cleared correctly [\#532](https://github.com/feathersjs/authentication/issues/532) - logoutTimer causing early logouts [\#404](https://github.com/feathersjs/authentication/issues/404) **Merged pull requests:** - fixed meta undefined error [\#542](https://github.com/feathersjs/authentication/pull/542) ([markacola](https://github.com/markacola)) ## [v1.2.6](https://github.com/feathersjs/authentication/tree/v1.2.6) (2017-06-22) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.2.5...v1.2.6) **Closed issues:** - OAuth 2 login for cordova [\#530](https://github.com/feathersjs/authentication/issues/530) **Merged pull requests:** - Change cleartimeout\(\) to lt.clearTimeout\(\) [\#534](https://github.com/feathersjs/authentication/pull/534) ([wnxhaja](https://github.com/wnxhaja)) - Update feathers-authentication-local to the latest version 🚀 [\#533](https://github.com/feathersjs/authentication/pull/533) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) ## [v1.2.5](https://github.com/feathersjs/authentication/tree/v1.2.5) (2017-06-21) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.2.4...v1.2.5) **Closed issues:** - Cannot read property 'user' of undefined - lib\socket\update-entity.js:26:104 [\#529](https://github.com/feathersjs/authentication/issues/529) - Provider is undefined when using restrictToRoles [\#525](https://github.com/feathersjs/authentication/issues/525) - How to make a request to an Endpoint that requires authentication from nodejs? [\#523](https://github.com/feathersjs/authentication/issues/523) **Merged pull requests:** - fixes several issues with update-entity w/ test cases [\#531](https://github.com/feathersjs/authentication/pull/531) ([jerfowler](https://github.com/jerfowler)) ## [v1.2.4](https://github.com/feathersjs/authentication/tree/v1.2.4) (2017-06-08) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.2.3...v1.2.4) **Fixed bugs:** - User \(Entity\) needs to be updated on the socket after authentication [\#293](https://github.com/feathersjs/authentication/issues/293) **Closed issues:** - Express Middleware local -\> jwt does not authorize on redirect [\#518](https://github.com/feathersjs/authentication/issues/518) - Issue with feathers-authentication [\#512](https://github.com/feathersjs/authentication/issues/512) - User Authentication Missing Credentials error \(and subsequent nav authorization\) [\#508](https://github.com/feathersjs/authentication/issues/508) - passport log failure [\#505](https://github.com/feathersjs/authentication/issues/505) - authenticate with a custom username field \(rather than email\) [\#502](https://github.com/feathersjs/authentication/issues/502) - app.get\('auth'\) vs app.get\('authentication'\) [\#497](https://github.com/feathersjs/authentication/issues/497) - Can't get success authorization with pure feathers server [\#491](https://github.com/feathersjs/authentication/issues/491) **Merged pull requests:** - Test and fix for authenticate event with invalid data [\#524](https://github.com/feathersjs/authentication/pull/524) ([daffl](https://github.com/daffl)) - Remove hook.data.payload [\#522](https://github.com/feathersjs/authentication/pull/522) ([marshallswain](https://github.com/marshallswain)) - Update socket entity [\#521](https://github.com/feathersjs/authentication/pull/521) ([marshallswain](https://github.com/marshallswain)) - Made each option, optional [\#515](https://github.com/feathersjs/authentication/pull/515) ([cranesandcaff](https://github.com/cranesandcaff)) - Add feathers-authentication-hooks in readme [\#510](https://github.com/feathersjs/authentication/pull/510) ([bertho-zero](https://github.com/bertho-zero)) - Update ms to the latest version 🚀 [\#509](https://github.com/feathersjs/authentication/pull/509) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - Fix default authentication config keys [\#506](https://github.com/feathersjs/authentication/pull/506) ([ekryski](https://github.com/ekryski)) ## [v1.2.3](https://github.com/feathersjs/authentication/tree/v1.2.3) (2017-05-10) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.2.2...v1.2.3) **Closed issues:** - Validating custom express routes [\#498](https://github.com/feathersjs/authentication/issues/498) - Payload won't include userId when logging in with stored localStorage token [\#496](https://github.com/feathersjs/authentication/issues/496) - How to send oauth token authentication to another client server [\#493](https://github.com/feathersjs/authentication/issues/493) - Unhandled Promise Rejection error. [\#489](https://github.com/feathersjs/authentication/issues/489) - No Auth token on authentication resource [\#488](https://github.com/feathersjs/authentication/issues/488) - How to verify JWT in feathers issued by another feathers instance ? [\#484](https://github.com/feathersjs/authentication/issues/484) - hook.params.user [\#483](https://github.com/feathersjs/authentication/issues/483) - Overriding JWT's expiresIn with a value more than 20d prevents users from signing in [\#458](https://github.com/feathersjs/authentication/issues/458) **Merged pull requests:** - Update feathers-socketio to the latest version 🚀 [\#503](https://github.com/feathersjs/authentication/pull/503) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - Update socket.io-client to the latest version 🚀 [\#501](https://github.com/feathersjs/authentication/pull/501) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - Fix issue with very large token timeout. [\#499](https://github.com/feathersjs/authentication/pull/499) ([asdacap](https://github.com/asdacap)) - Typo [\#492](https://github.com/feathersjs/authentication/pull/492) ([wdmtech](https://github.com/wdmtech)) - Update migrating.md [\#490](https://github.com/feathersjs/authentication/pull/490) ([MichaelErmer](https://github.com/MichaelErmer)) - Update semistandard to the latest version 🚀 [\#487](https://github.com/feathersjs/authentication/pull/487) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - Update feathers-hooks to the latest version 🚀 [\#485](https://github.com/feathersjs/authentication/pull/485) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - Update dependencies to enable Greenkeeper 🌴 [\#482](https://github.com/feathersjs/authentication/pull/482) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) ## [v1.2.2](https://github.com/feathersjs/authentication/tree/v1.2.2) (2017-04-12) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.2.1...v1.2.2) **Fixed bugs:** - accessToken not being used when provided by client over socketio [\#400](https://github.com/feathersjs/authentication/issues/400) **Closed issues:** - Incompatible old client dependency [\#479](https://github.com/feathersjs/authentication/issues/479) - Using feathers-authentication-client for an existing API? [\#478](https://github.com/feathersjs/authentication/issues/478) - app.authenticate error : UnhandledPromiseRejectionWarning: Unhandled promise rejection \(rejection id: 2\): \* Error \* [\#476](https://github.com/feathersjs/authentication/issues/476) - Make `socket.feathers` data available in authentication hooks [\#475](https://github.com/feathersjs/authentication/issues/475) - Allow the authenticate hook to be called with no parameters [\#473](https://github.com/feathersjs/authentication/issues/473) - Authenticate : How to return more infos ? [\#471](https://github.com/feathersjs/authentication/issues/471) **Merged pull requests:** - Use latest version of feathers-authentication-client [\#480](https://github.com/feathersjs/authentication/pull/480) ([daffl](https://github.com/daffl)) - Resolves \#475 - Socket params are made available to authentication hooks [\#477](https://github.com/feathersjs/authentication/pull/477) ([thomas-p-wilson](https://github.com/thomas-p-wilson)) ## [v1.2.1](https://github.com/feathersjs/authentication/tree/v1.2.1) (2017-04-07) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.2.0...v1.2.1) **Fixed bugs:** - failureRedirect is never used when using with oauth2 [\#387](https://github.com/feathersjs/authentication/issues/387) **Closed issues:** - OAuth guides [\#470](https://github.com/feathersjs/authentication/issues/470) - app.authenticate not working [\#466](https://github.com/feathersjs/authentication/issues/466) - how can I logout using local authentication? [\#465](https://github.com/feathersjs/authentication/issues/465) - How to do Socket.io Authentication [\#462](https://github.com/feathersjs/authentication/issues/462) - Add event filtering by default \(socket.io\) [\#460](https://github.com/feathersjs/authentication/issues/460) - Add ability to control if socket is marked as authenticated. [\#448](https://github.com/feathersjs/authentication/issues/448) - Auth redirect issue [\#425](https://github.com/feathersjs/authentication/issues/425) - E-mail verification step can be bypassed using Postman or Curl [\#391](https://github.com/feathersjs/authentication/issues/391) - Example app [\#386](https://github.com/feathersjs/authentication/issues/386) **Merged pull requests:** - Allow the cookie to be set if action is not `remove` [\#474](https://github.com/feathersjs/authentication/pull/474) ([marshallswain](https://github.com/marshallswain)) ## [v1.2.0](https://github.com/feathersjs/authentication/tree/v1.2.0) (2017-03-23) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.1.1...v1.2.0) **Fixed bugs:** - 1.0 authentication service hooks don't run when client uses feathers-socketio [\#455](https://github.com/feathersjs/authentication/issues/455) - `hook.params.provider` is not set when calling `client.authenticate\(\)` [\#432](https://github.com/feathersjs/authentication/issues/432) - remove method failed with JsonWebTokenError: invalid token [\#388](https://github.com/feathersjs/authentication/issues/388) **Closed issues:** - Token creation has side effect [\#454](https://github.com/feathersjs/authentication/issues/454) - Question: When is userId set? [\#453](https://github.com/feathersjs/authentication/issues/453) - How to authenticate SPA? More precisely how does the redirect works? [\#451](https://github.com/feathersjs/authentication/issues/451) - POST to auth/facebook for FacebookTokenStrategy 404? [\#447](https://github.com/feathersjs/authentication/issues/447) - feathers-authentication 1.1.1 `No auth token` [\#445](https://github.com/feathersjs/authentication/issues/445) - Another readme incorrect and maybe docs to [\#441](https://github.com/feathersjs/authentication/issues/441) - Readme incorrect and maybe docs to [\#440](https://github.com/feathersjs/authentication/issues/440) - npm version issue? [\#439](https://github.com/feathersjs/authentication/issues/439) - setCookie express middleware only works inside hooks [\#438](https://github.com/feathersjs/authentication/issues/438) - createJWT throws 'secret must provided' [\#437](https://github.com/feathersjs/authentication/issues/437) - Not useful error message on NotAuthenticated error [\#436](https://github.com/feathersjs/authentication/issues/436) - Passwordfeld in auth.local does not work as expected [\#435](https://github.com/feathersjs/authentication/issues/435) - Authentication via REST returns token without finding user on db [\#430](https://github.com/feathersjs/authentication/issues/430) **Merged pull requests:** - Filter out all events [\#461](https://github.com/feathersjs/authentication/pull/461) ([daffl](https://github.com/daffl)) - Fix socket auth [\#459](https://github.com/feathersjs/authentication/pull/459) ([marshallswain](https://github.com/marshallswain)) - Fix \#454 Token create has side effect [\#456](https://github.com/feathersjs/authentication/pull/456) ([whollacsek](https://github.com/whollacsek)) - Windows compatible version of the original compile comand with public folder support. [\#442](https://github.com/feathersjs/authentication/pull/442) ([appurist](https://github.com/appurist)) - Add client.js back for consistency [\#433](https://github.com/feathersjs/authentication/pull/433) ([daffl](https://github.com/daffl)) - add string to authenticate \(typescript\) [\#431](https://github.com/feathersjs/authentication/pull/431) ([superbarne](https://github.com/superbarne)) - Add support for Bearer scheme in remove method [\#403](https://github.com/feathersjs/authentication/pull/403) ([boybundit](https://github.com/boybundit)) ## [v1.1.1](https://github.com/feathersjs/authentication/tree/v1.1.1) (2017-03-02) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.1.0...v1.1.1) **Closed issues:** - Authentication over socket.io never answers [\#428](https://github.com/feathersjs/authentication/issues/428) **Merged pull requests:** - Remove lots of hardcoded values for config, and adds the `authenticate` hook [\#427](https://github.com/feathersjs/authentication/pull/427) ([myknbani](https://github.com/myknbani)) ## [v1.1.0](https://github.com/feathersjs/authentication/tree/v1.1.0) (2017-03-01) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.0.2...v1.1.0) **Fixed bugs:** - Mongo update error after logging into Facebook [\#244](https://github.com/feathersjs/authentication/issues/244) **Closed issues:** - Feature Request: Anonymous Authentication Strategy Support [\#423](https://github.com/feathersjs/authentication/issues/423) - Error is not thrown if token that is provided is invalid [\#421](https://github.com/feathersjs/authentication/issues/421) - Request body 'token' parameter disappears [\#420](https://github.com/feathersjs/authentication/issues/420) - Auth2 issue getting JWT token from server when different ports [\#416](https://github.com/feathersjs/authentication/issues/416) - Cookie-based authentication with XHR is not possible [\#413](https://github.com/feathersjs/authentication/issues/413) - JWT Authentication setup failing [\#411](https://github.com/feathersjs/authentication/issues/411) - how to disable service for external usage in version 1.0 [\#410](https://github.com/feathersjs/authentication/issues/410) - v1.0 is removed from npm? [\#408](https://github.com/feathersjs/authentication/issues/408) - Make JWT data more configurable [\#407](https://github.com/feathersjs/authentication/issues/407) - Possible typo [\#406](https://github.com/feathersjs/authentication/issues/406) - Authentication with an existing database with existing hashed \(md5\) passwords [\#398](https://github.com/feathersjs/authentication/issues/398) - can modify selected fields only [\#397](https://github.com/feathersjs/authentication/issues/397) - \[Discussion\] Migrating to 1.0 - hook changes [\#396](https://github.com/feathersjs/authentication/issues/396) - feathers-authentication 'local' strategy requires token? [\#394](https://github.com/feathersjs/authentication/issues/394) - JWT for local auth. [\#390](https://github.com/feathersjs/authentication/issues/390) - Feathers 'Twitter API' style [\#385](https://github.com/feathersjs/authentication/issues/385) - Missing code in example app [\#383](https://github.com/feathersjs/authentication/issues/383) - feathers-authentication errors with any view error, and redirects to /auth/failure [\#381](https://github.com/feathersjs/authentication/issues/381) - what does app.service\('authentication'\).remove\(...\) mean? [\#379](https://github.com/feathersjs/authentication/issues/379) - Rest Endpoints. [\#375](https://github.com/feathersjs/authentication/issues/375) - cordova google-plus signUp with id_token [\#373](https://github.com/feathersjs/authentication/issues/373) - How to reconnect socket with cookie after page refresh ? [\#372](https://github.com/feathersjs/authentication/issues/372) - Error: Could not find stored JWT and no authentication strategy was given [\#367](https://github.com/feathersjs/authentication/issues/367) - "No auth token" using authenticate strategy: 'jwt' \(v.1.0.0-beta-2\) [\#366](https://github.com/feathersjs/authentication/issues/366) - Navigating to /auth/\ twice redirects to /auth/failed [\#344](https://github.com/feathersjs/authentication/issues/344) - Meteor auth migration guide [\#334](https://github.com/feathersjs/authentication/issues/334) - Auth 1.0 [\#330](https://github.com/feathersjs/authentication/issues/330) - RSA token secret [\#309](https://github.com/feathersjs/authentication/issues/309) - Add option to use bcrypt [\#300](https://github.com/feathersjs/authentication/issues/300) - Better example of how to change hashing algorithm? \[Question\] [\#289](https://github.com/feathersjs/authentication/issues/289) - issuer doesn't work [\#284](https://github.com/feathersjs/authentication/issues/284) - passport auth question [\#274](https://github.com/feathersjs/authentication/issues/274) - Add support for authenticating active users only [\#259](https://github.com/feathersjs/authentication/issues/259) - 404 response from populateUser\(\) hook [\#258](https://github.com/feathersjs/authentication/issues/258) - Responses hang when token.secret is undefined for local authentication [\#249](https://github.com/feathersjs/authentication/issues/249) - Authentication without password [\#246](https://github.com/feathersjs/authentication/issues/246) - Fix successRedirect to not override cookie path [\#243](https://github.com/feathersjs/authentication/issues/243) - Deprecate verifyToken and populateUser hooks in favour of middleware [\#227](https://github.com/feathersjs/authentication/issues/227) - Authenticating and creating [\#100](https://github.com/feathersjs/authentication/issues/100) - Add a password service [\#83](https://github.com/feathersjs/authentication/issues/83) **Merged pull requests:** - Fix JWT options typo [\#415](https://github.com/feathersjs/authentication/pull/415) ([daffl](https://github.com/daffl)) - Prevent setCookie from mutating authOptions [\#414](https://github.com/feathersjs/authentication/pull/414) ([adrien-k](https://github.com/adrien-k)) - Typescript Definitions [\#412](https://github.com/feathersjs/authentication/pull/412) ([AbraaoAlves](https://github.com/AbraaoAlves)) - Docs for migrating to auth.hooks.authenticate hook [\#399](https://github.com/feathersjs/authentication/pull/399) ([petermikitsh](https://github.com/petermikitsh)) - Typo 'cookie.enable' should be 'cookie.enabled' [\#380](https://github.com/feathersjs/authentication/pull/380) ([whollacsek](https://github.com/whollacsek)) - Docs: Equalize usage of feathers-authenticate [\#378](https://github.com/feathersjs/authentication/pull/378) ([eikaramba](https://github.com/eikaramba)) ## [v1.0.2](https://github.com/feathersjs/authentication/tree/v1.0.2) (2016-12-14) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.0.1...v1.0.2) **Closed issues:** - successRedirect not redirecting [\#364](https://github.com/feathersjs/authentication/issues/364) ## [v1.0.1](https://github.com/feathersjs/authentication/tree/v1.0.1) (2016-12-14) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.0.0...v1.0.1) ## [v1.0.0](https://github.com/feathersjs/authentication/tree/v1.0.0) (2016-12-14) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.7.12...v1.0.0) **Fixed bugs:** - restrictToOwner does not support multi patch, update and remove [\#228](https://github.com/feathersjs/authentication/issues/228) **Closed issues:** - auth.express.authenticate got undefined [\#363](https://github.com/feathersjs/authentication/issues/363) - Non-standard header structure [\#361](https://github.com/feathersjs/authentication/issues/361) - localEndpoint without local strategy [\#359](https://github.com/feathersjs/authentication/issues/359) - Using custom passport strategies [\#356](https://github.com/feathersjs/authentication/issues/356) - Client-side app.on\('login'\) [\#355](https://github.com/feathersjs/authentication/issues/355) - Payload limiting on `app.get\('user'\)`? [\#354](https://github.com/feathersjs/authentication/issues/354) - Authentication token is missing [\#352](https://github.com/feathersjs/authentication/issues/352) - \[1.0\] The entity on the socket should pull from the strategy options. [\#348](https://github.com/feathersjs/authentication/issues/348) - \[1.0\] Only the first failure is returned on auth failure when chaining multiple strategies [\#346](https://github.com/feathersjs/authentication/issues/346) - Build 0.7.11 does not contain current code on NPMJS [\#342](https://github.com/feathersjs/authentication/issues/342) - feathers-authentication branch 0.8 did not work with payload \(tested on socket\) [\#264](https://github.com/feathersjs/authentication/issues/264) - Add method for updating JWT [\#260](https://github.com/feathersjs/authentication/issues/260) - 1.0 architecture considerations [\#226](https://github.com/feathersjs/authentication/issues/226) - Features/RFC [\#213](https://github.com/feathersjs/authentication/issues/213) - Support access_token based OAuth2 providers [\#169](https://github.com/feathersjs/authentication/issues/169) - Support openID [\#154](https://github.com/feathersjs/authentication/issues/154) - Disable cookie by default if not using OAuth [\#152](https://github.com/feathersjs/authentication/issues/152) - Add token service tests [\#144](https://github.com/feathersjs/authentication/issues/144) - Add local service tests [\#143](https://github.com/feathersjs/authentication/issues/143) - Add OAuth2 service tests [\#142](https://github.com/feathersjs/authentication/issues/142) - Add OAuth2 integration tests [\#141](https://github.com/feathersjs/authentication/issues/141) - Add integration tests for custom redirects [\#125](https://github.com/feathersjs/authentication/issues/125) - Support mobile authentication via OAuth1 [\#47](https://github.com/feathersjs/authentication/issues/47) - Support OAuth1 [\#42](https://github.com/feathersjs/authentication/issues/42) - Password-less Local Auth with Email / SMS [\#7](https://github.com/feathersjs/authentication/issues/7) **Merged pull requests:** - migrating to semistandard [\#371](https://github.com/feathersjs/authentication/pull/371) ([ekryski](https://github.com/ekryski)) - Logout should always give a response. [\#369](https://github.com/feathersjs/authentication/pull/369) ([marshallswain](https://github.com/marshallswain)) - Clarify that the authenticate hook is required. [\#368](https://github.com/feathersjs/authentication/pull/368) ([marshallswain](https://github.com/marshallswain)) - Fix README example [\#365](https://github.com/feathersjs/authentication/pull/365) ([saiberz](https://github.com/saiberz)) - Remove additional deprecation notice [\#362](https://github.com/feathersjs/authentication/pull/362) ([porsager](https://github.com/porsager)) - fix typo [\#360](https://github.com/feathersjs/authentication/pull/360) ([osenvosem](https://github.com/osenvosem)) - Update feathers-primus to version 2.0.0 🚀 [\#358](https://github.com/feathersjs/authentication/pull/358) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) - Create .codeclimate.yml [\#357](https://github.com/feathersjs/authentication/pull/357) ([larkinscott](https://github.com/larkinscott)) - fixing redirect middleware [\#353](https://github.com/feathersjs/authentication/pull/353) ([ekryski](https://github.com/ekryski)) - Remove useless quotes [\#351](https://github.com/feathersjs/authentication/pull/351) ([bertho-zero](https://github.com/bertho-zero)) - A bunch of bug fixes [\#349](https://github.com/feathersjs/authentication/pull/349) ([ekryski](https://github.com/ekryski)) - fix\(docs/new-features\): syntax highlighting [\#347](https://github.com/feathersjs/authentication/pull/347) ([justingreenberg](https://github.com/justingreenberg)) - Update superagent to version 3.0.0 🚀 [\#345](https://github.com/feathersjs/authentication/pull/345) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) - Update feathers-memory to version 1.0.0 🚀 [\#343](https://github.com/feathersjs/authentication/pull/343) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) - 1.0 Pre-release [\#336](https://github.com/feathersjs/authentication/pull/336) ([ekryski](https://github.com/ekryski)) ## [v0.7.12](https://github.com/feathersjs/authentication/tree/v0.7.12) (2016-11-11) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.7.11...v0.7.12) **Closed issues:** - App.authenticate uses wrong `this` reference [\#341](https://github.com/feathersjs/authentication/issues/341) - Getting more done in GitHub with ZenHub [\#331](https://github.com/feathersjs/authentication/issues/331) - Need help to use feathers authentication storage in vue vuex [\#329](https://github.com/feathersjs/authentication/issues/329) - How to get user id in hooks? [\#322](https://github.com/feathersjs/authentication/issues/322) - I checked out my new feathersjs app in another machine, created a new user but I can't log in! [\#320](https://github.com/feathersjs/authentication/issues/320) - restrict-to-owner throws error when user id is 0 [\#319](https://github.com/feathersjs/authentication/issues/319) - Not providing sufficient details for an auth provider should not be an error. [\#318](https://github.com/feathersjs/authentication/issues/318) - \[Question\] Is there a way to verify a user with password? [\#316](https://github.com/feathersjs/authentication/issues/316) - 0.8.0 beta 1 bug - this is not defined [\#315](https://github.com/feathersjs/authentication/issues/315) - Client: Document getJWT & verifyJWT [\#313](https://github.com/feathersjs/authentication/issues/313) - Socket client should automatically auth on reconnect [\#310](https://github.com/feathersjs/authentication/issues/310) - app.get\('token'\) doesn't work after a browser refresh. [\#303](https://github.com/feathersjs/authentication/issues/303) - Problem issuing multiple jwt's for the same user [\#302](https://github.com/feathersjs/authentication/issues/302) - restrict-to-owner does not allow Service.remove\(null\) from internal systems [\#301](https://github.com/feathersjs/authentication/issues/301) - How to migrate from restrictToOwner to checkPermissions [\#299](https://github.com/feathersjs/authentication/issues/299) - "username" cannot be used as local strategy usernameField [\#294](https://github.com/feathersjs/authentication/issues/294) - Bad Hook API Design: Hooks are inconsistent and impure functions [\#288](https://github.com/feathersjs/authentication/issues/288) - Mutliple 'user' models for authentication [\#282](https://github.com/feathersjs/authentication/issues/282) - Client should ensure socket.io upgrade is complete before authenticating [\#275](https://github.com/feathersjs/authentication/issues/275) - JWT is not sent after socket reconnection [\#272](https://github.com/feathersjs/authentication/issues/272) - 401 after service is moved/refactored [\#270](https://github.com/feathersjs/authentication/issues/270) - Client side auth should subscribe to user updates so that app.get\('user'\) is fresh [\#195](https://github.com/feathersjs/authentication/issues/195) - Make oauth2 more general [\#179](https://github.com/feathersjs/authentication/issues/179) - Add integration tests for custom service endpoints [\#145](https://github.com/feathersjs/authentication/issues/145) - Create a `requireAuth` wrapper for `verifyToken`, `populateUser`, `restrictToAuth` [\#118](https://github.com/feathersjs/authentication/issues/118) **Merged pull requests:** - babel-core@6.18.2 breaks build 🚨 [\#339](https://github.com/feathersjs/authentication/pull/339) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) - 👻😱 Node.js 0.10 is unmaintained 😱👻 [\#337](https://github.com/feathersjs/authentication/pull/337) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) - restrictToOwner -Fix check for methodNotAllowed [\#335](https://github.com/feathersjs/authentication/pull/335) ([daffl](https://github.com/daffl)) - Implement login and logout events for REST authentication [\#325](https://github.com/feathersjs/authentication/pull/325) ([daffl](https://github.com/daffl)) - Socket.io authentication tests and login logout event [\#324](https://github.com/feathersjs/authentication/pull/324) ([daffl](https://github.com/daffl)) - Reorganization [\#321](https://github.com/feathersjs/authentication/pull/321) ([ekryski](https://github.com/ekryski)) - client: use Authentication class, make `getJWT` and `verifyJWT` async [\#317](https://github.com/feathersjs/authentication/pull/317) ([marshallswain](https://github.com/marshallswain)) - 0.8 client decode jwt [\#314](https://github.com/feathersjs/authentication/pull/314) ([marshallswain](https://github.com/marshallswain)) - Store config at `app.config` [\#312](https://github.com/feathersjs/authentication/pull/312) ([marshallswain](https://github.com/marshallswain)) - Cookies will match jwt expiry by default. [\#308](https://github.com/feathersjs/authentication/pull/308) ([marshallswain](https://github.com/marshallswain)) - Remove permissions hooks and middleware [\#307](https://github.com/feathersjs/authentication/pull/307) ([daffl](https://github.com/daffl)) - First cut for authentication middleware [\#305](https://github.com/feathersjs/authentication/pull/305) ([daffl](https://github.com/daffl)) - 0.8 - OAuth fixes [\#304](https://github.com/feathersjs/authentication/pull/304) ([marshallswain](https://github.com/marshallswain)) ## [v0.7.11](https://github.com/feathersjs/authentication/tree/v0.7.11) (2016-09-28) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.7.10...v0.7.11) **Closed issues:** - Unable to authenticate with passport-google-oauth20 [\#295](https://github.com/feathersjs/authentication/issues/295) - "Unauthorized" Response with Hook Data [\#291](https://github.com/feathersjs/authentication/issues/291) - hashPassword in patch [\#286](https://github.com/feathersjs/authentication/issues/286) - Mobile App Facebook Login [\#276](https://github.com/feathersjs/authentication/issues/276) - Socket user should update automatically [\#266](https://github.com/feathersjs/authentication/issues/266) - Get user outside a service [\#261](https://github.com/feathersjs/authentication/issues/261) **Merged pull requests:** - hashPassword fall-through if there's no password [\#287](https://github.com/feathersjs/authentication/pull/287) ([marshallswain](https://github.com/marshallswain)) - Update feathers-memory to version 0.8.0 🚀 [\#285](https://github.com/feathersjs/authentication/pull/285) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) - Allow multiple username fields for local auth [\#283](https://github.com/feathersjs/authentication/pull/283) ([sdbondi](https://github.com/sdbondi)) ## [v0.7.10](https://github.com/feathersjs/authentication/tree/v0.7.10) (2016-08-31) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.7.9...v0.7.10) **Fixed bugs:** - restrictToOwner should not throw an error on mass deletions [\#175](https://github.com/feathersjs/authentication/issues/175) **Closed issues:** - Duplicate Email should be rejected by Default [\#281](https://github.com/feathersjs/authentication/issues/281) - Auth0 & featherjs authorization only [\#277](https://github.com/feathersjs/authentication/issues/277) - Cannot read property 'scope' of undefined [\#273](https://github.com/feathersjs/authentication/issues/273) - Socker.js | Custom successHandler [\#271](https://github.com/feathersjs/authentication/issues/271) - Use feathers-socketio? and rest&socket share session maybe? [\#269](https://github.com/feathersjs/authentication/issues/269) - Ability to invalidate old token/session when user login with another machine. [\#267](https://github.com/feathersjs/authentication/issues/267) - 0.8 authentication before hooks - only ever getting a 401 Unauthorised [\#263](https://github.com/feathersjs/authentication/issues/263) - REST Middleware breaks local auth [\#262](https://github.com/feathersjs/authentication/issues/262) - 0.8: Token Service errors on token auth using client [\#254](https://github.com/feathersjs/authentication/issues/254) - 0.8: Cookies, turning off feathers-session cookie also turns off feathers-jwt cookie. [\#253](https://github.com/feathersjs/authentication/issues/253) - Any example of how to do refresh token? [\#248](https://github.com/feathersjs/authentication/issues/248) - Custom Authentication Hooks [\#236](https://github.com/feathersjs/authentication/issues/236) - Is there an Authenticated Event [\#235](https://github.com/feathersjs/authentication/issues/235) - Error while using /auth/local [\#233](https://github.com/feathersjs/authentication/issues/233) - Providing token to feathers.authentication doesn't work [\#230](https://github.com/feathersjs/authentication/issues/230) - bundled hooks customize errors [\#215](https://github.com/feathersjs/authentication/issues/215) - Hooks should support a callback for conditionally running [\#210](https://github.com/feathersjs/authentication/issues/210) - restrictToRoles hook: More complex determination of "owner". [\#205](https://github.com/feathersjs/authentication/issues/205) - verifyToken hook option to error [\#200](https://github.com/feathersjs/authentication/issues/200) - Allow using restrictToOwner as an after hook [\#123](https://github.com/feathersjs/authentication/issues/123) **Merged pull requests:** - Manually supply an endpoint to the Client authenticate\(\) method [\#278](https://github.com/feathersjs/authentication/pull/278) ([mcnamee](https://github.com/mcnamee)) - Update mocha to version 3.0.0 🚀 [\#257](https://github.com/feathersjs/authentication/pull/257) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) - Don’t mix options when signing tokens [\#255](https://github.com/feathersjs/authentication/pull/255) ([marshallswain](https://github.com/marshallswain)) - Attempt to get token right away. [\#252](https://github.com/feathersjs/authentication/pull/252) ([marshallswain](https://github.com/marshallswain)) - Update async to version 2.0.0 🚀 [\#240](https://github.com/feathersjs/authentication/pull/240) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) - Creates better way or returning data in a familiar format [\#234](https://github.com/feathersjs/authentication/pull/234) ([codingfriend1](https://github.com/codingfriend1)) - Throws an error if restriction methods are used outside of a find or get hook [\#232](https://github.com/feathersjs/authentication/pull/232) ([codingfriend1](https://github.com/codingfriend1)) - RestrictToOwner now takes an array [\#231](https://github.com/feathersjs/authentication/pull/231) ([sscaff1](https://github.com/sscaff1)) - Adds ability to limit queries unless authenticated and authorized [\#229](https://github.com/feathersjs/authentication/pull/229) ([codingfriend1](https://github.com/codingfriend1)) ## [v0.7.9](https://github.com/feathersjs/authentication/tree/v0.7.9) (2016-06-20) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.7.8...v0.7.9) **Fixed bugs:** - Calling logout should revoke/blacklist a JWT [\#133](https://github.com/feathersjs/authentication/issues/133) **Closed issues:** - Query email rather than oauth provider id on /auth/\ [\#223](https://github.com/feathersjs/authentication/issues/223) - Cannot read property \'service\' of undefined [\#222](https://github.com/feathersjs/authentication/issues/222) **Merged pull requests:** - added support for hashing passwords when hook.data is an array [\#225](https://github.com/feathersjs/authentication/pull/225) ([eblin](https://github.com/eblin)) - jwt ssl warning [\#214](https://github.com/feathersjs/authentication/pull/214) ([aboutlo](https://github.com/aboutlo)) ## [v0.7.8](https://github.com/feathersjs/authentication/tree/v0.7.8) (2016-06-09) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.7.7...v0.7.8) **Closed issues:** - Feathers-authentication assumptions [\#220](https://github.com/feathersjs/authentication/issues/220) - Server-side header option does not accept capital letters [\#218](https://github.com/feathersjs/authentication/issues/218) - How to figure out why redirect to /auth/failure? [\#217](https://github.com/feathersjs/authentication/issues/217) - Getting token via REST is not documented [\#216](https://github.com/feathersjs/authentication/issues/216) - How to use Feathers Client to Authenticate Facebook/Instagram credentials [\#204](https://github.com/feathersjs/authentication/issues/204) - Remove token from localstorage [\#203](https://github.com/feathersjs/authentication/issues/203) - Check user password [\#193](https://github.com/feathersjs/authentication/issues/193) - app.authenticate\(\): Warning: a promise was rejected with a non-error: \[object Object\] [\#191](https://github.com/feathersjs/authentication/issues/191) - Authentication provider for Facebook Account Kit [\#189](https://github.com/feathersjs/authentication/issues/189) **Merged pull requests:** - Lowercase custom header [\#219](https://github.com/feathersjs/authentication/pull/219) ([mmwtsn](https://github.com/mmwtsn)) - mocha@2.5.0 breaks build 🚨 [\#212](https://github.com/feathersjs/authentication/pull/212) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) - Small refactoring to simplify structure and remove code duplication [\#209](https://github.com/feathersjs/authentication/pull/209) ([daffl](https://github.com/daffl)) - Use removeItem in the storage on logout [\#208](https://github.com/feathersjs/authentication/pull/208) ([daffl](https://github.com/daffl)) - Misspelled in a comment [\#201](https://github.com/feathersjs/authentication/pull/201) ([tryy3](https://github.com/tryy3)) - Update babel-plugin-add-module-exports to version 0.2.0 🚀 [\#199](https://github.com/feathersjs/authentication/pull/199) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) ## [v0.7.7](https://github.com/feathersjs/authentication/tree/v0.7.7) (2016-05-05) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.7.6...v0.7.7) **Fixed bugs:** - OAuth2 authentication callback failing due to missing property [\#196](https://github.com/feathersjs/authentication/issues/196) **Merged pull requests:** - properly handle optional `\_json` property [\#197](https://github.com/feathersjs/authentication/pull/197) ([nyaaao](https://github.com/nyaaao)) ## [v0.7.6](https://github.com/feathersjs/authentication/tree/v0.7.6) (2016-05-03) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.7.5...v0.7.6) **Fixed bugs:** - Facebook Authentication should do a patch not an update. [\#174](https://github.com/feathersjs/authentication/issues/174) **Closed issues:** - Authenticated user [\#192](https://github.com/feathersjs/authentication/issues/192) - REST token revoke [\#185](https://github.com/feathersjs/authentication/issues/185) - TypeError: Cannot read property 'service' of undefined [\#173](https://github.com/feathersjs/authentication/issues/173) - Optionally Include password in the params.query object passed to User.find\(\) [\#171](https://github.com/feathersjs/authentication/issues/171) - Pass more to local authentication params [\#165](https://github.com/feathersjs/authentication/issues/165) - Support custom authentication strategies [\#157](https://github.com/feathersjs/authentication/issues/157) **Merged pull requests:** - Allow manipulation of params before checking credentials [\#186](https://github.com/feathersjs/authentication/pull/186) ([saiichihashimoto](https://github.com/saiichihashimoto)) - Update feathers to version 2.0.1 🚀 [\#184](https://github.com/feathersjs/authentication/pull/184) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) - fix\(oauth2\): Use patch to update user in oauthCallback [\#183](https://github.com/feathersjs/authentication/pull/183) ([beevelop](https://github.com/beevelop)) ## [v0.7.5](https://github.com/feathersjs/authentication/tree/v0.7.5) (2016-04-23) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.7.4...v0.7.5) **Fixed bugs:** - restrictToOwner and restrictToRoles have invalid type checking [\#172](https://github.com/feathersjs/authentication/issues/172) **Closed issues:** - user fails to signup with facebook if there is also local auth [\#168](https://github.com/feathersjs/authentication/issues/168) - Unable to authenticate requests when using vanilla Socket.IO [\#166](https://github.com/feathersjs/authentication/issues/166) ## [v0.7.4](https://github.com/feathersjs/authentication/tree/v0.7.4) (2016-04-18) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.7.3...v0.7.4) **Fixed bugs:** - restrictToOwner and restrictToRoles hooks don't work with nested models [\#163](https://github.com/feathersjs/authentication/issues/163) - Change restrictToOwner error when a request does not contain ID [\#160](https://github.com/feathersjs/authentication/issues/160) **Closed issues:** - authenticate\(\) can leak sensetive user data via token service [\#162](https://github.com/feathersjs/authentication/issues/162) - onBeforeLogin Hook [\#161](https://github.com/feathersjs/authentication/issues/161) **Merged pull requests:** - Hook fixes [\#164](https://github.com/feathersjs/authentication/pull/164) ([ekryski](https://github.com/ekryski)) ## [v0.7.3](https://github.com/feathersjs/authentication/tree/v0.7.3) (2016-04-16) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.7.2...v0.7.3) ## [v0.7.2](https://github.com/feathersjs/authentication/tree/v0.7.2) (2016-04-16) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.7.1...v0.7.2) **Closed issues:** - Auth doesn't work with non default local.userEndpoint [\#159](https://github.com/feathersjs/authentication/issues/159) - Automatically add the hashPassword hook to local.userEndpoint [\#158](https://github.com/feathersjs/authentication/issues/158) - Client authentication\(\) storage option not documented [\#155](https://github.com/feathersjs/authentication/issues/155) - restrictToRoles availability inconsistency [\#153](https://github.com/feathersjs/authentication/issues/153) - Does not populate user for other services [\#150](https://github.com/feathersjs/authentication/issues/150) **Merged pull requests:** - Steal Compatibility [\#156](https://github.com/feathersjs/authentication/pull/156) ([marshallswain](https://github.com/marshallswain)) ## [v0.7.1](https://github.com/feathersjs/authentication/tree/v0.7.1) (2016-04-08) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.7.0...v0.7.1) **Closed issues:** - Documentation discrepancies [\#148](https://github.com/feathersjs/authentication/issues/148) - bcrypt is hardcoded [\#146](https://github.com/feathersjs/authentication/issues/146) - Update Docs, Guides, Examples for v0.7 [\#129](https://github.com/feathersjs/authentication/issues/129) - populateUser: allow option to populate without db call. [\#92](https://github.com/feathersjs/authentication/issues/92) **Merged pull requests:** - Update feathers-memory to version 0.7.0 🚀 [\#149](https://github.com/feathersjs/authentication/pull/149) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) - fix a typo [\#147](https://github.com/feathersjs/authentication/pull/147) ([chrjean](https://github.com/chrjean)) - Fix copy paste typo in queryWithCurrentUser hook. [\#140](https://github.com/feathersjs/authentication/pull/140) ([juodumas](https://github.com/juodumas)) ## [v0.7.0](https://github.com/feathersjs/authentication/tree/v0.7.0) (2016-03-30) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.6.0...v0.7.0) **Fixed bugs:** - logout should de-authenticate a socket [\#136](https://github.com/feathersjs/authentication/issues/136) - \[Security\] JsonWebToken Lifecycle Concerns; Set HttpOnly = true in JWT cookie [\#132](https://github.com/feathersjs/authentication/issues/132) - restrictToRoles hook needs to throw an error and not scope the query [\#128](https://github.com/feathersjs/authentication/issues/128) - restrictToOwner hook needs to throw an error and not scope the query [\#127](https://github.com/feathersjs/authentication/issues/127) - \[security\] Generated tokens are broadcast to all socket clients \(by default\) [\#126](https://github.com/feathersjs/authentication/issues/126) - \[oAuth\] User profile should be updated every time they are authenticated [\#124](https://github.com/feathersjs/authentication/issues/124) - Logout should clear the cookie [\#122](https://github.com/feathersjs/authentication/issues/122) - Want the default success/fail routes, not the sendFile [\#121](https://github.com/feathersjs/authentication/issues/121) **Closed issues:** - Make all hooks optional if used internally [\#138](https://github.com/feathersjs/authentication/issues/138) - Throw errors for deprecated hooks and update documentation [\#134](https://github.com/feathersjs/authentication/issues/134) - v6.0.0: How can I return the user object along with the token ? [\#131](https://github.com/feathersjs/authentication/issues/131) - user field not getting populated [\#119](https://github.com/feathersjs/authentication/issues/119) - Move to bcryptjs [\#112](https://github.com/feathersjs/authentication/issues/112) - Bundled hooks should pull from auth config to avoid having to pass duplicate props. [\#93](https://github.com/feathersjs/authentication/issues/93) - Customize the JWT payload [\#78](https://github.com/feathersjs/authentication/issues/78) - Needs a test for verifying that a custom tokenEndpoint works. [\#59](https://github.com/feathersjs/authentication/issues/59) - Finish test coverage for existing features. [\#9](https://github.com/feathersjs/authentication/issues/9) **Merged pull requests:** - 0.7 Release [\#139](https://github.com/feathersjs/authentication/pull/139) ([ekryski](https://github.com/ekryski)) ## [v0.6.0](https://github.com/feathersjs/authentication/tree/v0.6.0) (2016-03-24) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.5.1...v0.6.0) **Fixed bugs:** - Token encoding is not using the idField option. [\#107](https://github.com/feathersjs/authentication/issues/107) - Logging out breaks in React Native [\#105](https://github.com/feathersjs/authentication/issues/105) - Updating User Attached to Params in Client [\#102](https://github.com/feathersjs/authentication/issues/102) - local auth should not redirect by default [\#89](https://github.com/feathersjs/authentication/issues/89) **Closed issues:** - Id of user can't be 0 for auth [\#116](https://github.com/feathersjs/authentication/issues/116) - how to authenticate user in the socket.io? [\#111](https://github.com/feathersjs/authentication/issues/111) - Wrong Status Error [\#110](https://github.com/feathersjs/authentication/issues/110) - TypeError: Cannot read property 'service' of undefined \(continued\) [\#108](https://github.com/feathersjs/authentication/issues/108) - `idField` breaks from `tokenService.create\(\)` to `populateUser\(\)` after hook [\#103](https://github.com/feathersjs/authentication/issues/103) **Merged pull requests:** - Bcryptjs [\#137](https://github.com/feathersjs/authentication/pull/137) ([ekryski](https://github.com/ekryski)) - Allow user.id to be 0. Fixes \#116 [\#117](https://github.com/feathersjs/authentication/pull/117) ([marshallswain](https://github.com/marshallswain)) - client should return a 401 error code when no token is provided [\#115](https://github.com/feathersjs/authentication/pull/115) ([ccummings](https://github.com/ccummings)) - v0.6 - Bugs fixes, new hooks, and hook tests [\#109](https://github.com/feathersjs/authentication/pull/109) ([ekryski](https://github.com/ekryski)) - primus client connect event is 'open' [\#106](https://github.com/feathersjs/authentication/pull/106) ([ahdinosaur](https://github.com/ahdinosaur)) ## [v0.5.1](https://github.com/feathersjs/authentication/tree/v0.5.1) (2016-03-15) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.5.0...v0.5.1) ## [v0.5.0](https://github.com/feathersjs/authentication/tree/v0.5.0) (2016-03-14) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.4.1...v0.5.0) **Fixed bugs:** - Client should store token string and not the token object [\#95](https://github.com/feathersjs/authentication/issues/95) **Closed issues:** - using feathers-rest/client with feathers-authentication/client [\#94](https://github.com/feathersjs/authentication/issues/94) - populateUser can pull defaults from config, if available. [\#91](https://github.com/feathersjs/authentication/issues/91) - App level auth routes for multiple sub-routes [\#90](https://github.com/feathersjs/authentication/issues/90) - POST to /auth/local never gets response [\#88](https://github.com/feathersjs/authentication/issues/88) - populate-user.js do not get settings [\#86](https://github.com/feathersjs/authentication/issues/86) - Add rate limiting [\#81](https://github.com/feathersjs/authentication/issues/81) **Merged pull requests:** - Finalizing client side authentication module [\#101](https://github.com/feathersjs/authentication/pull/101) ([daffl](https://github.com/daffl)) - Ten hours is only 36 seconds [\#99](https://github.com/feathersjs/authentication/pull/99) ([mileswilson](https://github.com/mileswilson)) - Fix examples [\#98](https://github.com/feathersjs/authentication/pull/98) ([mastertinner](https://github.com/mastertinner)) - fix html in templates [\#97](https://github.com/feathersjs/authentication/pull/97) ([mastertinner](https://github.com/mastertinner)) - update populateUser\(\) hook [\#87](https://github.com/feathersjs/authentication/pull/87) ([kulakowka](https://github.com/kulakowka)) - Customize the JWT payload [\#80](https://github.com/feathersjs/authentication/pull/80) ([enten](https://github.com/enten)) ## [v0.4.1](https://github.com/feathersjs/authentication/tree/v0.4.1) (2016-02-28) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.4.0...v0.4.1) **Fixed bugs:** - app.logout\(\) fails [\#85](https://github.com/feathersjs/authentication/issues/85) **Closed issues:** - Username response ? [\#84](https://github.com/feathersjs/authentication/issues/84) - User doesn't get populated after authentication with databases that don't use \_id [\#71](https://github.com/feathersjs/authentication/issues/71) - Support client usage in NodeJS [\#52](https://github.com/feathersjs/authentication/issues/52) - Support async storage for React Native [\#51](https://github.com/feathersjs/authentication/issues/51) - RequireAdmin on userService [\#36](https://github.com/feathersjs/authentication/issues/36) - Create test for changing the `usernameField` [\#1](https://github.com/feathersjs/authentication/issues/1) ## [v0.4.0](https://github.com/feathersjs/authentication/tree/v0.4.0) (2016-02-27) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.3.5...v0.4.0) **Closed issues:** - Authentication not worked with hooks.remove\('password'\) [\#82](https://github.com/feathersjs/authentication/issues/82) **Merged pull requests:** - Refactoring for storage service [\#76](https://github.com/feathersjs/authentication/pull/76) ([ekryski](https://github.com/ekryski)) ## [v0.3.5](https://github.com/feathersjs/authentication/tree/v0.3.5) (2016-02-25) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.3.4...v0.3.5) **Merged pull requests:** - Adding support for OAuth2 token based auth strategies. Closes \#46. [\#77](https://github.com/feathersjs/authentication/pull/77) ([ekryski](https://github.com/ekryski)) ## [v0.3.4](https://github.com/feathersjs/authentication/tree/v0.3.4) (2016-02-25) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.3.3...v0.3.4) ## [v0.3.3](https://github.com/feathersjs/authentication/tree/v0.3.3) (2016-02-25) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.3.2...v0.3.3) ## [v0.3.2](https://github.com/feathersjs/authentication/tree/v0.3.2) (2016-02-24) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.3.1...v0.3.2) **Merged pull requests:** - bumping feathers-errors version [\#79](https://github.com/feathersjs/authentication/pull/79) ([ekryski](https://github.com/ekryski)) ## [v0.3.1](https://github.com/feathersjs/authentication/tree/v0.3.1) (2016-02-23) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.3.0...v0.3.1) **Closed issues:** - Fix toLowerCase hook [\#74](https://github.com/feathersjs/authentication/issues/74) - REST auth/local not working if socketio\(\) not set [\#72](https://github.com/feathersjs/authentication/issues/72) - Support mobile authentication via OAuth2 [\#46](https://github.com/feathersjs/authentication/issues/46) **Merged pull requests:** - Fix toLowerCase hook [\#75](https://github.com/feathersjs/authentication/pull/75) ([enten](https://github.com/enten)) ## [v0.3.0](https://github.com/feathersjs/authentication/tree/v0.3.0) (2016-02-19) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.2.4...v0.3.0) **Fixed bugs:** - Don't register successRedirect route if custom one is passed in [\#61](https://github.com/feathersjs/authentication/issues/61) **Closed issues:** - Specify the secret in one place instead of two [\#69](https://github.com/feathersjs/authentication/issues/69) - support a failRedirect [\#62](https://github.com/feathersjs/authentication/issues/62) - Document authentication updates [\#50](https://github.com/feathersjs/authentication/issues/50) **Merged pull requests:** - Config options [\#70](https://github.com/feathersjs/authentication/pull/70) ([ekryski](https://github.com/ekryski)) ## [v0.2.4](https://github.com/feathersjs/authentication/tree/v0.2.4) (2016-02-17) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.2.3...v0.2.4) **Closed issues:** - Find "query" is replaced by token [\#64](https://github.com/feathersjs/authentication/issues/64) **Merged pull requests:** - Add module exports Babel module and test CommonJS compatibility [\#68](https://github.com/feathersjs/authentication/pull/68) ([daffl](https://github.com/daffl)) ## [v0.2.3](https://github.com/feathersjs/authentication/tree/v0.2.3) (2016-02-15) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.2.2...v0.2.3) **Closed issues:** - How to forbid get and find on the userEndpoint? [\#66](https://github.com/feathersjs/authentication/issues/66) - userEndpoint problem in sub-app [\#63](https://github.com/feathersjs/authentication/issues/63) - How to modify successRedirect in local authentication? [\#60](https://github.com/feathersjs/authentication/issues/60) **Merged pull requests:** - Removing assigning token to params.query for sockets. [\#67](https://github.com/feathersjs/authentication/pull/67) ([ekryski](https://github.com/ekryski)) - Fixing client query [\#65](https://github.com/feathersjs/authentication/pull/65) ([fastlorenzo](https://github.com/fastlorenzo)) ## [v0.2.2](https://github.com/feathersjs/authentication/tree/v0.2.2) (2016-02-13) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.2.1...v0.2.2) **Closed issues:** - Custom tokenEndpoint failing [\#57](https://github.com/feathersjs/authentication/issues/57) - TypeError: Cannot read property 'service' of undefined [\#56](https://github.com/feathersjs/authentication/issues/56) - Login returns 500: Internal server error [\#54](https://github.com/feathersjs/authentication/issues/54) **Merged pull requests:** - Fixing token endpoint [\#58](https://github.com/feathersjs/authentication/pull/58) ([marshallswain](https://github.com/marshallswain)) ## [v0.2.1](https://github.com/feathersjs/authentication/tree/v0.2.1) (2016-02-12) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.2.0...v0.2.1) **Closed issues:** - Custom local options not being respected. [\#55](https://github.com/feathersjs/authentication/issues/55) - node can not require\("feathers-authentication"\).default [\#53](https://github.com/feathersjs/authentication/issues/53) ## [v0.2.0](https://github.com/feathersjs/authentication/tree/v0.2.0) (2016-02-12) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.1.2...v0.2.0) **Closed issues:** - Support graceful fallback to cookies [\#45](https://github.com/feathersjs/authentication/issues/45) - Add a client side component for authentication [\#44](https://github.com/feathersjs/authentication/issues/44) - Support OAuth2 [\#43](https://github.com/feathersjs/authentication/issues/43) - Support token based authentication [\#41](https://github.com/feathersjs/authentication/issues/41) - Support local authentication [\#40](https://github.com/feathersjs/authentication/issues/40) - Only sign the JWT with user id. Not the whole user object [\#38](https://github.com/feathersjs/authentication/issues/38) - Discussion: Securing token for socket.io auth [\#33](https://github.com/feathersjs/authentication/issues/33) - Handling expired tokens [\#25](https://github.com/feathersjs/authentication/issues/25) - Support multiple auth providers [\#6](https://github.com/feathersjs/authentication/issues/6) **Merged pull requests:** - Decoupling [\#49](https://github.com/feathersjs/authentication/pull/49) ([ekryski](https://github.com/ekryski)) - Adding an auth client [\#48](https://github.com/feathersjs/authentication/pull/48) ([ekryski](https://github.com/ekryski)) - Validate if provider [\#39](https://github.com/feathersjs/authentication/pull/39) ([mastertinner](https://github.com/mastertinner)) ## [v0.1.2](https://github.com/feathersjs/authentication/tree/v0.1.2) (2016-02-04) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.1.1...v0.1.2) **Closed issues:** - Hooks should support incoming data as arrays of objects. [\#34](https://github.com/feathersjs/authentication/issues/34) - Support authenticating with Username and Password via sockets [\#32](https://github.com/feathersjs/authentication/issues/32) **Merged pull requests:** - Check for params.provider in requireAuth hook [\#37](https://github.com/feathersjs/authentication/pull/37) ([marshallswain](https://github.com/marshallswain)) - safety check for data [\#35](https://github.com/feathersjs/authentication/pull/35) ([deanmcpherson](https://github.com/deanmcpherson)) ## [v0.1.1](https://github.com/feathersjs/authentication/tree/v0.1.1) (2016-01-30) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.1.0...v0.1.1) ## [v0.1.0](https://github.com/feathersjs/authentication/tree/v0.1.0) (2016-01-25) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.0.8...v0.1.0) **Closed issues:** - Get the Travis build to work. [\#27](https://github.com/feathersjs/authentication/issues/27) - Login not working [\#24](https://github.com/feathersjs/authentication/issues/24) - Hooks should be configurable \(they should be functions\) [\#11](https://github.com/feathersjs/authentication/issues/11) - Document the bundled hooks. [\#10](https://github.com/feathersjs/authentication/issues/10) **Merged pull requests:** - Migrate docs to book [\#31](https://github.com/feathersjs/authentication/pull/31) ([marshallswain](https://github.com/marshallswain)) - hashPassword: Async bcrypt usage needs a promise [\#30](https://github.com/feathersjs/authentication/pull/30) ([marshallswain](https://github.com/marshallswain)) - Removing extras from travis.yml [\#29](https://github.com/feathersjs/authentication/pull/29) ([marshallswain](https://github.com/marshallswain)) - Fixing build [\#28](https://github.com/feathersjs/authentication/pull/28) ([marshallswain](https://github.com/marshallswain)) - Adding nsp check [\#26](https://github.com/feathersjs/authentication/pull/26) ([marshallswain](https://github.com/marshallswain)) ## [v0.0.8](https://github.com/feathersjs/authentication/tree/v0.0.8) (2016-01-16) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.0.7...v0.0.8) **Merged pull requests:** - Support services that use pagination. [\#23](https://github.com/feathersjs/authentication/pull/23) ([marshallswain](https://github.com/marshallswain)) ## [v0.0.7](https://github.com/feathersjs/authentication/tree/v0.0.7) (2016-01-07) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.0.6...v0.0.7) **Closed issues:** - Password isn't removed from responses when using a mongoose service for users endpoint [\#19](https://github.com/feathersjs/authentication/issues/19) - next called twice using socket.io and using an unauthenticated service [\#17](https://github.com/feathersjs/authentication/issues/17) - Switch to a callback-based field configuration? [\#15](https://github.com/feathersjs/authentication/issues/15) - Cannot authenticate [\#14](https://github.com/feathersjs/authentication/issues/14) - Allow require without `.default` [\#13](https://github.com/feathersjs/authentication/issues/13) - Login validation [\#2](https://github.com/feathersjs/authentication/issues/2) **Merged pull requests:** - Adding separate route for refreshing a login token. [\#21](https://github.com/feathersjs/authentication/pull/21) ([corymsmith](https://github.com/corymsmith)) - Converting user model to object when using mongoose service [\#20](https://github.com/feathersjs/authentication/pull/20) ([corymsmith](https://github.com/corymsmith)) - Fixing issue where next is called twice when hitting an unauthenticated service via socket.io [\#18](https://github.com/feathersjs/authentication/pull/18) ([corymsmith](https://github.com/corymsmith)) - Fixing usage of mongoose service [\#16](https://github.com/feathersjs/authentication/pull/16) ([corymsmith](https://github.com/corymsmith)) ## [v0.0.6](https://github.com/feathersjs/authentication/tree/v0.0.6) (2015-11-22) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.0.5...v0.0.6) **Closed issues:** - Feathers Auth Configuration Error [\#12](https://github.com/feathersjs/authentication/issues/12) - Make sure we're returning proper error responses. [\#8](https://github.com/feathersjs/authentication/issues/8) ## [v0.0.5](https://github.com/feathersjs/authentication/tree/v0.0.5) (2015-11-19) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.0.4...v0.0.5) ## [v0.0.4](https://github.com/feathersjs/authentication/tree/v0.0.4) (2015-11-19) [Full Changelog](https://github.com/feathersjs/authentication/compare/v0.0.3...v0.0.4) ## [v0.0.3](https://github.com/feathersjs/authentication/tree/v0.0.3) (2015-11-18) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.0.6...v0.0.3) **Merged pull requests:** - allow runtime auth via socket.io [\#4](https://github.com/feathersjs/authentication/pull/4) ([randomnerd](https://github.com/randomnerd)) ## [v1.0.6](https://github.com/feathersjs/authentication/tree/v1.0.6) (2015-11-02) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.0.5...v1.0.6) ## [v1.0.5](https://github.com/feathersjs/authentication/tree/v1.0.5) (2015-11-02) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.0.4...v1.0.5) ## [v1.0.4](https://github.com/feathersjs/authentication/tree/v1.0.4) (2015-11-02) [Full Changelog](https://github.com/feathersjs/authentication/compare/v1.0.3...v1.0.4) ## [v1.0.3](https://github.com/feathersjs/authentication/tree/v1.0.3) (2015-10-12) \* _This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)_ ================================================ FILE: packages/authentication/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2024 Feathers Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: packages/authentication/README.md ================================================ # @feathersjs/authentication [](https://github.com/feathersjs/feathers/actions?query=workflow%3ACI) [](https://www.npmjs.com/package/@feathersjs/authentication) [](https://discord.gg/qa8kez8QBx) > Add Authentication to your FeathersJS app. ## Installation ``` npm install @feathersjs/authentication --save ``` ## Documentation Refer to the [Feathers authentication API documentation](https://feathersjs.com/api/authentication/) for more details. ## License Copyright (c) 2024 [Feathers contributors](https://github.com/feathersjs/feathers/graphs/contributors) Licensed under the [MIT license](LICENSE). ================================================ FILE: packages/authentication/package.json ================================================ { "name": "@feathersjs/authentication", "description": "Add Authentication to your FeathersJS app.", "version": "5.0.42", "homepage": "https://feathersjs.com", "main": "lib/", "types": "lib/", "keywords": [ "feathers", "feathers-plugin" ], "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/daffl" }, "repository": { "type": "git", "url": "git://github.com/feathersjs/feathers.git", "directory": "packages/authentication" }, "author": { "name": "Feathers contributors", "email": "hello@feathersjs.com", "url": "https://feathersjs.com" }, "files": [ "CHANGELOG.md", "LICENSE", "README.md", "src/**", "lib/**", "*.d.ts", "*.js" ], "contributors": [], "bugs": { "url": "https://github.com/feathersjs/feathers/issues" }, "engines": { "node": ">= 12" }, "scripts": { "prepublish": "npm run compile", "pack": "npm pack --pack-destination ../generators/test/build", "compile": "shx rm -rf lib/ && tsc && npm run pack", "test": "mocha --config ../../.mocharc.json --recursive test/**.test.ts test/**/*.test.ts" }, "directories": { "lib": "lib" }, "publishConfig": { "access": "public" }, "dependencies": { "@feathersjs/commons": "^5.0.42", "@feathersjs/errors": "^5.0.42", "@feathersjs/feathers": "^5.0.42", "@feathersjs/hooks": "^0.9.0", "@feathersjs/schema": "^5.0.42", "@feathersjs/transport-commons": "^5.0.42", "@types/jsonwebtoken": "^9.0.10", "jsonwebtoken": "^9.0.3", "lodash": "^4.17.23", "long-timeout": "^0.1.1", "uuid": "^11.1.0" }, "devDependencies": { "@feathersjs/memory": "^5.0.42", "@types/lodash": "^4.17.24", "@types/mocha": "^10.0.10", "@types/node": "^25.3.3", "@types/uuid": "^10.0.0", "mocha": "^11.7.5", "shx": "^0.4.0", "ts-node": "^10.9.2", "typescript": "^5.9.3" }, "gitHead": "90caf635aec850550b9d37bea2762af959d9e8d5" } ================================================ FILE: packages/authentication/src/core.ts ================================================ import merge from 'lodash/merge' import jsonwebtoken, { SignOptions, Secret, VerifyOptions, Algorithm } from 'jsonwebtoken' import { v4 as uuidv4 } from 'uuid' import { NotAuthenticated } from '@feathersjs/errors' import { createDebug } from '@feathersjs/commons' import { Application, Params } from '@feathersjs/feathers' import { IncomingMessage, ServerResponse } from 'http' import { AuthenticationConfiguration, defaultOptions } from './options' const debug = createDebug('@feathersjs/authentication/base') export interface AuthenticationResult { [key: string]: any } export interface AuthenticationRequest { strategy?: string [key: string]: any } export interface AuthenticationParams extends Params { payload?: { [key: string]: any } jwtOptions?: SignOptions authStrategies?: string[] secret?: string [key: string]: any } export type ConnectionEvent = 'login' | 'logout' | 'disconnect' export interface AuthenticationStrategy { /** * Implement this method to get access to the AuthenticationService * * @param auth The AuthenticationService */ setAuthentication?(auth: AuthenticationBase): void /** * Implement this method to get access to the Feathers application * * @param app The Feathers application instance */ setApplication?(app: Application): void /** * Implement this method to get access to the strategy name * * @param name The name of the strategy */ setName?(name: string): void /** * Implement this method to verify the current configuration * and throw an error if it is invalid. */ verifyConfiguration?(): void /** * Implement this method to setup this strategy * @param auth The AuthenticationService * @param name The name of the strategy */ setup?(auth: AuthenticationBase, name: string): Promise /** * Authenticate an authentication request with this strategy. * Should throw an error if the strategy did not succeed. * * @param authentication The authentication request * @param params The service call parameters */ authenticate?( authentication: AuthenticationRequest, params: AuthenticationParams ): Promise /** * Update a real-time connection according to this strategy. * * @param connection The real-time connection * @param context The hook context */ handleConnection?(event: ConnectionEvent, connection: any, authResult?: AuthenticationResult): Promise /** * Parse a basic HTTP request and response for authentication request information. * * @param req The HTTP request * @param res The HTTP response */ parse?(req: IncomingMessage, res: ServerResponse): Promise