#48
Open API :
Un contrat pour vos API.s
https://www.caen.camp/talks/openapi-un-contrat-pour-vos-apis
@caencamp
@alexisjanvier
@marmelab
Alexis Janvier
developpeur chez marmelab
depuis 2014
Atelier d'innovation digitale, développe vos projets d'innovation web et mobile avec agilité, pragmatisme et gourmandise.
15 personnes presque partout en France
1) Peu nombreux
2) On ne facture que des sprints
3) On choisiT nos technos
4) On choisiT nos clients
5) On fait de l'open-source
6) On se questionne sur l'impact de Marmelab sur NOTRE environnement
Open API
Pourquoi je vous en parle ?
#28 - 28 mars 2017
Le GraphQL
c'est vraiment la fin du REST ?
https://www.caen.camp/talks/edition-28-le-graphql-cest-vraiment-la-fin-du-rest
Déroulé du talk :
- La place d'OpenAPI dans la jungle du Rest
- Comment écrire un contrat OpenAPI
- Que peut-on d'un contrat OpenAPI
- Au final, c'est bien ou pas ?
1 - La place d'OpenAPI
dans la jungle du Rest
Qu'est-ce qu'une API ?
Wikipedia : "En informatique, une interface de programmation d’application ou interface de programmation applicative (souvent désignée par le terme API pour Application Programming Interface) est un ensemble normalisé de classes, de méthodes, de fonctions et de constantes qui sert de façade par laquelle un logiciel offre des services à d'autres logiciels."
Qu'est ce qu'Une API Rest ?
Wikipedia : "REST (representational state transfer) est un style d'architecture logicielle définissant un ensemble de contraintes à utiliser pour créer des services web. Les services web conformes au style d'architecture REST, aussi appelés services web RESTful, établissent une interopérabilité entre les ordinateurs sur Internet. Les services web REST permettent aux systèmes effectuant des requêtes de manipuler des ressources web via leurs représentations textuelles à travers un ensemble d'opérations uniformes et prédéfinies sans état."
Rest est un style d'architecture permettant de construire des API.
* l’URI comme identifiant des ressources
* les verbes HTTP comme identifiant des opérations
(GET, POST, PUT, PATCH, DELETE)
* les réponses HTTP comme représentation des ressources
The Fabulous Roller Derby API
exemple
Simple CRUD* pour les joueurs.euses
(* Create Read Update Delete)
- Liste des joueurs.euses -
=> GET - https://api.domain/players
- Créer un joueur.euse -
=> POST - https://api.domain/players
- Informations sur un joueur.euse -
=> GET - https://api.domain/players/playerId
- Mettre à jour les informations sur un joueur.euse -
=> PUT - https://api.domain/players/playerId
- Supprimer un joueur.euse -
=> DELETE - https://api.domain/players/playerId
Ce que le Rest ne dit pas
Mais ...
1 ) Quel sera le format le formatage de la représentation de la ressource retournée dans le corps de la réponse http ?
Postulons que l'on utilisera l'en tête http content-type pour demander un format json
2) Ce que signifierons les données retournées dans le documents json
3) Quels seront les liens entre les différents objets retournés
4) Comment naviguer au sein de l'API au delà de la simple logique du Rest
Donner du sens aux documents retournés par l'API Rest
(sémantique)
Wikipedia : "JSON-LD, ou JavaScript Object Notation for Linked Data, est une méthode permettant d'encoder des données structurées (en anglais linked data) en utilisant du JSON."
C'est une recommandation du W3C depuis 2014
{
"id": "valquirit",
"name": "Valquirit",
"number": 75,
"picture": "http://rdc.fr/pictures/RDC_B_75-Valquirit.jpg",
}
{
"@context": "http://schema.org",
"@type": "Person",
"identifier": "valquirit",
"name": "Valquirit",
"number": 75,
"image": "http://rdc.fr/pictures/RDC_B_75-Valquirit.jpg",
}
=> GET - https://api.domain/players/valquirit
Les données structurées vues par google
La décentralisation des données vue par Tim Berners-lee (Solid)
Naviguer au sein de l'API Rest
Le modèle de maturité des API.s de Leonard Richardson
Level 3
=
HATEOAS
ou
Hypermedia As The Engine Of Application State
Mais HATEOAS n'est qu'une contrainte d'architecture.
[
{
"number": 187,
"picture": "http://rdc.fr/pictures/RDC_B_187-Rice-Cooker-.jpg",
"id": "rice-cooker",
"name": "Rice Cooker"
},
{
"number": 171,
"picture": "http://rdc.fr/pictures/RDC_B_171-Claraclette.jpg",
"id": "claraclette",
"name": "Claraclette"
}
]
=> GET - https://api.domain/players?pagination=[2-2]
{
"meta": {
"totalPages": 9
},
"data": [
{
"type": "player",
"id": "rice-cooker",
"attributes": {
"number": 187,
"picture": "http://rdc.fr/pictures/RDC_B_187-Rice-Cooker-.jpg",
"name": "Rice Cooker"
}
},
{
"type": "player",
"id": "claraclette",
"attributes": {
"number": 171,
"picture": "http://rdc.fr/pictures/RDC_B_171-Claraclette.jpg",
"name": "Claraclette"
}
}
],
"links": {
"self": "https://api.domain/players?pagination[2-2]",
"first": "https://api.domain/players?pagination[1-2]",
"prev": "https://api.domain/players?pagination[1-2]",
"next": "https://api.domain/players?pagination[4-2]",
"last": "https://api.domain/players?pagination[9-2]"
}
}
Ou se place OpenAPI ?
Linked-data
Hypermedia
- HATEOAS -
JSON-LD
- HAL
- Manson
- JSON-API
- ...
Null part !
Cela n'a rien à voir
OpenAPI est une spécification permettant d'écrire un fichier(en json ou en yaml)
qui va permettre de
décrire, produire, consommer et visualiser un services Web REST.
Ce fichier, complètement indépendant des documents servis par l'API, va constituer
le contrat fourni par votre API
2 - Comment écrire
un contrat OpenAPI
Le contenu d'un contrat OpEnApi
openapi: 3.0.2
info:
version: 1.0.0
title: The famous Roller Derby API
contact:
email: contact@alexisjanvier.dev
security: []
servers: []
tags:
- name: my tag
paths: {}
components:
securitySchemes: {}
schemas: {}
parameters: {}
Des informations générales
info:
version: 1.0.0
title: The Famous Roller Derby API - FRDA
description: >-
Trouver toutes les informations sur les équipes
et les matchs de Roller Derby en Europe.
![Roller Derby](https://roller-derby.io/logo.png)
termsOfService: https://roller-derby.io/api/terms/
contact:
name: FRDA Support
url: https://roller-derby.io/api/support
email: api@roller-derby.io
license:
name: GNU GPLv3
url: https://roller-derby.io/api/LICENSE
Des informations sur les objets
retournés dans les documents
components:
securitySchemes: {}
schemas:
Player:
description: "Un.e joueur.euse de Roller Derby"
type: object
properties:
id:
type: string
readOnly: true
name:
type: string
number:
type: integer
picture:
type: string
parameters: {}
Player:
description: "Un.e joueur.euse de Roller Derby"
type: object
properties:
id:
type: string
readOnly: true
name:
type: string
minLength: 3
maxLength: 50
number:
type: integer
maximum: 9999
format: int32
picture:
type: string
nullable: true
required:
- id
- name
- number
- picture
Des Informations de Validation (Json Schema)
paths:
/players:
get:
description: Retourne la liste des tous.tes les joureurs.euses.
operationId: getPlayerList
tags:
- Players
parameters:
- name: pagination
in: query
description: Pagination parameters.
required: false
schema:
type: string
example: "[10,3]"
- name: filters
$ref: "#/component/parameters/Filters"
responses:
"200":
...
put:
...
delete:
...
Des Informations sur les points d'entrée
paths:
/players:
get:
description: Retourne la liste des tous.tes les joureurs.euses.
operationId: getPlayerList
tags:
- Players
parameters:
...
responses:
"200":
description: Une liste de joueurs.euses.
content:
application/json:
schema:
type: array
items:
$ref: "#/component/schemas/Player"
examples:
shortList:
summary: Une courte liste de un joueur.euse
value: >-
[
{
"number": 187,
"picture": "http://roller-derby-caen.fr/wp-content/uploads/2019/12/RDC_B_187-Rice-Cooker-.jpg",
"id": "rice-cooker",
"name": "Rice Cooker",
},
]
put:
...
Les aides à la rédaction
Les références $ref
Player:
get:
description: Informations sur un.e joueur.euse.
operationId: getPlayer
tags:
- Players
parameters:
- name: playerId
$ref: "#/component/parameters/PlayerId"
responses:
"200":
description: Le.a joueur.euse demandé.e.
content:
application/json:
schema:
$ref: "#/component/schemas/Player"
Composition d'objet
Player:
get:
operationId: getPlayer
parameters:
- name: playerId
$ref: "#/component/parameters/PlayerId"
responses:
"200":
description: Le.a joueur.euse demandé.e.
content:
application/json:
schema:
allOf:
$ref: "#/component/schemas/Person"
$ref: "#/component/schemas/Player"
Possibilité de découper le contrat en plusieurs fichiers
puis de reconstituer un unique fichier avec des outils comme swagger-cli
➜ tree openapi -L 2
openapi
├── index.yaml
├── openapi.json
├── package.json
├── package-lock.json
├── paths
│ ├── index.yaml
│ └── players.yaml
└── schemas
├── index.yaml
└── player.yaml
Utilisation de linter.s pour valider le formatage
Par exemple Eslint et Prettier prennent en charge le yaml et le json. Il existe un plugin ESLINT openapi.
Utilisation de validateurs de contrats
Par exemple swagger-cli ou speccy
Il existe aussi des outils permettant de valider la sécurité de son API d'après son contrat openapi : APISecurity.io
Utilisation de plugins pour les IDE
Existance d'éditeurs dédiés
Comme Swagger editor ou StopLight Studio
Et puis aussi
...
Non, plus tard !
3 - Que peut-on Faire
d'un contrat OpenAPI
Simuler des serveurs d'API
Sandox (SaAs)
Prism (JS)
API Sprout (Go)
Générer de la documentation
Automatiser la validation
Rapprochement avec le vocabulaire JSON Schema.
Par exemple, on trouve express-openapi-validator pour réaliser en "live" une validation des objets `request` et `response` d'une API codée avec Express.js.
Générer des tests
Générer des Kits de développement (SDK)
On trouve :
- des projets spécifiques à une techno, comme restful-react,
- des projets générant du code pour plusieurs technologies (Php, Javascript, Java, Go, Rust, Ruby ...), comme swagger-editor ou ApiMatic.
Et puis ...
revenons à l'aide à la rédaction
Générer le contrat OPENAPI
depuis le code
4 - Au final,
C'est bien ou pas OpenAPI ?
Les moins
-
Ce n'est pas un standard
-
C'est très long et très fastidieux à écrire
le "'ptetRe bien que oui, ptetre bien que non"'
- Approche "Contrat first" (Schema first du GraphQL)
- Le "tooling"
LES PLUS
- La documentation, proche du code.
- Une documentation qui se partage facilement.
- C'est techno-agnostique
- Le potentiel de génération de code.