Como construir um servidor Node.js que dimensione

Zafar Saleem Blocked Unblock Seguir Seguindo 3 de dezembro de 2018

Escrever lógica de back-end para qualquer projeto nos dias de hoje é bem fácil, graças ao JavaScript de pilha inteira. Isso é especialmente verdade com a introdução de dezenas de estruturas para a implementação do lado do cliente e do lado do servidor.

Um dos frameworks mais populares do Node.js é o Express.js . Ele oferece uma abordagem fácil para construir aplicativos em diferentes escalas. No entanto, à medida que um projeto cresce, torna-se difícil escalar em algum momento.

Muitos desenvolvedores tendem a continuar adicionando novos arquivos e modelos de rota para novos serviços e pontos de extremidade da API. Essa abordagem funciona, mas dificulta que os futuros engenheiros dimensionem e adicionem novos serviços.

Neste blog eu vou construir um sistema de login e registro que usa autenticação JWT com arquitetura escalável. Para aqueles que preferem entrar no código, vá em frente e clone este repositório.

Haverá quatro partes neste blog.

  1. Configuração Básica da Arquitetura
  2. Cadastro
  3. Entrar
  4. painel de controle

Este blog assume que você já instalou o Node.js em seu sistema. Vamos entrar no primeiro passo – configuração básica da arquitetura.

Configuração Básica da Arquitetura

Primeiramente, crie um novo diretório em seu sistema de arquivos e chame-o de auth (ou qualquer coisa que você goste).

 mkdir auth 

Agora, cd para esse diretório e crie um arquivo package.json. Adicione as linhas abaixo nela.

 { 
"nome": "auth",
"versão": "0.0.0",
"privado": verdadeiro
"main": "index.js",
"scripts": {
"start": "node index.js"
}
"dependências": {
"bcrypt": "mais recente",
"body-parser": "^ 1.18.2",
"cookie-parser": "~ 1.4.3",
"express": "~ 4.15.5",
"jsonwebtoken": "^ 8.1.1",
"mongoose": "^ 5.0.3",
"lodash": "^ 4.17.11",
"morgan": "^ 1.9.0",
"passaporte": "^ 0.4.0",
"passport-jwt": "^ 3.0.1",
"serve-favicon": "~ 2.4.5"
}
}

A parte mais importante do arquivo acima é a propriedade dependencies . Estas são as dependências necessárias para o projeto. Eles serão usados como middleware posteriormente neste blog.

Agora vá em frente e execute o comando abaixo para instalar todas essas dependências. Você pode precisar aguardar alguns segundos.

 npm install 

Depois de instalar todas as dependências acima, vá em frente e crie um arquivo index.js em sua pasta raiz, como abaixo:

 touch index.js 

Este arquivo específico é responsável apenas por iniciar o servidor. Para fazer isso, adicione o código abaixo:

 'use strict'; 
 servidor const = require ('./ server') (); 
const config = require ('./ configs');
const db = require ('./ configs / db');
 server.create (config, db); 
server.start ();

Como você pode ver, este arquivo requer três arquivos:

  1. servidor
  2. config
  3. db

Nós vamos criar estes próximos.

O código acima chama o método create no módulo do servidor. Finalmente, chama o método start , que inicia o servidor.

1. Crie a pasta do server

 servidor mkdir 

Uma vez feito, cd para essa pasta e crie outro arquivo index.js .

 touch index.js 

Agora adicione o código abaixo neste arquivo:

 'use strict'; 
 const express = require ('express'); 
const bodyParser = require ('body-parser');
logger const = require ('morgan');
const mongoose = require ('mangusto');
const passport = require ('passaporte');
const cookieParser = require ('cookie-parser');
 module.exports = function () { 
let server = express (),
crio,
começar;
 create = function (config, db) { 
deixe rotas = require ('./ routes');
 // Configurações do servidor 
server.set ('env', config.env);
server.set ('port', config.port);
server.set ('hostname', config.hostname);
 // Retorna o middleware que analisa json 
server.use (bodyParser.json ());
server.use (bodyParser.urlencoded ({extended: false}));
server.use (cookieParser ());
server.use (logger ('dev'));
server.use (passport.initialize ());
mongoose.connect (db.database);
require ('../ configs / passport') (passaporte);
 // Configurar rotas 
routes.init (servidor);
};
 start = function () { 
deixe hostname = server.get ('hostname'),
porta = server.get ('porta');
 server.listen (port, function () { 
console.log ('Servidor expresso atendendo - http: //' + hostname + ':' + porta);
});
};
 Retorna { 
criar: criar,
começar: começar
};
};

