Build APIs You Won't Hate

Publié le 09/01/2019

Quelques conseils repris du livre Build APIs You Won't Hate.

Un chouette livre à parcourir, mais pour ceux qui ont déjà un peu d'expérience dans la construction d'APIs (= pas moi). Il fera gagner du temps sur certains points, et évitera de prendre de mauvaises décisions, qui pourraient avoir un impact à plus long terme. Par exemple:

  • Stocker les timezones, pas les offsets. De cette manière, on ne dépend pas des différents calculs de zones, ni des zones pourries comme les îles Chatham qui passent en UTC/GMT+13h45 en été.
  • En règle générale, le verbe PUT est utilisé si on connait l'entièreté de l'URL et que l'action est idempotente (on peut l'exécuter autant de fois qu'on le souhaite, le résultat sera toujours identique).
  • Utilisez des dénominations plurielles. Cela permet par exemple d'envoyer un GET vers l'URL /places/1 ou /places/1,2. Cela permet de conserver une cohérence globale.
  • Tout doit être une ressource, et chaque ressource doit avoir un contrôleur.
  • On ne passe pas du JSON dans les paramètres d'une URL ! Ca fait moche: checkin[place_id]=1&checkin[message]=This is a bunch of text&checkin[with_friends]=1....

Structure des réponses

Soit on envoie tout dans une structure pluralisée (même s'il n'y a qu'un seul résultat):

{
    "posts": [{
        "id": 1,
        "title": "Zen of Python"
    }]
}

Soit on envoie le résultat concerné par la réponse (un objet ou une liste d'objets) - c'est l'approche minimaliste conseillée par Twitter:

// Un seul objet en retour -> pas d'encapsulation dans une liste
{
    "name": "Hulk Hogan",
    "id": "10002"
}

// Plusieurs objets -> encapsulés dans une liste
[
    {
        "name": "Hulk Hogan",
        "id": "10002"
    },
    {
        "name": "Mick Foley",
        "id": "10003"
    }
]

Soit on embarque les collections dans une propriété data (Facebook-style):

// Un seul objet en retour -> pas d'encapsulation dans une liste
{
    "name": "Hulk Hogan",
    "id": "10002"
}

// Plusieurs objets -> On crée une collection dans un attribut `data`
{
    "data": [
        {
            "name": "Hulk Hogan",
            "id": "10002"
        },
        {
            "name": "Mick Foley",
            "id": "10003"
        }
    ]
}

Soit on ajoute par défaut un namespace sur le résultat de retour:

{
    "data": {
        "name": "Hulk Hogan",
        "id": "10002"
    }
}

{
    "data": [
        {
            "name": "Hulk Hogan",
            "id": "10002"
        },
        {
            "name": "Mick Foley",
            "id": "10003"
        }
    ]
}

L'avantage de cette dernière proposition est que chaque résultat est wrappé dans une propriété data (même les sous-propriétés). En gros, on devrait pouvoir faire profiter de pagination, des liens, ... à n'importe quel niveau de l'API.

Voir aussi JSON-API.

Codes de retour

  • 2XX pour les trucs OK
  • 3XX pour les redirections
  • 4XX pour les erreurs côtés clients
  • 5XX pour les erreurs côtés serveur/service.

Tests des points de terminaison

En plus des tests unitaires, on peut envisager du BDD - Behavior Driven Development - notamment avec Cucumber.

Les librairies à garder en mémoire