Skip to content

eduardozaniboni/api-fundamentals-node

Repository files navigation

🚀 API de Cursos - Node.js com Fastify

Uma API REST moderna construída com Node.js, Fastify, TypeScript e PostgreSQL, utilizando Drizzle ORM para gerenciamento de banco de dados. Projeto completo com autenticação JWT, testes automatizados, filtros, paginação e deploy com Docker.

📋 Descrição

Este projeto é uma API completa para gerenciamento de cursos e usuários, desenvolvida como parte do desafio da Rocketseat. A aplicação demonstra boas práticas de desenvolvimento com TypeScript, validação de dados com Zod, autenticação JWT, testes automatizados com Vitest, documentação automática com Swagger/OpenAPI e gerenciamento de banco de dados com Drizzle ORM.

🛠️ Tecnologias Utilizadas

  • Node.js - Runtime JavaScript
  • TypeScript - Linguagem de programação tipada
  • Fastify - Framework web rápido e eficiente
  • Drizzle ORM - ORM moderno para TypeScript
  • PostgreSQL - Banco de dados relacional
  • Docker - Containerização
  • Zod - Validação de esquemas
  • Swagger/OpenAPI - Documentação da API
  • Pino Pretty - Logs formatados
  • JWT - Autenticação stateless
  • Argon2 - Criptografia de senhas
  • Vitest - Framework de testes
  • Supertest - Testes de integração
  • Faker.js - Geração de dados fictícios

🚀 Como Executar

Pré-requisitos

  • Node.js 18+
  • Docker e Docker Compose
  • npm ou yarn

Instalação

  1. Clone o repositório
git clone <url-do-repositorio>
cd aulas
  1. Instale as dependências
npm install
  1. Configure as variáveis de ambiente
cp .env.example .env
# Edite o arquivo .env com suas configurações
  1. Suba o banco de dados com Docker
docker compose up -d
  1. Execute as migrações
npm run db:migrate
  1. Popule o banco com dados de teste (opcional)
npm run db:seed
  1. Inicie o servidor de desenvolvimento
npm run dev

O servidor estará rodando em http://localhost:3333

📚 Documentação da API

A documentação da API está disponível em:

  • Swagger UI: http://localhost:3333/docs (apenas em desenvolvimento)
  • Scalar API Reference: Interface mais moderna e bonita

🗄️ Banco de Dados

Estrutura das Tabelas

Tabela users

  • id (UUID, Primary Key)
  • name (Text, Not Null)
  • email (Text, Not Null, Unique)
  • password (Text, Not Null) - Criptografada com Argon2
  • role (Enum: 'student' | 'manager', Default: 'student')

Tabela courses

  • id (UUID, Primary Key)
  • title (Text, Not Null, Unique)
  • description (Text, Optional)

Tabela enrollments

  • id (UUID, Primary Key)
  • userId (UUID, Foreign Key -> users.id)
  • courseId (UUID, Foreign Key -> courses.id)
  • createdAt (Timestamp, Default: Now)
  • Índice único em (userId, courseId)

Comandos do Drizzle

# Gerar SQL baseado no schema
npm run db:generate

# Executar migrações
npm run db:migrate

# Abrir o Drizzle Studio
npm run db:studio

# Popular banco com dados de teste
npm run db:seed

📁 Estrutura do Projeto

├── src/
│   ├── database/
│   │   ├── client.ts      # Configuração do cliente Drizzle
│   │   ├── schema.ts      # Definição das tabelas
│   │   └── seed.ts        # População do banco com dados fictícios
│   ├── routes/
│   │   ├── create-course.ts
│   │   ├── get-courses.ts
│   │   ├── get-course-by-id.ts
│   │   ├── login.ts
│   │   └── hooks/
│   ├── tests/
│   │   └── factories/     # Factories para testes
│   ├── utils/
│   │   └── get-authenticated-user-from-request.ts
│   ├── @types/
│   └── app.ts            # Configuração da aplicação
├── drizzle/               # Arquivos de migração gerados
├── server.ts             # Arquivo principal do servidor
├── drizzle.config.ts     # Configuração do Drizzle
├── docker-compose.yml    # Configuração do Docker
├── Dockerfile           # Configuração para deploy
└── requests.http         # Exemplos de requisições

🔧 Scripts Disponíveis

  • npm run dev - Inicia o servidor em modo desenvolvimento
  • npm run db:generate - Gera arquivos de migração
  • npm run db:migrate - Executa migrações pendentes
  • npm run db:studio - Abre o Drizzle Studio
  • npm run db:seed - Popula o banco com dados de teste
  • npm test - Executa os testes automatizados
  • npm run build - Constrói a imagem Docker

📡 Endpoints da API

Autenticação

  • POST /login - Autenticar usuário e gerar JWT

