Managing Service Logic with FeathersJS Hooks
Learn how to use FeathersJS hooks to decouple business logic from data access, keeping your services clean and your application scalable.
18 Oct 2026, 21:47 UTC

When building real-time applications, it is common for service methods to become bloated with validation, authentication checks, and notification triggers. If business logic is buried directly inside a service method, the code becomes difficult to test and nearly impossible to reuse across different transport layers, such as REST APIs and WebSocket events.
The solution in FeathersJS is the hooks system. Hooks act as middleware, intercepting service calls at specific lifecycle stages. By moving logic out of the service method and into hooks, you keep your services focused purely on data access while handling cross-cutting concerns in modular, reusable functions.
Understanding the Hook Lifecycle
Every hook is an asynchronous function that receives a single context object. This object contains the method being called, the data being sent, parameters, and eventually the result of the service call. Hooks are categorized into four types:
- before: Executed before the service method. Use this for validation, sanitization, or checking permissions.
- after: Executed after the service method succeeds. Use this for formatting output or triggering side effects like sending emails.
- wrap: Wraps the entire service call, allowing you to handle both the result and potential errors in a single block.
- error: Executed only if a previous hook or the service method fails. Use this for logging or transforming error messages for the client.
Practical Example: Validation and Transformation
Imagine a users service where you must ensure every new user provides an email and that you never send the password hash back to the client. Instead of writing this logic in the create method, you can use a before and after hook.
The following example demonstrates a hook that validates input and another that strips sensitive data. These should be run in a Node.js environment with FeathersJS installed:
// services/users/hooks/validate.js
export const validateUser = async (context) => {
const { email } = context.data;
if (!email || !email.includes('@')) {
throw new Error('A valid email is required');
}
// Sanitize input by making email lowercase
context.data.email = email.toLowerCase();
return context;
};
// services/users/hooks/stripPassword.js
export const stripPassword = async (context) => {
// Remove the password from the result before it returns to the client
if (context.result) {
delete context.result.password;
}
return context;
}
Register these hooks in your service configuration to apply them to specific methods:
import { service } from '@feathers';
import { validateUser } from './hooks/validate';
import { stripPassword } from './hooks/stripPassword';
export default service('users', userModel, {
hooks: {
before: {
create: [validateUser]
},
after: {
create: [stripPassword],
find: [stripPassword]
}
}
});
Architectural Trade-offs
While hooks are powerful, they introduce risks. Because hooks mutate the context object, a long chain of hooks can make it difficult to trace how data changed. If one hook modifies context.data and a subsequent hook fails, you may encounter inconsistent states if the operation is not atomic.
Avoid placing complex business workflows inside hooks. If a process requires multiple database queries and complex conditional branching, consider moving that logic into a dedicated service layer or a custom service method. Use hooks for "plumbing"—logic that should happen to the request regardless of the specific business-rule intent.
Verifying Hook Execution
To ensure your hooks are firing as expected, you can create a simple logger hook. Place this at the start of your chain to inspect the state of the context during a request:
export const logContext = async (context) => {
console.log(`Method: ${context.method}, Data:`, context.data);
return context;
};
Run your service via a POST request or WebSocket event and check your terminal. If the logs do not appear, verify that the hook is correctly registered in the service configuration and that the method being called matches the hook's assigned method.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.