Pular para o conteúdo principal

Construindo APIs com Cursor: REST, GraphQL e FastAPI

Cursor API Development

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);
expect(response.body.data.email).toBe('[email protected]');
});

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áticaImplementação
VersionamentoUse prefixo /api/v1/ na URL
PaginaçãoRetorne { data, meta: { page, limit, total } }
FiltragemUse query params: ?status=active&sort=created_at
Formato de ErroConsistente { success: false, error: "message" }
Rate Limiting100 requisições por 15 minutos para endpoints públicos
CacheUse 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​

TarefaPrompt 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