GraphQL est le nouveau mot à la mode dans le développement des API. Si les API RESTful restent le moyen le plus populaire d’exposer des données à partir d’applications, elles présentent de nombreuses limitations que GraphQL vise à résoudre.

GraphQL est un langage de requête créé par Facebook, qui est devenu un projet open source en 2015. Il offre une syntaxe intuitive et flexible pour décrire et accéder aux données d’une API.

Ce guide va explorer comment construire un projet GraphQL Node.js. Nous utiliserons GraphQL pour construire une application Todo dans le framework Express.js pour Node.

Qu’est-ce que GraphQL ?

Extrait de la documentation officielle: « GraphQL est un langage de requête pour les API et un runtime pour remplir ces requêtes avec vos données existantes. GraphQL fournit une description complète et compréhensible des données de votre API, donne aux clients le pouvoir de demander exactement ce dont ils ont besoin et rien de plus, facilite l’évolution des API au fil du temps et permet d’utiliser de puissants outils de développement. »

GraphQL est un runtime côté serveur permettant d’exécuter des requêtes à l’aide du système de types que vous avez défini pour vos données. En outre, GraphQL n’est pas lié à une base de données ou à un moteur de stockage spécifique. Au lieu de cela, il s’appuie sur votre code et votre magasin de données existants. Vous pouvez obtenir une comparaison détaillée de ces technologies avec le guide GraphQL vs. RESTful API.

Pour créer un service GraphQL, vous commencez par définir des types de schéma et créer des champs à l’aide de ces types. Ensuite, vous fournissez un résolveur de fonctions à exécuter sur chaque champ et chaque type chaque fois que des données sont demandées par le côté client.

Terminologie GraphQL

Le système de types GraphQL est utilisé pour décrire les données qui peuvent être interrogées et celles que vous pouvez manipuler. Il s’agit du cœur de GraphQL. Discutons des différentes façons dont nous pouvons décrire et manipuler les données dans GraphQ.

Types d’objets

Les types d’objets GraphQL sont des modèles de données contenant des champs fortement typés. Il devrait y avoir une correspondance 1 à 1 entre vos modèles et les types GraphQL. Vous trouverez ci-dessous un exemple de type GraphQL :

type User {
  id: ID! # The "!" means required
  firstname: String
  lastname: String
  email: String
  username: String
  todos: [Todo] # Todo is another GraphQL type
}

Requêtes

GraphQL Query définit toutes les requêtes qu’un client peut exécuter sur l’API GraphQL. Par convention, vous devez définir un RootQuery qui contiendra toutes les requêtes existantes.

Ci-dessous, nous définissons et mappons les requêtes à l’API RESTful correspondante :

type RootQuery {
  user(id: ID): User           # Corresponds to GET /api/users/:id
  users: [User]                # Corresponds to GET /api/users
  todo(id: ID!): Todo    # Corresponds to GET /api/todos/:id
  todos: [Todo]          # Corresponds to GET /api/todos
}

Mutations

Si les requêtes GraphQL sont des demandes GET, les mutations sont des requêtes POST, PUT, PATCH, et DELETE qui manipulent l’API GraphQL.

Nous allons mettre toutes les mutations dans un seul RootMutation pour faire la démonstration :

type RootMutation {
  createUser(input: UserInput!): User             # Corresponds to POST /api/users
  updateUser(id: ID!, input: UserInput!): User    # Corresponds to PATCH /api/users
  removeUser(id: ID!): User                       # Corresponds to DELETE /api/users
  createTodo(input: TodoInput!): Todo
  updateTodo(id: ID!, input: TodoInput!): Todo
  removeTodo(id: ID!): Todo
}

Vous avez remarqué l’utilisation de types -input pour les mutations tels que UserInput, TodoInput. La meilleure pratique consiste toujours à définir des types Input pour la création et la mise à jour de vos ressources.

Vous pouvez définir les types d’entrée comme celui ci-dessous :

input UserInput {
  firstname: String!
  lastname: String
  email: String!
  username: String!
}

Résolveurs

Les résolveurs indiquent à GraphQL ce qu’il doit faire lorsque chaque requête ou mutation est demandée. Il s’agit d’une fonction de base qui fait le travail difficile de frapper la couche de base de données pour effectuer les opérations CRUD (création, lecture, mise à jour, suppression), de frapper un point de terminaison interne RESTful API, ou d’appeler un microservice pour répondre à la demande du client.

Vous pouvez créer un nouveau fichier resolvers.js et ajouter le code suivant :

