GráficoQL permite que os clientes solicitem exatamente os dados de que precisam em uma única solicitação — sem busca excessiva, sem busca insuficiente. Combinado comServidor Apolo e TypeScript, é uma maneira poderosa de criar APIs flexíveis. Este guia cria uma API GraphQL completa do zero.
📋 Table of Contents
GraphQL vs REST — Comparação Rápida
| Aspecto | DESCANSO | GráficoQL |
|---|---|---|
| Busca de dados | Vários pontos de extremidade | Endpoint único, consultas precisas |
| Busca excessiva | Comum | Eliminado |
| Versionamento | Versões de URL (v1, v2) | Evolução do esquema |
| Melhor para | CRUD simples, cache | Dados complexos, aninhados e orientados ao cliente |
Configuração do Projeto
mkdir graphql-api && cd graphql-api
npm init -y
npm install @apollo/server graphql
npm install -D typescript tsx @types/node
npx tsc --init
Defina o esquema
// src/schema.ts
export const typeDefs = `#graphql
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
published: Boolean!
}
type Query {
users: [User!]!
user(id: ID!): User
posts: [Post!]!
}
type Mutation {
createUser(name: String!, email: String!): User!
createPost(title: String!, content: String!, authorId: ID!): Post!
publishPost(id: ID!): Post!
}
`;
Resolvedores de gravação
// src/resolvers.ts
import { db } from './db';
export const resolvers = {
Query: {
users: () => db.users.findAll(),
user: (_: any, { id }: { id: string }) => db.users.findById(id),
posts: () => db.posts.findAll(),
},
Mutation: {
createUser: (_: any, { name, email }: { name: string; email: string }) => {
return db.users.create({ name, email });
},
createPost: (_: any, args: { title: string; content: string; authorId: string }) => {
return db.posts.create({ ...args, published: false });
},
publishPost: (_: any, { id }: { id: string }) => {
return db.posts.update(id, { published: true });
},
},
// Field resolvers for relationships
User: {
posts: (parent: { id: string }) => db.posts.findByAuthor(parent.id),
},
Post: {
author: (parent: { authorId: string }) => db.users.findById(parent.authorId),
},
};
Crie o Servidor
// src/index.ts
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { typeDefs } from './schema';
import { resolvers } from './resolvers';
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
context: async ({ req }) => {
// Extract auth token, attach user to context
const token = req.headers.authorization?.replace('Bearer ', '');
const user = token ? await verifyToken(token) : null;
return { user };
},
});
console.log("GraphQL ready at " + url);
Autenticação em Resolvedores
Mutation: {
createPost: (_: any, args, context) => {
// Require authentication
if (!context.user) {
throw new GraphQLError('Not authenticated', {
extensions: { code: 'UNAUTHENTICATED' },
});
}
return db.posts.create({ ...args, authorId: context.user.id });
},
},
Resolvendo o problema N+1 com DataLoader
Os resolvedores de campo podem acionar consultas N+1 (uma consulta por pai). O DataLoader agrupa e armazena em cache:
npm install dataloader
import DataLoader from 'dataloader';
// Create a loader that batches user lookups
const userLoader = new DataLoader(async (ids: readonly string[]) => {
const users = await db.users.findByIds(ids);
// Return in the same order as requested ids
return ids.map(id => users.find(u => u.id === id));
});
// Use in resolver — batches all author lookups into one query
Post: {
author: (parent, _, context) => context.userLoader.load(parent.authorId),
},
Consulta de exemplo
# Client requests exactly what it needs
query {
user(id: "1") {
name
posts {
title
published
}
}
}
# Response contains only requested fields:
# { "data": { "user": { "name": "Alice", "posts": [...] } } }
Perguntas Frequentes
P: GraphQL ou REST para meu projeto?
R: GraphQL para dados complexos, aninhados e orientados ao cliente (aplicativos móveis, painéis com necessidades variadas). REST para CRUD simples e quando o cache HTTP é mais importante. Muitas equipes usam ambos.
P: Como lidar com uploads de arquivos no GraphQL?
R: Use o pacote graphql-upload, ou melhor, carregue arquivos para armazenamento de objetos (S3) por meio de um endpoint REST separado ou URL pré-assinado e passe o URL em sua mutação GraphQL.
P: O GraphQL é mais difícil de armazenar em cache do que o REST?
R: Sim no nível HTTP (endpoint único, solicitações POST). Use o cache normalizado do Apollo Client no cliente e consultas persistentes ou cache de resposta no servidor para compensar.
P: Como posso evitar consultas caras/maliciosos?
R: Adicione limitação de profundidade de consulta, análise de complexidade e limitação de taxa. Pacotes como graphql-profundidade-limit e graphql-query-complexity protegem contra consultas abusivas profundamente aninhadas.
P: Devo usar o código primeiro ou o esquema primeiro?
R: O esquema primeiro (strings SDL, mostrados aqui) é claro e independente de linguagem. Code-first (Nexus, TypeGraphQL) oferece melhor integração com TypeScript. Ambos funcionam; escolha com base na preferência da equipe.
Conclusão
GraphQL com Apollo Server oferece aos clientes controle preciso sobre os dados que buscam, eliminando a busca excessiva e insuficiente. A configuração aqui – esquema digitado, resolvedores com relacionamentos, autenticação baseada em contexto e DataLoader para o problema N+1 – cobre o essencial de uma API GraphQL de produção. Adicione limitação de profundidade e análise de complexidade antes de entrar no ar e emparelhe com o Apollo Client para obter um cache normalizado no frontend.
🔗 Share this article
✍️ Leave a Comment