GraphQL é a linguagem de consulta da API que permite aos clientes solicitar exatamente os dados de que precisam. Em 2026, o GraphQL é usado pelo Facebook, GitHub, Shopify, Twitter e milhares de startups. Este guia cobre design de esquema GraphQL, consultas, mutações, assinaturas e padrões de produção.
📋 Table of Contents
GraphQL versus REST
| Aspecto | DESCANSAR | GráficoQL |
|---|---|---|
| Busca de dados | Vários endpoints, resposta fixa | Terminal único, resposta flexível |
| Busca excessiva | Problema comum | Eliminado – obtenha exatamente o que você precisa |
| Busca insuficiente | Várias solicitações necessárias | Uma solicitação para dados aninhados |
| Digite segurança | OpenAPI (opcional) | Digitação forte integrada |
| Curva de aprendizado | Low | Médio |
| Melhor para | APIs CRUD simples | Dados complexos e interconectados |
Esquema GraphQL
# Schema Definition Language (SDL)
type Query {
user(id: ID!): User
users(first: Int, after: String): UserConnection!
post(id: ID!): Post
posts(filter: PostFilter, orderBy: PostOrderBy): [Post!]!
me: User
}
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
updateUser(id: ID!, input: UpdateUserInput!): User!
deleteUser(id: ID!): Boolean!
createPost(input: CreatePostInput!): Post!
}
type Subscription {
userCreated: User!
postPublished: Post!
}
type User {
id: ID!
name: String!
email: String!
posts(first: Int): [Post!]!
createdAt: DateTime!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
tags: [String!]!
published: Boolean!
publishedAt: DateTime
}
input CreateUserInput {
name: String!
email: String!
password: String!
}
type CreateUserPayload {
user: User!
token: String!
}
type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type UserEdge {
node: User!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
endCursor: String
}
scalar DateTime
Consultas e mutações
# Basic query
query GetUser {
user(id: "1") {
id
name
email
posts(first: 5) {
title
publishedAt
}
}
}
# Query with variables (best practice)
query GetUserPosts($userId: ID!, $first: Int = 10) {
user(id: $userId) {
name
posts(first: $first) {
id
title
tags
author {
name
}
}
}
}
# Mutation
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
author {
name
}
publishedAt
}
}
# Variables JSON:
# {
# "input": {
# "title": "TypeScript Guide 2026",
# "content": "...",
# "tags": ["typescript", "javascript"]
# }
# }
# Fragments — reuse field selections
fragment UserFields on User {
id
name
email
}
query {
user(id: "1") { ...UserFields }
me { ...UserFields }
}
GraphQL com Python (Morango)
pip install strawberry-graphql fastapi uvicorn
import strawberry
from strawberry.fastapi import GraphQLRouter
from fastapi import FastAPI
from typing import Optional
import asyncio
@strawberry.type
class User:
id: strawberry.ID
name: str
email: str
@strawberry.type
class Post:
id: strawberry.ID
title: str
content: str
author: User
@strawberry.input
class CreatePostInput:
title: str
content: str
tags: list[str] = strawberry.field(default_factory=list)
@strawberry.type
class Query:
@strawberry.field
async def user(self, id: strawberry.ID) -> Optional[User]:
# Fetch from database
user_data = await db.users.find_one({"id": id})
if not user_data:
return None
return User(id=user_data["id"], name=user_data["name"], email=user_data["email"])
@strawberry.field
async def posts(self, first: int = 10) -> list[Post]:
posts_data = await db.posts.find().limit(first).to_list(first)
return [Post(id=p["id"], title=p["title"], content=p["content"],
author=User(id=p["author_id"], name=p["author_name"], email=""))
for p in posts_data]
@strawberry.type
class Mutation:
@strawberry.mutation
async def create_post(self, input: CreatePostInput) -> Post:
new_post = {"title": input.title, "content": input.content, "tags": input.tags}
result = await db.posts.insert_one(new_post)
return Post(id=str(result.inserted_id), title=input.title, content=input.content,
author=User(id="1", name="Alice", email="alice@example.com"))
schema = strawberry.Schema(query=Query, mutation=Mutation)
graphql_app = GraphQLRouter(schema)
app = FastAPI()
app.include_router(graphql_app, prefix="/graphql")
# Playground at http://localhost:8000/graphql
Problema N+1 e DataLoader
O problema N+1: buscar uma lista de postagens e, em seguida, fazer 1 consulta ao banco de dados por postagem para obter seu autor = N+1 total de consultas.
from strawberry.dataloader import DataLoader
async def load_users_by_ids(user_ids: list[str]) -> list[User]:
# ONE query for all user IDs
users_data = await db.users.find({"id": {"$in": user_ids}}).to_list(None)
user_map = {u["id"]: u for u in users_data}
return [user_map.get(uid) for uid in user_ids]
# In your GraphQL context
context = {
"user_loader": DataLoader(load_fn=load_users_by_ids)
}
# In resolver — batches all user lookups into one query
@strawberry.type
class Post:
author_id: strawberry.Private[str]
@strawberry.field
async def author(self, info: strawberry.types.Info) -> User:
return await info.context["user_loader"].load(self.author_id)
Cliente Apollo (Reagir)
// Setup
import { ApolloClient, InMemoryCache, ApolloProvider, gql, useQuery, useMutation } from '@apollo/client';
const client = new ApolloClient({
uri: 'https://api.example.com/graphql',
cache: new InMemoryCache(),
});
// Wrap app
function App() {
return <ApolloProvider client={client}><Router /></ApolloProvider>;
}
// Query hook
const GET_POSTS = gql`
query GetPosts($first: Int!) {
posts(first: $first) {
id title author { name }
}
}
`;
function PostList() {
const { data, loading, error } = useQuery(GET_POSTS, {
variables: { first: 10 },
fetchPolicy: 'cache-and-network',
});
if (loading) return <Spinner />;
if (error) return <Error message={error.message} />;
return (
<ul>
{data.posts.map(post => (
<li key={post.id}>{post.title} by {post.author.name}</li>
))}
</ul>
);
}
// Mutation hook
const CREATE_POST = gql`
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) { id title }
}
`;
function NewPostForm() {
const [createPost, { loading }] = useMutation(CREATE_POST, {
update(cache, { data: { createPost } }) {
// Update cache to include new post
cache.modify({
fields: {
posts(existingPosts = []) {
return [...existingPosts, createPost];
}
}
});
}
});
const handleSubmit = (title: string) => {
createPost({ variables: { input: { title, content: '' } } });
};
}
GraphQL em 2026 está maduro e pronto para produção. Use-o quando seu frontend precisar de busca flexível de dados, quando clientes móveis e web precisarem de diferentes formatos de dados ou quando você tiver dados interconectados (usuários/postagens/comentários). Use REST para APIs CRUD simples ou quando os clientes não controlam quais dados recebem.
🔗 Share this article
✍️ Leave a Comment