O que é Back End (Node.js): servidor, rotas, middleware e banco de dados

Back End é a parte que roda no servidor: recebe a requisição, escolhe a rota, lê o banco de dados e devolve a resposta.

Escrito por André Rafael - Origamid

Curso Back End Node.js

Publicado em

O Back End é a parte da aplicação que roda no servidor, longe do navegador da pessoa, e que fica esperando uma requisição chegar para decidir o que fazer com ela: qual rota (ex: /cursos, /contato e /conta) foi acessada, se o usuário está logado, o que precisa ser lido ou escrito no banco de dados e qual resposta vai voltar. Quando você abre a página de cursos da Origamid, o navegador manda uma requisição, e todo o trabalho de buscar esses cursos no banco de dados e devolver a lista acontece no Back End.

O navegador manda uma requisição para localhost:3000/cursos. No Back End, o router encontra a rota /cursos entre /cursos, /contato e /conta e chama o coursesHandler, que lê ou escreve no banco de dados db.sqlite. A resposta "Cursos de Front End" volta para o navegador.

O Back End é feito de algumas peças que aparecem em quase todo projeto, e cada uma delas vem abaixo com um exemplo curto em Node.js. Os exemplos abaixo são um esboço de como cada peça vai ser usada depois de construída, e não um código para copiar e rodar (o código completo geralmente é maior e cabe um curso para ensinar o mesmo). A ideia é você ter a visão geral de como as peças se encaixam antes de construir qualquer uma delas.

O servidor do back end e a porta

A primeira coisa que um back end precisa é de um servidor. Iniciar um servidor significa fazer a sua máquina passar a aceitar conexões numa porta, que é um número que identifica por onde as conexões chegam. O Node tem uma função nativa para isso, a createServer, do módulo node:http:

server.js
import { createServer } from "node:http"; const app = createServer((request, response) => { response.end("Servidor Funcionando"); }); app.listen(3000);

O listen(3000) deixa o servidor escutando qualquer conexão na porta 3000. Com ele rodando (node server.js no terminal), quando você acessa localhost:3000 no navegador, o que aparece na tela é o texto que o servidor enviou, “Servidor Funcionando”. A função passada para o createServer executa a cada requisição que chega, e o response.end() é o que encerra a resposta e manda o conteúdo de volta.

Apenas com esse código já temos um Back End funcionando, o problema é que ele que responde a mesma coisa para qualquer endereço que você acessar, seja /cursos, /login ou qualquer outro. Para responder de forma diferente em cada caminho, você precisa de um router, que registra cada rota e liga ela à função que vai responder.

Rotas e handler

O que é uma rota? É o caminho que a pessoa acessa. Quando ela entra em origamid.com/cursos, /cursos é uma rota, e /login é outra. Para cada rota acessada o back end deve responder de um jeito diferente, e a função que faz essa resposta se chama handler. É dentro dela que você acessa o banco de dados, escreve ou deleta alguma coisa, e faz o que for necessário para aquele caminho.

O router é a peça que registra essas rotas e liga cada uma ao seu handler, para que uma função execute quando a pessoa acessar /cursos, outra quando acessar a raiz e outra para /aulas. O registro de uma rota costuma ter esta forma:

function coursesHandler(request, response) { response.end("Cursos de Front End e Back End"); } app.get("/cursos", coursesHandler);

Primeiro vem o método, depois a rota e por último o handler. O método get é o que o navegador usa quando você simplesmente entra numa página, e o post é o que se usa para enviar informações, como os dados de um formulário. É comum também escrever o handler direto, como uma arrow function:

app.get("/cursos", (request, response) => { response.end("Cursos de Front End e Front End"); });

O createServer do Node não tem esse app.get. Ele entrega toda requisição para a mesma função, e o router é algo que você constrói em cima dela. Os frameworks de back end trazem um pronto, como Express, Fastify ou Honor.

Request e Response

Todo handler recebe dois objetos, o request e o response, e você vai lidar com os dois o tempo inteiro. O request são os dados que vieram com a requisição: o caminho que foi acessado (que é de onde o router tira a rota), o IP do cliente, o corpo da requisição quando um formulário enviou algum dado, e várias outras informações. O response é o que você usa para responder.

Um exemplo de uma rota que cria um curso:

app.post("/criar/curso", (request, response) => { const { name, lessons } = request.body; db.createCourse(name, lessons); response.end("Curso Criado"); });

A pessoa envia um post para /criar/curso com o nome do curso e as aulas no corpo. O handler pega esses dados do request.body, cria o curso no banco de dados e usa o response para avisar que o curso foi criado. Quase toda rota que você vai escrever segue esse mesmo ciclo, que é receber o dado, fazer alguma coisa com ele no banco e devolver uma resposta.

