Documentare APIs con API Blueprint e Aglio
May 05, 2015
Quando si lavora ad un’API pubblica o comunque di interesse per più persone all’interno di un team di sviluppo diventa di primaria necessità corredarne lo sviluppo con un’adeguata e aggiornata documentazione.
Esistono vari tools per automatizzare questo processo, e tra i più utilizzati troviamo API Blueprint. API Blueprint ci permettere di “descrivere” la nostra API tramite una sintassi derivata dal markdown, che viene parsata da un’apposita libreria (Drafter) in maniera che possa essere “consumata” da una serie di librerie e applicazioni satellite che ci permettono di formattarla come pagina web, di effettuare test e molto altro ancora.
Creare un’api di test
Per questo tutorial utilizzeremo come esempio un’api scritta per il popolare framework Expressjs su Nodejs, ma ovviamente siete liberi di creare le vostre api nel linguaggio che volete, API Blueprint è agnostico da questo punto di vista. Inoltre, allo scopo di rendere più semplice e immediato questo tutorial, i dati ritornati dalla nostra api provengono da una sorgente statica (la variabile users).
Vediamo ora il nostro server:
// index.js
var express = require('express');
var _ = require('lodash');
var port = process.env.PORT || 3000;
var app = express();
var users = [
{
id: 1,
username: 'admin',
email: 'admin@example.com'
},
{
id: 2,
username: 'user',
email: 'user@example.com'
}];
app.get('/users', function (req, res) {
res.status(200).json(users);
});
app.get('/users/:id', function (req, res) {
var id = parseInt(req.params.id);
var user = _.find(users, {'id': id});
if (user) {
res.status(200).json(user);
} else {
res.status(404).json({ message: 'User not found'});
}
});
app.listen(port, function() {
console.log('API server listening on port ' + port + ' ...');
});
Anche se non avete mai utilizzato Express il codice dovrebbe essere abbastanza chiaro, la nostra api consiste di queste due rotte:
- GET /users : ritorna la lista di tutti gli utenti
- GET /users/:id : ritorna l’utente identificato dal parametro id
Scrivere la documentazione
Bene, ora che abbiamo un’api “funzionante” possiamo procedere con il passo successivo, dopo aver dato una lettura alla documentazione che descrive la sintassi utilizzata da API Blueprint.
Creiamo quindi un nuovo file (l’estensione non è importante ma di solito si usa md o apib) e iniziamo a scrivere!
<!-- api.md -->
FORMAT: 1A
HOST: http://api.example.com
# Example.com API
Api di esempio basata sul formato [Api Blueprint](https://apiblueprint.org/).
# Group Users
Risorse collegate agli **utenti**
Analizziamo quanto abbiamo appena scritto. In cima troviamo due chiavi, FORMAT e HOST che altro non sono che dei meta-dati per il parser e che al momento ci interessano relativamente. Poi troviamo l’intestazione e finalmente la prima keyword, Group, che ci permette di raggruppare più rotte sotto la stessa risorsa, in questo caso Users.
Andiamo ora a descrivere la rotta GET /users:
<!-- api.md -->
...
## Users Collection [/users]
Lista di *tutti* gli utenti
+ Model (application/json; charset=utf-8)
Rappresentazione JSON della risorsa Users List.
+ Body
[
{
"id": 1,
"username": "admin",
"email": "admin@example.com"
},
{
"id": 2,
"username": "user",
"email": "user@example.com"
}
]
### Ottieni tutti gli utenti [GET]
+ Response 200
[Users Collection][]
Qui iniziamo a fare sul serio.
- per prima cosa dichiariamo la risorsa che vogliamo descrivere, in questo caso Users Collection, con il relativo url;
- tramite la keyword Model, rappresentiamo la risposta attesa tramite un esempio, riportando sotto la keyword Body il contenuto della risposta, mentre il content type viene specificato tra parentesi (application/json);
- infine passiamo all’azione vera e propria, di tipo GET, di cui descriviamo la risposta, che avrà uno status code uguale a 200 e un body con un contenuto uguale al modello descritto dalla risorsa Users Collection.
Passiamo velocemente alla prossima azione, GET /users/:id:
<!-- api.md -->
...
## User [/users/{id}]
Un singolo utente.
+ Parameters
+ id: 1 (number) - ID dell'utente
+ Model (application/json; charset=utf-8)
Rappresentazione JSON della risorsa User.
+ Body
{
"id": 1,
"username": "admin",
"email": "admin@example.com"
}
### Ottiene un singolo utente [GET]
+ Response 200
[User][]
+ Response 404
+ Body
{
"message": "User not found"
}
Quanto scritto qui sopra ricalca in gran parte quanto abbiamo già visto, ma ci sono alcune aggiunte:
- la keyword Parameters rappresenta i parametri che ci aspettiamo dalla richiesta, in questo caso l’id dell’utente desiderato (un parametro é condiserato non opzionale se non specificato altrimenti);
- la nostra api prevede una risposta diversa nel caso l’id dell’utente richiesto non esista, per questo è stata aggiunta una nuova sezione Response con codice di stato 404 (not found) contenente il messaggio di errore relativo
Rendering della documentazione tramite Aglio
Bene, ora che abbiamo descritto la nostra api vediamo come possiamo generare una documentazione leggibile e funzionale.
Per fare questo ci affidiamo ad Aglio, un tool di rendering che si appoggia al solito Nodejs e utilizza il parser Protagonist per interpretare correttamente la sintassi di API Blueprint.
Innanzitutto installiamolo grazie ad npm: npm install -g aglio
Ora per visualizzare la documentazione non dovremo fare altro che digitare: aglio -i api.md -s
. Verrà avviato un server statico (tramite il flag -s) che ci permette di vedere l’anteprima della documentazione generata all’indirizzo http://localhost:3000. Per salvare il risultato in un file eseguiamo invece: aglio -i api.md -o docs.html
.
Ora se siete come me e non vi piace dovervi ricordare opzioni e flags dei tools da riga di comando e preferite avere uno script statico a cui fare affidamento non preoccupatevi, è possibile usare Aglio anche dall’interno di uno script Nodejs.
Per prima cosa installiamo Aglio localmente al nostro progetto: npm install aglio --save-dev
, poi creiamo un nuovo file, renderer.js, nel quale inseriremo il codice necessario a generare la pagina della documentazione e salvare il risultato nel file public/apidocs.html:
// renderer.js
var aglio = require('aglio');
var fs = require('fs');
var blueprintPath = __dirname + '/api.md';
var blueprint = fs.readFileSync(__dirname + '/api.md', { encoding: 'utf8'});
var template = 'flatly';
aglio.renderFile(blueprintPath, __dirname + '/public/apidocs.html', template, function (err, warnings) {
if (err) return console.log(err);
if (warnings) console.log(warnings);
});
Fatto questo possiamo aggiungere una rotta nella nostra applicazione express per servire la pagina come contenuto statico:
app.get('/docs', function (req, res) {
res.sendFile(__dirname + '/public/apidocs.html');
});
Ora navigando alla pagina http://localhost:3000/docs possiamo vedere la documentazione in tutto il suo splendore :) Aglio prevede già di default 4 diversi templates (presi dai temi di Bootswatch) ma è comunque possibile creare il proprio tema personalizzato tramite sintassi Jade.
Bonus: testare la documentazione con Dredd
Un altro tool che abbiamo a disposizione nell’ecosistema di API Blueprint è Dredd, che ci permette di testare la documentazione che abbiamo scritto su una vera implementazione della nostra api in modo da essere sicuri che tutto sia aggiornato.
Anche Dredd và installato tramite npm: npm install -g dredd
Una volta installato possiamo lanciarlo tramite riga di comando: dredd api.md http://localhost:3000
e se abbiamo scritto tutto correttamente otteremo un output simile a questo:
warn: Runtime compilation warning: Multiple responses, using first.
on Users > User > Ottiene un singolo utente
info: Beginning Dredd testing...
pass: GET /users duration: 43ms
pass: GET /users/1 duration: 14ms
complete: 2 passing, 0 failing, 0 errors, 0 skipped, 2 total
complete: Tests took 61ms
Dato che la nostra API è molto semplice e soprattutto statica è estramemente semplice testarla, per casi più complessi Dredd mette comunque a disposizione degli hooks da lanciare prima della sua esecuzione, in modo da portare la nostra api ad uno stato “testabile”. Potete leggere di più a riguardo nella documentazione ufficiale.
Concludendo
Come abbiamo visto creare una documentazione funzionale per le nostre api è estremamente facile e veloce utilizzando API Blueprint e i tools che si appoggiano alla sua sintassi. In questo tutorial siamo partiti da un’api reale per scrivere la documentazione ma è perfettamente possibile l’inverso, ovvero iniziare a descrivere un’api e poi usare librerie come Drakov o Api-Mock per creare dei server mock della nostra api in attesa di un’implementazione concreta. API Blueprint mette inoltre a disposizione anche un servizio cloud, apiary.io, che permette di condividere il design di api, generare mock, documentazione etc.