Neste arquivo, primeiro precisamos de todas as dependências necessárias para este projeto. Note que mais dependências podem ser adicionadas a este arquivo sempre que necessário.

Em seguida, exportamos uma função anônima deste módulo usando module.exports . Dentro dessa função, crie três variáveis: server , create e start .

A variável do server é para o servidor Express.js. Então, chame a função express() e atribua isso ao server . Vamos atribuir funções anônimas às variáveis create e start .

Agora, é hora de escrever uma função create com dois parâmetros: config e db .

Em seguida, defina algumas configurações do servidor usando a função server.use () ou seja, env, port e hostname. Em seguida, use os cookieParser, bodyParser, logger and passport . Em seguida, conecte-se ao banco de dados do mongoose e, finalmente, solicite o arquivo de configuração do passaporte e chame-o com o passaporte necessário.

O middleware Passport é usado para autenticação, que usaremos posteriormente neste blog. Para saber mais sobre isso, clique aqui .

Agora é hora de pontos finais da API, ou seja, rotas. Simplesmente chame a função init nas rotas e passe o server para isso.

Em seguida, escreva a função de start . Configure o hostname e a port e inicie o servidor com o comando listen dentro desta função.

Em seguida, retorne as funções create e start para torná-las disponíveis para uso por outros módulos.

2. Crie a pasta de configuração

No nível raiz, crie uma pasta de configs :

 mkdir configs 

cd nessa pasta e crie um arquivo index.js:

 touch index.js 

Adicione o código abaixo ao arquivo index.js:

 'use strict'; 
 const _ = require ('lodash'); 
const env = process.env.NODE_ENV || 'local';
const envConfig = require ('./' + env);
 deixar defaultConfig = { 
env: env
};
 module.exports = _.merge (defaultConfig, envConfig); 

Agora crie um arquivo local.js:

 toque em local.js 

Abra-o e adicione o código abaixo:

 'use strict'; 
 deixe localConfig = { 
hostname: 'localhost',
porta: 3000
};
 module.exports = localConfig; 

Este também é simples. Estamos criando um objeto localConfig e adicionando algumas propriedades, como hostname e port . Em seguida, exporte-o para usá-lo como estamos fazendo no arquivo ./index.js .

3. Agora crie um banco de dados

 toque em db.js 

Abra db.js no seu editor favorito e cole o código abaixo nele.

 module.exports = { 
'secret': 'putsomethingsecretehere',
'database': 'mongodb: //127.0.0.1: 27017 / formediumblog'
};

Estamos exportando um objeto JavaScript com propriedades secret e de database . Estes são usados para se conectar com um banco de dados MongoDB usando middleware chamado mangusto.

Construindo o aplicativo

Agora terminamos com a configuração básica do nosso projeto, tempo para as coisas divertidas!

cd na pasta do server e crie as seguintes pastas:

 mkdir controllers modela serviços de rotas 

Primeiro, vamos cobrir a pasta de routes . Essa pasta é usada para adicionar todos os terminais que estão disponíveis para uso no lado do cliente. Primeiro de tudo, vá em frente e crie o arquivo index.js primeiro dentro da pasta de routes .

 touch index.js 

E coloque o código abaixo neste arquivo:

 'use strict'; 
 const apiRoute = require ('./ apis'); 
 função init (servidor) { 
server.get ('*', function (req, res, next) {
console.log ('Pedido foi feito para:' + req.originalUrl);
return next ();
});
 server.use ('/ api', apiRoute); 
}
 module.exports = { 
init: init
};

Primeiro, exija a pasta apiRoute que vamos criar em seguida. Esta pasta conterá outra pasta com o número da versão da API, ou seja, v1 .

Em segundo lugar, crie uma função init . Estamos chamando essa função do arquivo server/index.js dentro da função create na parte inferior e passando o server como um parâmetro. Ele simplesmente obtém todas as rotas e retorna a próxima função de retorno de chamada.

Em seguida, use o apiRoute que estamos exigindo acima. Finalmente, exporte a função init para disponibilizar essa função no restante do projeto.

Agora vá em frente, crie uma pasta apis . Dentro dessa pasta, crie um arquivo index.js .

 mkdir apis 
touch index.js

Cole o código abaixo no arquivo index.js .

 'use strict'; 
 const express = require ('express'); 
const v1ApiController = require ('./ v1');
 vamos router = express.Router (); 
 router.use ('/ v1', v1ApiController); 
 module.exports = roteador; 

