📜 ⬆️ ⬇️

Self-documented microservices (ArangoDB + swagger)

Maintaining documentation for microservices up to date still requires the utmost discipline in the development, well, and large labor costs. A very reasonable approach to creating documentation is, for example, GraphQL, where the documentation is inextricably linked to the program code and this guarantees 100% compliance with the documentation and the services being documented. However, the unusualness of the GraphQL approach for developers accustomed to the REST-API still makes it difficult to promote this technology in practical application development. Here you can also recall SOAP, which has long solved the problem of matching documentation and services, but due to over-complication did not take root among the masses of developers.

I would like to find such a stack of technologies for the development of microservices, which ensured the same self-documenting software code when developing the “traditional” REST-API microservices. And he, as it turned out, already exists.

We define the actors and performers who will be involved in our small example.

ArangoDB is a hybrid, document-based + graph-oriented database.
')
UPD. More detailed acquaintance with this database was the reason for another disappointment. As it turned out, after the database has exceeded the non-commitit limit, which is limited by free RAM, the base starts to slow down, it just stops along with the server. At the same time, tentative suggestions were made that the transition to new storage engines would all work better, which had not yet been confirmed.

Foxx is a microservice framework built into the ArangoDB database. It runs on a JavaScript engine, which (unlike nodejs) can simultaneously run in an unlimited number of parallel threads (not blocking each other), as a result of which there is no need for promise / than / canch and async / await constructions. Unlike mongodb, in which it is not recommended to abuse server procedures, and relational databases, in which stored procedures are also used carefully and certainly do not interact via REST-API with clients (browsers, mobile applications, etc.) - microservice framework Foxx was developed just to develop microservices that communicate directly over the http protocol with clients.

Swagger is an open source software environment supported by a large ecosystem of tools that helps developers develop, create, document, and consume RESTful web services. Although most users identify Swagger with the Swagger UI, the Swagger toolkit includes support for automatic documentation, code generation, and test case generation.

The fact that Swagger includes supporting code generation is a situation that opposes the one that I would like to receive - when the code supports the generation of documentation. What offers us ArangoDB + Foxx just includes the opposite option. When the microservice code generates a scheme for Swagger. However, now this can be seen for yourself having done a minimum of work.

You must have ArangoDB installed to take further action.

  1. Enter the admin area and select the item to create a new microservice Services-> Add service-> New.
  2. We fill in the required details in the opened form. In the field “Document Collections” we add the name of the collection of documents that will be created when the microservice is deployed. For example, cats.
  3. We click on the install button, enter in the url field, to which the microservice will be assigned.

When installing microservice, for each collection from the field “Document Collections” (see p.2), routes will be created that implement CRUD operations using the POST, GET, PUT and DELETE methods. However, these are only methods that can be changed, deleted, and added new ones. We chose one collection when creating a microservice (cats), although we could not do this, but add everything later manually.

Now our cats collection has routes for CRUD operations, and we can start calling these routes from the database admin panel by selecting the API tab (Services -> [Microservice name] -> API). This tab contains the familiar Swagger interface. It is also possible to publish the Swagger interface on an external route accessible not only through the admin area, but as a regular URL.

If we try to add a document to the cats collection using the POST {"name": "Tom"} method, we will get the status with an error. Since we have not defined a name field anywhere. Therefore, we will continue to work with the admin ArangoDB.

4. For more convenient development, ArangoDB provides the Development mode, which is enabled on the Settings tab (Services -> [Microservice name] -> Settings-Set development)

Now you can change the worldservice code and immediately observe the result (without additional deployment). The directory in which the microservice program code is located can be found in the admin panel on the Info tab (Services -> [Microservice name] -> Info).

Let's see how the defined POST route looks.

'use strict'; const dd = require('dedent'); const joi = require('joi'); const httpError = require('http-errors'); const status = require('statuses'); const errors = require('@arangodb').errors; const createRouter = require('@arangodb/foxx/router'); const Cat = require('../models/cat'); const cats = module.context.collection('cats'); const keySchema = joi.string().required() .description('The key of the cat'); const ARANGO_NOT_FOUND = errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code; const ARANGO_DUPLICATE = errors.ERROR_ARANGO_UNIQUE_CONSTRAINT_VIOLATED.code; const ARANGO_CONFLICT = errors.ERROR_ARANGO_CONFLICT.code; const HTTP_NOT_FOUND = status('not found'); const HTTP_CONFLICT = status('conflict'); const router = createRouter(); module.exports = router; router.tag('cat'); router.post(function (req, res) { const cat = req.body; let meta; try { meta = cats.save(cat); } catch (e) { if (e.isArangoError && e.errorNum === ARANGO_DUPLICATE) { throw httpError(HTTP_CONFLICT, e.message); } throw e; } Object.assign(cat, meta); res.status(201); res.set('location', req.makeAbsolute( req.reverse('detail', {key: cat._key}) )); res.send(cat); }, 'create') .body(Cat, 'The cat to create.') .response(201, Cat, 'The created cat.') .error(HTTP_CONFLICT, 'The cat already exists.') .summary('Create a new cat') .description(dd` Creates a new cat from the request body and returns the saved document. `); 


Both validation and documentation are based on the use of the object schema. Let's make some small changes to it by adding the name field:

 'use strict'; const _ = require('lodash'); const joi = require('joi'); module.exports = { schema: { // Describe the attributes with joi here _key: joi.string(), name: joi.string().description('cat`s name'), //    }, forClient(obj) { // Implement outgoing transformations here obj = _.omit(obj, ['_id', '_rev', '_oldRev']); return obj; }, fromClient(obj) { // Implement incoming transformations here return obj; } }; 


Turning to the API zakadku, you can make sure that the scheme has changed and now an object with the name field can be added to the cats collection.

apapacy@gmail.com
November 12, 2018.

Source: https://habr.com/ru/post/429532/


All Articles