Skip to main content

Service

1. Overview#

Service 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 decorator#

Technically, a valid service handler in CellularJS must be decorated by Service decorator. Service decorator options includes:

2.1. Scope#

Abstraction 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 handler#

In 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 proxy#

Service 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 providers#

In 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() {    // ...  }}