Ce que vous saurez faire à la fin
- Monter un serveur GraphQL Apollo
- Schéma, résolveurs, mutations, subscriptions
- Résoudre N+1 avec DataLoader
- Sécuriser avec depth-limit et auth
- Consommer avec Apollo Client React
Étape 1 — Installation
npm install @apollo/server graphql
Étape 2 — Schéma et résolveurs
import { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone";
const typeDefs = `#graphql
type Client {
id: ID!
nom: String!
factures: [Facture!]!
}
type Facture {
id: ID!
montant: Float!
client: Client!
}
type Query {
client(id: ID!): Client
clients: [Client!]!
}
`;
const resolvers = {
Query: {
client: (_, { id }, { db }) => db.findClient(id),
clients: (_, __, { db }) => db.allClients(),
},
Client: {
factures: (parent, _, { db }) => db.facturesDuClient(parent.id),
},
};
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, { listen: { port: 4000 } });
Étape 3 — Mutations
const typeDefs = `#graphql
input CreerClientInput {
nom: String!
ville: String
}
type Mutation {
creerClient(input: CreerClientInput!): Client!
}
`;
const resolvers = {
Mutation: {
creerClient: (_, { input }, { db }) => db.createClient(input),
},
};
Étape 4 — DataLoader anti-N+1
npm install dataloader
import DataLoader from "dataloader";
function createLoaders(db) {
return {
clientLoader: new DataLoader(async (ids) => {
const rows = await db.query("SELECT * FROM clients WHERE id = ANY($1)", [ids]);
return ids.map(id => rows.find(r => r.id === id));
}),
};
}
// Context par requête
await startStandaloneServer(server, {
context: async () => ({ db, ...createLoaders(db) }),
});
const resolvers = {
Facture: {
client: (parent, _, { clientLoader }) => clientLoader.load(parent.client_id),
},
};
Étape 5 — Auth
import jwt from "jsonwebtoken";
context: async ({ req }) => {
const token = req.headers.authorization?.replace("Bearer ", "");
let user = null;
if (token) {
try { user = jwt.verify(token, process.env.JWT_SECRET); } catch {}
}
return { user };
}
const resolvers = {
Query: {
utilisateurs: (_, __, { user }) => {
if (!user || user.role !== "admin") throw new Error("interdit");
return db.listUsers();
},
},
};
Étape 6 — Subscriptions
import { PubSub } from "graphql-subscriptions";
const pubsub = new PubSub();
const typeDefs = `#graphql
type Subscription {
venteCreee: Facture!
}
`;
const resolvers = {
Mutation: {
creerFacture: async (_, { input }) => {
const f = await db.createFacture(input);
pubsub.publish("VENTE_CREEE", { venteCreee: f });
return f;
},
},
Subscription: {
venteCreee: {
subscribe: () => pubsub.asyncIterator("VENTE_CREEE"),
},
},
};
Étape 7 — Sécurité
import depthLimit from "graphql-depth-limit";
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [depthLimit(5)],
});
Étape 8 — Client React
npm install @apollo/client
import { ApolloClient, InMemoryCache, ApolloProvider, gql, useQuery } from "@apollo/client";
const client = new ApolloClient({
uri: "http://localhost:4000/",
cache: new InMemoryCache(),
});
function Clients() {
const { data, loading } = useQuery(gql`query { clients { id nom } }`);
if (loading) return <p>Chargement...</p>;
return <ul>{data.clients.map(c => <li key={c.id}>{c.nom}</li>)}</ul>;
}
Étape 9 — Codegen TypeScript
npm install -D @graphql-codegen/cli @graphql-codegen/typescript-react-apollo
npx graphql-codegen
Étape 10 — Checklist prod
✓ Désactiver introspection en prod
✓ depthLimit + rate-limit + complexity
✓ DataLoader sur résolveurs relationnels
✓ Persisted queries
✓ Tracing Apollo Studio
✓ Auth obligatoire sur mutations
✓ Validation Zod des inputs
Besoin d'un site web ?
Confiez-nous la Création de Votre Site Web
Site vitrine, e-commerce ou application web — nous transformons votre vision en réalité digitale. Accompagnement personnalisé de A à Z.
À partir de 250.000 FCFA
Parlons de Votre Projet
Publicité