Skip to content

Repository files navigation

Jitterbit Challenge API

CI Pipeline

PostgresSwagger

Esta é uma API REST desenvolvida em Node.js para o desafio da Jitterbit. O objetivo principal do projeto é expor endpoints para o gerenciamento de pedidos, recebendo dados em um formato específico e transformando esses dados antes de persistí-los num banco de dados PostgreSQL.

Tecnologias Utilizadas

  • Node.js (v18+) com Express (v4.18.3)
  • TypeScript (v5.3.3) (Tipagem forte e segurança no desenvolvimento)
  • PostgreSQL (Banco de dados relacional escolhido)
  • Prisma ORM (v5.10.2) (Interação com o banco de dados e migrations seguras)
  • Swagger (swagger-ui-express v5.0.0) (Documentação da API via endpoint /api-docs)
  • Jest (v29.7.0) & Supertest (v6.3.4) (Para testes de integração)
  • JWT (JSON Web Token) (jsonwebtoken v9.0.2) (Para autenticação básica das rotas)
  • Docker Compose (Para levantar de maneira rápida o banco de dados em ambiente local)
  • Zod (v4.3.6)
  • GitHub Actions (Pipeline de CI para rodar os testes da suíte a cada nova alteração)

RODANDO LOCALMENTE

1. Pré-requisitos

  • Ter o Node.js (v18+) e NPM instalados.
  • Ter o Docker e Docker Compose instalados para o banco de dados.

2. Configurando o Ambiente

  1. Faça o clone deste repositório.
  2. Na raiz do projeto, instale as dependências:
    npm install
  3. Suba o banco de dados PostgreSQL com Docker:
    docker-compose up -d
  4. Crie o arquivo .env baseando-se no que está no projeto. O banco subirá na porta 5432 com o schema jitterbit_orders.

3. Banco de Dados e Migrations

Padrão: PostgreSQL (com Docker)

Gere e implemente o schema do banco rodando o seguinte comando:

npx prisma migrate dev --name init

Este comando criará as tabelas necessárias (Order e Item).

Alternativa Avaliador: Rodando sem Docker (via SQLite)

Important

AVISO: Caso você não tenha Docker instalado na máquina, é possível rodar tudo perfeitamente alterando o banco de dados temporariamente para SQLite. O Prisma criará um arquivo local no próprio projeto para servir de banco, sem necessidade de configurar nada extra.

Para fazer isso:

  1. Abra o arquivo prisma/schema.prisma.
  2. Onde está escrito provider = "postgresql", mude para provider = "sqlite".
  3. No seu arquivo .env, mude a DATABASE_URL para: DATABASE_URL="file:./dev.db"
  4. Apague a pasta prisma/migrations (caso a pasta exista).
  5. Rode o comando de migração do Prisma:
    npx prisma migrate dev --name init_sqlite

4. Executando a API

Inicie o projeto em modo de desenvolvimento (faz o reload automático):

npm run dev

A API estará rodando em: http://localhost:3000. A documentação interativa (Swagger UI) pode ser acessada em: http://localhost:3000/api-docs

Autenticação

Todos os endpoints expostos sob /order requerem autenticação Bearer Token (JWT). Como se trata de um desafio, o sistema inclui uma rota de simulação para gerar esse token.

  1. Faça um POST para http://localhost:3000/auth/login.
  2. Pegue o token retornado e adicione no header das suas solicitações: Authorization: Bearer <seu_token>

Tip

O Swagger possui o botão "Authorize", no canto superior direito, onde você pode colar o token gerado. 👽

Testes Automatizados

O projeto utiliza o Jest como framework de testes em conjunto com o Supertest. Para rodar a suíte de testes inteira:

npm test

Observação: Os testes irão apagar e recriar os registros do banco na tabela principal. Rodar preferencialmente em ambiente de testes isolado, ou no pipeline onde será instanciado.

Transformação de Dados e Estrutura

Conforme os requisitos, a aplicação recebe o seguinte formato:

{
  "numeroPedido": "v10089015vdb-01",
  "valorTotal": 10000,
  "dataCriacao": "2023-07-19...",
  "items": [{ "idItem": "2434" ... }]
}

E no momento de persistir os dados no banco usando o prisma, ele faz o mapeamento para os campos (orderId, value, creationDate, productId e afins), assim estruturando adequadamente num cenário corporativo real. A criação do Pedido + Itens é protegida usando Transactions providas pelo próprio Prisma (caso falhe o item, a ordem não é criada pela metade).


Documentação Técnica e Decisões de Arquitetura

Prisma ORM

Note

O Prisma foi escolhido como Object-Relational Mapper (ORM) principal por sua forte sinergia com o TypeScript.

