-
Notifications
You must be signed in to change notification settings - Fork 42
Backend
Algumas variáveis de ambiente são fundamentais para que a aplicação e/ou os testes rodem, você pode verificar o arquivo .env para saber quais as variáveis você irá necessitar.
Este arquivo é só um template e não será carregado automaticamente pela aplicação. Você também pode modificar estas variáveis para que a aplicação funcione corretamente, exemplo para usuários que utilizam o bash como terminal: NODE_ENV=production npm run start.
Você pode iniciar seus estudos com a funcionalidade hello-world, esta funcionalidade somente é carregada quando a aplicação é iniciada em modo de desenvolvimento e contém todas as características que você irá necessitar para desenvolver outras funcionalidades da aplicação.
Para iniciar e rodar a aplicação localmente você precisará seguir alguns passos para garantir que tudo irá rodar como planejado:
- Instale todos os pacotes necessários com o comando
npm install - Verifique se todas as variáveis de ambiente estão corretas, principalmente a
DATABASE_URL - Verifique se o banco de dados está rodando e o usuário e senha estão de acordo com a variável de ambiente
DATABASE_URL - Verifique se você rodou a migração com o comando
npm run knex -- migrate:latest - Inicie a aplicação com o comando
npm start
O projeto é estruturado de forma simples e separado por funcionalidades:
-
libesta pasta contém arquivos que podem ser compartilhados entre as demais partes do projeto. Ex: a bibliotecadbé utilizada tanto dentro da pastaresourcesquanto da pastatest. -
resourcescada funcionalidade da aplicação deve ficar dentro da sua respectiva pasta. Assim uma funcionalidade pode ter os seguintes arquivos:-
model.jscontém definição de modelo para esta funcionalidade se a mesma necessitar de um modelo -
routes.jscontém as definições de rotas caso a funcionalidade necessite declarar algum endpoint de acesso -
schemas.jscontém um grupo de schemas de validação que podem ser utilizados pelas rotas ou modelos -
handlers.jscontém todos os controladores que o arquivoroutes.jsirá utilizar quando definir as rotas
-
Toda funcionalidade que apresentar um arquivo routes.js terá sua rota incluída na aplicação automaticamente, sendo assim você não precisa se preocupar em inserir nenhuma rota na aplicação mas somente criar o arquivo e declarar suas rotas.
Imagine que você necessitasse criar uma rota GET /hello-world. para isso seria necessário apenas criar o arquivo resources/hello-world/routes.js com o seguinte conteúdo:
const router = require('express').Router()
const handlers = require('./handlers')
// Declare a rota que você necessita
router.get('/hello-world', handlers.findAll)
// Não esqueça de exportar o seu router para que ele seja incluído automaticamente na aplicação
module.exports = routerOs handlers são os arquivos mais fáceis de entender pois representam um grupo de controladores para uma determinada funcionalidade. Se quisermos implementar o handler findAll para o exemplo acima basta criar o arquivo resources/hello-world/handlers.js com o seguinte conteúdo:
exports.findAll = (req, res) => {
// Deste ponto em diante é simplemente Express
res.status(422).send({ error: true })
}Toda rota que irá receber dados externos necessita de validação. Utilizando o mesmo exemplo acima podemos imaginar a necessidade de uma rota que receba nome e sobrenome, para isso basta declarar um schema de validação utilizando o módulo Joi dentro do arquivo resources/hello-world/schemas.js. Nosso arquivo ficaria assim:
const Joi = require('joi')
exports.create = Joi.object({
nome: Joi.string().required(),
sobrenome: Joi.string.required()
})Com a definição do schema pronta, basta criar uma rota para utilizar este schema. Para utilizar o Joi como middleware para o Express utilizamos uma pequena ferramenta dentro de nossas lib: lib/validator.js. Com esta lib fica muito fácil de utilizar nosso schema com nossa rota:
const bodyParser = require('body-parser')
const router = require('express').Router()
const handlers = require('./handlers')
const schemas = require('./schemas')
const validator = require('../../lib/validator')
router.post('/hello-world',
bodyParser.json(), // Não esqueça de utilizar o bodyParser antes da validação
validator({ body: schemas.create }), // Você pode verificar qualquer aspecto da requisição: body, headers, query, params...
handlers.findAll)
module.exports = routerCom isso pronto você não precisará se preocupar em validar os dados no seu handler pois caso o mesmo seja executado significa que o campo req.body contém dados validados.
Outro aspecto positivo de utilizar esta lib para validação é que a mesma cuida da resposta automaticamente em caso de dados inválidos, deixando seu handler mais enxuto.
Aconselhamos que você leia a documentação do Joi para aprender mais como utilizar esta biblioteca.
As sessões dependem da biblioteca lib/session.js, basta incorporar ela em sua rota para proteger um handler. Abaixo está um exemplo de uma rota que requer autenticação.
...
const session = require('../../lib/session')
router.get('/protegido', session(), handlers.protegido)Agora o handler protegido terá acesso ao ID do usuário que está logado a partir do object req.session.user_id. Você não precisa se preocupar em verificar se o usuário tem uma sessão ou não pois a lib session automaticamente interrompe a sessão caso alguém esteja tentando acessar um endpoint protegido sem estar logado.
Toda e qualquer alteração no banco de dados necessita de uma migração. Imagine um cenário onde seria necessário adicionar a coluna email na tabela usuarios. O primeiro passo para criar esta migração será rodar o comando npm run knex -- migrate:make adicionar_coluna_email. Como você pode perceber o último parâmetro é o nome da sua migração. Este comando irá gerar um arquivo e mostrar no seu terminal o caminho para o arquivo gerado.
> nodebr@1.0.0 knex /Users/alanhoff/Projects/nodebr
> knex --knexfile ./lib/db/knexfile.js "migrate:make" "adicionar_coluna_email"
Working directory changed to ~/Projects/nodebr/lib/db
Using environment: development
Created Migration: ./lib/db/migrations/20161115012233_adicionar_coluna_email.js
Com este arquivo criado basta modifica-lo para refletir as mudanças que desejamos no nosso banco de dados. Neste exemplo o arquivo ficaria assim:
// A função up serve como instrução mara migrar o banco de dados
exports.up = knex => knex.schemas.table('usuarios', table => {
table.string('email').notNull()
})
// Já a função down serve para fazer o rollback caso seja necessário
exports.down = knex => knex.schemas.table('usuarios', table => {
table.dropColumn('email')
})Com este arquivo pronto basta rodar o comando npm run knex -- migrate:latest para que o seu banco de dados seja atualizado com as modificações que você adicionou.
Atenção: Uma vez que um arquivo de migração rodar, o knex não rodará o arquivo novamente mesmo que você o altere. Para resolver isto você pode realizar um rollback com o comando npm run knex -- migrate:rollback e logo após realizar a migração novamente ou apagar o seu banco de dados completamente, recriá-lo e rodar a migração novamente.
Recomendamos que você leia a documentação do knex para saber como utilizá-lo.
Toda a interação com o banco de dados na lógica da aplicação é realizada através de modelos. Para criar um modelo basta criar um arquivo model.js na pasta da funcionalidade específica. Podemos imaginar que queremos criar o modelo para a tabela usuarios, para isto seria necessário criar o arquivo resources/usuarios/model.js com o seguinte conteúdo:
module.exports = bookshelf => bookshelf.model('Usuario', {
tableName: 'usuarios'
})
Após isso nossa biblioteca lib/db se encarregará de carregar o seu modelo automaticamente quando necessário. Para utilizar o seu modelo recém criado basta realizar o require da lib/db em qualquer parte do projeto:
const db = require('../../lib/db')
const Usuario = db.model('Usuario') // Assim carregamos o modelo que desejamos utilizar
Usuario.forge({
nome: 'Alan',
sobrenome: 'Hoffmeister',
email: 'alanhoffmeister@gmail.com'
})
.save()
.then(() => console.log('Usuário criado!'))Alguns módulos estão ativados por padrão, incluíndo o módulo bookshelf-uuid. Este módulo se encarrega de criar uma GUID para cada nova informação salva no banco de dados, por isso é necessário cuidar para que quando criar uma migração que cria uma tabela você tenha certeza que também estará incluíndo os seguintes campos:
exports.up knex => knex.schema.createTableIfNotExists('usuarios', table => {
// Os campos abaixo são obrigatórios
table.uuid('id').primary()
table.timestamps()
// Qualquer outro campo é opcional de acordo com a funcionalidade que você está criando
table.string('nome').notNullable()
table.string('sobrenome').notNullable()
table.string('email').notNullable()
})Aconselhamos que você leia a documentação do bookshelf para familiarizar-se com a ferramenta.