O projeto consolida a utilização das ferramentas Node.js, Sequelize e JWT (JSON Web Token) para a criação de uma API RESTful com CRUD completo e banco de dados, focada na produção de conteúdo para um blog.
Este projeto integra um sistema de autenticação de pessoas usuárias utilizando JWT (JSON Web Token), permitindo que apenas pessoas usuárias autenticadas possam criar, editar e pesquisar posts no blog.
- Node.js
- Sequelize
- Construção de uma API CRUD e banco de dados
- Sistema de autenticação utilizando JWT (JSON Web Token)
- Node.js
- Sequelize
- JWT (JSON Web Token)
⚠️ É necessário ter o Docker instalado para executar este projeto
Passo a passo
- Clone o repositório em uma pasta de preferência
git clone git@github.com:allysonbogo/project-blogs-api.git
- Entre na pasta raíz do projeto e instale todas as dependências
npm install
- Para rodar o projeto é necessário executar o comando abaixo no diretório raiz do projeto. Isso fará com que os containers docker sejam orquestrados e a aplicação esteja disponível
docker-compose up -d
- O comando abaixo irá criar o bando de dados, versionar o schema do banco utilizando as
migrations
e popular o banco com uso dosseeders
npm run populate
- Para iniciar o servidor com live-reload, digite o comando abaixo
npm run dev
- Para visualização da interface da API podem ser utilizados o Thunder Client, Postman, Insomnia ou alguma outra ferramenta de sua preferência
Rotas
Método | Funcionalidade | URL |
---|---|---|
POST |
Realiza o login de uma pessoa usuária cadastrada | http://localhost:3001/login |
A estrutura do body da requisição deverá seguir o padrão abaixo:
{
"email": "lewishamilton@gmail.com",
"password": "123456"
}
A resposta da requisição é a seguinte com status 200
:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXlsb2FkIjp7ImlkIjo1LCJkaXNwbGF5TmFtZSI6InVzdWFyaW8gZGUgdGVzdGUiLCJlbWFpbCI6InRlc3RlQGVtYWlsLmNvbSIsImltYWdlIjoibnVsbCJ9LCJpYXQiOjE2MjAyNDQxODcsImV4cCI6MTYyMDY3NjE4N30.Roc4byj6mYakYqd9LTCozU1hd9k_Vw5IWKGL4hcCVG8"
}
⚠️ O token acima é fictício, o token verdadeiro é gerado a partir da ferramenta JWT (JSON Web Token), utilizando uma palavra-passe e um payload secretos
A requisição irá falhar nos seguintes casos:
- A rota retorna um erro400
{ "message": "Some required fields are missing" }
, caso a requisição não tenha todos os campos devidamente preenchidos; - A rota retorna um erro
400
{ "message": "Invalid fields" }
, caso a requisição receba um par de email
e password
errados ou inexistentes; Autenticação
⚠️ Após o login de uma pessoa usuária cadastrada, é gerado umtoken
válido por 15 minutos, o qual será autenticado em todas as rotas a seguir, exceto na rota de cadastro de uma pessoa usuária
As requisições irão falhar nos seguintes casos:
- É disparado o erro401
{ "message": "Token not found" }
, ao fazer uma operação sem um token; - É disparado o erro
401
{ "message": "Expired or invalid token" }
, ao fazer uma operação com um token expirado ou inválido; Rotas
Método | Funcionalidade | URL |
---|---|---|
POST |
Realiza o cadastro de uma pessoa usuária | http://localhost:3001/user |
A estrutura do body da requisição deverá seguir o padrão abaixo:
{
"displayName": "Brett Wiltshire",
"email": "brett@email.com",
"password": "123456",
"image": "http://4.bp.blogspot.com/_YA50adQ-7vQ/S1gfR_6ufpI/AAAAAAAAAAk/1ErJGgRWZDg/S45/brett.png"
// a imagem não é obrigatória
}
Para o cadastro de uma pessoa usuária não é necessário estar autenticado no sistema. Após o cadastro, todas as outras requisições exigem um token de autenticação. A resposta da requisição é a seguinte com status 201
:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXlsb2FkIjp7ImlkIjo1LCJkaXNwbGF5TmFtZSI6InVzdWFyaW8gZGUgdGVzdGUiLCJlbWFpbCI6InRlc3RlQGVtYWlsLmNvbSIsImltYWdlIjoibnVsbCJ9LCJpYXQiOjE2MjAyNDQxODcsImV4cCI6MTYyMDY3NjE4N30.Roc4byj6mYakYqd9LTCozU1hd9k_Vw5IWKGL4hcCVG8"
}
⚠️ O token acima é fictício, o token verdadeiro é gerado a partir da ferramenta JWT (JSON Web Token), utilizando uma palavra-passe e um payload secretos
A requisição irá falhar nos seguintes casos:
- A rota retorna um erro400
{ "message": "\"displayName\" length must be at least 8 characters long" }
, caso a requisição não receba o campo displayName
devidamente preenchido com pelo menos 8 caracteres; - A rota retorna um erro
400
{ "message": "\"email\" must be a valid email" }
, caso a requisição não receba o campo email
com formato válido; - A rota retorna um erro
400
{ "message": "\"password\" length must be at least 6 characters long" }
, caso a requisição não receba o campo password
devidamente preenchido com pelo menos 6 caracteres; - A rota retorna um erro
409
{ "message": "User already registered" }
, caso o campo email
já esteja cadastrado no banco de dados; Método | Funcionalidade | URL |
---|---|---|
GET |
Retorna uma lista de pessoas usuárias | http://localhost:3001/user |
A resposta da requisição é a seguinte com status 200
:
[
{
"id": 1,
"displayName": "Lewis Hamilton",
"email": "lewishamilton@gmail.com",
"image": "https://upload.wikimedia.org/wikipedia/commons/1/18/Lewis_Hamilton_2016_Malaysia_2.jpg"
},
...
]
Método | Funcionalidade | URL |
---|---|---|
GET |
Retorna uma pessoa usuária a partir do id | http://localhost:3001/user/:id |
A resposta da requisição é a seguinte com status 200
:
{
"id": 1,
"displayName": "Lewis Hamilton",
"email": "lewishamilton@gmail.com",
"image": "https://upload.wikimedia.org/wikipedia/commons/1/18/Lewis_Hamilton_2016_Malaysia_2.jpg"
}
A requisição irá falhar nos seguintes casos:
- É disparado o erro404
{ message: "User does not exist" }
, caso a pessoa usuária não esteja cadastrada no banco de dados; Método | Funcionalidade | URL |
---|---|---|
DELETE |
Deleta uma pessoa usuária a partir do token de autenticação | http://localhost:3001/user/me |
- A resposta da requisição é
204
e sem body em caso de sucesso
Rotas
Método | Funcionalidade | URL |
---|---|---|
POST |
Realiza o cadastro de uma categoria | http://localhost:3001/categories |
A estrutura do body da requisição deverá seguir o padrão abaixo:
{
"name": "Typescript"
}
A resposta da requisição é a seguinte com status 201
:
{
"id": 3,
"name": "Typescript"
}
A requisição irá falhar nos seguintes casos:
- A rota retorna um erro400
{ "message": "\"name\" is required" }
, caso a requisição não receba o campo name
devidamente preenchido; Método | Funcionalidade | URL |
---|---|---|
GET |
Retorna uma lista de categorias | http://localhost:3001/categories |
A resposta da requisição é a seguinte com status 200
:
[
{
"id": 1,
"name": "Inovação"
},
{
"id": 2,
"name": "Escola"
},
...
]
Rotas
Método | Funcionalidade | URL |
---|---|---|
POST |
Realiza o cadastro de um post | http://localhost:3001/post |
A estrutura do body da requisição deverá seguir o padrão abaixo:
{
"title": "Latest updates, August 1st",
"content": "The whole text for the blog post goes here in this key",
"categoryIds": [1, 2]
}
A resposta da requisição é a seguinte com status 201
:
{
"id": 3,
"title": "Latest updates, August 1st",
"content": "The whole text for the blog post goes here in this key",
"userId": 1,
"updated": "2023-06-16T10:00:01.196Z",
"published": "2023-06-16T10:00:01.196Z"
}
A requisição irá falhar nos seguintes casos:
- A rota retorna um erro400
{ "message": "Some required fields are missing" }
, caso todos os campos não estejam devidamente preenchidos; - A rota retorna um erro
400
{ "message": "one or more \"categoryIds\" not found" }
, caso o campo categoryIds
não esteja devidamente preenchido com um array contendo apenas categorias existentes no banco de dados; Método | Funcionalidade | URL |
---|---|---|
GET |
Retorna uma lista de posts | http://localhost:3001/post |
A resposta da requisição é a seguinte com status 200
:
[
{
"id": 1,
"title": "Post do Ano",
"content": "Melhor post do ano",
"userId": 1,
"published": "2011-08-01T19:58:00.000Z",
"updated": "2011-08-01T19:58:51.000Z",
"user": {
"id": 1,
"displayName": "Lewis Hamilton",
"email": "lewishamilton@gmail.com",
"image": "https://upload.wikimedia.org/wikipedia/commons/1/18/Lewis_Hamilton_2016_Malaysia_2.jpg"
},
"categories": [
{
"id": 1,
"name": "Inovação"
}
]
},
...
]
Método | Funcionalidade | URL |
---|---|---|
GET |
Retorna um post a partir do id | http://localhost:3001/post/:id |
A resposta da requisição é a seguinte com status 200
:
{
"id": 1,
"title": "Post do Ano",
"content": "Melhor post do ano",
"userId": 1,
"published": "2011-08-01T19:58:00.000Z",
"updated": "2011-08-01T19:58:51.000Z",
"user": {
"id": 1,
"displayName": "Lewis Hamilton",
"email": "lewishamilton@gmail.com",
"image": "https://upload.wikimedia.org/wikipedia/commons/1/18/Lewis_Hamilton_2016_Malaysia_2.jpg"
},
"categories": [
{
"id": 1,
"name": "Inovação"
}
]
}
A requisição irá falhar nos seguintes casos:
- É disparado o erro404
{ message: "Post does not exist" }
, caso o post não esteja cadastrado no banco de dados; Método | Funcionalidade | URL |
---|---|---|
PUT |
Atualiza um post a partir do id | http://localhost:3001/post/:id |
A estrutura do body da requisição deverá seguir o padrão abaixo:
{
"title": "Latest updates, August 1st",
"content": "The whole text for the blog post goes here in this key"
}
A resposta da requisição é a seguinte com status 200
:
{
"id": 3,
"title": "Latest updates, August 1st",
"content": "The whole text for the blog post goes here in this key",
"userId": 1,
"published": "2022-05-18T18:00:01.000Z",
"updated": "2022-05-18T18:07:32.000Z",
"user": {
"id": 1,
"displayName": "Lewis Hamilton",
"email": "lewishamilton@gmail.com",
"image": "https://upload.wikimedia.org/wikipedia/commons/1/18/Lewis_Hamilton_2016_Malaysia_2.jpg"
},
"categories": [
{
"id": 1,
"name": "Inovação"
},
{
"id": 2,
"name": "Escola"
}
]
}
A requisição irá falhar nos seguintes casos:
- A rota retorna um erro401
{ "message": "Unauthorized user" }
, caso o post editado não tenha sido criado pela pessoa usuária autenticada; - A rota retorna um erro
400
{ "message": "Some required fields are missing" }
, caso todos os campos não estejam devidamente preenchidos; - A rota retorna um erro
404
{ message: "Post does not exist" }
, caso o post não esteja cadastrado no banco de dados; Método | Funcionalidade | URL |
---|---|---|
DELETE |
Deleta um post a partir do id | http://localhost:3001/post/:id |
- A resposta da requisição é
204
e sem body em caso de sucesso
A requisição irá falhar nos seguintes casos:
- A rota retorna um erro401
{ "message": "Unauthorized user" }
, caso o post deletado não tenha sido criado pela pessoa usuária autenticada; - É disparado o erro
404
{ "message": "Post does not exist" }
, caso o post não esteja cadastrado no banco de dados; Método | Funcionalidade | URL |
---|---|---|
GET |
Retorna uma lista de posts de acordo com o termo de pesquisa | http://localhost:3001/post/search?q=searchTerm |
A estrutura da URL deverá seguir o padrão abaixo:
/talker/search?q=vamos
O termo de pesquisa pode ser referente ao title
ou content
do post. A resposta da requisição é a seguinte com status 200
:
[
{
"id": 2,
"title": "Vamos que vamos",
"content": "Foguete não tem ré",
"userId": 1,
"published": "2011-08-01T19:58:00.000Z",
"updated": "2011-08-01T19:58:51.000Z",
"user": {
"id": 1,
"displayName": "Lewis Hamilton",
"email": "lewishamilton@gmail.com",
"image": "https://upload.wikimedia.org/wikipedia/commons/1/18/Lewis_Hamilton_2016_Malaysia_2.jpg"
},
"categories": [
{
"id": 2,
"name": "Escola"
}
]
}
]
Caso o termo de pesquisa não seja informado ou esteja vazio, o endpoint deverá retornar o status 200
e um array com todos os posts cadastrados:
[
{
"id": 1,
"title": "Post do Ano",
"content": "Melhor post do ano",
"userId": 1,
"published": "2011-08-01T19:58:00.000Z",
"updated": "2011-08-01T19:58:51.000Z",
"user": {
"id": 1,
"displayName": "Lewis Hamilton",
"email": "lewishamilton@gmail.com",
"image": "https://upload.wikimedia.org/wikipedia/commons/1/18/Lewis_Hamilton_2016_Malaysia_2.jpg"
},
"categories": [
{
"id": 1,
"name": "Inovação"
}
]
},
...
]
Caso nenhum post satisfaça a busca, o endpoint deve retornar o status 200
e um array vazio. Exemplo:
[]