Service
#
1. OverviewService terminology is used in many situations with different meaning. In this article, the service that we talk about is a stuff that expose business functionality.
#
2. Service decoratorTechnically, a valid service handler in CellularJS must be decorated by Service
decorator. Service
decorator options includes:
#
2.1. ScopeAbstraction and encapsulation are the most important fundamental concepts of programming, not just in OOP world. To help you protect your excellent code from unexpected behaviour, CellularJS provides 3 types of access scope for service.
publish
: with this scope, the service is accessible from any client. It is named as publish(~published), just to emphasize the difference from the normal public modifier of a class.space
: accessible only from cells or services within the same space.private
: accessible only from within the current cell.
Example: SignIn
service with publish
scope is accessible from any where.
// ./services/sign-in.service.tsimport { Service, ServiceHandler } from '@cellularjs/net';
@Service({ scope: 'publish' })export class SignIn implements ServiceHandler { handle() { return { token: '**.*******.**', rf_token: '**.****.**', }; }}
Example 2: InnerTask
service with private
scope is accessible from internal cell only.
// ./services/inner-task.service.tsimport { Service, ServiceHandler } from '@cellularjs/net';
@Service({ scope: 'private' })export class InnerTask implements ServiceHandler { handle() { // ... }}
#
3. Service handlerIn CellularJS, a service handler is a class that implements the ServiceHandler
interface or at least it must have the handle
method. This method will be invoked by CellularJS whenever there is request to the service.
note
If your handle
method don't return IRS
object, the returned data will be wrapped inside IRS
body and this response will have default success response header.
Eg: the code below has the same meaning.
import { Service, ServiceHandler, IRS } from '@cellularjs/net';
// Type 1: return IRS object.@Service({ scope: 'publish' })export class Task implements ServiceHandler { handle() { return new IRS({ status: 200 }, { data: true }); }}
// Type 2: return other data type.@Service({ scope: 'publish' })export class Task implements ServiceHandler { handle() { return { data: true }; }}
#
4. Service helpers#
4.1. Service proxyService proxy is a type of ServiceHandler
, it help you modify/add logic to a specific service. You can use addServiceProxies
to register proxies for a service.
What is NextHandler
?
You can consider it as next
function in express
.
Instead of resolve all handlers/proxies and it's dependencies at once, NextHandler
allow you to resolve and run next (proxy/service) handler lazily.
Example: create simple AuthGuard
decorator.
import { Injectable } from '@cellularjs/di';import { Service, ServiceHandler, NextHandler, addServiceProxies, IRS, IRQ } from '@cellularjs/net';
@Injectable()class AuthGuardProxy implements ServiceHandler { constructor( private irq: IRQ, private nextHandler: NextHandler, ) { }
handle() { const { irq, nextHandler } = this;
if (!irq.header.accessToken) { throw new IRS({ status: 401 }, { err: 'UNAUTHORIZED' }); }
// It work like a pipeline of service handlers return nextHandler.handle(); }}
const AuthGuard = () => service => { addServiceProxies(service, [AuthGuardProxy]);
return service;}
@AuthGuard()@Service({ scope: 'publish' })export class CreatePost { handle() { // ... }}
#
4.2. Service providersIn addition to providers configured in Cell
decorator, you can add more providers to a specific service with addServiceProviders
.
note
Service providers added by addServiceProviders
differ from normal providers. Service providers are resolved upon each internal request. This means that a permanent provider is only "permanent" within a single request.
Example: create a dummy AuthGuard
decorator that support role based access control.
import { Injectable, Inject } from '@cellularjs/di';import { Service, ServiceHandler, NextHandler, addServiceProxies, addServiceProviders, IRS, IRQ } from '@cellularjs/net';
@Injectable()class AuthGuardProxy implements ServiceHandler { constructor( private irq: IRQ, private nextHandler: NextHandler, @Inject('allowedRoles') private allowedRoles: string[], ) { }
handle() { const { irq, nextHandler, allowedRoles } = this;
if (!irq.header.accessToken) { throw new IRS({ status: 401 }, { err: 'UNAUTHORIZED' }); }
const user = { role: 'guest' }; if (allowedRoles.includes(user.role)) { throw new IRS({ status: 403 }, { err: 'ACCESS_FORBIDDEN' }); }
return nextHandler.handle(); }}
const AuthGuard = (allowedRoles) => service => { addServiceProxies(service, [AuthGuardProxy]); addServiceProviders(service, [ { token: 'allowedRoles', useValue: allowedRoles }, ]);
return service;}
@AuthGuard(['admin'])@Service({ scope: 'publish' })export class CreatePost { handle() { // ... }}