In addition to standard components (e.g., CRUD), you can create your own microservices that encapsulate ad-hoc logics that are autonomously developed and integrated. Your microservice receives HTTP requests, its cycle of use and deploy is managed by the platform.
A microservice encapsulates ad-hoc business logics that can be developed by any user of the platform and potentially in any programming language. However, to facilitate its adoption and use, Mia-Platform team has created Mia Service Node.js Library, a library in node.js, based on the fastify library. Using
Mia Service Node.js Library it is possible to create your own microservice by implementing the following steps:
- HTTP Routes handler
- changing the behaviour according to the client that is making the request, whether the user is logged in and its belonging groups
- requests to other services of the platform
- PRE and POST decorators
In the remaining part of this guide it will be described how to develop, test and deploy your microservice in Node.js withing the platform ecosystem using the
Mia Service Node.js Library library.
Installation and Bootstrap
Install from Marketplace template
From the service area it is possible to add a new service starting from the node template that is already set up and configured to use the
Mia Service Node.js Library.
Check out the Marketplace Documentation for further information
Manual install in a new repository
To start developing it is necessary to have installed
node.js on your laptop and to initialize a node project with the following commands:
package.json file and modify the
description fields accordingly.
version field at the beginning is best valued at
Mia Service Node.js Library can be installed with
npm, along with its
fastify-cli dependency, necessary for booting
and the execution of the microservice
The library can be used to instantiate an HTTP server.
To start developing with
Mia Service Node.js Library the variables need to be available to the
nodejs process environment:
- USERID_HEADER_KEY = miauserid
- USER_PROPERTIES_HEADER_KEY = miauserproperties
- GROUPS_HEADER_KEY = miausergroups
- CLIENTTYPE_HEADER_KEY = miaclienttype
- BACKOFFICE_HEADER_KEY = isbackoffice
- MICROSERVICE_GATEWAY_SERVICE_NAME = Microservice-gateway
Among these variables, the most interesting is
MICROSERVICE_GATEWAY_SERVICE_NAME, which contains the network name (or IP address) at which
microservice-gateway is accessible and is used during internal communication with other services in your project namespace. The implication is that
MICROSERVICE_GATEWAY_SERVICE_NAME makes it possible to configure your local microservice to query a specific microservice inside your Mia-Platform project. For example
To instantiate the HTTP server you can paste the following piece of code in the service entrypoint (tipically the
To start the microservice, simply edit the
package.json file in this way
npm start and open a browser at the url
http://localhost:3000/status/alive, to get an answer.
Factory exposed by Mia Service Node.js Library
Mia Service Node.js Library exports a function which creates the infrastructure ready to accept the definition
of routes and decorators. This code extract exemplifies its use.
The argument of the
customPlugin function is a declaration function whose argument is an object that allows
to define routes and decorators.
Mia Service Node.js Library allows to define the behavior of the microservice in response to an HTTP request, in a declarative style.
For this purpose, the
addRawCustomPlugin function is used as shown in the first argument of the declaration function.
whose arguments are, in order
httpVerb- the HTTP verb of the request (e.g.,
path- the route path (e.g.,
handler- function that contains the actual behavior. It must respect the same interface defined in the documentation of the handlers of fastify.
schema- definition of the request and response data schema. The format is the one accepted by fastify
handler is a function that respects the handler interface of fastify and
accepts a Request and a Reply.
In addition to the fastify Request interface,
Mia Service Node.js Library decorates the Request instance with information related to the Platform as
id user currently logged in, its groups, the type of client that performed the HTTP request and if the request comes from the CMS.
Furthermore, the Request instance is also decorated with methods that allow HTTP requests to be made to other services released on the Platform.
User and Client Identification
The instance of
Request (the first argument of a handler) is decorated with functions
getUserId- exposes the user's id, if logged in or, null
getUserProperties- exposes the user's properties of the logged user or null
getGroups- exposes an array containing strings that identify the groups to which the logged in user belongs
getClientType- exposes the type of client that performed the HTTP request
isFromBackOffice- exposes a boolean to discriminate whether the HTTP request from the CMS
Inside the handler scope it's possible to access fastify instance using
Endpoint queries and Platform services
Both from the
Request (the first argument of a handler) or the
Service (the first argument of the declaration function) it is possible to obtain a proxy object to invoke other endpoints or services running in the Platform project. For example, if you need to connect to a CRUD, you have to use a Proxy towards the
crud-service. These proxies are already configured to automatically transmit Platform headers.
There are two types of proxies, returned by two distinct functions:
getServiceProxy(options)- proxy passing through
getDirectServiceProxy(serviceName, options)- direct proxy to the service
The fundamental difference between the two proxies is that the first one activates all the logics that are registered in
while the second does not. For example, if a resource exposed by the CRUD service is protected by ACL, this protection will come
bypassed using the direct proxy.
For the direct proxy it is necessary to specify the
serviceName of the service to be queried. The port cannot be specified in the
serviceName but must be passed in the
port field of the
options. In the case of
getServiceProxy, you should not specify the name of the service as it is implicitly that of the
options parameter is an object with the following optional fields:
port- an integer that identifies the port of the service to be queried
protocol- a string that identifies the protocol to use (only
httpsare supported, default value is
headers- an object that represents the set of headers to forward to the service
prefix- a string representing the prefix of the service call path
getDirectServiceProxy method allows you to also query services outside the platform. In this case, however, it is necessary to bear in mind that the platform headers will be automatically forwarded.
Both proxies, by default, forward the four mia-headers to the service called. To do this, the following environment variables must be present:
The values of these variables will specify the key of the four mia-headers.
In addition, other headers of the original request can also be forwarded to the named service. To do this it is necessary to define an additional environment variable,
ADDITIONAL_HEADERS_TO_PROXY, whose value must be a string containing the keys of the headers to be forwarded separated by a comma.
Both proxies expose the functions
get(path, querystring, options)
post(path, body, querystring, options)
put(path, body, querystring, options)
patch(path, body, querystring, options)
delete(path, body, querystring, options)
The topics to be passed to these functions are:
path- a string that identifies the route to which you want to send the request
body- optional, the body of the request which can be:
querystring- optional, an object that represents the querystring
options- optional, an object that admits all the
optionslisted above for the
getDirectServiceProxymethods (which will eventually be overwritten), plus the following fields:
returnAs- a string that identifies the format in which you want to receive the response. It can be
allowedStatusCodes- an array of integers that defines which status codes of the response are accepted. If the response status code is not contained in this array, the promise will be rejected. If this parameter is omitted, the promise is resolved in any case (even if the interrogated server answers 500).
isMiaHeaderInjected- Boolean value that identifies whether Mia's headers should be forwarded in the request. Default
PRE and POST decorators
Mia Service Node.js Library it is possible to declare PRE and POST decorators. From a conceptual point of view, a decorator
of (1) PRE or (2) POST is a transformation applied from
microservice-gateway to (1) a request addressed
to a service (original request) or (2) to the reply (original reply) that this service sends to
caller. From a practical point of view, decorators are implemented as HTTP requests in
POST to a specified microservice. In order to use the decorators it is imporant to configure them also in the console. More information are available in the Decorators docs.
The declaration of a decorator using
Mia Service Node.js Library occurs in a similar way to the declaration of a route
Effective received HTTP request
PRE and POST decorator receive a POST HTTP request from
microservice-gateway with the following json body:
PRE decorator schema
POST decorator schema
Access and Handling of the Original Request With Pre decorator
The utility functions exposed by the
Request instance (the first parameter of a handler) are used to access the original request
getOriginalRequestBody()- returns the body of the original request
getOriginalRequestHeaders()- returns the headers of the original request
getOriginalRequestMethod()- returns the original request method
getOriginalRequestPath()- returns the path of the original request
getOriginalRequestQuery()- returns the querystring of the original request
In addition to the methods described above, the
Request instance exposes an interface to modify the original request, which will come
microservice-gateway to the target service. This interface is accessible using the
Request instance method
changeOriginalRequest which returns an object by the following methods:
setBody(newBody)- change the body of the original request
setHeaders(newHeaders)- modify the headers of the original request
setQuery(newQuery)- modify the querystring of the original request
To leave the original request unchanged, the
leaveOriginalRequestUnmodified function is used instead.
In all cases the PRE decorator handler must return either the object returned by
changeOriginalRequest or the object returned by
Example of PRE Decorators
Access and Manipulation of the Original Response With POST Decorator
As with the original request, the
Request instance (the first parameter of a handler) is decorated with useful functions for
also access the original service original response information (these are available only for POST decorators)
getOriginalResponseBody()- returns the body of the original response
getOriginalResponseHeaders()- returns the headers of the original response
getOriginalResponseStatusCode()- returns the status code of the original response
In addition to the functions described above, the
Request instance exposes an interface to modify the original response, which will come
microservice-gateway to the calling client. This interface is accessible using the function
changeOriginalResponse concatenating it with invocations to functions
setBody (newBody)- change the body of the original answer
setHeaders (newHeaders)- modify the headers of the original answer
setQuery (newQuery)- modify the querystring of the original answer
setStatusCode (newStatusCode)- change the status code of the original response
To leave the original answer unchanged, instead, the
leaveOriginalResponseUnmodified function is used.
In all cases the decorator handler must return either the object returned by
changeOriginalResponse or the object returned by
Example of POST Decorators
Decorator Chain Stop
microservice-gateway it is possible to define a sequencer of decorators, so that the output of a
single decorator is passed to the next decorator. In special cases, however, it may be necessary
interrupt the chain and return a response to the original caller.
For this purpose, the
Request instance (the first argument of a handler) exposes the function
Route Diagram and Documentation
A microservice developed with
Mia Service Node.js Library automatically also exposes the documentation of the routes and decorators that
are implemented. The documentation is specified using the OpenAPI 2.0 standard
and exhibited through Swagger. Once the microservice is started, its documentation can be accessed at
route http://localhost:3000/documentation. The specification of the request scheme
and responses to a route must conform to the format accepted by
Like any service on the Platform, a microservice must be set up to be released in different environments, starting from the local environment (the development machine) to development, test and production environments. The differences between various environments are managed through the mechanism of environment variables.
In addition to the mandatory ones, using
Mia Service Node.js Library it is possible to define other environment variables based on
needs of the single microservice, to then access them and use their values in the code of the handlers. For the definition yes
use the JSON schema format.
If the correct set of environment variables is not supplied to the microservice, the microservice does not start by returning in output which environment variable is missing.
Mia Service Node.js Library is built on fastify and therefore integrates with testing tools
made available by the framework. A complete example of this type of test is present online in the repository of
Mia Service Node.js Library on GitHub.
Integration and Unit test
The testing of a microservice built with
Mia Service Node.js Library can be performed at multiple levels of abstraction. One of
Possibility is to use a technique called fake http injection for which it is possible to simulate
receiving an HTTP request. In this way, all the microservice logic is exercised from the HTTP layer to the handlers and
this is an example of Integration Testing.
Example Integration Test
In the example below the test framework Mocha.