api-ms-framework
v1.7.2
Published
An API framework for microservices
Downloads
23
Readme
Microservice template
This repository is a typescript NODEJS application that serves as a template for a microservice API instance for the SHELL ON YOU project.
It is deployable using docker-compose
and is scalable while relying on NGINX loadbalancing.
The routes of the API it is running are stored in the routes folder so that all of them are centralized.
Installation
npm install
will install all depencies required for this application.
Build
npm run build
will compile typescript files to ES6 javascript files and output them inside the dist
folder.
Note : This command will clear the
dist
folder if one already exists and also copy*.html
files in it. if you have other files with a different extensions that you want to add in the compiled code, edit thepostbuild
script in thepackage.json
file.
Test
npm run test
will run jest
testing package on all ./tests/**/*.test.ts
files.
Start
npm run start
will build
then executes the server instance
How to contribute
Commit guidelines
https://www.conventionalcommits.org/en/v1.0.0/#summary
Use commitizen
commit formats in order to name your commit messages and descriptions in order to automate the changelog generation.
Overview
This microservice template is a framework in which developers can add their features in a pre-established folder and logic structure. The framework is made to be used as a single-purpose component to a Microservice (MS) Architecture application.
Limitations
Because of that, this setup comes with a lot of complexity and limitations compared to a classic Monolithic setup:
- Difficult to maintain schema coherence across MS components
- Higher chance of failure during communication between different services
- Needs to solve network latency and load balancing
- Complex testing over a distributed environment
Advantages
This framework comes with the advantages of using the flexibility and scalability of Microservices Architecture. If implemented correctly, each component can be developed, tested, deployed and updated independently leading to faster deployment and troubleshooting turnaround times.
It also confines the developers to a set of rules and a predetermined structure with established design patterns, reducing the time needed to adapt to the framework.
Architecture
Route
Route declaration
The template application works with a sectionized
logic, so that the developer may declare groups of routes based on their functionnality.
An API section
is a group of Route
s declared in a TS (or JS) file inside the ./router/sections
folder that module.exports
an Object of type {[method: string]: Route}
.
The framework will automatically retrieve the exported object of the section files inside the section folder. It will not retrieve files in subfolder however.
A Route
is an object that contains all the information of a route (expect for its path which is declared in an Section
) that the request handler will need. We will talk more about request handling in the Route handling section.
export class Route {
public handler: Handler
public requireLogin: boolean
public requireAdmin: boolean
public authorizations: RouteAuthorization[]
public description: string
public parameters: RouteParameters
public returns: RouteReturn
}
export class RouteParameters {
public params: {[name: string]: {required: boolean, type: Object}} = {}
public body: {[name: string]: {required: boolean, type: Object}} = {}
public query: {[name: string]: {required: boolean, type: Object}} = {}
}
export class RouteReturn {
public onSuccess?: RouteReturnType
public onError?: {[code: number]: RouteReturnType}[]
public onSuccessToString(): string{return JSON.stringify(this.onSuccess)}
public onErrorToString(): string{return JSON.stringify(this.onError)}
public toString(): string{return JSON.stringify(this)}
}
Route handling
The template logic uses Handler
s, instead of directly letting the developer create routers and routes, to structure and split the code between 3 parts:
- The before part is to be used for pre-request operations like parameter checks (e.g. if a parameter is of the right type, of a special format, etc).
- The during part is the active handling of the request in which the "business logic" takes place.
- The after part is to be used for post-request operations like response checks or reformatting (e.g. Taking the response from the business logic and wrap it inside an object, or having a database call to report a change)
When a user request arrives, it is first handled by the express
package which will route it based on its path and to the corresponding Handler
.
A Handler
is an interface that is used to implement the three-part logic described above. It basically contains three methods which will be called sequentially in the following order :
beforeHandle
handleRequest
afterHandle
Each method will be provided by the router with request and contextual data (authentification and other middlewares) as well as two types of callback functions: onSuccess
and onError
(beforeHandle
has an additional callback onMalformedRequest
).
To make a handler, the developer must implement the
HandlerBase
class (that contains existing logic for parameter checks from the route data) in another class and instanciate it in theAPI section
route declaration.
In each step, when something goes wrong and the developer wants to return an error, the onError
(or onMalformedRequest
) callback should be used. Once called, a response with the error information is sent to the client and the request handling is closed (no further step will be taken).
RabbitMQ
RabbitMQ is a Message Broker that transports information from one place to another with the use of message queues. It is a completely different application that runs on its own docker container that is used to communicate between microservices.
This is what the template use it for. In essence, the communication protocol is handled by a RabbitMQ service
(in the ./services
folder) that provides functions to send and receive message to and from another microservice.
In all exchanges between microservices via the queues, the RabbitMQService
which has been implemented in the template uses RabbitMQMessage
object put inside the amqplib.Message
object (RabbitMQ
package) array buffer.
This object used by the service is structured as follow:
export class RabbitMQMessage {
public origin_ms_name: string
public origin_host_name: string
public origin_full_name: string
public recipient: string
public operation_type: string
public correlation_id?: string
public error?: {
requeued: boolean,
code: string,
content: any
}
public payload: any
}