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.
- 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)
- Ter o Node.js (v18+) e NPM instalados.
- Ter o Docker e Docker Compose instalados para o banco de dados.
- Faça o clone deste repositório.
- Na raiz do projeto, instale as dependências:
npm install
- Suba o banco de dados PostgreSQL com Docker:
docker-compose up -d
- Crie o arquivo
.envbaseando-se no que está no projeto. O banco subirá na porta 5432 com o schemajitterbit_orders.
Gere e implemente o schema do banco rodando o seguinte comando:
npx prisma migrate dev --name initEste comando criará as tabelas necessárias (Order e Item).
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:
- Abra o arquivo
prisma/schema.prisma. - Onde está escrito
provider = "postgresql", mude paraprovider = "sqlite". - No seu arquivo
.env, mude aDATABASE_URLpara:DATABASE_URL="file:./dev.db" - Apague a pasta
prisma/migrations(caso a pasta exista). - Rode o comando de migração do Prisma:
npx prisma migrate dev --name init_sqlite
Inicie o projeto em modo de desenvolvimento (faz o reload automático):
npm run devA API estará rodando em: http://localhost:3000.
A documentação interativa (Swagger UI) pode ser acessada em: http://localhost:3000/api-docs
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.
- Faça um
POSTparahttp://localhost:3000/auth/login. - 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. 👽
O projeto utiliza o Jest como framework de testes em conjunto com o Supertest. Para rodar a suíte de testes inteira:
npm testObservaçã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.
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).
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"
}
- 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 salvardatCriacaono lugar dedataCriacao). - 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 umprisma.$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.
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/ordertêm o middleware injetado nelas. - Validação: Ele inspeciona o cabeçalho
Authorizationde todas as requisições em busca de uma string no formatoBearer <token>. - Descriptografia e Controle de Acesso: Se a string JWT estiver presente, a biblioteca
jsonwebtokententa 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.
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:valorTotaldeve ser positivo,idItemdeve 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.
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.
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 arquivoroutes.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!
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.
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:
- 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.
- 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.
- 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 doJest + Supertest. Se qualquer teste da API falhar, a pipeline quebra, impedindo a aprovação do Pull Request.
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)
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).
[!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/listretorna 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 usandotakeeskipdo 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).