UNPKG

@o-galaxy/ether

Version:

Rest Api Framework

339 lines (246 loc) 8.34 kB
# Ether ## Open Galaxy - Ether REST API Framework, module based, each module constructed of - + controller(s) + provider(s) + guard(s) + middleware(s) Those are the building blocks of an Ether API application. <br> <br> <br> ## API <br> ### **ether/core** <br> #### Controller `Controller(options: {path: string} = { path: '/'})` Decorator that marks a class as a Controller. <br> a controller is a class where each class-method defines a route. <br> (to define a route you must decorate the class-method with an [Rest-Method decorator](REST-Method)). ```ts import { Request, Response, NextFunction } from "express"; import { UserHandler } from "./user.handler"; import { Controller, Get } from "@o-galaxy/ether/core"; @Controller({ path: '/user' }) export class UserController { constructor(private userHandler: UserHandler) {} @Get() async getUser(req: Request, res: Response, next: NextFunction) { const uid = res.locals.uid; try { let result = await this.userHandler.getUser(uid); return res.send(result); } catch (error) { next(error) } } } ``` <br> <hr> <br> #### REST Methods `<METHOD>(route: string = '/', middlewares: (Array<RequestHandler> | RequestHandler) = [])` <br> * **Get** <br> `Get(route: string = '/', middlewares: (Array<RequestHandler> | RequestHandler) = [])` Decorator that marks a class-method as a Get method for the provided `route`, where the provided middlewares precede the current handler method. <br> * **Post** <br> `Post( : string = '/', middlewares: (Array<RequestHandler> | RequestHandler) = [])` Decorator that marks a class-method as a Post method for the provided `route`, where the provided middlewares precede the current handler method. <br> * **Put** <br> `Put(route: string = '/', middlewares: (Array<RequestHandler> | RequestHandler) = [])` Decorator that marks a class-method as a Put method for the provided `route`, where the provided middlewares precede the current handler method. <br> * **Delete** <br> `Delete(route: string = '/', middlewares: (Array<RequestHandler> | RequestHandler) = [])` defines a Delete method for the provided `route`, where the provided middlewares precede the current handler method. <br> * **All** <br> `All(route: string = '/', middlewares: (Array<RequestHandler> | RequestHandler) = [])` Decorator that marks a class-method as a route handler for all api methods, for the provided `route`, where the provided middlewares precede the current handler method. <br> <hr> <br> #### Guard `Guard()` ```ts interface IGuard { guard(req:Request, res:Response): (boolean | Promise<boolean>); } ``` <br> Decorator that marks a class as a Guard. <br> a guard is a middleware on a module level. <br> It's basically a class implementing the `IGuard` interface. <br> The `guard` method implements the logic of the guard middleware, returning `false` value of throwing an error will lead to an error handler. <br> ```ts import { Guard } from "@o-galaxy/ether/core"; import { IGuard } from "@o-galaxy/ether/models"; @Guard() export class AuthUserGuard implements IGuard { async guard(req, res): Promise<boolean> { try { let { authorization } = req.headers; authorization = this.parseAuthHeader(authorization); if(!authorization) { return false; } // ... return true; } catch (error) { throw error; } } parseAuthHeader(header: string): string { if(header.indexOf('Bearer') != 0) { throw 'bad auth header'; } return header.slice('Bearer '.length); } } ``` <br> <hr> <br> #### Provider Decorator that marks a class as a Provider. <br> To inject a provider class in another controller / provider / guard class constructor the class must be decorated with `@Provider()`. a provider is a class that ideally do one of : * holds the api call's business logic. * function as a separation layer between the controller and the db layer. * function as a generic (or not) utility<br> ```ts @Provider() export class UserProvider { public async findAndUpdateUser(uid: string, email: string, payload: any ) { try { const user = await UserModel.findOneAndUpdate( // ... ); return user; } catch (error) { throw error; } } } ``` <br> <hr> <br> #### Module `Module(params: Partial<ModuleParameters>)` <br> ```ts interface ModuleParameters { path: string; controllers: Array<any>; providers: Array<any>; modules: Array<any>; guards: Array<any>; } ``` <br> Modules are a way to group together set of code; controllers, providers, middlewares, that have related functionalities and workflow. <br> Modules can be plugged into other modules, by doing so, any routes defined in the sub-module, prefixed by the path of the module is plugged into. <br> ```ts import { Module } from "@o-galaxy/ether/core"; import { LogisterController } from './logister/logister.controller'; import { UserController } from './user/user.controller'; import { AuthUserGuard } from '../../guards/auth-user.guard'; @Module({ path: '/v1/', guards: [ AuthUserGuard ], controllers: [ UserController, LogisterController ], }) export class AuthUserModule { } ``` <br> <hr> <br> <br> ### **ether/common** <br> #### build `build(module: any): express.Router` <br> function used to build a router from a `Module` decorated class. <br> ```ts // -- file: app.module.ts import { Module } from "@o-galaxy/ether/core"; import { PublicModule } from "../v1/modules/public/public.module"; import { AdminModule } from "../v1/modules/admin/admin.module"; import { AuthUserModule } from "../v1/modules/auth-user/user.module"; @Module({ modules: [ AdminModule, PublicModule, AuthUserModule, ] }) export class AppModule { } ``` ```ts // -- file: index.ts import { build } from 'ether/common' import { AppModule } from './app.module'; export const apiRouter = build(AppModule); ``` ```ts // -- file: server.ts import { apiRouter } from './api/router' const app = express(); app.use('/api/', apiRouter); app.listen(3000); ``` <br> <hr> <br> #### middlewareFactory `middlewareFactory(handler: RequestHandler, context?: any): () => any` A function used to create a class-method middleware decorator function, from the provided `handler` function, if a `context` object was provided the `handler` function will be bound to it. <br> On the following example, using `middlewareFactory`, we're creating a `Log` middleware decorator function, by decorating `postSubject` route with `Log` decorator, the `Log` middleware will precede the `postSubject` route handler, and write the request url and body to the console. <br> *Note* : <br> The middleware decorator function must code before the Rest-Method decorator. <br> ```ts import { middlewareFactory } from 'ether/core'; export const Log = middlewareFactory((req, res, next) => { console.log('request url: ' + req.originalUrl); console.log('request body: ' + req.body); next(); }) ``` ```ts import { Request, Response, NextFunction } from "express"; import { Controller, Post } from "@o-galaxy/ether/core"; import { SubjectController } from "../../public/subject/subject.controller"; import { Log } from "../../..//middlewares"; @Controller({ path: '/subject' }) export class AdminSubjectController extends SubjectController { @Log() @Post() public async postSubject(req: Request, res: Response, next: NextFunction) { try { const { subject } = req.body; const result = await this.adminSubjectService.postSubject(subject); return res.send(result); } catch(error) { return next(error); } } } ``` ### TODO * system error table - describable errors. * add better errors for injecting non provider classes. * support passing router options to controller. * support using guard as decorator on module level. * defaultize body / params - spec solution or use middlewares (with example). * abstract / hide the (req, res, next) signature, spec for inject body, params, query, etc..