Skip to content

yviscool/egg-pig

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

egg-pig

nest.js in egg.

NPM version build status coverage

Installation

$ npm i egg-pig --save

Config

// app/config/plugin.js
eggpig: {
  enable: true,
  package: 'egg-pig'
}

Usage

Controller

import { IService, EggAppConfig, Application, Context } from 'egg';
import { Controller, Get, Post } from 'egg-pig';

@Controller('cats') // => /cats
export class CatsController {
    
    constructor(
        private ctx: Context,
        private app: Application,
        private config: EggAppConfig,
        private service: IService,
    ) { }

    @Get()  // => router.get('/cats', index)
    async index() {
        this.ctx.body = 'index';
        // or return 'index'
    }

    @Get('get')   // => router.get('/cats/get', foo)
    async foo() {
        return 'add'
        // or this.ctx.body = 'add'; 
    }

    @Post('/add')   // => router.post('/cats/add', bar)
    async bar(@Body() body) {
        return body;
        // or this.ctx.body = body;
    }

}

another way

import { BaseContextClass } from 'egg';
import { Controller, Get, Post } from 'egg-pig';

@Controller('cats') // => /cats
export class CatsController extends BaseContextClass{

    @Get()
    async foo() {
        return await this.service.foo.bar();
    }
}

return value

@Controller('cats')
export class CatsController {

    @Get('/add')  // router.get('/cats/add', foo)
    async foo() {
        return 'zjl'; // this.ctx.body = 'zjl;
    }

    @Get('bar')    // router.get('/cats/foo', bar)
    async bar() {
        return await this.service.xxx.yyy(); // this.ctx.body = '';
    }
}

use return value replace ctx.body;

Param Decorators

import { Context, Request, Response, Param, Query, Body, Session, Headers, Res, Req, UploadedFile, UploadedFiles, UploadedFileStream, UploadedFilesStream } from 'egg-pig';

@Controller('cats')
export class CatsController {
    public async foo(
        @Request() req,
        @Response() res,
        @Req() req, // alias
        @Res() res,// alias
        @Param() param,
        @Query() query,
        @Body() body,
        @Session() session,
        @Headers() headers,
        @UploadedFile() file,
        @UploadedFiles() fils,
        @UploadedFileStream() stream,
        @UploadedFilesStream() parts
    ) {
        // req=  this.ctx.request;
        // res = this.ctx.response;
        // param = this.ctx.params;
        // query = this.ctx.query;
        // body = this.ctx.request.body;
        // session = this.ctx.session;
        // headers = this.ctx.headers;
        // file = this.ctx.request.files[0]
        // files = this.ctx.request.files;
        // stream = await this.ctx.getFileStream();
        // parts = this.ctx.multipart();
    }
}

route name

import { Controller, Get, Param } from 'egg-pig';

@Controller('cats')
export class CatsController {

    @Get(':id',{ routerName: 'cats'}) // router.get('cats', '/cats/:id', foo)
    async foo(@Param('id') param) {
        return param;
    }
}

restful

import { Resources, Get } from 'egg-pig';

@Resources('cats')    // => router.resources(''cats', /cats', CastController)
// or @Restful('cats')
export class CatsController {

    async index() {
        return 'index';
    }

    async new() {
        return 'new';
    }

    @Get('/add')    //  router.get('/cats/add', add)
    async add() {
        return 'add';
    }

}

You can also use @Restful() Decorator, the same as Resources;

multiple-middleware

  1. use decorator router options
@Controller('/', {middleware: ['homeMiddleware']})   //  this.app.middleware['homeMiddleware']
export class Test {
    
  @Get('/', {middleware: ['apiMiddleware']})  // this.app.middleware['apiMiddleware']
  async index() {
    const ctx = this.ctx;
    return ctx.home + ctx.api;
  }
   
}
  1. use koa-router feature
// router.ts
export default (app:Application) => {

  const { router } = app;

  router.get('/cats/add', async (_,next) => {
    console.log('this is middleware');
    return next();
  });

};


// cats.ts
@Controller('cats')
export class CatsController {

  @Get('/add')
  async foo(){
    this.ctx.body = 'add'; 
  }
}

render-header

import { Render,Controller, Get, Header } from 'egg-pig';

@Controller('home')
export class HomeController {

  @Get()   // /home
  @Render('list.tpl')
  async foo() {
    const dataList = {
      list: [
        { id: 1, title: 'this is news 1', url: '/news/1' },
        { id: 2, title: 'this is news 2', url: '/news/2' }
      ]
    };
    return dataList;
  }

  @Header('ETag','123')
  @Get('etag')
  async bar() {
    ...
  }

  @Header({
      'Etag': '1234',
      'Last-Modified': new Date(),
  })
  @Get('other')
  async baz() {
      ...
  }
}

use

you can see nest.js.

Guard

import { Injectable, CanActivate, ExecutionContext, UseGuards } from 'egg-pig';
import { Observable } from 'rxjs/Observable';

@Injectable()
class XXGuard extends CanActivate{
  canActivate(context: ExecutionContext)
  : boolean | Promise<boolean> | Observable<boolean> {
    return true;
  }
}

@UseGuards(XXGuard)
export class HomeController {
  // @UseGuards(XXGuard)
  public async foo() {
    // some logic
  }
}

Pipe

import { PipeTransform, Injectable, ArgumentMetadata, UsePipes, Param, Query, Body  } from 'egg-pig';

@Injectable()
class XXPipe extends PipeTransform{
  async transform(value: any, metadata: ArgumentMetadata) {
    return value;
  }
}

@UsePipes(XXPipe)
export class HomeController {
  // @UsePipes(XXPipe)
  async foo(@Param('xx', XXPipe) param; @Body('xx', XXPipe) body, @Query(XXPipe) quey) {
    // some logic
  }
}

