Skip to main content

Interceptors

The service-level interceptor option wraps every operation on a service instance. It receives a next callback, the CommandInfo for the current operation, and a reference to the service itself.

Use interceptors for cross-cutting concerns that apply to all operations uniformly — logging, tracing, retries, access control.


Request logging

export class CustomersService extends MongoCollectionService<Customer> {
constructor(db: Db, private readonly logger: Logger) {
super(Customer, {
db,
interceptor: async (next, command, _this) => {
const start = Date.now();
try {
const result = await next();
logger.debug(`${command.method} completed in ${Date.now() - start}ms`);
return result;
} catch (err) {
logger.error(`${command.method} failed`, err);
throw err;
}
},
});
}
}

Read-only enforcement

Reject write operations entirely for a read-only replica:

interceptor: async (next, command) => {
if (command.crud !== 'read') {
throw new ForbiddenError('This service is read-only');
}
return next();
}

Retry on transient errors

interceptor: async (next, command) => {
for (let attempt = 1; attempt <= 3; attempt++) {
try {
return await next();
} catch (err: any) {
if (attempt === 3 || !isTransientError(err)) throw err;
await sleep(50 * attempt);
}
}
}

Interceptors vs lifecycle hooks

InterceptorLifecycle hook
ScopeEvery operationSpecific operation type
AccessCommandInfo, full next() wrapperTyped command + result
Use forLogging, tracing, retries, access controlTimestamps, validation, audit log, cache bust

Use hooks when you need typed access to command.input or the operation result. Use the interceptor when you want to wrap the call generically.


Full API reference

MongoServiceinterceptor, Options