Guide Apollo Server 5.x
Apollo Server est un serveur GraphQL open-source qui fonctionne avec tout schéma GraphQL. Apollo Server 5 est agnostique au framework et s'exécute de manière autonome ou s'intègre avec Express, Fastify et les environnements serverless.
Démarrage rapide
Étape 1 : Installation
npm install @apollo/server graphql
Pour l'intégration Express :
npm install @apollo/server @as-integrations/express5 express graphql cors
Étape 2 : Définir le schéma
const typeDefs = `#graphql
type Book {
title: String
author: String
}
type Query {
books: [Book]
}
`;
Étape 3 : Écrire les resolvers
const resolvers = {
Query: {
books: () => [
{ title: "The Great Gatsby", author: "F. Scott Fitzgerald" },
{ title: "1984", author: "George Orwell" },
],
},
};
Étape 4 : Démarrer le serveur
Mode autonome (Recommandé pour le prototypage) :
Le serveur autonome est parfait pour le prototypage, mais pour les services en production, nous recommandons d'intégrer Apollo Server avec un framework web plus complet comme Express, Koa ou Fastify. Passer du serveur autonome à un framework web ultérieurement est simple.
import { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone";
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
});
console.log(`Server ready at ${url}`);
Express :
import { ApolloServer } from "@apollo/server";
import { expressMiddleware } from "@as-integrations/express5";
import { ApolloServerPluginDrainHttpServer } from "@apollo/server/plugin/drainHttpServer";
import express from "express";
import http from "http";
import cors from "cors";
const app = express();
const httpServer = http.createServer(app);
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
await server.start();
app.use(
"/graphql",
cors(),
express.json(),
expressMiddleware(server, {
context: async ({ req }) => ({ token: req.headers.authorization }),
}),
);
await new Promise<void>((resolve) => httpServer.listen({ port: 4000 }, resolve));
console.log("Server ready at http://localhost:4000/graphql");
Définition du schéma
Types scalaires
Int- Entier 32 bitsFloat- Nombre décimal double précisionString- Chaîne UTF-8Boolean- vrai/fauxID- Identifiant unique (sérialisé en String)
Définitions de types
type User {
id: ID!
name: String!
email: String
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String
author: User!
}
input CreatePostInput {
title: String!
content: String
}
type Query {
user(id: ID!): User
users: [User!]!
}
type Mutation {
createPost(input: CreatePostInput!): Post!
}
Enums et interfaces
enum Status {
DRAFT
PUBLISHED
ARCHIVED
}
interface Node {
id: ID!
}
type Article implements Node {
id: ID!
title: String!
}
Vue d'ensemble des resolvers
Les resolvers suivent la signature : (parent, args, contextValue, info)
- parent : Résultat du resolver parent (les resolvers root reçoivent undefined)
- args : Arguments passés au champ
- contextValue : Objet de contexte partagé (authentification, dataSources, etc.)
- info : Informations spécifiques au champ et détails du schéma (rarement utilisé)
const resolvers = {
Query: {
user: async (_, { id }, { dataSources }) => {
return dataSources.usersAPI.getUser(id);
},
},
User: {
posts: async (parent, _, { dataSources }) => {
return dataSources.postsAPI.getPostsByAuthor(parent.id);
},
},
Mutation: {
createPost: async (_, { input }, { dataSources, user }) => {
if (!user) throw new GraphQLError("Not authenticated");
return dataSources.postsAPI.create({ ...input, authorId: user.id });
},
},
};
Configuration du contexte
Le contexte est créé par requête et passé à tous les resolvers.
interface MyContext {
token?: string;
user?: User;
dataSources: {
usersAPI: UsersDataSource;
postsAPI: PostsDataSource;
};
}
const server = new ApolloServer<MyContext>({
typeDefs,
resolvers,
});
// Mode autonome
const { url } = await startStandaloneServer(server, {
context: async ({ req }) => ({
token: req.headers.authorization || "",
user: await getUser(req.headers.authorization || ""),
dataSources: {
usersAPI: new UsersDataSource(),
postsAPI: new PostsDataSource(),
},
}),
});
// Middleware Express
expressMiddleware(server, {
context: async ({ req, res }) => ({
token: req.headers.authorization,
user: await getUser(req.headers.authorization),
dataSources: {
usersAPI: new UsersDataSource(),
postsAPI: new PostsDataSource(),
},
}),
});
Fichiers de référence
Documentation détaillée pour des sujets spécifiques :
- Resolvers - Motifs et bonnes pratiques des resolvers
- Context and Auth - Authentification et autorisation
- Plugins - Hooks du cycle de vie du serveur et des requêtes
- Data Sources - RESTDataSource et DataLoader
- Error Handling - GraphQLError et formatage des erreurs
- Troubleshooting - Problèmes courants et solutions
Règles clés
Conception du schéma
- Utilisez ! (non-null) pour les champs qui ont toujours des valeurs
- Préférez les types input pour les mutations plutôt que les arguments inline
- Utilisez des interfaces pour les types polymorphes
- Conservez les descriptions du schéma pour la documentation
Bonnes pratiques des resolvers
- Gardez les resolvers simples - déléguez aux services/sources de données
- Gérez toujours les erreurs explicitement
- Utilisez DataLoader pour regrouper les requêtes connexes
- Retournez des données partielles quand c'est possible (la force de GraphQL)
Performance
- Utilisez
@deferet@streampour les grandes réponses - Implémentez DataLoader pour résoudre les requêtes N+1
- Envisagez les requêtes persistantes pour la production
- Utilisez les en-têtes de cache et CDN le cas échéant
Règles fondamentales
- UTILISEZ TOUJOURS les motifs Apollo Server 5.x (pas v4 ou versions antérieures)
- TYPEZ TOUJOURS votre contexte avec les génériques TypeScript
- UTILISEZ TOUJOURS
GraphQLErrordu packagegraphqlpour les erreurs - N'EXPOSEZ JAMAIS les stack traces dans les erreurs de production
- PRÉFÉREZ
startStandaloneServerpour le prototypage uniquement - UTILISEZ une intégration avec un framework serveur comme Express, Koa, Fastify, Next, etc. pour les applications en production
- IMPLÉMENTEZ l'authentification dans le contexte, l'autorisation dans les resolvers