import sequelize from '../models';
export default function resolvers () {
  const models = sequelize.models;
  return {
    // Resolvers for Queries
    RootQuery: {
      user (root, { id }, context) {
        return models.User.findById(id, context);
      },
      users (root, args, context) {
        return models.User.findAll({}, context);
      }
    },
    User: {
      todos (user) {
        return user.getTodos();
      }
    },
  }
  // Resolvers for Mutations
  RootMutation: {
    createUser (root, { input }, context) {
      return models.User.create(input, context);    
    },
    updateUser (root, { id, input }, context) {
      return models.User.update(input, { ...context, where: { id } });
    },
    removeUser (root, { id }, context) {
      return models.User.destroy(input, { ...context, where: { id } });
    },
    // ... Resolvers for Todos go here
  }
}

Schéma

Le schéma GraphQL est ce que GraphQL expose au monde. Par conséquent, les types, les requêtes et les mutations seront inclus dans le schéma pour être exposés au monde.

Voici comment exposer les types, les requêtes et les mutations au monde :

schema {
  query: RootQuery
  mutation: RootMutation
}

Dans le script ci-dessus, nous avons inclus le RootQuery et le RootMutation que nous avons créés plus tôt pour être exposés au monde.

Comment GraphQL fonctionne-t-il avec Node.js et Express.js ?

GraphQL fournit une implémentation pour tous les principaux langages de programmation, et Node.js n’est pas exempté. Sur le site officiel de GraphQL, il y a une section pour le support JavaScript, et aussi, il y a d’autres implémentations de GraphQL pour rendre l’écriture et le codage simple.

GraphQL Apollo fournit une implémentation pour Node.js et Express.js et facilite la prise en main de GraphQL.

Vous apprendrez comment créer et développer votre première application GraphQL dans Node.js et le cadre backend Express.js à l’aide de GraphQL Apollo dans la section suivante.

Mise en place de GraphQL avec Express.js

La mise en place d’un serveur d’API GraphQL avec Express.js est très simple pour commencer. Dans cette section, nous allons explorer comment construire un serveur GraphQL.

Initialiser le projet avec Express

Tout d’abord, vous devez installer et configurer un nouveau projet Express.js. Créez un dossier pour votre projet et installez Express.js en utilisant cette commande :

cd <project-name> && npm init -y
npm install express

La commande ci-dessus crée un nouveau fichier package.json et installe la bibliothèque Express.js dans votre projet.

Ensuite, nous allons structurer notre projet comme indiqué dans l’image ci-dessous. Il contiendra différents modules pour les fonctionnalités du projet telles que les utilisateurs, les todos, etc.

Fichiers pour graphql-todo.
Fichiers pour graphql-todo.

Initialiser GraphQL

Commençons par installer les dépendances GraphQL Express.js. Exécutez la commande suivante pour les installer :

npm install apollo-server-express graphql @graphql-tools/schema --save

Création de schémas et de types

Ensuite, nous allons créer un fichier index.js dans le dossier modules et ajouter l’extrait de code suivant :

const { gql } = require('apollo-server-express');
const users = require('./users');
const todos = require('./todos');
const { GraphQLScalarType } = require('graphql');
const { makeExecutableSchema } = require('@graphql-tools/schema');
const typeDefs = gql`
  scalar Time
  type Query {
    getVersion: String!
  }
  type Mutation {
    version: String!
  }
`;
const timeScalar = new GraphQLScalarType({
  name: 'Time',
  description: 'Time custom scalar type',
 serialize: (value) => value,
});
const resolvers = {
  Time: timeScalar,
  Query: {
    getVersion: () => `v1`,
  },
};
const schema = makeExecutableSchema({
  typeDefs: [typeDefs, users.typeDefs, todos.typeDefs],
  resolvers: [resolvers, users.resolvers, todos.resolvers],
});
module.exports = schema;

Code Walkthrough

Passons en revue cet extrait de code et décomposons-le :

Étape 1

Tout d’abord, nous avons importé les bibliothèques requises et créé des types de requête et de mutation par défaut. Pour l’instant, la requête et la mutation ne définissent que la version de l’API GraphQL. Cependant, nous étendrons la requête et la mutation pour inclure d’autres schémas au fur et à mesure.

Importation de GraphQL et des extensions.
Importation de GraphQL et des extensions.
Étape 2 :

Nous avons ensuite créé un nouveau type scalaire pour le temps et notre premier résolveur pour la requête et la mutation créées ci-dessus. En outre, nous avons également généré un schéma à l’aide de la fonction makeExecutableSchema.

Le schéma généré inclut tous les autres schémas que nous avons importés et en inclura également d’autres lorsque nous les créerons et les importerons.

Création d'un type scalaire pour le temps ainsi que de notre premier résolveur.
Création d’un type scalaire pour le temps ainsi que de notre premier résolveur.

L’extrait de code ci-dessus montre que nous avons importé différents schémas dans la fonction makeExecutableSchema. Cette approche nous aide à structurer l’application pour la complexité. Ensuite, nous allons créer les schémas Todo et User que nous avons importés.

Création du schéma Todo

Le schéma Todo montre les opérations CRUD simples que les utilisateurs de l’application peuvent effectuer. Voici le schéma qui implémente l’opération CRUD Todo.

