Methodus
[https://sonarcloud.io/api/project_badges/measure?project=nodulusteam_-methodus-server&metric=alert_status]
https://sonarcloud.io/api/project_badges/measure?project=nodulusteam_-methodus-server&metric=coverage
Greetings,
my fellow adventures developer, I welcome you into Methodus.
You did not arrive here by mistake, nor was it an accident, it is your fate.
Methodus will guide you through the evolution of your application as it grows and gains complexity.
But first you should ask yourself this:
- Do you wish to build a nodejs based api monolithic server?
- Would you like to be able to break this monolith into micro-services as you scale it out?
- How about an automatic testing plugin, allowing you to autodocument your apis and test them?
- Would you like to concentrate on your logic code only, without the need to wire it to a specific framework?
- How about event sourcing would you like some of that?
- Are you using typescript?
- Do you need a frontend for this server?
Well,
https://github.com/nodulusteam/-methodus-client
https://github.com/nodulusteam/-methodus-server
https://github.com/nodulusteam/-methodus-describe
https://github.com/nodulusteam/-methodus-contracts
Methodus is a micro-service & RPC framework, so let's build a micro-service from scratch using this beautiful framework.
The controller
Methodus uses controllers in the same manner the express framework does. It binds functions or class methods to a route. and then routes the requests to these functions.
in Methodus it will look like this
import { Method, MethodConfig, Files, Verbs, MethodType, Body, Response, Request, Param, Query, SecurityContext, MethodError, MethodResult } from '@methodus/server';
import * as fs from 'fs';
import * as path from 'path';
@MethodConfig('@ns/hellow-world')
export class Hello {
@Method(Verbs.Post, '/api/hello', [upload.any(), autoReap])
public static async upload( @Files('0') file: any, @Query('originalname') originalname: string, @Query('keep_original') keepOriginalName: boolean = true) {
return new MethodResult(result);
};
@Method(Verbs.Get, '/api/hello/:file_id')
public static async getById( @Param('file_id') file_id) {
return new MethodResult(result);
};
@Method(Verbs.Get, '/api/hello/name/:file_name/')
public static async getByName( @Param('file_name') file_name, @SecurityContext() att) {
return new MethodResult(result);
};
@Method(Verbs.Delete, '/api/hello/id/:file_id')
public static async delete( @Param('file_id') file_id, @SecurityContext() att) {
return new MethodResult(deleteResult);
};
}
The controller class is decorated with a @MethodConfig decorator stating the name of the npm package the controller refers to.
Each method is then decorated with a @Method decorator and the route parameters (Http verb, path, middlewares).
The arguments passed into these decorated methods will be mapped according to the argument variables that precede them.
same as body in express, a name can be passed to get a specific key within the body object
| Decorator | Verb | Description |
| @Body() | Post | |
| @Query() | All | All same as query in express, a name can be passed to get a specific key within the query object |
| @Param() | All | All same as param in express, a name can be passed to get a specific key within the param object. |
| @File() | Post | same as files in express + multer , a name can be passed to get a specific key within the body object |
| Special Mappings | | |
| @Response() | All | same as res in express, the mapping should be used when we need to pipe a stream to the response |
| @Request() | All | same as req in express, the mapping should be used when we need to pipe a stream to the request |
A Methodus method should return an object of type MethodResult. this object can be used to set the status code for the response as well as paging and total records information.
@Method(Verbs.Get, '/api/hello/name/:file_name/')
public static async getByName( @Param('file_name') file_name, @SecurityContext() att) {
return new MethodResult(result);
};
Methodus methods are automatiaclly loged using the Trace log level.
you may have noticed the use of static methods for the controller class. this is not mandatory as you may use either static or instance approach,
as long as you do that for all the methods in the class.
Server activation
The controller is ready, let's bind it to a methodus server.
in our node app we create an entry point in the form of host.ts file.
this host file starts an express server using the configured port and binds our controller to it.
import { ServerType, Server, MethodType, MethodusConfig } from '@methodus/server';
import { Hello } from './controllers/hello-controller';
(async () => {
let config = new MethodusConfig();
config.run(ServerType.Express, { port: +process.env.PORT });
config.use(Hello, MethodType.Local, ServerType.Express);
let server = await new Server(+process.env.PORT).configure(config).start();
})()
the async function is an IIFE ( Immidiatly Invoked Function Expression) that executes the server code, but you may use any invocation method you see fit.
if all goes well you should see
__ _ _|_|_ _ _| _
|||(/_ |_| |(_)(_||_|_>
Starting REST server on port xxxx
which means that every thing wen well and you're ready to browser or postman your routes.
Your microservice ready, but if it is to be consumed using the Methodus rpc it need's to generate a contract.