Build-in ValidationPipe

import { ParseIntPipe, ValidationPipe } from 'egg-pig';
import { IsInt, IsString, Length } from "class-validator";

class User {

    @IsInt()
    age: number;

    @Length(2, 10)
    firstName: string;

    @IsString()
    lastName: string;

    getName() {
        return this.firstName + ' ' + this.lastName;
    }

}

@Controller('pipetest')
export class PipetestController {

    @Get('parseint')
    async foo(@Query('id', ParseIntPipe) id) {
        return id;
    }

    @Post('validation')
    async bar(@Body(new ValidationPipe({ transform: true })) user: User) {
        return user.getName();
    }

}

Notice You may find more information about the class-validator/ class-transformer

Interceptor

import { EggInterceptor, UseInterceptors, Interceptor} from 'egg-pig';
import { Injectable, EggInterceptor, ExecutionContext, CallHandler } from 'egg-pig';
import { Observable } from 'rxjs/Observable';
import { tap } from 'rxjs/operators';

@Injectable()
class LoggingInterceptor extends EggInterceptor {
  intercept(
    context: ExecutionContext,
    call$: CallHandler,
  ): Observable<any> {
    console.log('Before...');
    const now = Date.now();
    return call$.handle().pipe(
      tap(() => console.log(`After... ${Date.now() - now}ms`)),
    );
  }
}

@UseInterceptors(LoggingInterceptor)
export class HomeController {

  //@UseInterceptors(LoggingInterceptor)
  public async foo() {
    // some login
  }
}

Build-in interceptor

import { UseInterceptors, ClassSerializerInterceptor } from 'egg-pig';

class RoleEntity {
    id: number;
    name: string;
    constructor(partial: Partial<RoleEntity>) {
        Object.assign(this, partial);
    }
}

class UserEntity {
    id: number;
    firstName: string;
    lastName: string;

    @Exclude()
    password: string;

    @Expose()
    get fullName() {
        return `${this.firstName} ${this.lastName}`
    }
    @Transform(role => role.name)
    role: RoleEntity

    constructor(partial: Partial<UserEntity>) {
        Object.assign(this, partial);
    }
}


@Controller('serializer')
@UseInterceptors(new ClassSerializerInterceptor())
export class SerializerController {

    @Get()
    async foo() {
        return [new UserEntity({
            id: 1,
            firstName: 'jay',
            lastName: 'chou',
            password: '123456',
            role: new RoleEntity({ id: 1, name: 'admin' })
        }), new UserEntity({
            id: 2,
            firstName: 'kamic',
            lastName: 'xxxla',
            password: '45678',
            role: new RoleEntity({ id: 2, name: 'user01' })
        })]
    }
}

filter

import { Controller, Get } from 'egg-pig';


@Controller('cats')
export class CatsController {

  @Get()
  async foo(){
    throw new Error('some ..')
  }  
}

When the client calls this endpoint,the eggjs will hanlde.

import {
  Controller,
  Get,
  HttpException,
  HttpStatus,
} from 'egg-pig';

@Controller('cats')
export class CatsController {
  @Get()
  async foo(){
     throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
  }  
}

When the client calls this endpoint, the response would look like this:

{
  "statusCode": 403,
  "message": "Forbidden"
}

another way

  async foo(){
    throw new HttpException({
     status: HttpStatus.FORBIDDEN,
      error: 'This is a custom message',
    }, 403);
  }  
class ForbiddenException extends HttpException {
  constructor() {
    super('Forbidden', HttpStatus.FORBIDDEN);
  }
}

async foo(){
    throw new ForbiddenException()
}  

UseFilters

import { Controller, Get, HttpException, HttpStatus, ExceptionFilter, UseFilters, Catch } from 'egg-pig';

@Catch(HttpException)
class HttpExceptionFilter extends ExceptionFilter {
  catch(exception) {
    this.ctx.status = HttpStatus.FORBIDDEN;
    this.ctx.body = {
      statusCode: exception.getStatus(),
      timestamp: new Date().toISOString(),
      path: ctx.req.url
    }
  }
}

class ForbiddenException extends HttpException {
  constructor() {
    super('Forbidden', HttpStatus.FORBIDDEN);
  }
}


@Controller('cats')
@UseFilters(HttpExceptionFilter)
export class CatsController {
  @Get()
  async foo(){
    throw new ForbiddenException();
  }  
}

tips

CanActivate, EgggInterceptor, PipeTransform, ExceptionFilter are all abstract class which extends egg/BaseContextClass, it means you can use this on methods . such as

class XXPipe extends PipeTransform{
  async transform(value, metadata){
    await this.service.foo.bar();
    console.log(this.config)
    this.foo(); 
    return value;
  }
  foo(){
    // logic
  }
}

global

global prefix/guards/pipes/interceptors/filters

// config.defalut.js
export default (appInfo: EggAppConfig) => {

  config.globalPrefix = '/api/v1';

  config.globalGuards = [new FooGuard(), FooGuard];

  config.globalPipes = [new FooPipe(), FooPipe];

  config.globalInterceptors = [new FooIn(), FooIn];

  config.globalFilters = [new FooFilter(), FooFilter]

  return config;
};

Custom Decorators

import { BaseContextClass } from 'egg';
import { Controller, Get, PipeTransform, Pipe, createParamDecorator } from 'egg-pig';

const User = createParamDecorator((data, ctx) => {
  // data = 'test' => @User('test')
  return ctx.user;
});

@Pipe()
class APipe extends PipeTransform {
  transform(val, metadata) {
    // val => ctx.user;
    val && metadata;
    return val;
  }
}


@Controller('user')
export class HomeController {
  @Get()
  public async index(@User('test', APipe) user){
    return user;
  }
}