OmniStack11: backend da aplicação com node e express. [Dia 02]
![OmniStack11: backend da aplicação com node e express. [Dia 02]](http://abneroliveira.eti.br/wp-content/uploads/2020/03/OmniStack-11-1.png)
Enfim vamos iniciar a o desenvolvimento do backend da aplicação com node e express.
A proposta é desenvolver a aplicação Be the Hero que visa conectar pessoas com capital investidor a ONGs nessa semana Omnistack e vamos fazer com Node, ReactJS e React Native.
Abaixo os links para todos os dias da Semana Omnistack 11
- DIA 01 – Conceitos e ambiente de desenvolvimento
- DIA 02 – Backend – Criando a base da aplicação
- DIA 03 – Frontend – Construindo a interface web
- DIA 04 – Mobile – Desenvolvimento o app mobile
- DIA 05 – Funcionalidades Avançadas
Dia 02 – Criando a base da aplicação
Ok! Vamos começar o segundo dia da semana OmniStack 11. Nessa fase vamos conhecer algumas funcionalidades das ferramentas e frameworks que utilizaremos e com finalmente começar a desenvolver o backend da aplicação.
Node e express
Inicialmente são passados conceitos e definições sobre Node e Express.
Rotas e Recursos
Vamos usar como exemplo para definir o que são as Rotas e Recursos no Express, usaremos o método app.get( ...
criada no arquivo index.js
na pasta /omnistack11/backend
app.get('/', (req,res) => {
return res.json({
evento: 'Semana Omnistack 11',
aluno: 'Abner Oliveira'
});
});
Lembrando que para acessar o conteúdo da rota era preciso ir no navegador e acesar localhost:3333/
. Definimos ROTA todo esse caminho e RECURSO tudo que vem depois da /
.
Se alterarmos o parâmetro '/'
para '/users'
, estamos alterando o recurso da rota. Geralmente o recurso está relacionado a uma entidade do banco de dados.
A definição de rotas aceita a seguinte estrutura:
app.METHOD(PATH, HANDLER)
Onde:
app
é uma instância doexpress
.METHOD
método de solicitação HTTP.PATH
o caminho no servidor ou recursoHANDLER
é a função executada quando a rota é correspondida.
Métodos HTTP
Existem 9 métodos HTTP, porém você só vai utilizar 4 nesse projeto e na maioria dos outros também. São basicamente os métodos que você pode utilizar para criar um CRUD são eles:
GET
: buscar uma informação no backendPOST
: inserir uma informação na APIPUT
: atualizar uma informação na APIDELETE
: deletar uma informação no backend
Se você alterar a método get
para post
, lá no arquivo index.js o navegador vai retornar a seguinte resposta Cannot get /users
Por padrão onavegador faz requisições apenas GET, sendo assim vamos precisar de uma ferramenta para acessar as com rotas com outros métodos HTTP.
Utilizando Insomnia
O Insomnia é um cliente REST open source, através dele é possível fazer todos os tipo de testes de requisições, o que auxilia bastante no desenvolvimento da API.
Para começar a utilizar você precisa entender sobre os tipos de parâmetros: Query Params, Route Params e Request Body
Os Query Params são parâmetros nomeados enviados na rota após o "?"
, geralmente utilizados para filtros, paginações. Pode ser acessado pelo request.query
http://localhost:3333/users/?page=2&name=Abner
Os Route Params são parâmetros utilizados para identificar recursos. Pode ser acessado pelo request.params
.
http://localhost:3333/users/1234
O Request Body é usado quando você quer criar ou atualizar informações. São os dados que você deve enviar no corpo da requisição. Para acessar utilize request.body
Configurando o Nodemon
O nodemon é um aplicativo que detecta toda vez que um arquivo é alterado no seu projeto e automaticamente reinicia o servidor Node, o que agiliza bastante o desenvolvimento.
Com o nodemon é possível definir quais extensões e pastas serão monitoradas.
#adicionar o nodemon ao projeto como dependência de desevolvimento
yarn add nodemon -D
Agora é necessário definir o script no package.json
para facilitar a chamada do nodemon. Sendo assim deve ser inserido o código abaixo no seu package.json
"scripts": {
"start": "nodemon index.js"
},
Com essa alteração para iniciar o servidor basta utitlizar yarn start
uma única vez e o nodemon reiniciará o servidor toda vez que for detectada alguma alteração nos arquivos .js
do projeto.
Diferenças entre banco de dados
Existem basicamente 2 tipos de bancos de dados: SQL e NoSQL.
Costumamos dizer que bancos SQL seguem uma modelagem relacional, pois estes se baseiam no fato de que todos seus dados sejam guardados em tabelas. Temos com exemplos o: MySQL, SQLite, Oracle e etc.
NoSQL (Not Only SQL) é o termo utilizado para banco de dados não relacionais de alto desempenho, onde geralmente não é utilizado o SQL como linguagem de consulta.
O NoSQL foi criado para ter uma performance melhor e uma escalabilidade mais horizontal para suprir necessidades onde os bancos relacionais não são eficazes. Podem ser baseados em: Documentos; Colunas; Grafos e Chave-Valor.
Resumindo o conceito de modelo relacional (SQL) se baseia no fato de que todos os dados sejam guardados em tabelas. Ao modelo não-relacional (NoSQL) não se aplica o conceito de schema: uma chave de valor é que é utilizada para recuperar valores, conjunto de colunas ou documentos.
No projeto do backend da semana Omnistack 11 você deve utilizar o SQLite. Com isso não vai preciso instalar nada na máquina, pois o banco vai ficar salvo em arquivo dentro da própria aplicação.
Configurando o banco de dados do backend da aplicação com node e express.
Para a integração do projeto com o banco de dados você vai utilizar o KNEX.JS.
Primeiramente instale o knex.js
yarn add knex
E depois instale o driver que é o sqlite3
yarn add sqlite3
Agora para criar o arquivo de configurações do KNEX
npx knex init
Ok! O arquivo knexfile.js
foi criado e ele contem as configurações da conexão com o banco de dados
Agora vamos organizar a estrutura de pastas do projeto. Crie na raiz uma pasta /src
coloque o arquivo index.js
lá dentro e altere o script start
no package.json
para:
"scripts": {
"start": "nodemon src/index.js"
},
Agora vamos criar um arquivo routes.js
que conterá todas as rotas da nossa API. Recorte o código da rota no arquivo index.js
e cole dentro do arquivo de rotas e faça as importações e a exportação. O arquivo de rotas deve ficar assim:
const express = require('express');
const routes = express.Router();
routes.get('/', (req,res) => {
return res.json({
evento: 'Semana Omnistack 11',
aluno: 'Abner Oliveira'
});
});
module.exports = routes;
No final o arquivo index.js deve ficar dessa forma
const express = require('express');
const routes = require('./routes');
const app = express();
app.use(express.json());
app.use(routes);
app.listen(3333);
A linha app.use(express.json())
define o JSON para as requisições e respostas. Enquanto app.use(routes)
, que deve ficar obrigatoriamente depois após a do json, define o uso do arquivo de rotas.
Para testar vá no terminal e rode o script yarn start
Continuando a configuração banco de dados.
Crie a pasta /database
dentro de /src
. Agora crie a pasta /migrations
dentro de /database
depois vá no arquivo knexfiles.js
no pasta raiz do projeto e altere a configuração da conexão de desenvolvimento para:
development: {
client: 'sqlite3',
connection: {
filename: './src/database/db.sqlite'
},
migrations: {
directory: './src/database/migrations'
},
useNullAsDefault: true
},
Mas o que essas migrations
? A migrations
são nada mais do que o código que define a estrutura do banco. Elas são feitas em um padrão em que pode ser usado para qualquer banco de dados. Na documentação do KNEX tem toda as definições de como usar esse recurso.
Análise do backend da aplicação com node e express.
Durante a análise do projeto foram definidas as entidades e funcionalidades do sistema
Entidade
- ONGs
- Casos
Funcionalidades
- Login da ONG
- Logout da ONG
- Cadastro da ONG
- Cadastrar novos casos
- Listas casos de uma ONG
- Listar todos os casos
- Entrar em contato com a ONG
Criando as migrations
Você deve criar as duas entidades
npx knex migrate:make create_ongs
npx knex migrate:make create_incidents
A migration de ONGs deve ficar assim
exports.up = function(knex) {
return knex.schema.createTable('ongs', function (table) {
table.string('id').primary();
table.string('name').notNullable();
table.string('email').notNullable();
table.string('whatsapp').notNullable();
table.string('city').notNullable();
table.string('uf', 2).notNullable();
})
};
exports.down = function(knex) {
return knex.schema.dropTable('ongs');
};
A de Casos assim:
exports.up = function(knex) {
return knex.schema.createTable('incidents', function (table) {
table.increments();
table.string('title').notNullable();
table.string('description').notNullable();
table.string('value').notNullable();
table.string('ong_id').notNullable();
table.foreign('ong_id').references('id').inTable('ongs');
})
};
exports.down = function(knex) {
return knex.schema.dropTable('incidents');
};
Agora rode o comando npx knex migrate:latest
no terminal para criar as tabelas no banco a partir das migrations. Isso também de criar o arquivo db.sqlite
dentro da pasta /src/database
.
Construção do backend da aplicação com node e express.
Quando você for realizar as requisições o backend precisará da conexão com o banco de dados então crie o arquivo connections.js dentro da pasta /src/database
const knex = require('knex');
const configuration = require('../../knexfile');
const connection = knex(configuration.development);
module.exports = connection;
Poderíamos colocar os métodos que compões as rotas diretamente no arquivo de rotas, mas para melhorar a estrutura de pastas de projeto crie a pasta /src/controllers
. Lá crie os arquivos OngController.js
, IncidentController.js
, ProfileController.js
e SessionController.js
.
Agora basicamente iremos mexer nos arquivos de controllers e no arquivo de rotas. Nos controllers serão definidas todas as funcionalidades listadas acima.
Controller de ONGs
Então vamos lá iniciar pelo controller de ONGs. Lá nos temos que controlar duas funcionalidades: Listar todas as ONGs, criaremos o método assíncrono index
e Criar uma ONG, que será feito a partir do método post
.
O index
deve ficar assim
async index(req, res) {
const ongs = await connection('ongs').select('*');
return res.json(ongs);
}
O método index
só usa a conexão para selecionar todos dados da tabela, salva em uma variável ongs
e retorna em formato json
O post
necessita da biblioteca crypto pois ao criar uma ONG é criada uma chave de acesso para logar com essa ONG. Portanto o biblioteca crypto que é nativa do node deve ser importada nesse arquivo.
O post
deve ficar assim
async create(req, res) {
const {name, email, whatsapp, city, uf} = req.body;
const id = crypto.randomBytes(4).toString('HEX');
console.log(id);
await connection('ongs').insert({
id,
name,
email,
whatsapp,
city,
uf,
})
return res.json({id});
}
O método post
usa o desmebramento do javascript para pegar todos os campos do corpo da requisação (req.body
), cria um hash para o id
da tabela e usa a conexão para salvar tudo na tabela ongs
do db.sqlite
.
No final o arquivo fica com OngController.js
fica dessa forma
const crypto = require('crypto');
const connection = require('../database/connection');
module.exports = {
async index(req, res) {
const ongs = await connection('ongs').select('*');
return res.json(ongs);
},
async create(req, res) {
const {name, email, whatsapp, city, uf} = req.body;
const id = crypto.randomBytes(4).toString('HEX');
console.log(id);
await connection('ongs').insert({
id,
name,
email,
whatsapp,
city,
uf,
})
return res.json({id});
}
}
Controller de ONGs OK! Agora você deve atualizar o arquivo de rotas importando o OngController.js
, bem como definir as rotas no routes.js
a partir das funções index
e create
. O arquivo routes.js
deve ficar com o código abaixo:
const express = require('express');
const routes = express.Router();
const OngController = require('./controllers/OngController');
routes.get('/ongs', OngController.index);
routes.post('/ongs', OngController.create);
module.exports = routes;
Agora só falta testar no Insomnia. Crie uma pasta Ongs depois crie duas requisições uma get
e uma post
, ambas com a URL http://localhost:3333/ongs
.
Vamos lá! Crie a primeira ONG passando o JSON
abaixo como body
da requisição.
{
"name": "Athus",
"email": "athus@gmail.com",
"whatsapp": "85987654321",
"city": "Fortaleza",
"uf": "CE"
}
A resposta deve ser algo como isso:

Para listar todas as ONGs no Insomnia acesse a rota GET
utilizando o mesmo recurso.
Controller de Casos
No controller de Casos, o IncidentController.js
, vamos ter 3 métodos o index, create e delete.
O index
como já sabe é usado para listar todos os casos. Nele tem alguns recursos a mais, pois utiliza o Request Params e o Header Params, para paginação e contagem de registros. Verifique o método
async index(req, res) {
const { page = 1 } = req.query;
const [count] = await connection('incidents').count();
const incidents = await connection('incidents')
.join('ongs', 'ongs.id', '=', 'incidents.ong_id')
.limit(5).offset((page - 1)*5)
.select([
'incidents.*',
'ongs.name',
'ongs.email',
'ongs.whatsapp',
'ongs.city',
'ongs.uf']);
res.header('X-Total-Count', count['count(*)']);
return res.json(incidents);
},
Você pode verificar que ele faz um join da tabela incidents
com a tabela de ongs
para listar os casos.
Agora vamos fazer os insert de casos!!!
Só quem pode cadastrar casos é a ONG “logada”, sendo assim temos que de alguma forma dizer ao método create
qual o id
da ONG logada.
async create(req, res) {
const {title, description, value} = req.body;
const ong_id = req.headers.authorization;
const [id] = await connection('incidents').insert({
title,
description,
value,
ong_id,
});
return res.json({id});
},
Isso é feito na linha const ong_id = req.headers.authorization
que pega o id
salvo no cabeçalho da requisição e coloca seu valor com método connection('incidents').insert( ...
e registra o Caso em nome da ONG logada.
Logo mais veremos o controller que faz a autenticação. Calma!!
O delete
usa o mesmo recurso para confirmar que somente a ONG que cadastrou o caso pode deletá-lo
async delete(req, res) {
const { id } = req.params;
const ong_id = req.headers.authorization;
const incident = await connection('incidents').where('id', id).select('ong_id').first();
console.log(incident);
if(incident.ong_id !== ong_id) {
return res.status(401).json({error: 'Operation not permited'});
}
await connection('incidents').where('id', id).delete();
return res.status(204).send();
}
Importe o arquivo de IncidentsController.js no arquivos de rotas tal como foi feito para o controller de ONGs.
Vamos testar no Insomnia!!!
Primeiro crie a pasta Casos no Insomnia e dentro crie as 3 requisições:
- GET:
http://localhost:3333/incidents
- POST:
http://localhost:3333/incidents
- DELETE:
http://localhost:3333/incidents/1
—id
Mas primeiro insira no HEADER
da requisição o campo Authorization
e como valor o id
de uma ONG já cadastrar com ilustra a imagem. O Authorization
vai agir como a ONG logada. Faça isso para a rota post
e delete
.

Agora você já pode criar, deletar e listar casos. A imagem a seguir é a rota de criação já com o Header preenchido.

Abaixo a listagem de Casos

E por fim o delete para excluir a rota http://localhost:3333/incidents/1
para deletar o caso de id = 1
.
Controller de Perfil do backend da aplicação com node e express.
Esse controller de perfil terá somente o método index
, que listará todos os casos da ONG logada.
Já saber então precisaremos novamente do authorization
no Header. O ProfileController.js
deve ficar assim.
const connection = require('../database/connection');
module.exports = {
async index(req, res) {
const ong_id = req.headers.authorization;
const incidents = await connection('incidents').where('ong_id', ong_id).select('*');
return res.json(incidents);
}
}
Ok! Para testar no Insomnia crie a requisição GET e insira o Header necessário. Ah e não esqueça de atualizar o arquivo de rotas no mesmo padrão dos outros controllers.

Controller de Sessão
O controller de sessão vai autenticar apenas utilizando esse hash de cada ONG. Esse mesmo que estamos colocando no Header.
Crie o SessionController.js
, atualize o routes.js
e faça a autenticação pelo Insomnia
const connection = require('../database/connection');
module.exports = {
async create(req, res) {
const { id } = req.body;
const ong = await connection('ongs').where('id', id).select('name').first();
if(!ong) {
return res.status(400).json({error: 'No ONG exist'});
}
return res.json(ong);
}
}
Então você deve passar o hash da ONG que deseja logar e a requisição deve retornar o nome dela.

Adicionando o CORS
No arquivo index.js
você de adicionar o CORS, que é uma módulo de segurança que define quais domínios podem acessar o backend.
Instale o CORS
yarn add cors
Importe o CORS para o index.js
const cors = require('cors');
E depois da definição da constante app
insira o código abaixo
app.use(cors());
Ufa! Hoje criamos todos backend da aplicação com node e express. Por hoje é só amanhã começaremos a criar o frontend com ReactJS.
Até logo!!
0 Comentários