Advanced Typescript
Typage au runtime

GraphQL

GraphQL est un langage de requête pour les APIs, souvent présenté comme une alternative à REST. Son avantage principal est de permettre au client de demander exactement les données dont il a besoin, ni plus, ni moins, en une seule requête, ce qui résout les problèmes de sur-récupération (over-fetching) ou de sous-récupération (under-fetching) de données

Types GraphQL

Il existe différentes familles de type en GraphQL:

Scalar Types

Il s'agit des types primitifs:

  • Int: 357
  • Float: 3.57
  • String: "bonsoir"
  • Boolean: true | false
  • ID: "001" (serialisé en tant que String)

Object Types

La plupart des types que l'on va définir font parti de cette famille.

2 object types peuvent s'inclure l'un dans l'autre comme dans l'exemple précédent.

type Address {
  street: String
  city: String
  postalCode: String
  doctors: [Doctor]
}

type Doctor {
  name: String
  speciality: OPHTALMOLIGST | PSYCHOLOGIST
  addresses: [Address]
}

Le champs __typename

Tous les object types de votre schéma contienne un champs __typename.
Celui ci retourne le nom de votre Object Type en tant que String.
Vous n'aurez pas besoin de le spécifier.
On verra par la suite que ce champs est très utile pour identifier quel type d'object type le client récupère.

Query GraphQL

Fields

GraphQL permet de requêter des champs spécifiques sur des objets.
Voici un exemple:

{
  doctors {
    name
    addresses {
      street
      city
    }
  }
}

La requête ci dessus fournira une réponse sous la forme suivante:

{
  "data": {
    "doctors": [
      {
        "name": "Dr Samia Mekamene",
        "addresses": [
          {
            "street": "2 Avenue Ambroise Croizat",
            "city": "Garges-lès-Gonesse"
          }
        ]
      }
    ]
  }
}

Arguments

Il est possible d'ajouter des arguments à une resolver si le resolver le permet.
Par exemple:

{
  doctor(id: "001") {
    name
  }
}

pourrait fournir la réponse:

{
  "data": {
    "doctor": {
      "name": "Dr Samia Mekamene",
      "addresses": [
        {
          "street": "2 Avenue Ambroise Croizat",
          "city": "Garges-lès-Gonesse"
        }
      ]
    }
  }
}

Aliases

Vous aurez pu remarquer que dans le résultat, l'objet retourné correspond au champs demandé.
Dans l'exemple précédent il nous serait donc pas possible de récupérer 2 docteurs ayant des ids différents.
Dans ce cas là vous devrez utiliser des alias pour renommer les champs que vous avez requêté.

{
  closestOphtalmogists: doctors(speciality: OPHTALMOLIGST) {
    name
  }
  closestPsychologists: doctors(speciality: PSYCHOLOGIST) {
    name
  }
}
{
  "data": {
    "closestOphtalmogists": [
      {
        "name": "Dr. Clara Legrand"
      }
    ],
    "closestPsychologists": [
      {
        "name": "Dr. Alexis Martin"
      }
    ]
  }
}

Fragments

Supposons qu'en utilisant l'exemple précédent vous souhaitez maintenant ajouter le champs speciality dans la liste des docteurs récupérés.
Vous devrez alors l'ajouter dans les 2 listes.

{
  closestOphtalmogists: doctors(speciality: OPHTALMOLIGST) {
    name
    speciality
  }
  closestPsychologists: doctors(speciality: PSYCHOLOGIST) {
    name
    speciality
  }
}

Pour ce genre de cas GraphQL permet grâce au fragments de pouvoir réutiliser un ensemble de champs sur un object type.

{
  closestOphtalmogists: doctors(speciality: OPHTALMOLIGST) {
    ...doctorFields
  }
  closestPsychologists: doctors(speciality: PSYCHOLOGIST) {
    ...doctorFields
  }
}

fragment doctorFields on Doctor {
  name
  speciality
}

Operation name

Vous avez sûrement remarqué que dans les exemples précédents nous utilisions une syntaxe raccourci pour écrire une query en omettant le mot clé query et le nom de la query.

query GetDoctorByID {
  doctor(id: "001") {
    name
  }
}

Il est important de les ajouter pour faciliter le debuggage que ce soit en local ou dans un environnement de production.
Cela permet de garder une certaine tracabilité et une signature à votre query.
On verra par la suite que l'utilisation du mot clé query et de l'operationName sera obligatoire pour pouvoir générer des types (typescript) avec l'outil graphql-codegen.

Variables

Une query, tout comme une mutation peut prendre des paramètres (qu'on appelle Variables).
Dans l'exemple précédent on remarque que le paramètre est passé statiquement (en dur).
Ces paramètres seront dans la plupart des cas passés de manière dynamique dans votre application.

GraphQL permet de passer un objet sous forme de clés valeur en même temps que la query.

query GetDoctorBy($id: ID!) {
  doctor(id: $id) {
    name
  }
}
{"id": "001"}

Dans le cas d'une requête à une API GraphQL en POST, le body ressemblerait donc à:

{
  "query": "query GetDoctorBy($id: ID!) {\n  doctor(id: $id) {\n    name\n  }\n",
  "variables": {
    "id": "001"
  }
}