Este arquivo requer express e a pasta da versão api, por exemplo, v1 . Em seguida, crie o roteador e faça o ponto final /v1 usando o método router.use() . Finalmente, exporte o roteador.

Está na hora de criar o arquivo apis/v1.js Cole o código abaixo dentro do arquivo v1.js :

 'use strict'; 
 const registerController = require ('../../ controllers / apis / register'); 

const express = require ('express');
 vamos router = express.Router (); 
 router.use ('/ register', registerController); 
 module.exports = roteador; 

Precisamos registrar o controlador e express.js e criar um roteador. Em seguida, precisamos expor endpoints da API de register para uso no lado do cliente. Finalmente, devemos exportar o roteador deste módulo.

Este é o arquivo que vamos continuar modificando. Vamos exigir mais controladores aqui quando os criarmos.

Agora terminamos a pasta de rotas e chegou a hora da pasta dos controladores. Vá em frente e CD para essa pasta e crie uma pasta apis .

 mkdir apis 

Agora que temos a pasta apis dentro dos controllers , vamos criar os três controladores a seguir e seus respectivos services .

  1. Configuração Básica da Arquitetura
  2. Cadastro
  3. Entrar
  4. painel de controle

O primeiro é o registerController . Vá em frente e crie o arquivo abaixo.

 toque em register.js 

Abra este arquivo em seu editor favorito e cole o código abaixo nele:

 'use strict'; 
 const express = require ('express'); 
const registerService = require ('../../ serviços / autenticação / registro');
 vamos router = express.Router (); 
 router.post ('/', registerService.registerUser); 
 module.exports = roteador; 

Primeiro, é necessário o express.js e o serviço de register (que vamos escrever mais tarde). Em seguida, crie um roteador usando o método express.Router() e faça uma solicitação de postagem para o caminho '/' . Em seguida, chame o método registerUser no registerService (que vamos escrever mais tarde). Finalmente, exporte o roteador deste módulo.

Agora precisamos exigir este controlador dentro do arquivo routes/apis/v1.js que já fizemos.

Agora registrando o controlador está feito. É hora de chegar à pasta de services . CD para essa pasta e crie uma pasta de authentication . Primeiras coisas primeiro, cd na authentication e crie um arquivo register.js .

 toque em register.js 

Em seguida, abra o arquivo register.js e cole o código abaixo nele:

 'use strict'; 
 const express = require ('express'); 
const Usuário = require ('../../ models / User');
 httpMessages const = { 
onValidationError: {
sucesso: falso,
mensagem: 'Por favor insira e-mail e senha.'
}
onUserSaveError: {
sucesso: falso,
mensagem: "Esse endereço de e-mail já existe".
}
onUserSaveSuccess: {
sucesso: verdade
mensagem: "Usuário criado com sucesso".
}
}
 // Registrar novos usuários 
function registerUser (request, response) {
let {email, password} = request.body;
 if (! email ||! password) { 
response.json (httpMessages.onValidationError);
} outro {
deixe newUser = new User ({
email: email,
senha: senha
});
 // Tentativa de salvar o usuário 
newUser.save (error => {
if (erro) {
return response.json (httpMessages.onUserSaveError);
}
response.json (httpMessages.onUserSaveSuccess);
});
}
}
 module.exports = { 
registerUser: registerUser
};

No serviço de register , primeiro estamos exigindo expressjs e User model. Então, estamos criando um objeto JavaScript, ou seja, httpMessages que é basicamente uma lista de todas as mensagens que vamos enviar para os clientes por meio da API quando o cliente envia a solicitação.

Então a função registerUser que realmente realiza o processo de registro. Antes de salvar o usuário, há uma verificação se o usuário forneceu seu email e senha. Se eles o fizeram, crie um novoUsuário usando a new palavra-chave com o email e a senha fornecidos.

Em seguida, simplesmente chame a função save em newUser para salvar esse usuário no banco de dados e envie a resposta apropriada usando response.json .

Finalmente, exporte essa função usando module.exports para fazer uso dela no restante do projeto. Estamos usando isso dentro do arquivo controllers/register.js .

Antes de testar isso para ver se funciona, primeiro precisamos criar um modelo de User . Vá em frente, crie um arquivo User.js dentro da pasta de models .

 toque em User.js 

E cole este código no arquivo acima:

 const mongoose = require ('mangusto'); 