Middleware

Um middleware é uma função que executa antes do handler, ou seja, antes daquela função final que responde a rota. Quase sempre existem outros processos que precisam acontecer antes da resposta, e o exemplo mais comum é a autenticação. Quando a pessoa acessa /cursos, antes de mostrar o curso ou liberar uma aula, eu quero saber se ela está logada. Essa verificação é o middleware, e ele tem esse nome porque roda no meio, entre a requisição e a resposta.

function auth(req) { return db.validateSession(req.cookie); } app.get( "/cursos", (req, res) => { res.end("Cursos de Front End e Back End"); }, [auth] );

A rota continua com o seu handler, e agora ganha uma array com a lista de middlewares que executam antes da resposta final. Neste exemplo, request e response viraram req e res só para encurtar, porque são parâmetros da função e podem ter o nome que você quiser.

O caminho completo de uma requisição até a resposta fica assim:

Diagrama: request: chega na porta 3000 → router: encontra a rota → auth: middleware valida a sessão → handler: lê ou escreve no banco → response: volta para o navegador

O body chega em pedaços

No exemplo de criar curso, o request.body aparece prontinho, mas no Node.js ele não vem assim. O corpo da requisição não chega de uma vez, ele chega de parte em parte (é uma stream). Pense num arquivo grande sendo enviado: a internet funciona como um cano, e o dado passa em pedaços, nunca tudo junto. Por isso o back end precisa receber esses pedaços, juntar todos eles, transformar em string e só depois fazer o parse com JSON.parse:

async function bodyParser(req) { const chunks = []; for await (const chunk of req) { chunks.push(chunk); } const body = Buffer.concat(chunks).toString("utf-8"); return JSON.parse(body); }

O for await funciona porque o objeto da requisição no Node é uma stream que pode ser lida aos poucos. Cada chunk é um pedaço do corpo, o Buffer.concat junta todos eles e o toString("utf-8") transforma o resultado em texto. Esse é um dos pontos em que o Node puro te obriga a entender o que está acontecendo, porque nada disso é automático.

Banco de Dados e Sessão

O back end precisa de uma forma de se conectar a um banco de dados para ler, escrever e atualizar o que for necessário. Nos exemplos o banco é o SQLite, e o Node já traz um módulo nativo para ele, o node:sqlite. Com a conexão aberta, você executa os seus comandos SQL, como SELECT, INSERT, UPDATE e CREATE TABLE, direto do Node:

import { Database } from "node:sqlite"; const db = new Database("./db.sqlite"); db.prepare('SELECT * FROM "users" WHERE "id" = ?').get(1);

O prepare prepara a consulta e o get(1) executa trocando o ? pelo valor 1, devolvendo a primeira linha encontrada, que nesse caso é o usuário de id 1.

Com o banco funcionando, dá para autenticar o usuário através de sessões. A sessão é criada no primeiro login, quando a pessoa manda o e-mail e a senha: o back end gera um cookie (que é apenas um pequeno texto) na máquina do usuário e um registro no banco de dados. Nas próximas vezes que ela acessar o site ou a API, você não precisa pedir login e senha de novo, porque o navegador manda o cookie automaticamente e o back end só confere no banco se aquele cookie é válido para liberar a sessão.

app.post("/login", (req, res) => { const { email, password } = req.body; createSession(email, password); res.end("logged"); }); app.get("/session", (req, res) => { validateSession(req.cookies); res.end("valid"); });

É essa validação de sessão que o middleware auth faz antes de liberar a rota de cursos.

Validação de Dados

Outra peça é a validação dos dados. O dado vem do usuário, e o back end não confia nele, porque a pessoa pode mandar qualquer coisa. Você valida se o e-mail tem mesmo cara de e-mail, com arroba e ponto, e não é um texto qualquer só para gerar um cadastro de spam no seu banco. Valida também se a senha cumpre os padrões que você definiu, por exemplo ter mais de 10 caracteres, pelo menos um número, um caractere especial e uma letra maiúscula:

app.post("/login", (req, res) => { const email = validate.email(req.body.email); const password = validate.password(req.body.password); });

Outras Funções

O que vimos até aqui ocupa mais de 80% do que o Back End faz, porém nada é tão simples. Nele você vai precisar lidar também com disparo de emails (a pessoa esqueceu a senha), filas (requisições pesadas, por exemplo uma exportação grande de dados, geralmente entram em uma fila que é executada automaticamente pelo servidor, e apenas depois notifica o usuário), rate-limit (limitar quantas vezes uma máquina pode acessar por segundo uma rota específica) e mais.

Esse artigo foi escrito com base na aula 0102 Back End do curso Back End Node.js

Referências

Artigos relacionados