#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 :

  1. La place d'OpenAPI dans la jungle du Rest
  2. Comment écrire un contrat OpenAPI
  3. Que peut-on d'un contrat OpenAPI
  4. 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.
  • UBER (Uniform Basis for Exchanging Representations)
  • HAL (Hypertext Application Language)
  • Siren (Structured Interface for Representing Entities)
  • Mason
  • JSON-API
  • ...
[
  {
    "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

Quentin Desramé - @qdesrame

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"

En utilisant les mots clés allOf, oneOf ou anyOf

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

Plugin pour IDE JetBrains

Plugin pour Atom

Plugin pour VsCode

Plug-ins pour Vim

...

Existance d'éditeurs dédiés

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

Quelques exemples :

 - oas (agnostique)

 - tso (Typescript)

 - nestjs-openapi3 (Nest)

- ... mais surtout ...

4 - Au final,

C'est bien ou pas OpenAPI ?

Les moins

  1. Ce n'est pas un standard

  2. C'est très long et très fastidieux à écrire

le "'ptetRe bien que oui, ptetre bien que non"'

  1. Approche "Contrat first" (Schema first du GraphQL)
  2. Le "tooling"

LES PLUS

  1. La documentation, proche du code.
  2. Une documentation qui se partage facilement.
  3. C'est techno-agnostique
  4. Le potentiel de génération de code.

MERCI