erDiagram
    Order ||--|{ Item : "contém (1:N)"
    
    Order {
        string orderId PK "Ex: v10089016vdb"
        int value "Valor em centavos"
        DateTime creationDate
    }
    
    Item {
        int id PK "Autoincrement"
        string orderId FK "Ref. Order"
        int productId "Ex: 2434"
        int quantity "Quantidade"
        int price "Valor unitário"
    }
Loading
  • Tipagem Automática: Ao definir o banco de dados no arquivo prisma/schema.prisma, o Prisma gera arquivos de tipos estritos. Isso significa que o autocomplete do editor de código sabe exatamente quais tabelas e colunas existem, prevenindo erros de digitação (ex: tentar salvar datCriacao no lugar de dataCriacao).
  • Segurança (SQL Injection): O Prisma abstrai a criação das queries (consultas) em SQL puras por debaixo dos panos, sanitizando automaticamente as variáveis do usuário, o que virtualmente elimina os ricos de injeção de SQL.
  • Transações (Atomicidade): No arquivo order.controller.ts, a inserção do Pedido e de seus vários Itens ocorre dentro de um prisma.$transaction(). Se um item falhar ao ser inserido (falta de campo exigido, por exemplo), o Prisma faz o rollback automático e a "Order" principal não é criada parcialmente no banco.

Autenticação JWT (JSON Web Token)

Note

Para garantir que apenas clientes e serviços autorizados enviem requisições de modificação ou leitura de pedidos, a API é protegida por um Middleware de Autenticação.

  • O Middleware (auth.middleware.ts): Atua como um interceptador no Express. Diferente da rota livre de /auth/login, todas as rotas sob /order têm o middleware injetado nelas.
  • Validação: Ele inspeciona o cabeçalho Authorization de todas as requisições em busca de uma string no formato Bearer <token>.
  • Descriptografia e Controle de Acesso: Se a string JWT estiver presente, a biblioteca jsonwebtoken tenta validar a assinatura digital usando a chave secreta da API (JWT_SECRET). Caso o token for inválido, adulterado ou estiver expirado, a requisição é negada prematuramente com o status HTTP 401 (Não Autorizado), economizando processamento computacional.

Validação de Dados com Zod

Note

A API utiliza o Zod como motor de validação de esquemas (schemas).

  • Esquemas Rígidos: Definidos em src/schemas, os esquemas validam os tipos primitivos e também restrições de negócio como: valorTotal deve ser positivo, idItem deve ter conteúdo e o pedido deve conter pelo menos um item.
  • Fail-fast: A validação ocorre no Controller através do safeParse(). Se os dados recebidos estiverem incorretos, a API responde instantaneamente com 400 (Bad Request), detalhando exatamente quais campos falharam na validação.

Camada de Serviços (Service Layer)

Note

A arquitetura segue o padrão de Service Layer para separar as preocupações da aplicação.

  • Desacoplamento: No arquivo src/services/order.service.ts, concentramos toda a lógica de persistência e transformação dos dados específicos da Jitterbit.
  • Testabilidade: Isso permite que a lógica de negócio seja testada e mantida independentemente da tecnologia de transporte (HTTP/Express), facilitando evoluções futuras.

Documentação Interativa (Swagger)

Note

Criar uma API isolada sem que os clientes saibam como usá-la é um anti-padrão. Foi incluída a implementação de especificação Swagger/OpenAPI.

  • Geração Dinâmica: A biblioteca swagger-jsdoc é responsável por ler comentários mágicos (com a anotação /** @swagger ... */) deixados diretamente acima da declaração das rotas no arquivo routes.ts.
  • Interface Gráfica (swagger-ui-express): Ao acessar /api-docs, a UI do Swagger transforma a especificação estática em um painel interativo.
  • Testabilidade: A especificação contém esquemas (schemas), parâmetros requeridos, referências e a configuração do Bearer Token. Isso permite que o avaliador faça o Request de Login pela própria interface gráfica, injete o token gerado no botão "Authorize", e então execute requests de POST e GET diretamente pelo navegador, sem precisar configurar dependências externas ou importações do Postman ou Insomnia!

Docker com PostgreSQL

Note

O uso de um contêiner Docker rodando a imagem oficial do PostgreSQL foi adotado por vários motivos arquiteturais e de Developer Experience (DX):

  • Isolamento de Ambiente: Garante que o banco de dados rodará exatamente com a mesma versão (Postgres 15) e configurações, independente do sistema operacional (Windows, Mac ou Linux) de quem estiver avaliando ou rodando o projeto.
  • Setup em 1 Comando: Elimina a necessidade de instalar o PostgreSQL na máquina do hospedeiro, configurar serviços em background, usuários, senhas ou esquemas manualmente. Com apenas docker-compose up -d, o banco de dados e as variáveis necessárias ("root" e "rootpassword") sobem instantaneamente.
  • Eficiência na Avaliação: Como em um desafio técnico o tempo do avaliador é precioso, prover uma infraestrutura conteinerizada demonstra preparo para cenários de DevOps e Cloud, além de garantir que não haverá conflitos de porta caso o avaliador já possua um banco relacional rodando em sua máquina principal.

CI/CD (Integração Contínua com GitHub Actions)

Note

A automatização de testes e checagens de qualidade foi configurada usando GitHub Actions (.github/workflows/ci.yml). Essa esteira automatizada não só protege a branch principal de código quebrado, como mostra domínio em deploys corporativos.

Sempre que houver um Push ou Pull Request para a branch main:

  1. Ambiente Spin-up: A cloud do GitHub cria uma instância Ubuntu e sobe um contêiner auxiliar rodando o banco de dados do sistema (PostgreSQL 15), simulando perfeitamente o ambiente de produção/docker local.
  2. Quality Gates:
    • Prepara os pacotes do Node (NPM Install).
    • Roda verificações estáticas de estilo e erros de variável não utilizadas com ESLint.
    • Faz o Build e compilação (npm run build) checando erros estritos do TypeScript.
  3. Migrações e Testes: Executa de maneira headless as migrações do Prisma ORM (prisma migrate deploy) instanciando as tabelas e enfim, ativa a rotina inteira do Jest + Supertest. Se qualquer teste da API falhar, a pipeline quebra, impedindo a aprovação do Pull Request.

Estrutura Arquitetural (Project Structure)

Note

A base de código foi desenhada visando separação de responsabilidades (princípios de Clean Code) para facilitar escalabilidade futura.

src/
 ├── controllers/     # Orquestradores de requisição (Delegam para serviços)
 ├── services/        # Camada de Negócio e persistência (OrderService)
 ├── schemas/         # Definição de contratos de dados (Zod Schemas)
 ├── lib/             # Instâncias compartilhadas (Prisma Singleton)
 ├── middlewares/     # Interceptadores (Autenticação JWT, Captura de Erros)
 ├── utils/           # Ferramentas auxiliares globais (Configuração do Swagger-jsdoc)
 ├── routes.ts        # Mapeamento do roteamento Express + Anotações OpenAPI
 ├── app.ts           # Configuração de base do Express (CORS, Parsers JSON)
 └── index.ts         # Ponto de inicialização do Servidor (listen to Port)

Tratamento Centralizado de Erros (Error Handling)

Note

Em vez de vazar stack traces extensos e sensíveis diretamente para os clientes ou avaliadores quando algo falha, a arquitetura utiliza o error.middleware.ts injetado na raiz do app.ts.

Qualquer erro imprevisto mapeado via bloco try/catch nos controllers é repassado pela variável next(error) para esse middleware central, que mascara o lado servidor devolvendo um limpo Status 500 (Internal Server Error).

Próximos Passos

[!NOTE]

  • Redis: Integrar Redis nas requisições GET para economizar queries de I/O em banco relacional, porque ler do disco rígido (onde mora o PostgreSQL) exige muito mais latência e processamento da máquina. O Redis funciona salvando os dados na Memória RAM, entregando os mesmos pedidos numa fração de milissegundos.
  • Mensageria (RabbitMQ / Kafka): Se a fila de e-commerce sobrecarregasse, criar os pedidos assincronamente consumindo seria mais prudente. 😵‍💫😵‍💫
  • Paginação: Atualmente a rota /order/list retorna todos os pedidos de uma vez. Em uma aplicação real, isso não é lá muito inteligente, poderia causaria estouro de memória no servidor Node.js (OOM) e pesaria (muito) na rede. A solução ideal seria adicionar paginação usando take e skip do Prisma, permitindo buscar os dados em lotes (ex: ?page=1&limit=50), tornando a API muito mais leve e escalável ou implementar paginação no banco de dados.
  • Refresh Token: O JWT atual tem uma validade hard-coded fixa de 1 dia. Se esse token for interceptado e roubado, o invasor tem acesso irreversível à API por 24 horas. O padrão seria o uso de Short-lived Access Tokens combinados com Refresh Tokens salvos no banco. Isso permite revogar o acesso de sessões suspeitas, sem prejudicar o usuário (que renova seu acesso com o Refresh Token).

About

Esta é uma API REST desenvolvida em Node.js para o desafio da Jitterbit.

Resources

Stars

1 star

Watchers

1 watching

Forks

Contributors

Languages