GraphQL vs REST em TypeScript: duelo de titãs ou escolha baseada na tarefa?
\n\nQuando você programa em TypeScript, a questão da arquitetura do backend se torna especialmente crítica. Você não quer apenas "que funcione", mas que os tipos voem, os erros sejam capturados em tempo de compilação e o código seja previsível. E então o desenvolvedor se depara com a escolha eterna: o consagrado REST ou o moderno e flexível GraphQL? Ambas as tecnologias se dão muito bem com TypeScript, mas cada uma dita seu próprio estilo de desenvolvimento.
\n\nNeste artigo, não vamos enrolar e repetir verdades absolutas. Vamos analisar GraphQL vs REST em TypeScript com exemplos concretos, código, pontos críticos e cenários onde uma abordagem supera a outra. Vamos nessa!
\n\nREST: o velho amigo que nunca decepciona
\n\nREST (Representational State Transfer) é um estilo arquitetural onde cada recurso (usuário, post, comentário) tem sua própria URL. Você acessa /api/users/1 e obtém o usuário. Simples e previsível. Em projetos TypeScript, o REST é frequentemente implementado com Express, Fastify ou NestJS.
Tipagem em REST com TypeScript
\nA principal dor do REST é a fraca conexão entre backend e frontend. Você envia JSON, e o frontend adivinha: "O que veio?". O TypeScript salva através de tipos compartilhados ou OpenAPI (Swagger).
\n\nExemplo: backend com Express e TypeScript
\n\nimport express, { Request, Response } from 'express';\n\ninterface User {\n id: number;\n name: string;\n email: string;\n}\n\nconst app = express();\napp.get('/api/users/:id', (req: Request, res: Response) => {\n const user: User = { id: +req.params.id, name: 'Alice', email: 'alice@example.com' };\n res.json(user);\n});\n\n\nFrontend (fetch):
\n\ninterface User {\n id: number;\n name: string;\n email: string;\n}\n\nasync function getUser(id: number): Promise<User> {\n const res = await fetch(`/api/users/${id}`);\n return res.json() as Promise<User>;\n}\n\n\nProblema: se o backend alterar a estrutura do User, o frontend só descobrirá em tempo de execução. Os tipos no frontend são apenas uma cópia. Solução: geração de tipos a partir da especificação OpenAPI.
\n\nPrós do REST em TypeScript
\n- \n
- Simplicidade de cache: Cache HTTP (CDN, navegador) funciona nativamente. \n
- Estrutura clara: URL = recurso, métodos = ações (GET, POST, PUT, DELETE). \n
- Amplo suporte: Qualquer cliente (navegador, curl, Postman) entende REST. \n
- Baixa barreira de entrada: Até um júnior entende em uma hora. \n
Contras do REST
\n- \n
- Over-fetching e Under-fetching: Você ou carrega campos desnecessários ou faz várias requisições. \n
- Estrutura rígida: Para obter o usuário + seus posts, são necessárias duas requisições ou gambiarras (campo expand). \n
- Tipagem fraca: Sem OpenAPI, os tipos no cliente são apenas "palavra de honra". \n
GraphQL: flexibilidade e controle
\n\nGraphQL é uma linguagem de consulta que dá ao cliente o direito de decidir quais dados receber. Um endpoint (/graphql), uma consulta — e você obtém exatamente o que pediu. TypeScript e GraphQL formam o par ideal: os tipos são gerados automaticamente a partir do schema.
Tipagem em GraphQL com TypeScript
\nPrimeiro, escreve-se o schema (SDL):
\n\ntype User {\n id: ID!\n name: String!\n email: String!\n posts: [Post!]!\n}\n\ntype Post {\n id: ID!\n title: String!\n content: String!\n}\n\ntype Query {\n user(id: ID!): User\n}\n\n\nEm seguida, os tipos são gerados (via graphql-codegen):
// generated.ts\nexport type User = {\n __typename?: 'User';\n id: string;\n name: string;\n email: string;\n posts: Array<Post>;\n};\n\nexport type Post = {\n __typename?: 'Post';\n id: string;\n title: string;\n content: string;\n};\n