Cursos

  • POST /courses - Criar um novo curso (requer autenticação)
  • GET /courses - Listar cursos com filtros e paginação
  • GET /courses/:id - Buscar curso por ID

Exemplo de Uso

# 1. Fazer login para obter o token JWT
curl -X POST http://localhost:3333/login \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com", "password": "password123"}'

# 2. Usar o token para criar um curso
curl -X POST http://localhost:3333/courses \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -d '{"title": "Curso de Docker", "description": "Aprenda Docker do zero"}'

# 3. Listar cursos com filtros
curl "http://localhost:3333/courses?search=docker&page=1&perPage=10"

# 4. Buscar curso específico
curl http://localhost:3333/courses/{id}

🔐 Autenticação JWT

A API utiliza autenticação JWT (JSON Web Token) para proteger as rotas. O fluxo de autenticação funciona da seguinte forma:

  1. Login: Usuário envia email e senha
  2. Validação: Sistema verifica credenciais no banco
  3. Geração do Token: Se válido, gera um JWT com payload contendo:
    • sub: ID do usuário
    • role: Papel do usuário (student/manager)
    • iat: Timestamp de criação
  4. Uso: Token é enviado no header Authorization: Bearer <token>

Segurança

  • Senhas são criptografadas com Argon2 (hash irreversível)
  • JWT usa criptografia de chave simétrica (HS256)
  • Tokens não armazenam informações sensíveis
  • Qualquer alteração no token invalida a assinatura

🔍 Filtros e Paginação

Filtros Disponíveis

  • search: Busca por título do curso
  • orderBy: Ordenação (title, createdAt)
  • order: Direção da ordenação (asc, desc)

Paginação

A API implementa Cursor-based Pagination para melhor performance:

  • page: Número da página (padrão: 1)
  • perPage: Itens por página (padrão: 20, máximo: 100)

Exemplo de Uso

# Buscar cursos com filtros
GET /courses?search=docker&orderBy=title&order=asc&page=1&perPage=10

# Resposta
{
  "courses": [...],
  "pagination": {
    "page": 1,
    "perPage": 10,
    "total": 25,
    "totalPages": 3
  }
}

🧪 Testes Automatizados

O projeto inclui uma suíte completa de testes usando Vitest e Supertest:

Tipos de Testes

  • E2E (End-to-End): Testam a aplicação de ponta a ponta
  • Integração: Testam a comunicação entre componentes
  • Unitários: Testam funções isoladas

Executar Testes

# Executar todos os testes
npm test

# Executar com coverage
npm test -- --coverage

# Executar testes em modo watch
npm test -- --watch

Estrutura dos Testes

src/routes/
├── create-course.test.ts
├── get-courses.test.ts
├── get-course-by-id.test.ts
└── login.test.ts

Factories para Testes

O projeto utiliza factories para gerar dados de teste consistentes:

// src/tests/factories/make-course.ts
export function makeCourse(override: Partial<typeof courses.$inferInsert> = {}) {
  return {
    title: faker.lorem.words(4),
    description: faker.lorem.sentence(),
    ...override,
  }
}

🐳 Deploy com Docker

Construir a Imagem

# Construir a imagem
docker build -t api-cursos .

# Executar o container
docker run -p 3333:3333 api-cursos

Dockerfile

FROM node:22-alpine AS builder
WORKDIR /app
COPY . ./
RUN npm ci
EXPOSE 3333
CMD ["sh", "-c", "npm run db:migrate && node src/server.ts"]

Variáveis de Ambiente para Produção

DATABASE_URL=postgresql://user:password@host:5432/database
JWT_SECRET=your-super-secret-key
NODE_ENV=production

🔄 Fluxo da Aplicação

Arquitetura Geral

graph TD
    A[Cliente] --> B[Fastify Server]
    B --> C[JWT Middleware]
    C --> D[Zod Validation]
    D --> E[Route Handler]
    E --> F[Drizzle ORM]
    F --> G[PostgreSQL]
    G --> F
    F --> E
    E --> H[Response]
    H --> A
    
    I[Docker Compose] --> J[PostgreSQL Container]
    J --> G
    
    K[Vitest] --> L[Test Database]
    L --> G
Loading

Fluxo de Autenticação

sequenceDiagram
    participant Client as Cliente
    participant Fastify as Fastify Server
    participant Auth as Auth Handler
    participant DB as PostgreSQL
    participant JWT as JWT Service

    Client->>Fastify: POST /login
    Note over Client,Fastify: {email, password}
    
    Fastify->>Auth: Executar Login
    Auth->>DB: Buscar usuário por email
    DB-->>Auth: Dados do usuário
    
    Auth->>Auth: Verificar senha com Argon2
    Auth->>JWT: Gerar token
    JWT-->>Auth: JWT Token
    
    Auth-->>Fastify: {token: "jwt..."}
    Fastify-->>Client: 200 OK
Loading

Fluxo de Criação de Curso (Autenticado)

