Core components
Server core
Services (FeathersJS)
26 min
overview the services component is a fundamental element of the ir engine's server core that organizes business logic and data operations into modular, resource specific units each service manages a particular type of data or functionality, providing standardized methods for creating, reading, updating, and deleting resources by implementing the service oriented architecture pattern of feathersjs, this component creates a clean separation of concerns and enables consistent api access across different resources this chapter explores the implementation, structure, and usage of services within the ir engine core concepts service oriented architecture services organize application functionality into resource specific modules resource encapsulation each service manages a specific type of data or functionality separation of concerns business logic is isolated by resource type standardized interfaces consistent methods across different resources modular design services can be developed and tested independently reusability common patterns can be shared across services this architectural approach creates a maintainable and scalable codebase standard service methods feathersjs services implement a standard set of methods find(params) retrieve multiple resources, often with filtering get(id, params) retrieve a single resource by its identifier create(data, params) create a new resource update(id, data, params) replace an existing resource completely patch(id, data, params) update parts of an existing resource remove(id, params) delete a resource these methods provide a consistent interface for all data operations service registration services are registered with the application to make them available path binding each service is associated with a specific url path method mapping http methods are mapped to service methods middleware integration services participate in the request pipeline event emission service operations trigger events for real time updates centralized registration all services are registered in a coordinated way this registration process connects services to the application framework implementation service class definition services are typically implemented as classes // simplified from src/user/user/user class ts import { knexservice } from '@feathersjs/knex'; import { usertype, userdata, userparams, userpatch } from '@ir engine/common/schemas'; import { application } from ' / /declarations'; / user service for managing user accounts / export class userservice extends knexservice< usertype, // the type of a full user object userdata, // the type for data when creating a user userparams, // the type for parameters passed to service methods userpatch // the type for data when patching a user \> { / constructor for the user service @param options configuration options @param app feathers application / constructor(options any, app application) { // call the parent constructor with options super(options); // store the application instance this app = app; } / find users with optional filtering @param params query parameters @returns promise resolving to users / async find(params? userparams) promise\<usertype\[]> { // use the parent implementation for basic functionality return super find(params); } / get a user by id @param id user id @param params query parameters @returns promise resolving to a user / async get(id string, params? userparams) promise\<usertype> { // use the parent implementation for basic functionality return super get(id, params); } / create a new user @param data user data @param params query parameters @returns promise resolving to the created user / async create(data userdata, params? userparams) promise\<usertype> { // use the parent implementation for basic functionality return super create(data, params); } / update a user (replace completely) @param id user id @param data user data @param params query parameters @returns promise resolving to the updated user / async update(id string, data userdata, params? userparams) promise\<usertype> { // use the parent implementation for basic functionality return super update(id, data, params); } / patch a user (partial update) @param id user id @param data partial user data @param params query parameters @returns promise resolving to the patched user / async patch(id string, data userpatch, params? userparams) promise\<usertype> { // use the parent implementation for basic functionality return super patch(id, data, params); } / remove a user @param id user id @param params query parameters @returns promise resolving to the removed user / async remove(id string, params? userparams) promise\<usertype> { // use the parent implementation for basic functionality return super remove(id, params); } } this code defines a service class that extends knexservice for database operations specifies type parameters for type safety implements the standard service methods uses the parent class implementation for basic functionality can be extended with custom business logic as needed service registration services are registered with the application in dedicated setup files // simplified from src/user/user/user service ts import { application } from ' / /declarations'; import { userservice } from ' /user class'; import { userpath, usermethods } from ' /user shared'; import hooks from ' /user hooks'; / configures the user service @param app feathers application / export default function(app application) void { // service options const options = { model app get('knexclient'), name 'user', paginate app get('paginate') }; // register the service app use(userpath, new userservice(options, app)); // get the registered service const service = app service(userpath); // set up hooks service hooks(hooks); } this function creates options for the service, including the database connection registers the service on a specific path using app use() retrieves the registered service sets up hooks for the service (more on hooks in a later chapter) service aggregation all services are aggregated and registered in a central location // simplified from src/services ts import { application } from ' /declarations'; // import service configurators import userservice from ' /user/user/user service'; import authservice from ' /user/authentication/authentication service'; import projectservice from ' /project/project/project service'; import assetservice from ' /asset/asset/asset service'; // other service imports / configures all services for the application @param app feathers application / export default async function(app application) promise\<void> { const logger = app get('logger'); logger info('configuring services'); // configure authentication first app configure(authservice); // configure user related services app configure(userservice); // configure project related services app configure(projectservice); // configure asset related services app configure(assetservice); // configure other services logger info('services configured'); } this function imports all service configurator functions configures authentication first (as it's often a dependency) configures all other services in a logical order logs the progress for monitoring service usage services are used by calling their methods // example of service usage import { application } from ' /declarations'; / creates a new user @param app feathers application @param userdata user data @returns promise resolving to the created user / async function createuser(app application, userdata any) promise\<any> { // get the user service const userservice = app service('user'); // create a new user const user = await userservice create(userdata); return user; } / finds users matching criteria @param app feathers application @param query query parameters @returns promise resolving to matching users / async function findusers(app application, query any) promise\<any> { // get the user service const userservice = app service('user'); // find users matching the query const users = await userservice find({ query }); return users; } / updates a user @param app feathers application @param userid user id @param userdata user data @returns promise resolving to the updated user / async function updateuser(app application, userid string, userdata any) promise\<any> { // get the user service const userservice = app service('user'); // update the user const user = await userservice patch(userid, userdata); return user; } this code gets a reference to a service using app service(path) calls service methods like create() , find() , and patch() passes appropriate parameters to each method handles the returned promises service workflow the complete service workflow follows this sequence sequencediagram participant client as client code participant app as feathers application participant service as service object participant hooks as service hooks participant database as database (via knex) client >>app app service('user') create(userdata) app >>service lookup 'user' service app >>hooks run 'before' hooks hooks >>service modified request service >>database execute database operation database >>service return results service >>hooks run 'after' hooks hooks >>app modified response app >>client return final result this diagram illustrates client code calls a service method through the application the application looks up the registered service before hooks process the request (validation, authorization, etc ) the service executes the core logic, often involving database operations after hooks process the response (formatting, filtering, etc ) the final result is returned to the client service types the ir engine implements several types of services database services services that interact with database tables // example of a database service import { knexservice } from '@feathersjs/knex'; export class projectservice extends knexservice { // knexservice provides implementations for all standard methods // that interact with a database table } these services extend knexservice for database operations map service methods to sql operations handle pagination and filtering manage database connections provide consistent data access patterns custom services services with specialized business logic // example of a custom service import { servicemethods } from '@feathersjs/feathers'; export class analyticsservice implements servicemethods\<any, any> { app application; constructor(options any, app application) { this app = app; } // implement only the methods needed async find(params? any) promise\<any> { // custom implementation for analytics queries const knex = this app get('knexclient'); // complex query logic const results = await knex raw(` select date trunc('day', created at) as day, count( ) as count from events where event type = ? group by day order by day `, \[params query eventtype]); return results; } // other methods can throw "method not implemented" errors // or be implemented as needed } these services implement custom business logic may not use all standard methods often perform complex operations can integrate with external systems provide specialized functionality proxy services services that delegate to other services or external apis // example of a proxy service import axios from 'axios'; import { servicemethods } from '@feathersjs/feathers'; export class weatherservice implements servicemethods\<any, any> { apikey string; constructor(options any) { this apikey = options apikey; } async find(params? any) promise\<any> { // call external weather api const response = await axios get('https //api weather com/forecast', { params { key this apikey, location params query location } }); // transform and return the data return response data; } // implement other methods as needed } these services act as facades for external apis transform data between formats handle authentication with external systems cache responses when appropriate provide a consistent interface for external resources integration with other components services integrate with several other components of the server core database management services use the database client for data operations // example of database integration import { knexservice } from '@feathersjs/knex'; // service class export class userservice extends knexservice { // knexservice uses the provided model (knex client) // to perform database operations } // service registration export default function(app application) void { const options = { model app get('knexclient'), // get the database client name 'user' // table name }; app use('/users', new userservice(options, app)); } this integration uses the knex client for database operations maps service methods to sql queries handles database connections and transactions provides a consistent data access layer abstracts database details from service consumers hooks services use hooks for request and response processing // example of hooks integration import { hooks } from '@feathersjs/feathers'; import { authenticate } from '@feathersjs/authentication/hooks'; // service registration export default function(app application) void { // register the service app use('/users', new userservice(options, app)); // get the registered service const service = app service('/users'); // set up hooks service hooks({ before { all \[authenticate('jwt')], // require authentication for all methods find \[hooks disallow('external')], // prevent external find calls create \[validateuserdata()] // validate data before creation }, after { all \[hooks discard('password')] // remove password from responses } }); } this integration applies hooks to service methods processes requests before they reach the service processes responses before they reach the client implements cross cutting concerns like authentication keeps services focused on core business logic real time communication services emit events for real time updates // example of real time integration // this happens automatically when services are registered // when a service method completes, events are emitted // for example, when userservice create() completes // 1 a 'created' event is emitted with the new user data // 2 the event is published to appropriate channels // 3 connected clients receive the event via websockets // client side code can listen for these events const userservice = app service('users'); userservice on('created', (user) => { console log('new user created ', user); // update ui or take other actions }); this integration automatically emits events for service operations distributes events to connected clients enables real time updates in the user interface maintains data consistency across clients creates a reactive application experience benefits of services the services component provides several key advantages organization structures the application around specific resources standardization provides consistent interfaces for data operations separation of concerns isolates business logic by resource type reusability enables code sharing across similar resources testability makes services easier to test in isolation scalability allows the application to grow with additional services api consistency creates a uniform api for clients these benefits make services an essential foundation for the ir engine's server core next steps with an understanding of how services organize business logic, the next chapter explores how these services interact with the database next database management (knex) docid 7lr9zjl2phlapzzmunasw