Documentiamo la nostra REST API con Swagger

Documentiamo la nostra REST API con Swagger

In questo articolo vedremo come documentare la nostra REST API, sviluppata in un articolo precedente utilizzando Swagger.Swagger è una libreria O

Gestiamo i processi Node.js con PM2
Creaimo un server di mappe geospaziali con Node.js e Mapbox
Creiamo una REST API con Node.js e MongoDB

In questo articolo vedremo come documentare la nostra REST API, sviluppata in un articolo precedente utilizzando Swagger.

Swagger è una libreria Open Source che ci permette di documentare gli endpoint delle nostre API REST ed è utilizzabile in moltissimi linguaggi quali Node.js, Spring Java e Python.

In particolare Swagger implementa la Specifica OpenAPI (conosciuta originariamente come la Specifica Swagger) la quale è una specifica per file di interfaccia leggibili dalle macchine per descrivere, produrre, consumare e visualizzare servizi web RESTful.

Il framework Swagger comprende una serie di tool Open Source:

  • Swagger Editor: un editor che ci permette di descrivere le nostre API REST in linguaggio JSON o YAML all’interno del nostro browser ed avere una anteprima real-time della documentazione prodotta;
  • Swagger UI: una collezione di asset HTML, CSS e Javascript che viene generata automaticamente dalla documentazione della nostra API (che deve essere conforme allo standard OpenAPI);
  • Swagger Codegen: genera automaticamente stub per la parte client e server a partire dalla specifica OpenAPI.

Descriviamo la nostra REST API in standard OpenAPI

Per integrare il nostro progetto Node.js con la Swagger UI dobbiamo prima creare un file di specifica della nostra API REST che chiameremo apiDocumentation.js (useremo il formato JSON) ed è fatto in questo modo:

export default apiDocumentation = {
  openapi: '3.0.1',
  info: {
    version: '1.0.0',
    title: 'Node.js Posts REST API',
    description: 'Posts management API',
    termsOfService: 'http://api_url/terms/',
    contact: {
      name: 'AppuntiSoftware.it',
      email: 'info@appuntiSoftware.it',
      url: 'https://www.appuntiSoftware.it/'
    },
    license: {
      name: 'Apache 2.0',
      url: 'https://www.apache.org/licenses/LICENSE-2.0.html'
    }
  },
  servers: [
    {
      url: 'http://localhost:4000/',
      description: 'Local server'
    },
    {
      url: 'https://www.devnode.it',
      description: 'Testing server'
    }
  ],
  tags: [
    {
      name: 'CRUD operations'
    }
  ],
}

Abbiamo descritto le informazioni base del nostro progetto quali il titolo, la versione, la descrizione e la licenza. Poi abbiamo definito due server dove verrà deployata la nostra applicazione: uno locale su localhost e l’altro remoto sul server devnode.it.

Infine definiamo una tag (CRUD Operations) che servirà per raggruppare i nostri endpoints.

Dopo inizia la sezione paths, che include tutti i path della nostra applicazione (al momento ne abbiamo 3) con i metodi di accesso (i metodi HTTP) e le relative risposte.

Il seguente è un path di esempio /posts/findById:

paths: {        
    '/posts/findById': {
        get: {
          tags: ['CRUD operations'],
          description: 'Get post by Id by query string',
          operationId: 'postId',
          parameters: [
            {
                name: 'postId',
                in: 'query',
                schema: {
                  $ref: '#/components/schemas/postId'
                },
                required: true,
                description: 'id of the post'
            }
          ],
          responses: {
            '200': {
              description: 'Post was obtained',
              content: {
                'application/json': {
                  schema: {
                    $ref: '#/components/schemas/Posts'
                  }
                }
              }
            },
            '400': {
              description: 'Missing parameters',
              content: {
                'application/json': {
                  schema: {
                    $ref: '#/components/schemas/Error'
                  },
                  example: {
                    message: 'Id is missing',
                    error: 'missing_parameters'
                  }
                }
              }
            }
          }
        }
    },

L’array parameters contiene gli input di tipo params che il nostro endpoint si aspetta, e con la proprietà in specifichiamo dove questi parametri verranno posizionati. Nell’esempio abbiamo specificato che il parametro con nome postId sarà di tipo query string (per intenderci del tipo /findById?postId). Come valori della proprietà in possiamo specificare:

  • path
  • query
  • header
  • cookie

Nel successivo snippet di codice, invece utilizziamo l’oggetto requestBody per indicare che la nostra operazione (in questo caso una POST) richiede che vengano inviati dei parametri via body (in questo caso un oggetto di tipo Post referenziato tramite $ref):

    '/posts': {
      post: {
        tags: ['CRUD operations'],
        description: 'Create post',
        operationId: 'createPost',
        parameters: [

        ],
        requestBody: {
          content: {
            'application/json': {
              schema: {
                $ref: '#/components/schemas/Post'
              }
            }
          },
          required: true
        },
        responses: {
          '200': {
            description: 'New post was created'
          },
          '400': {
            description: 'Invalid parameters',
            content: {
              'application/json': {
                schema: {
                  $ref: '#/components/schemas/Error'
                },
                example: {
                  message: 'Post creation failed'
                }
              }
            }
          }
        }
      }
    }

L’oggetto di tipo Post viene definito nella sezione Schemas:

components: {
    schemas: {
      Post: {
        type: 'object',
        properties: {
          title: {
            $ref: '#/components/schemas/title'
          },
          description: {
            $ref: '#/components/schemas/description'
          },
          date: {
            $ref: '#/components/schemas/date'
          }
        }
      }
...
}
}

Utilizziamo lo Swagger Editor

Per verificare la correttezza della specifica OpenAPI appena scritta, possiamo utilizzare lo Swagger Editor ricopiando il file apiDocumentation.js all’interno dell’editor eliminando la parte non JSON (export default apiDocumentation):

Lo Swagger Editor ci permette di modificare interattivamente il file di specifica

Includiamo la documentazione nel nostro progetto Node.js

Una volta descritti tutti gli endpoint della nostra REST API, includeremo questo file JSON all’interno della nostra applicazione in modo da generare in automatico una Swagger UI. Per farlo bisogna per prima cosa installare il pacchetto npm swagger-ui-express:

npm install swagger-ui-express

Dopodiché importiamo nel nostro file app.js la libreria swagger-ui-express e il file JSON:

import swaggerUi  from 'swagger-ui-express'
import apiDocumentation from './doc/apiDocumentation'

Infine ci basterà questa linea di codice per abilitare la Swagger UI:

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(apiDocumentation));

Andando quindi all’url /api-docs dovremmo visualizzare la Swagger UI come in figura:

Swagger UI
Swagger UI visualizzata in /api-docs

Testiamo la nostra REST API con la Swagger UI

La Swagger UI è molto comoda perché ci permette di testare i nostri servizi REST senza scrivere ulteriore codice per il testing e senza utilizzare tool aggiuntivi come Postman.

Infatti basta cliccare su una delle operazioni presenti e, inserendo i campi necessari, (che possono essere di tipo parameters o di tipo request body) possiamo invocare il servizio REST corrispondente.

La Swagger UI generata è possibile visionarla e testarla a questo indirizzo:

https://www.devnode.it/rest-api/api-docs/

L’esempio disponibile su GitHub

Il codice Javascript completo dell’applicazione è disponibile su GitHub:

https://github.com/angeloonline/NodeJSRestAPI

COMMENTS

WORDPRESS: 2
  • comment-avatar

    Grazie per questo articolo!E per il precedente!