O Bolão da Copa 2026 é uma API RESTful para criação e gerenciamento de bolões de palpites da Copa do Mundo FIFA 2026. O projeto foi desenvolvido como trabalho prático da disciplina de Desenvolvimento Web 3, aplicando Vertical Slice, Orientação a Objetos, Injeção de Dependência e modelagem relacional em PostgreSQL, além de documentação automática com Swagger/OpenAPI.
Um script auxiliar importa dados reais da Copa (seleções, grupos, calendário e resultados) a partir da API pública da ESPN.
- 🗄️ Banco de Dados
- 🏗️ Arquitetura
- ⚡ Funcionalidades e Regras de Negócio
- 🚀 Como Executar Localmente
- 📡 Documentação da API
- 📌 Script de Importação
- 📄 Licença
Modelado em PostgreSQL, com 8 tabelas:
| Tabela | Descrição |
|---|---|
grupos |
Grupos da Copa do Mundo (A a L). |
paises |
Seleções participantes, com bandeira, sigla FIFA e grupo. |
jogos |
Partidas da competição, incluindo fase, estádio e chaveamento. |
resultados |
Resultados oficiais dos jogos. |
perfis |
Participantes do bolĂŁo. |
boloes |
Bolões criados por um perfil. |
participantes_bolao |
Tabela pivô entre perfis e bolões, com pontuação acumulada. |
palpites |
Palpites registrados por participantes para cada jogo de um bolĂŁo. |
- 1:1 —
resultados↔jogos(UNIQUEemjogo_id+ FK). - 1:N —
grupos→paises;perfis→boloes(criador);paises→jogos(casa/fora). - N:N —
participantes_bolao(tabela associativa entreperfiseboloes, com atributos extras comopontuacao_total).palpitestambém representa uma relação N:N "resolvida" entreperfis/boloesejogos.
O arquivo database.sql, na raiz do projeto, contém todos os CREATE TABLE com PKs, FKs e constraints necessárias para recriar o banco do zero.
O backend usa Fastify, seguindo rigorosamente Vertical Slice, OOP e Injeção de Dependência.
bolaoCopaDW3/
├── database.sql
├── package.json
├── .env
├── scripts/
│ ├── importarCopa.js # busca dados reais da Copa na API da ESPN
│ └── runImport.js # roda a importação e encerra o processo
└── src/
├── server.js # Fastify, CORS, Swagger e registro das rotas
├── database/
│ └── pool.js # única fonte de conexão com o pg
├── shared/
│ ├── AppError.js # classe de erro customizada
│ ├── errorHandler.js # Error Handler Global
│ └── errorSchema.js # schema de erro reutilizável no Swagger
└── features/
├── perfis/
│ ├── controller.js
│ ├── service.js
│ ├── repository.js
│ └── route.js
├── paises/ (mesma estrutura)
├── boloes/ (mesma estrutura)
├── jogos/ (mesma estrutura)
├── resultados/ (mesma estrutura)
└── palpites/ (mesma estrutura)
Cada feature segue exatamente as mesmas camadas, implementadas como Classes:
- Controller — só recebe
req/reply, extrai dados e devolve a resposta. Não contém lógica de negócio. - Service — regras de negócio e validações. Lança
throw new AppError(mensagem, statusCode)quando algo é inválido. Nunca importapgnem conhecereq/reply. - Repository — única camada autorizada a importar
pg(viaPool) e rodar SQL. route.js— monta as instâncias comnewe injeta as dependências via construtor (new Service(repository),new Controller(service)). É o único lugar ondenewaparece fora do Repository.
- NĂŁo Ă© possĂvel cadastrar um resultado para um jogo que já possui resultado.
- NĂŁo Ă© possĂvel excluir um jogo já encerrado.
- NĂŁo Ă© possĂvel criar, atualizar ou remover um palpite de um jogo já encerrado.
- Um perfil nĂŁo pode participar do mesmo bolĂŁo duas vezes.
- Um perfil nĂŁo pode registrar dois palpites para o mesmo jogo dentro do mesmo bolĂŁo.
- Um palpite sĂł pode ser criado se o perfil for participante do bolĂŁo informado.
- Ao cadastrar um bolĂŁo, o
criador_perfil_idprecisa corresponder a um perfil existente. - Ao cadastrar/atualizar um resultado com
vencedor_id, o time vencedor Ă© propagado automaticamente para os jogos seguintes do chaveamento (ex: um jogo de oitavas que depende do "vencedor do jogo 49").
Toda exceção de negócio é lançada como AppError e capturada pelo Error Handler Global (src/shared/errorHandler.js), registrado via server.setErrorHandler(...). Ele também traduz erros nativos do driver pg (violação de UNIQUE, FOREIGN KEY e CHECK) para o mesmo formato de resposta, sem nenhum if/else de tratamento de erro nos Controllers:
{ "status": "error", "message": "Descrição do erro" }| Recurso | Rotas | Descrição |
|---|---|---|
| PaĂses | GET /paisesGET /paises/:id |
Consulta das seleções e seus grupos (JOIN com grupos). |
| Perfis | GET /perfisPOST /perfisPATCH /perfis/:idDELETE /perfis/:id |
CRUD completo dos participantes. |
| Bolões | GET /boloesGET /boloes/:idPOST /boloesPATCH /boloes/:idDELETE /boloes/:idGET /boloes/:id/participantesPOST /boloes/:id/participantes |
CRUD de bolões (JOIN com perfis para trazer o nome do criador) e gestão de participantes. |
| Jogos | GET /jogosGET /jogos/:idPOST /jogosPATCH /jogos/:idDELETE /jogos/:id |
CRUD de partidas. GET faz LEFT JOIN com paises (times da casa/fora) e resultados, trazendo nomes e placares — não apenas IDs. |
| Resultados | GET /resultadosGET /resultados/:idPOST /resultadosPATCH /resultados/:idDELETE /resultados/:id |
Registro de placares com propagação automática do chaveamento. |
| Palpites | GET /palpitesGET /palpites/:idPOST /palpitesPATCH /palpites/:idDELETE /palpites/:id |
CRUD de palpites, com JOIN trazendo nome do perfil, do bolĂŁo e dos paĂses do confronto. |
| Documentação | /docs |
Interface interativa do Swagger/OpenAPI. |
- Node.js 18 ou superior
- Um banco PostgreSQL acessĂvel (local ou serviço como Neon)
git clone https://github.com/MarlonUTF/bolaoCopaDW3.git
cd bolaoCopaDW3
npm installRecomendação: utilize o Neon DB para hospedar o PostgreSQL. Este foi o banco utilizado durante o desenvolvimento do projeto, sendo totalmente compatĂvel com a configuração apresentada abaixo.
Crie um arquivo .env na raiz do projeto (use .env.example como base):
DATABASE_URL="postgresql://usuario:senha@host:5432/nome_do_banco?sslmode=require"
PORT=3333Importante: o projeto utiliza dotenv para carregar as variáveis durante a execução da aplicação. Entretanto, o terminal não lê o arquivo
.envautomaticamente. Para utilizar opsql, carregue as variáveis na sessão atual do terminal:
set -a
source .env
set +aecho $DATABASE_URLSe o comando retornar a URL do banco do Neon DB, as variáveis foram carregadas corretamente.
Caso não retorne nada, confira se você está na pasta correta do projeto e se o arquivo .env existe:
pwd
ls -laVocê também pode confirmar se o arquivo possui a variável:
cat .envpsql "$DATABASE_URL" -f database.sqlSe tudo estiver correto, o script criará automaticamente todas as tabelas no banco hospedado no Neon DB.
npm run importarBusca seleções, calendário e placares na API pública da ESPN e preenche grupos, paises, jogos e resultados. Pode ser executado novamente a qualquer momento para atualizar os dados — o script usa ON CONFLICT para não duplicar registros.
Como a Copa ainda nĂŁo aconteceu, jogos com confronto ainda nĂŁo definido (
TBD vs TBD) ficam compais_casa_id/pais_fora_idnulos até serem definidos pela ESPN.
npm startA API estará em http://localhost:3333 e a documentação Swagger em http://localhost:3333/docs.
Para desenvolvimento com recarregamento automático:
npm run devCom o servidor rodando, acesse http://localhost:3333/docs.
A documentação Swagger cobre, para cada endpoint:
- Método HTTP (
GET,POST,PATCH,DELETE); - Parâmetros de rota (ex:
/jogos/:id); - Formato do corpo (
body) esperado emPOST/PATCH; - Ao menos uma resposta de sucesso (
200/201) e uma de erro (400/404) no formato padronizado do Error Handler Global.
scripts/importarCopa.js exporta a função importarDadosESPN(), responsável por:
- Criar os 12 grupos (A–L) e importar as 48 seleções via API da ESPN;
- Importar o calendário oficial de jogos (fase de grupos e mata-mata);
- Resolver automaticamente as origens do chaveamento (ex: converter
"Winner 49"no ID real do jogo 49); - Registrar os placares dos jogos já encerrados.
scripts/runImport.js apenas chama essa função e encerra o processo — é o que roda quando você executa npm run importar.
Os dados de seleções, grupos, calendário e resultados são obtidos da API pública da ESPN, com base na documentação mantida pela comunidade em pseudo-r/Public-ESPN-API (projeto independente, sem afiliação oficial com a ESPN).
Este projeto está licenciado sob a Licença MIT — veja o arquivo LICENSE para mais detalhes.
Feito com âš˝ e Node.js
API Bolão da Copa 2026 — Trabalho Prático de Arquitetura de Software