const bcrypt = require ('bcrypt');
 const UserSchema = new mongoose.Schema ({ 
o email: {
tipo: String,
minúsculas: verdadeiro
único: verdadeiro
obrigatório: true
}
senha: {
tipo: String,
obrigatório: true
}
Função: {
tipo: String,
enum: ['Client', 'Manager', 'Admin'],
padrão: 'Cliente'
}
});
 UserSchema.pre ('save', function (next) { 
deixe user = this;
 if (this.isModified ('password') || this.isNew) { 
bcrypt.genSalt (10, (err, salt) => {
if (err) {
console.log (err);
volte em seguida (err);
}
 bcrypt.hash (user.password, salt, (err, hash) => { 
if (err) {
console.log (err);
volte em seguida (err);
}
 user.password = hash; 
Próximo();
});
});
} outro {
return next ();
}
});
 // Cria um método para comparar a entrada de senha com a senha salva no banco de dados 
UserSchema.methods.comparePassword = function (pw, cb) {
bcrypt.compare (pw, this.password, function (err, isMatch) {
if (err) {
return cb (err);
}
 cb (null, isMatch); 
});
};
 module.exports = mongoose.model ('User', UserSchema); 

Primeiro de tudo, exigem os módulos mongoose e bcrypt . O Mongoose é usado para criar o esquema mongodb, enquanto o bcrypt é usado para criptografar senhas antes de armazená-las no banco de dados.

Crie UserSchema com propriedades de email, password and role . Em seguida, antes de salvar o usuário, execute algumas verificações antes de criptografar a senha.

A função final é comparar as senhas. Ele compara a senha do usuário com a senha com hash no banco de dados.

Agora, a fim de testar este código, abra o carteiro (se você não tiver instalado o carteiro, vá em frente e instale-o aqui ). Abra o carteiro e insira o URL abaixo:

 http: // localhost: 3000 / api / v1 / register 

Selecione POST como a solicitação, escolha a guia body e form-urlencoded e insira o email e a senha. Pressione o botão enviar e você verá a mensagem de sucesso abaixo.

Agora a parte de registro está pronta.

  1. Configuração Básica da Arquitetura
  2. registo
  3. Entrar
  4. painel de controle

É hora de se concentrar no login. Crie um arquivo login.js dentro da pasta controllers .

 toque em login.js 

Agora abra-o e cole o código abaixo:

 'use strict'; 
 const express = require ('express'); 
const loginService = require ('../../ serviços / autenticação / login');
 vamos router = express.Router (); 
 router.post ('/', loginService.loginUser); 
 module.exports = roteador; 

Mais uma vez é simples e o mesmo que o módulo de registo: após importar express.js e loginService estamos criando o roteador e fazer uma solicitação POST para o caminho raiz '/' com a loginUser função de retorno sobre loginService . Finalmente, exporte o roteador.

É hora de requerer o loginController no loginController routes/apis/v1.js Seu arquivo v1.js deve se parecer com o abaixo agora.

 'use strict'; 
 const registerController = require ('../../ controllers / apis / register'); 
const loginController = require ('../../ controllers / apis / login');
 const express = require ('express'); 
 vamos router = express.Router (); 
 router.use ('/ register', registerController); 
router.use ('/ login', loginController);
 module.exports = roteador; 

Agora, para o serviço de login, crie um arquivo login.js dentro de services/authentication/ :

 toque em login.js 

E cole o código abaixo neste arquivo:

 'use strict'; 
 const express = require ('express'); 
const apiRoutes = express.Router ();
 const jwt = require ('jsonwebtoken'); 
const passport = require ('passaporte');
const db = require ('../../../ configs / db');
 const Usuário = require ('../../ models / User'); 
 const httpResponse = { 
onUserNotFound: {
sucesso: falso,
mensagem: 'Usuário não encontrado'.
}
onAuthenticationFail: {
sucesso: falso,
mensagem: "As senhas não coincidiram".
}
}
 function loginUser (request, response) { 
let {email, password} = request.body;
 User.findOne ({ 
email: email
}, função (erro, usuário) {
se (erro) lançar erro;
 if (! user) { 
return response.send (httpResponse.onUserNotFound);
}
 // Verifique se a senha corresponde 
user.comparePassword (senha, função (erro, isMatch) {
if (isMatch &&! error) {
var token = jwt.sign (user.toJSON (), db.secret, {
expiresIn: 10080
});
 return response.json ({ 
success: true, token: 'JWT' + token
});
}
 response.send (httpResponse.onAuthenticationFail); 
});
});
};
 module.exports = { 
loginUser: loginUser
};

