@feathersjs/feathers
Version:
A framework for real-time applications and REST API with JavaScript and TypeScript
496 lines (417 loc) • 15.2 kB
text/typescript
import { EventEmitter } from 'events'
import { NextFunction, HookContext as BaseHookContext } from '@feathersjs/hooks'
type SelfOrArray<S> = S | S[]
type OptionalPick<T, K extends PropertyKey> = Pick<T, Extract<keyof T, K>>
type Entries<T> = {
[K in keyof T]: [K, T[K]]
}[keyof T][]
type GetKeyByValue<Obj, Value> = Extract<Entries<Obj>[number], [PropertyKey, Value]>[0]
export type { NextFunction }
/**
* The object returned from `.find` call by standard database adapters
*/
export interface Paginated<T> {
total: number
limit: number
skip: number
data: T[]
}
/**
* Options that can be passed when registering a service via `app.use(name, service, options)`
*/
export interface ServiceOptions<MethodTypes = string> {
/**
* A list of custom events that this service emits to clients
*/
events?: string[] | readonly string[]
/**
* A list of service methods that should be available __externally__ to clients
*/
methods?: MethodTypes[] | readonly MethodTypes[]
/**
* Provide a full list of events that this service should emit to clients.
* Unlike the `events` option, this will not be merged with the default events.
*/
serviceEvents?: string[] | readonly string[]
/**
* Initial data to always add as route params to this service.
*/
routeParams?: { [key: string]: any }
}
export interface ClientService<
Result = any,
Data = Partial<Result>,
PatchData = Data,
FindResult = Paginated<Result>,
P = Params
> {
find(params?: P): Promise<FindResult>
get(id: Id, params?: P): Promise<Result>
create(data: Data[], params?: P): Promise<Result[]>
create(data: Data, params?: P): Promise<Result>
update(id: Id, data: Data, params?: P): Promise<Result>
update(id: NullableId, data: Data, params?: P): Promise<Result | Result[]>
update(id: null, data: Data, params?: P): Promise<Result[]>
patch(id: NullableId, data: PatchData, params?: P): Promise<Result | Result[]>
patch(id: Id, data: PatchData, params?: P): Promise<Result>
patch(id: null, data: PatchData, params?: P): Promise<Result[]>
remove(id: NullableId, params?: P): Promise<Result | Result[]>
remove(id: Id, params?: P): Promise<Result>
remove(id: null, params?: P): Promise<Result[]>
}
export interface ServiceMethods<
Result = any,
Data = Partial<Result>,
ServiceParams = Params,
PatchData = Partial<Data>
> {
find(params?: ServiceParams & { paginate?: PaginationParams }): Promise<Result | Result[]>
get(id: Id, params?: ServiceParams): Promise<Result>
create(data: Data, params?: ServiceParams): Promise<Result>
update(id: NullableId, data: Data, params?: ServiceParams): Promise<Result | Result[]>
patch(id: NullableId, data: PatchData, params?: ServiceParams): Promise<Result | Result[]>
remove(id: NullableId, params?: ServiceParams): Promise<Result | Result[]>
setup?(app: Application, path: string): Promise<void>
teardown?(app: Application, path: string): Promise<void>
}
export interface ServiceOverloads<
Result = any,
Data = Partial<Result>,
ServiceParams = Params,
PatchData = Partial<Data>
> {
create?(data: Data[], params?: ServiceParams): Promise<Result[]>
update?(id: Id, data: Data, params?: ServiceParams): Promise<Result>
update?(id: null, data: Data, params?: ServiceParams): Promise<Result[]>
patch?(id: Id, data: PatchData, params?: ServiceParams): Promise<Result>
patch?(id: null, data: PatchData, params?: ServiceParams): Promise<Result[]>
remove?(id: Id, params?: ServiceParams): Promise<Result>
remove?(id: null, params?: ServiceParams): Promise<Result[]>
}
/**
* A complete service interface. The `ServiceInterface` type should be preferred for customs service
* implementations
*/
export type Service<
Result = any,
Data = Partial<Result>,
ServiceParams = Params,
PatchData = Partial<Data>
> = ServiceMethods<Result, Data, ServiceParams> & ServiceOverloads<Result, Data, ServiceParams, PatchData>
/**
* The `Service` service interface but with none of the methods required.
*/
export type ServiceInterface<
Result = any,
Data = Partial<Result>,
ServiceParams = Params,
PatchData = Partial<Data>
> = Partial<ServiceMethods<Result, Data, ServiceParams, PatchData>>
export interface ServiceAddons<A = Application, S = Service> extends EventEmitter {
id?: string
hooks(options: HookOptions<A, S>): this
}
export interface ServiceHookOverloads<S, P = Params> {
find(params: P & { paginate?: PaginationParams }, context: HookContext): Promise<HookContext>
get(id: Id, params: P, context: HookContext): Promise<HookContext>
create(
data: ServiceGenericData<S> | ServiceGenericData<S>[],
params: P,
context: HookContext
): Promise<HookContext>
update(id: NullableId, data: ServiceGenericData<S>, params: P, context: HookContext): Promise<HookContext>
patch(id: NullableId, data: ServiceGenericData<S>, params: P, context: HookContext): Promise<HookContext>
remove(id: NullableId, params: P, context: HookContext): Promise<HookContext>
}
export type FeathersService<A = FeathersApplication, S = Service> = S &
ServiceAddons<A, S> &
OptionalPick<ServiceHookOverloads<S>, keyof S>
export type CustomMethods<T extends { [key: string]: [any, any] }> = {
[K in keyof T]: (data: T[K][0], params?: Params) => Promise<T[K][1]>
}
/**
* An interface usually use by transport clients that represents a e.g. HTTP or websocket
* connection that can be configured on the application.
*/
export type TransportConnection<Services = any> = {
(app: Application<Services>): void
Service: any
service: <L extends keyof Services & string>(
name: L
) => keyof any extends keyof Services ? ServiceInterface : Services[L]
}
/**
* A real-time connection object
*/
export interface RealTimeConnection {
[key: string]: any
}
/**
* The interface for a custom service method. Can e.g. be used to type client side services.
*/
export type CustomMethod<T = any, R = T, P extends Params = Params> = (data: T, params?: P) => Promise<R>
export type ServiceMixin<A> = (service: FeathersService<A>, path: string, options: ServiceOptions) => void
export type ServiceGenericType<S> = S extends ServiceInterface<infer T> ? T : any
export type ServiceGenericData<S> = S extends ServiceInterface<infer _T, infer D> ? D : any
export type ServiceGenericParams<S> = S extends ServiceInterface<infer _T, infer _D, infer P> ? P : any
export interface FeathersApplication<Services = any, Settings = any> {
/**
* The Feathers application version
*/
version: string
/**
* A list of callbacks that run when a new service is registered
*/
mixins: ServiceMixin<Application<Services, Settings>>[]
/**
* The index of all services keyed by their path.
*
* __Important:__ Services should always be retrieved via `app.service('name')`
* not via `app.services`.
*/
services: Services
/**
* The application settings that can be used via
* `app.get` and `app.set`
*/
settings: Settings
/**
* A private-ish indicator if `app.setup()` has been called already
*/
_isSetup: boolean
/**
* Retrieve an application setting by name
*
* @param name The setting name
*/
get<L extends keyof Settings & string>(name: L): Settings[L]
/**
* Set an application setting
*
* @param name The setting name
* @param value The setting value
*/
set<L extends keyof Settings & string>(name: L, value: Settings[L]): this
/**
* Runs a callback configure function with the current application instance.
*
* @param callback The callback `(app: Application) => {}` to run
*/
configure(callback: (this: this, app: this) => void): this
/**
* Returns a fallback service instance that will be registered
* when no service was found. Usually throws a `NotFound` error
* but also used to instantiate client side services.
*
* @param location The path of the service
*/
defaultService(location: string): ServiceInterface
/**
* Register a new service or a sub-app. When passed another
* Feathers application, all its services will be re-registered
* with the `path` prefix.
*
* @param path The path for the service to register
* @param service The service object to register or another
* Feathers application to use a sub-app under the `path` prefix.
* @param options The options for this service
*/
use<L extends keyof Services & string>(
path: L,
service: keyof any extends keyof Services ? ServiceInterface | Application : Services[L],
options?: ServiceOptions<keyof any extends keyof Services ? string : keyof Services[L]>
): this
/**
* Unregister an existing service.
*
* @param path The name of the service to unregister
*/
unuse<L extends keyof Services & string>(
path: L
): Promise<FeathersService<this, keyof any extends keyof Services ? Service : Services[L]>>
/**
* Get the Feathers service instance for a path. This will
* be the service originally registered with Feathers functionality
* like hooks and events added.
*
* @param path The name of the service.
*/
service<L extends keyof Services & string>(
path: L
): FeathersService<this, keyof any extends keyof Services ? Service : Services[L]>
/**
* Set up the application and call all services `.setup` method if available.
*
* @param server A server instance (optional)
*/
setup(server?: any): Promise<this>
/**
* Tear down the application and call all services `.teardown` method if available.
*
* @param server A server instance (optional)
*/
teardown(server?: any): Promise<this>
/**
* Register application level hooks.
*
* @param map The application hook settings.
*/
hooks(map: ApplicationHookOptions<this>): this
}
// This needs to be an interface instead of a type
// so that the declaration can be extended by other modules
export interface Application<Services = any, Settings = any>
extends FeathersApplication<Services, Settings>,
EventEmitter {}
export type Id = number | string
export type NullableId = Id | null
export interface Query {
[key: string]: any
}
export interface Params<Q = Query> {
query?: Q
provider?: string
route?: { [key: string]: any }
headers?: { [key: string]: any }
}
export interface PaginationOptions {
default?: number
max?: number
}
export type PaginationParams = false | PaginationOptions
export interface Http {
/**
* A writeable, optional property with status code override.
*/
status?: number
/**
* A writeable, optional property with headers.
*/
headers?: { [key: string]: string | string[] }
/**
* A writeable, optional property with `Location` header's value.
*/
location?: string
}
export type HookType = 'before' | 'after' | 'error' | 'around'
type Serv<FA> = FA extends Application<infer S> ? S : never
export interface HookContext<A = Application, S = any> extends BaseHookContext<ServiceGenericType<S>> {
/**
* A read only property that contains the Feathers application object. This can be used to
* retrieve other services (via context.app.service('name')) or configuration values.
*/
readonly app: A
/**
* A read only property with the name of the service method (one of find, get,
* create, update, patch, remove).
*/
readonly method: string
/**
* A read only property and contains the service name (or path) without leading or
* trailing slashes.
*/
path: 0 extends 1 & S ? keyof Serv<A> & string : GetKeyByValue<Serv<A>, S> & string
/**
* A read only property and contains the service this hook currently runs on.
*/
readonly service: S
/**
* A read only property with the hook type (one of 'around', 'before', 'after' or 'error').
*/
readonly type: HookType
/**
* The list of method arguments. Should not be modified, modify the
* `params`, `data` and `id` properties instead.
*/
readonly arguments: any[]
/**
* A writeable property containing the data of a create, update and patch service
* method call.
*/
data?: ServiceGenericData<S>
/**
* A writeable property with the error object that was thrown in a failed method call.
* It is only available in error hooks.
*/
error?: any
/**
* 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.
*/
id?: Id
/**
* A writeable property that contains the service method parameters (including
* params.query).
*/
params: ServiceGenericParams<S>
/**
* 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
*
* - A before hook to skip the actual service method (database) call
* - An error hook to swallow the error and return a result instead
*/
result?: ServiceGenericType<S>
/**
* 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.
*/
dispatch?: ServiceGenericType<S>
/**
* A writeable, optional property that allows to override the standard HTTP status
* code that should be returned.
*
* @deprecated Use `http.status` instead.
*/
statusCode?: number
/**
* A writeable, optional property with options specific to HTTP transports.
*/
http?: Http
/**
* The event emitted by this method. Can be set to `null` to skip event emitting.
*/
event: string | null
}
// Regular hook typings
export type HookFunction<A = Application, S = Service> = (
this: S,
context: HookContext<A, S>
) => Promise<HookContext<Application, S> | void> | HookContext<Application, S> | void
export type Hook<A = Application, S = Service> = HookFunction<A, S>
type HookMethodMap<A, S> = {
[L in keyof S]?: SelfOrArray<HookFunction<A, S>>
} & { all?: SelfOrArray<HookFunction<A, S>> }
type HookTypeMap<A, S> = SelfOrArray<HookFunction<A, S>> | HookMethodMap<A, S>
// New @feathersjs/hook typings
export type AroundHookFunction<A = Application, S = Service> = (
context: HookContext<A, S>,
next: NextFunction
) => Promise<void>
export type AroundHookMap<A, S> = {
[L in keyof S]?: AroundHookFunction<A, S>[]
} & { all?: AroundHookFunction<A, S>[] }
export type HookMap<A, S> = {
around?: AroundHookMap<A, S>
before?: HookTypeMap<A, S>
after?: HookTypeMap<A, S>
error?: HookTypeMap<A, S>
}
export type HookOptions<A, S> = AroundHookMap<A, S> | AroundHookFunction<A, S>[] | HookMap<A, S>
export interface ApplicationHookContext<A = Application> extends BaseHookContext {
app: A
server: any
}
export type ApplicationHookFunction<A> = (
context: ApplicationHookContext<A>,
next: NextFunction
) => Promise<void>
export type ApplicationHookMap<A> = {
setup?: ApplicationHookFunction<A>[]
teardown?: ApplicationHookFunction<A>[]
}
export type ApplicationHookOptions<A> = HookOptions<A, any> | ApplicationHookMap<A>