const { gql } = require('apollo-server-express');
const createTodo = require('./mutations/create-todo');
const updateTodo = require('./mutations/update-todo');
const removeTodo = require('./mutations/delete-todo');
const todo = require('./queries/todo');
const todos = require('./queries/todos');
const typeDefs = gql`
  type Todo {
    id: ID!
    title: String
    description: String
    user: User
  }
  input CreateTodoInput {
    title: String!
    description: String
    isCompleted: Boolean
  }
  input UpdateTodoInput {
    title: String
    description: String
    isCompleted: Boolean
  }  extend type Query {
    todo(id: ID): Todo!
    todos: [Todo!]
  }
  extend type Mutation {
    createTodo(input: CreateTodoInput!): Todo
    updateTodo(id: ID!, input: UpdateTodoInput!): Todo
    removeTodo(id: ID!): Todo
  }
`;
// Provide resolver functions for your schema fields
const resolvers = {
  // Resolvers for Queries
  Query: {
    todo,
    todos,
  },
  // Resolvers for Mutations
  Mutation: {
    createTodo,
    updateTodo,
    removeTodo,
  },
};
module.exports = { typeDefs, resolvers };

Analyse du code

Passons en revue l’extrait de code et décomposons-le :

Étape 1 :

Tout d’abord, nous avons créé un schéma pour notre Todo en utilisant GraphQL type, input, et extend. Le mot-clé extend est utilisé pour hériter et ajouter de nouvelles requêtes et mutations à la requête et à la mutation racines existantes que nous avons créées ci-dessus.

A command line interface showing the schema for our Todo script, including new inputs.
Création du schéma pour notre Todo.
Étape 2 :

Ensuite, nous avons créé un résolveur, qui est utilisé pour récupérer les données correctes lorsqu’une requête ou une mutation particulière est appelée.

Création d'un résolveur.
Création d’un résolveur.

Une fois la fonction résolveur en place, nous pouvons créer des méthodes individuelles pour la logique commerciale et la manipulation de la base de données, comme le montre l’exemple create-todo.js.

Créez un fichier create-user.js dans le dossier ./mutations et ajoutez la logique métier pour créer un nouveau Todo dans votre base de données.

const models = require('../../../models');
module.exports = async (root, { input }, context) => {
  return models.todos.push({ ...input });
};

L’extrait de code ci-dessus est une façon simplifiée de créer un nouveau Todo dans notre base de données en utilisant l’ORM Sequelize. Vous pouvez en savoir plus sur Sequelize et comment le configurer avec Node.js.

Vous pouvez suivre la même étape pour créer plusieurs schémas en fonction de votre application ou vous pouvez cloner le projet complet depuis GitHub.

Ensuite, nous allons configurer le serveur avec Express.js et exécuter l’application Todo nouvellement créée avec GraphQL et Node.js.

Configuration et exécution du serveur

Enfin, nous allons mettre en place notre serveur à l’aide de la bibliothèque apollo-server-express que nous avons installée précédemment et le configurer.

Le apollo-server-express est un simple wrapper du serveur Apollo pour Express.js, il est recommandé car il a été développé pour s’adapter au développement d’Express.js.

En utilisant les exemples dont nous avons parlé plus haut, configurons le serveur Express.js pour qu’il fonctionne avec la bibliothèque apollo-server-express nouvellement installée .

Créez un fichier server.js dans le répertoire racine et collez-y le code suivant :


const express = require('express');
const { ApolloServer } = require('apollo-server-express');
const schema = require('./modules');
const app = express();
async function startServer() {
  const server = new ApolloServer({ schema });
  await server.start();
  server.applyMiddleware({ app });
}
startServer();
app.listen({ port: 3000 }, () =>
  console.log(`Server ready at http://localhost:3000`)
);

Dans le code ci-dessus, vous avez créé avec succès votre premier serveur CRUD GraphQL pour Todos et Users. Vous pouvez démarrer votre serveur de développement et accéder au terrain de jeu en utilisant http://localhost:3000/graphql. Si tout se passe bien, vous devriez voir l’écran ci-dessous :

L'écran de vérification.
L’écran de vérification.

Résumé

GraphQL est une technologie moderne soutenue par Facebook qui simplifie le travail fastidieux lié à la création d’API à grande échelle avec des modèles architecturaux RESTful.

Ce guide a élucidé GraphQL et montré comment développer votre première API GraphQL avec Express.js.

Faites-nous savoir ce que vous construisez à l’aide de GraphQL dans les commentaires ci-dessous.

Solomon Eseme

Je suis un ingénieur logiciel et un créateur de contenu orienté vers la construction de produits performants et innovants en suivant les meilleures pratiques et les normes de l'industrie. J'aime aussi écrire à ce sujet sur Masteringbackend.com. Suivez-moi sur X, LinkedIn et About Me.