Construindo APIs com Cursor: REST, GraphQL e FastAPI

Construir APIs robustas requer planejamento cuidadoso, padrões consistentes e testes rigorosos. Os recursos de IA do Cursor podem acelerar todas as etapas do desenvolvimento de APIs, desde o scaffolding de endpoints até a geração de documentação OpenAPI. Este guia aborda fluxos de trabalho REST, GraphQL e FastAPI.
Pré-requisitos
Antes de começar, você precisará de:
- Cursor instalado com plano Pro (recomendado)
- Node.js ou Python instalado
- Compreensão de conceitos HTTP e JSON
Desenvolvimento de API REST
Scaffolding do Projeto
Peça ao Cursor para fazer o scaffolding de uma API REST completa:
Gere uma API REST com Express.js incluindo:
- TypeScript
- Manipuladores de rota em src/routes/
- Middleware para logging e tratamento de erros
- Validação de entrada com Zod
- Autenticação JWT
- Rate limiting
- Documentação Swagger
Estrutura de Rotas Express.js
// src/routes/users.ts
import { Router } from 'express';
import { z } from 'zod';
import { authenticate } from '../middleware/auth';
import { validate } from '../middleware/validate';
const router = Router();
const createUserSchema = z.object({
email: z.string().email(),
name: z.string().min(2).max(100),
password: z.string().min(8),
});
router.post(
'/',
authenticate,
validate(createUserSchema),
async (req, res, next) => {
try {
const user = await createUser(req.body);
res.status(201).json({
success: true,
data: user,
});
} catch (error) {
next(error);
}
}
);
router.get('/:id', authenticate, async (req, res, next) => {
try {
const user = await getUserById(req.params.id);
if (!user) {
return res.status(404).json({
success: false,
error: 'User not found',
});
}
res.json({ success: true, data: user });
} catch (error) {
next(error);
}
});
export default router;
Tratamento de Erros Integrado
// src/middleware/errorHandler.ts
import { Request, Response, NextFunction } from 'express';
export class AppError extends Error {
constructor(
public statusCode: number,
public message: string,
public isOperational = true
) {
super(message);
Object.setPrototypeOf(this, AppError.prototype);
}
}
export const errorHandler = (
err: Error,
req: Request,
res: Response,
next: NextFunction
) => {
if (err instanceof AppError) {
return res.status(err.statusCode).json({
success: false,
error: err.message,
});
}
console.error('Unexpected error:', err);
res.status(500).json({
success: false,
error: 'Internal server error',
});
};
Desenvolvimento FastAPI com Cursor
Criando Endpoints FastAPI
# main.py
from fastapi import FastAPI, HTTPException, Depends, status
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel, EmailStr
from typing import List, Optional
import uvicorn
app = FastAPI(
title="My API",
description="A production-ready API built with FastAPI",
version="1.0.0",
)
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# Modelos Pydantic
class UserCreate(BaseModel):
email: EmailStr
name: str
password: str
class UserResponse(BaseModel):
id: int
email: str
name: str
class Config:
from_attributes = True
# Armazenamento em memória (substitua por banco de dados)
users_db = {}
user_counter = 0
@app.post(
"/users",
response_model=UserResponse,
status_code=status.HTTP_201_CREATED,
tags=["users"],
)
async def create_user(user: UserCreate):
global user_counter
user_counter += 1
new_user = {
"id": user_counter,
"email": user.email,
"name": user.name,
}
users_db[user_counter] = new_user
return new_user
@app.get(
"/users/{user_id}",
response_model=UserResponse,
tags=["users"],
)
async def get_user(user_id: int):
if user_id not in users_db:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="User not found",
)
return users_db[user_id]
@app.get("/users", response_model=List[UserResponse], tags=["users"])
async def list_users(
skip: int = 0,
limit: int = 100,
):
return list(users_db.values())[skip : skip + limit]
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
Injeção de Dependências
# dependencies.py
from fastapi import Depends, HTTPException, status
from sqlalchemy.orm import Session
from database import SessionLocal
async def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
async def get_current_user(token: str = Depends(oauth2_scheme)):
user = verify_token(token)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication",
)
return user
# Usage
@app.get("/items")
async def read_items(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
return db.query(Item).filter(Item.owner_id == current_user.id).all()
Desenvolvimento GraphQL
Configuração do Apollo Server
// src/schema.ts
import { gql } from 'apollo-server-express';
export const typeDefs = gql`
type User {
id: ID!
email: String!
name: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
}
type Query {
users: [User!]!
user(id: ID!): User
posts: [Post!]!
post(id: ID!): Post
}
type Mutation {
createUser(email: String!, name: String!, password: String!): User!
createPost(title: String!, content: String!, authorId: ID!): Post!
}
`;
Resolvers com Cursor
// src/resolvers.ts
export const resolvers = {
Query: {
users: async (_, __, { dataSources }) => {
return dataSources.userAPI.getAllUsers();
},
user: async (_, { id }, { dataSources }) => {
return dataSources.userAPI.getUserById(id);
},
},
User: {
posts: async (parent, _, { dataSources }) => {
return dataSources.postAPI.getPostsByAuthor(parent.id);
},
},
Mutation: {
createUser: async (_, args, { dataSources }) => {
return dataSources.userAPI.createUser(args);
},
},
};
Design de Schema GraphQL
Use o Cursor para gerar schemas GraphQL a partir do seu schema de banco de dados. Cole seu schema Prisma e peça as definições de tipo GraphQL correspondentes.
Autenticação e Autorização
Implementação JWT
// src/utils/jwt.ts
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';
const JWT_SECRET = process.env.JWT_SECRET!;
const SALT_ROUNDS = 12;
export const hashPassword = async (password: string): Promise<string> => {
return bcrypt.hash(password, SALT_ROUNDS);
};
export const comparePassword = async (
password: string,
hash: string
): Promise<boolean> => {
return bcrypt.compare(password, hash);
};
export const generateToken = (payload: object): string => {
return jwt.sign(payload, JWT_SECRET, { expiresIn: '24h' });
};
export const verifyToken = (token: string): any => {
return jwt.verify(token, JWT_SECRET);
};
Padrões de Middleware
// src/middleware/rateLimit.ts
import rateLimit from 'express-rate-limit';
export const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: {
success: false,
error: 'Too many requests, please try again later',
},
standardHeaders: true,
legacyHeaders: false,
});
Documentação de API
OpenAPI no FastAPI
O FastAPI gera documentação OpenAPI automaticamente. Acesse em:
- Swagger UI:
/docs - ReDoc:
/redoc
Express.js com Swagger
// src/config/swagger.ts
import swaggerJsdoc from 'swagger-jsdoc';
const options = {
definition: {
openapi: '3.0.0',
info: {
title: 'My API',
version: '1.0.0',
description: 'API documentation',
},
servers: [
{
url: 'http://localhost:3000/api',
},
],
},
apis: ['./src/routes/*.ts'],
};
export const specs = swaggerJsdoc(options);
Testando APIs
Exemplo de Teste de Integração
// tests/users.test.ts
import request from 'supertest';
import app from '../src/app';
describe('Users API', () => {
describe('POST /users', () => {
it('should create a new user', async () => {
const response = await request(app)
.post('/users')
.send({
name: 'Test User',
password: 'securePassword123',
})
.expect(201);
expect(response.body.success).toBe(true);
});
it('should reject invalid email', async () => {
await request(app)
.post('/users')
.send({
email: 'invalid-email',
name: 'Test',
password: 'password',
})
.expect(400);
});
});
});
Melhores Práticas de Design de API
| Prática | Implementação |
|---|---|
| Versionamento | Use prefixo /api/v1/ na URL |
| Paginação | Retorne { data, meta: { page, limit, total } } |
| Filtragem | Use query params: ?status=active&sort=created_at |
| Formato de Erro | Consistente { success: false, error: "message" } |
| Rate Limiting | 100 requisições por 15 minutos para endpoints públicos |
| Cache | Use ETag headers e cache de resposta com Redis |
Padrões Comuns de API
Padrão Repository
// src/repositories/UserRepository.ts
export class UserRepository {
async findById(id: string): Promise<User | null> {
return prisma.user.findUnique({ where: { id } });
}
async findByEmail(email: string): Promise<User | null> {
return prisma.user.findUnique({ where: { email } });
}
async create(data: CreateUserInput): Promise<User> {
return prisma.user.create({ data });
}
async update(id: string, data: UpdateUserInput): Promise<User> {
return prisma.user.update({ where: { id }, data });
}
async delete(id: string): Promise<void> {
await prisma.user.delete({ where: { id } });
}
}
Configuração de Ambiente
# .env
NODE_ENV=development
PORT=3000
DATABASE_URL=postgresql://user:pass@localhost:5432/myapp
JWT_SECRET=your-secret-key
REDIS_URL=redis://localhost:6379
RATE_LIMIT_MAX=100
Referência Rápida
| Tarefa | Prompt do Cursor |
|---|---|
| Gerar endpoints CRUD | "Create CRUD endpoints for a Product resource with validation" |
| Adicionar autenticação | "Add JWT authentication middleware to all protected routes" |
| Gerar schema GraphQL | "Generate GraphQL types from this Prisma schema" |
| Escrever testes | "Write integration tests for the user registration endpoint" |
| Adicionar rate limit | "Add rate limiting to the API with different tiers for auth vs public" |
Recursos Relacionados
Última atualização: Julho de 2025