sequenceDiagram
    participant Client as Cliente
    participant Fastify as Fastify Server
    participant JWT as JWT Middleware
    participant Zod as Zod Validation
    participant Route as Route Handler
    participant Drizzle as Drizzle ORM
    participant DB as PostgreSQL

    Client->>Fastify: POST /courses
    Note over Client,Fastify: Authorization: Bearer <token>
    
    Fastify->>JWT: Verificar token
    JWT-->>Fastify: ✅ Token válido
    
    Fastify->>Zod: Validar Body
    Zod-->>Fastify: ✅ Validação OK
    
    Fastify->>Route: Executar Handler
    Route->>Drizzle: db.insert(courses)
    Drizzle->>DB: INSERT INTO courses
    DB-->>Drizzle: ID gerado
    Drizzle-->>Route: Resultado
    Route-->>Fastify: {courseId: "uuid"}
    Fastify-->>Client: 201 Created
Loading

Fluxo de Listagem com Filtros

sequenceDiagram
    participant Client as Cliente
    participant Fastify as Fastify Server
    participant Route as Route Handler
    participant Drizzle as Drizzle ORM
    participant DB as PostgreSQL

    Client->>Fastify: GET /courses?search=docker&page=1
    
    Fastify->>Route: Executar Handler
    Route->>Drizzle: db.select().from(courses).where(like(title, '%docker%'))
    Drizzle->>DB: SELECT * FROM courses WHERE title ILIKE '%docker%' LIMIT 20 OFFSET 0
    DB-->>Drizzle: Cursos filtrados
    Drizzle-->>Route: Array de cursos
    Route-->>Fastify: {courses: [...], pagination: {...}}
    Fastify-->>Client: 200 OK
Loading

📈 Roadmap

✅ Concluído (Aulas 1-4)

  • Configuração inicial do projeto
  • Setup do TypeScript
  • Configuração do Fastify
  • Integração com Drizzle ORM
  • Configuração do PostgreSQL com Docker
  • Validação com Zod
  • Documentação com Swagger/OpenAPI
  • CRUD básico de cursos
  • Filtros e paginação (Cursor-based)
  • Autenticação JWT
  • Criptografia de senhas com Argon2
  • Testes automatizados com Vitest
  • Factories para testes
  • Deploy com Docker
  • População de dados com Faker.js

🔄 Melhorias Futuras

  • Soft Delete para cursos
  • Refresh tokens
  • Rate limiting
  • Logs estruturados
  • Métricas e observabilidade
  • CI/CD com GitHub Actions
  • Cache com Redis
  • Upload de arquivos

🧪 Executando Testes

Configuração de Testes

O projeto usa um banco de dados separado para testes:

# Banco de teste é criado automaticamente
npm test

# Ver coverage
npm test -- --coverage

Estrutura de Testes

// Exemplo de teste E2E
test('should create a course', async () => {
  const courseData = makeCourse()
  
  const response = await supertest(app)
    .post('/courses')
    .set('Authorization', `Bearer ${token}`)
    .send(courseData)
    
  expect(response.status).toBe(201)
  expect(response.body).toHaveProperty('courseId')
})

🐳 Docker

Subir apenas o banco de dados

docker compose up -d db

Ver containers rodando

docker ps

Construir e executar a aplicação

# Construir
docker build -t api-cursos .

# Executar
docker run -p 3333:3333 --env-file .env api-cursos

📝 Logs

A aplicação utiliza Pino Pretty para logs formatados e legíveis. Os logs incluem:

  • Requisições HTTP
  • Tempo de resposta
  • Erros e warnings
  • Informações de debug
  • Logs de autenticação

🔒 Segurança

  • Senhas: Criptografadas com Argon2 (hash irreversível)
  • JWT: Assinatura com chave secreta
  • Validação: Todos os inputs validados com Zod
  • SQL Injection: Prevenido pelo Drizzle ORM
  • Rate Limiting: Implementado nas rotas sensíveis

🤝 Contribuição

  1. Faça um fork do projeto
  2. Crie uma branch para sua feature (git checkout -b feature/AmazingFeature)
  3. Commit suas mudanças (git commit -m 'Add some AmazingFeature')
  4. Push para a branch (git push origin feature/AmazingFeature)
  5. Abra um Pull Request

📄 Licença

Este projeto está sob a licença ISC. Veja o arquivo LICENSE para mais detalhes.

👨‍💻 Autor

Desenvolvido como parte do desafio da Rocketseat.


Nota: Este projeto demonstra uma API moderna com algumas das funcionalidades essenciais para produção, incluindo autenticação, testes, filtros, paginação e deploy.

About

API REST moderna para gerenciamento de cursos, construída com Node.js, Fastify, TypeScript e PostgreSQL. Inclui autenticação JWT, testes automatizados, Docker e boas práticas de arquitetura.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors