Projeto de exemplo para a disciplina de Desenvolvimento Backend do IMD/UFRN.
A aplicação é uma API REST simples construída com Node.js, Express e Sequelize (ORM), usando SQLite como banco de dados. O objetivo é ilustrar conceitos como roteamento, middlewares, validação de dados, ORM e organização de projetos backend.
- Node.js v14+
- npm
npm installO projeto usa Sequelize como ORM e suporta dois ambientes com bancos diferentes:
| Ambiente | Banco | Configuração |
|---|---|---|
development |
SQLite (arquivo local) | src/db/dbend.sqlite — criado automaticamente |
production |
MySQL | Edite src/db/config/config.json com usuário, senha e host |
1. Rodar as migrações (cria as tabelas):
npm run migrate-dev2. Rodar as seeds (popula o banco com dados iniciais):
npx sequelize-cli db:seed:allPara desfazer:
# Desfaz a última migração
npx sequelize-cli db:migrate:undo
# Desfaz todas as seeds
npx sequelize-cli db:seed:undo:allO arquivo
dbend.sqliteé gerado automaticamente na pastasrc/db/após rodar as migrações.
Antes de rodar, certifique-se de que o banco MySQL está criado e atualize as credenciais em src/db/config/config.json:
"production": {
"username": "root",
"password": "changeme",
"database": "dbend",
"host": "127.0.0.1",
"dialect": "mysql"
}Em seguida, rode as migrações no ambiente de produção:
npm run migrate-prod# Modo desenvolvimento — SQLite + hot-reload (nodemon)
npm start
# Modo produção — MySQL
npm run productionO servidor sobe na porta 8080. Ao iniciar, você verá a mensagem:
Servidor pronto na porta 8080
Com o servidor rodando, a documentação interativa da API está disponível em:
http://localhost:8080/api-docs
A interface Swagger permite visualizar todas as rotas, ver os schemas de entrada e saída e executar chamadas diretamente pelo navegador.
Os exemplos de requisições também estão separados por recurso na raiz do projeto:
.usuario.http— rotas de/usuarios.post.http— rotas de/posts
Para usá-los você precisa da extensão REST Client no VS Code:
- Instale a extensão REST Client no VS Code.
- Abra um dos arquivos
.http. - Clique em "Send Request" acima de cada bloco para executar a chamada.
Você também pode importar os arquivos em ferramentas como Insomnia ou Postman.
dev-backend/
├── src/
│ ├── index.js # Ponto de entrada — configura e sobe o Express
│ ├── rotas/ # Definição das rotas por recurso
│ ├── middleware/ # Middlewares de validação de entrada
│ ├── schema/ # Schemas JSON para validação com AJV
│ ├── utils/ # Utilitários (ex: configuração do logger Winston)
│ └── db/
│ ├── config/ # Configuração do banco por ambiente (SQLite/MySQL)
│ ├── models/ # Models Sequelize
│ ├── migrations/ # Migrações do banco de dados
│ └── seeders/ # Seeds com dados iniciais
├── views/
│ ├── layouts/ # Layout base das páginas (express-ejs-layouts)
│ └── pages/ # Templates EJS de cada página
├── public/
│ └── uploads/ # Arquivos enviados via upload
├── .usuario.http # Exemplos de requisições de usuários
└── .post.http # Exemplos de requisições de posts
O projeto expõe duas interfaces distintas:
- Interface web — rotas na raiz (
/), renderizam páginas HTML com EJS - API REST — rotas sob o prefixo
/api, retornam JSON
| Método | Rota | Descrição |
|---|---|---|
| GET | / |
Lista os 10 posts mais recentes |
| GET | /post/:id |
Exibe o conteúdo completo de um post |
| Método | Rota | Descrição |
|---|---|---|
| GET | /api |
Verifica se a API está no ar |
Resposta:
{ "msg": "Hello from Express!" }Os dados de usuário são validados pelo middleware antes de serem persistidos. O campo email deve ser um e-mail válido e senha é obrigatória. As senhas são armazenadas com hash bcrypt.
| Método | Rota | Descrição |
|---|---|---|
| POST | /api/usuarios/login |
Autentica e retorna um token JWT |
| GET | /api/usuarios |
Lista todos os usuários cadastrados |
| GET | /api/usuarios/:id |
Busca um usuário pelo ID (path param) |
| POST | /api/usuarios |
Cria um novo usuário |
| PUT | /api/usuarios/?id=[ID] |
Atualiza um usuário existente pelo ID |
| DELETE | /api/usuarios/?id=[ID] |
Remove um usuário pelo ID |
Corpo esperado no POST e PUT:
{
"email": "someuser@gmail.com",
"senha": "changeit"
}Login — corpo esperado e resposta:
// POST /api/usuarios/login
{ "email": "someuser@gmail.com", "senha": "changeit" }
// Resposta (200)
{ "accessToken": "<jwt>" }
// Credenciais inválidas (403)
{ "msg": "usuário ou senha inválidos" }O token expira em 60 segundos. Inclua-o nas rotas protegidas via header
Authorization: Bearer <token>.
Exemplos de resposta:
- Criação bem-sucedida (
POST):{ "msg": "Usuário adicionado com sucesso!", "userId": 1 } - Atualização bem-sucedida (
PUT):{ "msg": "Usuário atualizado com sucesso!" } - Remoção bem-sucedida (
DELETE):{ "msg": "Usuário deletado com sucesso!" } - Dados inválidos (400):
{ "msg": "Dados inválidos", "erros": [...] } - ID não encontrado (400):
{ "msg": "Usuário não encontrado!" }
Cada post pertence a um usuário (userId). O campo userId deve corresponder ao id de um usuário existente.
Rotas marcadas com 🔒 exigem autenticação via header Authorization: Bearer <token>.
| Método | Rota | Descrição |
|---|---|---|
| GET | /api/posts |
Lista todos os posts cadastrados |
| GET | /api/posts/:id |
Busca um post pelo ID (path param) |
| POST | /api/posts |
🔒 Cria um novo post |
| PUT | /api/posts/?id=[ID] |
🔒 Atualiza um post existente pelo ID |
| DELETE | /api/posts/?id=[ID] |
Remove um post pelo ID |
Corpo esperado no POST e PUT:
{
"titulo": "Meu primeiro post",
"texto": "Conteúdo do post aqui.",
"userId": 1
}Exemplos de resposta:
- Criação bem-sucedida (
POST):{ "msg": "Post adicionado com sucesso!" } - Atualização bem-sucedida (
PUT):{ "msg": "Post atualizado com sucesso!" } - Remoção bem-sucedida (
DELETE):{ "msg": "Post deletado com sucesso!" } - ID não encontrado (400):
{ "msg": "Post não encontrado!" }
| Método | Rota | Descrição |
|---|---|---|
| POST | /api/posts/:id/upload |
Faz upload de uma foto e associa ao post |
O upload é feito via multipart/form-data com o campo foto. Apenas arquivos .jpg e .jpeg são aceitos. O arquivo é salvo em public/uploads/ e o caminho é armazenado no campo foto do post.
Exemplo de requisição (REST Client / curl):
POST http://localhost:8080/posts/1/upload
Content-Type: multipart/form-data; boundary=boundary
--boundary
Content-Disposition: form-data; name="foto"; filename="imagem.jpg"
Content-Type: image/jpeg
< ./imagem.jpg
--boundary--
Exemplos de resposta:
- Upload bem-sucedido:
{ "msg": "Upload realizado com sucesso!" } - Post não encontrado (400):
{ "msg": "Post não encontrado!" } - Formato inválido:
Arquivo não suportado. Apenas jpg e jpeg são suportados.
Os arquivos enviados ficam acessíveis via
/static/uploads/<nome-do-arquivo>.
A validação é feita via middleware usando a biblioteca AJV, com schemas definidos na pasta schema/.
Usuários (schema/usuario.schema.js):
email: obrigatório, formato de e-mail válidosenha: obrigatório, string- Campos extras não são permitidos (
additionalProperties: false)
Posts (schema/post.schema.js):
titulo: obrigatório, string entre 5 e 100 caracterestexto: obrigatório, string- Campos extras não são permitidos (
additionalProperties: false)
| Pacote | Uso |
|---|---|
| express | Framework web |
| sequelize | ORM para acesso ao banco de dados |
| sqlite3 | Driver SQLite usado pelo Sequelize |
| uuid | Geração de IDs únicos (UUIDv4) |
| ajv + ajv-formats | Validação de schemas JSON |
| multer | Upload de arquivos via multipart/form-data |
| ejs + express-ejs-layouts | Renderização de templates HTML no servidor |
| moment | Formatação de datas em português |
| swagger-ui-express + yamljs | Documentação interativa da API em /api-docs |
| bcrypt | Hash de senhas |
| jsonwebtoken | Geração e verificação de tokens JWT |
| winston | Logging estruturado da aplicação |
| nodemon (dev) | Hot-reload no desenvolvimento |