Standard service method interfaces and type definitions for implementing Feathers services.
Core CRUD operations that Feathers services can implement.
interface ServiceMethods<
Result = any,
Data = Partial<Result>,
ServiceParams = Params,
PatchData = Partial<Data>
> {
/**
* Retrieve multiple records, optionally filtered and paginated
* @param params - Query parameters including filters and pagination
* @returns Promise resolving to results or paginated results
*/
find(params?: ServiceParams & { paginate?: PaginationParams }): Promise<Result | Result[]>;
/**
* Retrieve a single record by ID
* @param id - The record identifier
* @param params - Query parameters
* @returns Promise resolving to the record
*/
get(id: Id, params?: ServiceParams): Promise<Result>;
/**
* Create a new record
* @param data - The data for the new record
* @param params - Query parameters
* @returns Promise resolving to the created record
*/
create(data: Data, params?: ServiceParams): Promise<Result>;
/**
* Replace a record by ID (full update)
* @param id - The record identifier, or null to update multiple records
* @param data - The replacement data
* @param params - Query parameters
* @returns Promise resolving to updated record(s)
*/
update(id: NullableId, data: Data, params?: ServiceParams): Promise<Result | Result[]>;
/**
* Partially update a record by ID
* @param id - The record identifier, or null to patch multiple records
* @param data - The partial update data
* @param params - Query parameters
* @returns Promise resolving to updated record(s)
*/
patch(id: NullableId, data: PatchData, params?: ServiceParams): Promise<Result | Result[]>;
/**
* Remove a record by ID
* @param id - The record identifier, or null to remove multiple records
* @param params - Query parameters
* @returns Promise resolving to removed record(s)
*/
remove(id: NullableId, params?: ServiceParams): Promise<Result | Result[]>;
/**
* Initialize the service when the application starts
* @param app - The Feathers application instance
* @param path - The service path
*/
setup?(app: Application, path: string): Promise<void>;
/**
* Clean up the service when the application shuts down
* @param app - The Feathers application instance
* @param path - The service path
*/
teardown?(app: Application, path: string): Promise<void>;
}Optional service interface where no methods are required, allowing flexible service implementations.
type ServiceInterface<
Result = any,
Data = Partial<Result>,
ServiceParams = Params,
PatchData = Partial<Data>
> = Partial<ServiceMethods<Result, Data, ServiceParams, PatchData>>;Usage Examples:
import { ServiceInterface, Params } from "@feathersjs/feathers";
// Simple read-only service
class ReadOnlyService implements ServiceInterface {
data = [
{ id: 1, name: "Item 1" },
{ id: 2, name: "Item 2" }
];
async find() {
return this.data;
}
async get(id: number) {
return this.data.find(item => item.id === id);
}
}
// Full CRUD service with typed parameters
interface Todo {
id: number;
text: string;
completed: boolean;
createdAt: Date;
}
interface TodoData {
text: string;
completed?: boolean;
}
interface TodoParams extends Params {
user?: { id: number };
}
class TodoService implements ServiceInterface<Todo, TodoData, TodoParams> {
todos: Todo[] = [];
async find(params?: TodoParams) {
let results = this.todos;
if (params?.query?.completed !== undefined) {
results = results.filter(todo => todo.completed === params.query.completed);
}
return results;
}
async get(id: number, params?: TodoParams) {
return this.todos.find(todo => todo.id === id);
}
async create(data: TodoData, params?: TodoParams) {
const todo: Todo = {
id: Date.now(),
text: data.text,
completed: data.completed || false,
createdAt: new Date()
};
this.todos.push(todo);
return todo;
}
async patch(id: number, data: Partial<TodoData>, params?: TodoParams) {
const todo = this.todos.find(t => t.id === id);
if (todo) {
Object.assign(todo, data);
}
return todo;
}
async remove(id: number, params?: TodoParams) {
const index = this.todos.findIndex(t => t.id === id);
if (index !== -1) {
return this.todos.splice(index, 1)[0];
}
}
async setup(app: Application, path: string) {
console.log(`TodoService setup at path: ${path}`);
}
async teardown(app: Application, path: string) {
console.log(`TodoService teardown at path: ${path}`);
}
}Method overloads for handling different parameter combinations.
interface ServiceOverloads<
Result = any,
Data = Partial<Result>,
ServiceParams = Params,
PatchData = Partial<Data>
> {
/**
* Create multiple records at once
*/
create?(data: Data[], params?: ServiceParams): Promise<Result[]>;
/**
* Update a specific record by ID
*/
update?(id: Id, data: Data, params?: ServiceParams): Promise<Result>;
/**
* Update all records matching query
*/
update?(id: null, data: Data, params?: ServiceParams): Promise<Result[]>;
/**
* Patch a specific record by ID
*/
patch?(id: Id, data: PatchData, params?: ServiceParams): Promise<Result>;
/**
* Patch all records matching query
*/
patch?(id: null, data: PatchData, params?: ServiceParams): Promise<Result[]>;
/**
* Remove a specific record by ID
*/
remove?(id: Id, params?: ServiceParams): Promise<Result>;
/**
* Remove all records matching query
*/
remove?(id: null, params?: ServiceParams): Promise<Result[]>;
}Complete service interface combining base methods and overloads.
type Service<
Result = any,
Data = Partial<Result>,
ServiceParams = Params,
PatchData = Partial<Data>
> = ServiceMethods<Result, Data, ServiceParams> & ServiceOverloads<Result, Data, ServiceParams, PatchData>;Interface for client-side services (e.g., REST, WebSocket clients).
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[]>;
}Interface for defining custom service methods beyond the standard CRUD operations.
/**
* Interface for custom service methods
*/
type CustomMethod<T = any, R = T, P extends Params = Params> = (data: T, params?: P) => Promise<R>;
/**
* Type helper for defining multiple custom methods
*/
type CustomMethods<T extends { [key: string]: [any, any] }> = {
[K in keyof T]: (data: T[K][0], params?: Params) => Promise<T[K][1]>;
};Usage Example:
interface MessageServiceCustomMethods {
markAsRead: CustomMethod<{ messageId: number }, { success: boolean }>;
bulkDelete: CustomMethod<{ ids: number[] }, { deleted: number }>;
}
class MessageService implements ServiceInterface, MessageServiceCustomMethods {
// Standard CRUD methods...
async markAsRead(data: { messageId: number }, params?: Params) {
// Mark message as read
return { success: true };
}
async bulkDelete(data: { ids: number[] }, params?: Params) {
// Delete multiple messages
return { deleted: data.ids.length };
}
}
// Register with custom methods
app.use("messages", new MessageService(), {
methods: ["find", "get", "create", "markAsRead", "bulkDelete"]
});/**
* Extract the Result type from a service
*/
type ServiceGenericType<S> = S extends ServiceInterface<infer T> ? T : any;
/**
* Extract the Data type from a service
*/
type ServiceGenericData<S> = S extends ServiceInterface<infer _T, infer D> ? D : any;
/**
* Extract the Params type from a service
*/
type ServiceGenericParams<S> = S extends ServiceInterface<infer _T, infer _D, infer P> ? P : any;