Framework to help building a REST API using typescript and node.
Install module:
npm install kiwi-server --savenpm install kiwi-server-cli -gOptional: https://github.com/ollita7/kiwi-cli
Add these options to the
tsconfig.jsonfile of your project:{"emitDecoratorMetadata": true,"experimentalDecorators": true}
Create your first controller class
user-controller.tsimport{Get,Post,Put,JsonController,Param,Body,QueryParam,Authorize,HeaderParam,Delete}from'../../../src/index';import{UserModel}from'../../models/models';import{isNil}from'lodash';import{Utils}from'../../utils'; @JsonController('/user')exportclassUserController{constructor(){} @Authorize(['Admin']) @Post('/create')publiccreate(@Body()user: UserModel){user.id=Utils.userList.length+1;Utils.userList.push(user);returnuser;} @Authorize(['Admin']) @Get('/get/:id')publicgetById(@Param('id')id: number){varuser=Utils.userList.filter(function(obj){returnobj.id===id;});returnuser;} @Authorize(['Admin']) @Put('/update')publicupdate(@Body()user: UserModel){letuserAux=Utils.userList.find(x=>x.id==user.id);letindex=Utils.userList.indexOf(userAux);Utils.userList[index]=user;returntrue;} @Authorize(['Admin']) @Delete('/delete/:id')publicdelete(@Param('id')id: number){Utils.userList=Utils.userList.filter(function(obj){returnobj.id!==id;});returntrue;}}
We can use QueryParams to get an object with the keys and values that we send on the url.
For example if you send something like http://.../testcontroller/queryparam/1?name=guille&lastname=fernandez you will receive an object like below:
@Get('/listFilter')publiclistFilter(@QueryParam()params: any){if(!isNil(params)){varusers=Utils.userList.filter(function(obj){returnobj.name===params.name&&obj.age===+params.age;});}returnusers;}
{"name": "guille","age": 33}
We can use HeaderParams to get http headers. In the next example we are going to receive the token HTTP header if it exists.
@Get('/search/:name')publicqueryparam(@Param('name')name: string, @HeaderParam('token')token: string){this.aux.print(token);if(!isNil(name)){varusers=Utils.userList.filter(function(obj){returnobj.name===name;});}returnusers;}
After creating the controller, create the server that uses that controller.
import{createKiwiServer}from'kiwi-server';import{UserController}from'./controllers/user/user-controller';constoptions={controllers: [UserController],};constserver=createKiwiServer(options);server.listen(8086);
You can create middlewares to execute activities before and after the execution of an action.
For example to enable CORS we use a specific middleware that is in charge of adding the HTTP headers for that. It's important to execute
nextif you want the flow to continue executing. Otherwise the flow finishes and you must do something with the response, if you don't the client never gets a response. Below is an example that executes before any action.Also you can add the order that you want to execute your middlewares:
import{IMiddleware}from'../../src/middlewares/middleware';import{MiddlewareAfter}from'../../src/decorators/middlewareAfter';import*ashttpfrom'http'; @MiddlewareAfter(1)exportclassUserMiddlewareimplementsIMiddleware{execute(request: http.IncomingMessage,response: http.ServerResponse,next: any){response.setHeader('Authorization','token');console.log('UserMiddleware execute');next();}}
On the controller specify what actions need to be authorized, using the
@Authorizedecorator. In the following example we only need to authorize theputaction. You can also put the decorator in the controller if all the actions need to be authorized.@Get('/list')publiclistAll(){returnUtils.userList;} @Authorize(['Admin']) @Put('/update')publicupdate(@Body()user: UserModel){letuserAux=Utils.userList.find(x=>x.id==user.id);letindex=Utils.userList.indexOf(userAux);Utils.userList[index]=user;returntrue;}
On the server define the function that is going to be executed everytime an action or a controller has the
@Authorizedecorator. If that function returnsfalsethe service is going to return 401 HTTP error, in other case it will continue the normal execution path.import{createKiwiServer}from'kiwi-server';import{UserController}from'./controllers/user/user-controller';asyncfunctionvalidateAuthentication(request: http.IncomingMessage,roles: Array<string>): Promise<AuthorizeResponse|boolean>{ console.log(roles);returnnewAuthorizeResponse(403,'custom message');// return true if want to continue execution}constoptions={controllers: [UserController],authorization: validateAuthentication}constserver=createKiwiServer(options);server.listen(8086);
You can enable cross domain by configuration
import{createKiwiServer}from'kiwi-server';import{UserController}from'./controllers/user/user-controller';constoptions={controllers: [UserController],cors: {enabled: true,domains: ['domain1.com','domain2.com']}}constserver=createKiwiServer(options);server.listen(8086);
You can add a prefix for all the URLs. In the following example, all the URLs will have the
v1/prefix:import{createKiwiServer}from'kiwi-server';import{UserController}from'./controllers/user/user-controller';constoptions={controllers: [UserController],prefix: 'v1/'}constserver=createKiwiServer(options);server.listen(8086);
We can set variable in context to use on methods.
@Post('/test123')publictest23(@Body()body: any, @Context('ctx')my_context: any){returnbody;}You can use dependency injection in your controllers, by adding arguments to the constructor. Then you can use that in any method that you want.
import{Get,Post,Put,JsonController,Param,Body,QueryParam,Authorize,HeaderParam,Delete}from'../../../src/index';import{UserModel}from'../../models/models';import{isNil}from'lodash';import{AuxiliaryFunctions}from'../../auxiliaryFunctions'; @JsonController('/user')exportclassUserController{constructor(privateaux: AuxiliaryFunctions){} @Get('/search/:name')publicqueryparam(@Param('name')name: string, @HeaderParam('token')token: string){this.aux.print(token);if(!isNil(name)){varusers=Utils.userList.filter(function(obj){returnobj.name===name;});}returnusers;}}
socket.io is integrated to our framework. Enable socket support by adding the
socketproperty to the options.constoptions={controllers: [UserController],documentation: {enabled: true,path: '/apidoc'},socket: true}constserver=createKiwiServer(options,socketInit);functionsocketInit(){constio=getSocket();io.on('connection',(socket: any)=>{socket.userId=socket.handshake.query.user;});}
Finally use
getSocketin any place of the application and start using it.
Enable automatic swagger documentation, setting the path where it will be accessible.
import{createKiwiServer}from'kiwi-server';import{UserController}from'../controllers/user/user-controller';constoptions={controllers: [UserController],prefix: 'v1/',cors: true,documentation: {enabled: true,path: '/apidoc'}}constserver=createKiwiServer(options);server.listen(8086);
Decorate your models
import{IsString,IsNumber,IsArray}from'../../src/index'exportclassAddressModel{ @IsString()publicstreet: string; @IsNumber()publicnumber: number;}exportclassUserModel{ @IsNumber()publicid: number; @IsString()publicname: string; @IsString()publiclastname: string; @IsNumber()publicage: number; @IsArray(()=>AddressModel)publicaddress: AddressModel[];}
Visit the documentation page, in this example it would be at http://localhost:8086/v1/apidoc