Primeiro, solicite alguns módulos necessários, como: express.js, jsonwebtoken, passport, db and User model . Crie um objeto JavaScript que tenha uma lista de mensagens a serem enviadas para o lado do cliente quando a solicitação http for feita para esse serviço.

Crie uma função loginUser, e dentro dela crie algumas variáveis, ou seja, email e senha, e atribua o email e a senha enviados pelo usuário para essas variáveis que estão em request.body .

Em seguida, use o método findOne() no modelo User para localizar um uso com base no email enviado pelo cliente pelo usuário. A função de retorno de chamada do findOne() aceita 2 parâmetros, error and user . Primeiro, verifique se o método findOne() acima gera algum erro – se ocorrer, então, um erro.

Em seguida, execute uma verificação: se nenhum usuário for encontrado, envie a resposta adequada com uma mensagem da lista de mensagens que declaramos acima neste módulo.

Em seguida, compare a senha que o usuário enviou com a senha do banco de dados usando a função de compare que escrevemos no modelo do User , anteriormente neste blog.

Se a senha corresponder e não retornar um erro, então criamos um token usando o módulo jsonwebtoken e retornamos esse token usando json.response() para o cliente. Caso contrário, enviamos uma mensagem authenticationFail .

Finalmente exportar o loginUser função com exports.module para que possamos usá-lo em nossos controladores e em qualquer outro lugar.

É hora de testar a funcionalidade de login. Volte para o carteiro e, desta vez, substitua o register pelo login como o ponto final da API no URL. Digite o email e a senha e pressione o botão enviar. Você deve poder receber um token. Vá em frente e copie isso para a área de transferência, porque você vai usá-lo mais tarde para acessar o painel.

  1. Configuração Básica da Arquitetura
  2. registo
  3. Entrar
  4. painel de controle

Agora é hora do arquivo dashboard.js . Crie uma pasta chamada dashboard dentro da pasta services e entre nessa pasta e crie o arquivo dashboard.js .

 toque em dashboard.js 

E abra-o e cole o código abaixo:

 'use strict'; 
 const passport = require ('passaporte'); 
const express = require ('express');
const dashboardService = require ('../../ services / dashboard / dashboard');
 vamos router = express.Router (); 
 router.get ('/', passport.authenticate ('jwt', {session: false}), dashboardService.getDashboard); 
 module.exports = roteador; 

Este controlador é diferente no sentido de que requer acesso autenticado. Ou seja, apenas um usuário conectado pode acessar o serviço do painel e fazer solicitações http diferentes.

Por esse motivo, também estamos importando o passaporte e, para a solicitação get, estamos usando a função passport.authenticate() para getDashboard serviço getDashboard .

Novamente, precisamos exigir o dashboardController no arquivo routes/apis/v1.js Seu arquivo v1.js deve se parecer com o abaixo:

 'use strict'; 
 const registerController = require ('../../ controllers / apis / register'); 
const loginController = require ('../../ controllers / apis / login');
const dashboardController = require ('../../ controllers / apis / dashboard');
 const express = require ('express'); 
 vamos router = express.Router (); 
 router.use ('/ register', registerController); 
router.use ('/ login', loginController);
router.use ('/ dashboard', dashboardController);
 module.exports = roteador; 

Agora que o dashboardController está disponível para ser usado para solicitações do lado do cliente, é hora de criar seu respectivo serviço. Vá para a pasta de serviços e crie uma pasta de dashboard dentro dela. Crie um arquivo dashboard.js e coloque o código abaixo dentro deste arquivo.

 'use strict'; 
 função getDashboard (request, response) { 
response.json ('Isto é do painel');
}
 module.exports = { 
getDashboard: getDashboard
}

Nada de fantasia acontecendo. Para fins de demonstração, estou simplesmente respondendo com uma mensagem de texto This is from dashboard . Em seguida, exporte esse método para ser usado em seu respectivo controlador, o qual já realizamos.

Agora está testando o tempo. Abra o carteiro e altere o ponto final da URL para o painel. Clique na guia de cabeçalhos e adicione Authorization e cole o JTW copiado na etapa anterior quando você efetuou login.

Você deve ver a mensagem This is from dashboard como uma resposta.

Como você pode ver, quando fazemos um novo serviço, precisamos de um controlador para isso e podemos continuar adicionando novos serviços à arquitetura. Se você quiser alterar a versão da API e também manter a atual, basta adicionar um novo arquivo v2.js e redirecionar todas as solicitações para esse ponto final. Esse é um exemplo simples.

Espero que você tenha gostado deste blog e até a próxima.