Skip to main content

Node JS



Introdução ao Express


O Express é um framework web para Node.js que simplifica o processo de criação de aplicativos web e APIs. Ele fornece uma série de recursos e utilitários que facilitam o desenvolvimento de servidores web robustos e escaláveis em Node.js.



Fundamentos


O Express possui algumas funções que ditam a configuração e consequentemente como o código vai trabalhar:

  • Roteamento: O Express facilita a definição de rotas para lidar com diferentes URLs e métodos HTTP. Isso permite criar endpoints para manipular solicitações específicas.

  • Middlewares: O Express utiliza middlewares para processar solicitações HTTP. Os middlewares são funções que têm acesso ao objeto de solicitação (request), objeto de resposta (response) e à próxima função de middleware no ciclo de solicitação-resposta. Isso é útil para adicionar funcionalidades como autenticação, logging, compressão, etc.

  • Templates: O Express suporta vários mecanismos de templates que permitem renderizar dados dinâmicos em HTML. Alguns dos mecanismos de template populares são EJS, Pug (anteriormente conhecido como Jade), Handlebars, entre outros.

  • Gerenciamento de sessão e cookies: O Express facilita o uso de sessões e cookies para manter o estado do usuário entre diferentes solicitações.

  • Integração com bancos de dados: O Express é flexível e pode ser facilmente integrado com diferentes bancos de dados, como MongoDB, MySQL, PostgreSQL, entre outros.

  • Extensível: O Express é altamente extensível, permitindo a adição de funcionalidades através de módulos de terceiros.


Para começar a usar o Express, você precisa instalá-lo em seu projeto usando o npm e configurar seu servidor Express com rotas, middlewares e outras configurações necessárias para atender às necessidades do seu aplicativo web ou API.



Rotas


Rotas em um aplicativo web são definições de URLs específicas que o servidor pode manipular. Em um contexto de servidor web, as rotas determinam como o servidor responderá a diferentes URLs e métodos HTTP.


Por exemplo, em um aplicativo web, você pode ter uma rota para lidar com solicitações de GET para a URL /users, outra rota para lidar com solicitações de POST para a URL /users, e assim por diante.


Resumindo, as Rotas são o caminho que o usuário acessa após o / (barra), sendo o / sem nada uma rota também.



Middlewares


Middleware é um tipo de software que atua como um intermediário entre diferentes sistemas, aplicativos ou componentes de software. Ele fica posicionado entre o sistema operacional e os aplicativos, facilitando a comunicação e integração entre as partes de um sistema distribuído.


Imagine o middleware como um tradutor universal. Ele permite que diferentes sistemas, que falam linguagens de programação diferentes ou possuem protocolos distintos, consigam se comunicar e trocar informações. Isso torna o desenvolvimento de aplicações mais fácil e eficiente.


Um exemplo de seria verificar se um usuário está logado, esse é um exemplo clássico de uso de middleware. Imagine um aplicativo web com diferentes páginas e funcionalidades. Algumas páginas podem ser acessíveis por qualquer usuário, enquanto outras exigem que o usuário esteja logado.


Nesse caso, o middleware pode ser usado para verificar se o usuário está logado antes de permitir o acesso à página. Se o usuário não estiver logado, o middleware pode redirecioná-lo para a página de login.



Instalando


Vamos instalar o express:

# Crie o dir (diretório) do projeto:
mkdir xpr && cd xpr

# Inicie o projeto:
npm init -y

# Instale o express:
npm install express

Adicione o start ao package.json:

package.json
{
"name": "p001",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"Sem teste configurado\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}


Express simples


Vamos criar um programinha simples.

index.js
const express = require('express')
const app = express() // a var 'app' está instanciando o módulo express e criando uma aplicação Express.
const port = 8080

// quando o user der um get na página '/', será mostrado o conteudo abaixo:
app.get('/', (req, res) => {
res.send('Olá Mundo!!')
})

// configura a porta em que o express vai ouvir:
app.listen(port, () => {
console.log(`App rodando em http://localhost:${port}`)
})

o que é instanciar?

Quando dizemos que "estamos instanciando um módulo", significa que estamos criando uma nova instância do módulo em questão.


Olhando o código do express, const app = express(), significa que estamos criando uma nova instância do módulo express, ou seja, estamos criando um novo objeto que representa uma aplicação Express.



Renderizando HTML com Express


Vamos enviar um arquivo html para que o código nodejs envie esse arquivo para o browser do cliente, para isso vamos usar o sendFile. Vamos continuar evoluindo o código acima:

index.js
const express = require('express')
const app = express() // a var 'app' está instanciando o módulo express e criando uma aplicação Express.

// Importando path para pegar o caminho do arquivo html
const path = require('path')

const port = 8080
const basePath = path.join(__dirname, '.') // __dirname = dir atual
// templates = é o diretório que queremos pegar o path.

// quando o user der um get na página '/', será mostrado o conteúdo abaixo:
app.get('/', (req, res) => {
res.sendFile(`${basePath}/index.html`)
})

// configura a porta em que o express vai ouvir:
app.listen(port, () => {
console.log(`App rodando em http://localhost:${port}`)
})

index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Home</title>
</head>
<body>
<h1>Home Express!</h1>
</body>
</html>


Nodemon


O Nodemon é uma ferramenta de linha de comando que monitora as alterações nos arquivos do seu projeto Node.js e reinicia automaticamente o servidor sempre que detecta uma modificação. Isso é extremamente útil durante o desenvolvimento, pois elimina a necessidade de reiniciar o servidor manualmente a cada alteração no código (parar o server web node e iniciar novamente).


Instale o Nodemon com o comando abaixo:

npm install --save-dev nodemon

--save-dev é a dependencia de desenvolvimento, só vai baixar quando instanciar a parte de dev. Isso significa que essa dependência será instalada apenas quando alguém estiver configurando o ambiente de desenvolvimento do projeto, e não será incluída em um ambiente de produção.


Adicione o run ao package.json:

package.json
{
"name": "xpr",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "nodemon ./index.js localhost 8080",
"start": "node index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.3"
},
"devDependencies": {
"nodemon": "^3.1.0"
}
}


Agora basta executar:

npm run

Agora podemos modificar o conteúdo do index.html e atualizar a página do browser que a alteração será exibida.



Aplicando um exemplo de Middleware


index.js
const express = require('express')
const app = express() // a var 'app' está instanciando o módulo express e criando uma aplicação Express.

// Importando path para pegar o caminho do arquivo html
const path = require('path')

const port = 8080

const basePath = path.join(__dirname, '.') // __dirname = dir atual
// templates = é o diretório que queremos pegar o path.

var checkAuth = function (req, res, next) {
req.authStatus = true
// A linha acima define a propriedade 'authStatus' no objeto de requisição (req) como true. Indica que está autenticado.

// Mude de true para false, assim vera o 'else' em ação!
if (req.authStatus) {
console.log('Está logado, pode continuar')
next()
// next() É usado para seguir para proxima etapa, se não usado, o browser ficará com loading infinito.
} else {
console.log('Não está logado, faça o login para continuar!')
next()
// next() É usado para seguir para proxima etapa, se não usado, o browser ficará com loading infinito.
}
}

app.use(checkAuth)
// Estamos usando o 'use' para conseguir usar o 'middleware'.

// quando o user der um get na página '/', será mostrado o conteúdo abaixo:
app.get('/', (req, res) => {
res.sendFile(`${basePath}/index.html`)
})

// configura a porta em que o express vai ouvir:
app.listen(port, () => {
console.log(`App rodando em http://localhost:${port}`)
})

var checkAuth = function (req, res, next) {...};

Esse trecho demonstra que a função está sendo atribuída a uma variável chamada checkAuth, isso é uma questão de conveniência e flexibilidade no JavaScript.


Em JS, as funções são cidadãos de primeira classe, o que significa que podem ser tratadas como qualquer outra variável. Isso permite que você as atribua a variáveis, passe-as como argumentos para outras funções e até mesmo retorne-as como valores de outras funções.



URL com Argumentos


Os argumentos ou parâmetros fornecidos via URL podem ser resgatados usando o req.


A sintaxe é raq.params.<nome>.

  • O nome é o que está definido na URL do Express.

Vejamos um exemplo simples e prático:

index.js
const express = require('express')
const app = express() // a var 'app' está instanciando o módulo express e criando uma aplicação Express.

// Importando path para pegar o caminho do arquivo html
const path = require('path')

const port = 8080

const basePath = path.join(__dirname, '.') // __dirname = dir atual
// templates = é o diretório que queremos pegar o path.

var checkAuth = function (req, res, next) {
req.authStatus = true // Define a propriedade 'authStatus' no objeto de requisição (req) como true. Indica que está autenticado.

// Mude de true para false, assim vera o 'else' em ação!

if (req.authStatus) {
console.log('Está logado, pode continuar')
next() // Usa para seguir para proxima etapa, se não usado, o browser ficará com loading infinito.
} else {
console.log('Não está logado, faça o login para continuar!')
next() // Usa para seguir para proxima etapa, se não usado, o browser ficará com loading infinito.
}
}

app.use(checkAuth) // estamos usando o 'use' para usar o middleware

// A URL / deve ser a última, se for a primeira,
// ela sempre será acessada e as outras não.

// Essa será a URL '/users':
app.get('/users/:id', (req, res) => {
console.log(`Acessando usuário com ID ${req.params.id}`)
// A linha acima pega o argumento chamado 'id' (req.params.id) digitado na URL.

res.sendFile(`${basePath}/users.html`)
})

// Essa é a URL '/':
app.get('/', (req, res) => {
res.sendFile(`${basePath}/index.html`)
})
// configura a porta em que o express vai ouvir:
app.listen(port, () => {
console.log(`App rodando em http://localhost:${port}`)
console.log(`Acesse /users acessando: http://localhost:${port}/users/5`)
})

index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Home</title>
</head>
<body>
<h1>Home Express! Atualização</h1>
</body>
</html>

users.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Usuários</title>
</head>
<body>
<h1>Carregando usuário...</h1>
</body>
</html>


Enviar dados com POST


O POST é um método HTTP usado para enviar informações do cliente para o servidor. O GET é outro métodos, usado para enviar informações do servidor para o cliente.


index.js
const express = require('express')
const app = express() // a var 'app' está instanciando o módulo express e criando uma aplicação Express.

// Importando path para pegar o caminho do arquivo html
const path = require('path')

const port = 8080

const basePath = path.join(__dirname, '.') // __dirname = dir atual
// templates = é o diretório que queremos pegar o path.

// ler o body da requisição
app.use(
express.urlencoded({
extended: true,
}),
)
// o trecho acima configura o middleware 'express.urlencoded()' no Express.
// Ele é usado para analisar os dados de formulário codificados em URL que são
// enviados em solicitações POST.


app.use(express.json()) // transforma toda a requisição obtida de body em json

var checkAuth = function (req, res, next) {
req.authStatus = true // Define a propriedade 'authStatus' no objeto de requisição (req) como true. Indica que está autenticado.

// Mude de true para false, assim vera o 'else' em ação!

if (req.authStatus) {
console.log('Está logado, pode continuar')
next() // Usa para seguir para proxima etapa, se não usado, o browser ficará com loading infinito.
} else {
console.log('Não está logado, faça o login para continuar!')
next() // Usa para seguir para proxima etapa, se não usado, o browser ficará com loading infinito.
}
}

// Estamos usando o 'use' para usar o middleware
app.use(checkAuth)

// A URL / deve ser a última, se for a primeira,
// ela sempre será acessada e as outras não.

// Essa será a URL '/users/add':
app.get('/users/add', (req, res) => {
res.sendFile(`${basePath}/userform.html`)
})

// Essa será a URL '/users/save':
app.post('/users/save', (req, res) => {
console.log(req.body)

const name = req.body.name // acessa o valor da chave 'name'
const age = req.body.age // acessa o valor da chave 'age'

res.sendFile(`${basePath}/userform.html`)
console.log(name)
console.log(age)

console.log(`Você está em /save. Seu nome é ${name} e sua idade é ${age}.`);
})

// Essa será a URL '/users', precisa ficar em baixo dos outros '/users',
// se não, vai achar que queremos saber parametro 'add', 'save' e não que 'add/save' é uma rota.
app.get('/users/:id', (req, res) => {
console.log(`Acessando usuário com ID ${req.params.id}`) // Pega o argumento chamado 'id'

res.sendFile(`${basePath}/users.html`)
})

// Essa é a URL '/':
app.get('/', (req, res) => {
res.sendFile(`${basePath}/index.html`)
})
// configura a porta em que o express vai ouvir:
app.listen(port, () => {
console.log(`App rodando em http://localhost:${port}`)
console.log(`Acesse /users/id acessando: http://localhost:${port}/users/5`)
console.log(`Acesse /users/add acessando: http://localhost:${port}/users/add`)
console.log(`Acesse /users/save acessando: http://localhost:${port}/users/save`)
})

index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Home</title>
</head>
<body>
<h1>Home Express! Atualização</h1>
</body>
</html>

users.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Usuários</title>
</head>
<body>
<h1>Carregando usuário...</h1>
</body>
</html>

userform.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Adicionar usuário</title>
</head>
<body>
<h1>Adicione um usuário:</h1>
<form action="/users/save" method="POST">
<div>
<input type="text" name="name" placeholder="Digite um nome" />
</div>
<div>
<input type="text" name="age" placeholder="Digite a idade" />
</div>
<div>
<input type="submit" value="Cadastrar" />
</div>
</form>
</body>
</html>


Módulo de Rotas


Vamos unir todas as nossas Rotas num único módulo, isso deixa o código mais organizado e flexível. Depois vamos usar um objeto chamado Router para adicionar as rotas nele.


Vamos começar preparando o ambiente.

# Crie os dois diretórios abaixo:
mkdir users templates

# Mova os HTMLs usados no exercício anterior para 'templates':
mv *.html templates/

Em users crie o index.js, ele será o módulo que vamos criar contendo as Rotas.

users/index.js
var express = require('express')
var router = express.Router()

const path = require('path')

const basePath = path.join(__dirname, '../templates')

router.get('/add', (req, res) => {
res.sendFile(`${basePath}/userform.html`)
})

router.post('/save', (req, res) => {
console.log(req.body)

const name = req.body.name // acessa o valor da chave 'name'
const age = req.body.age // acessa o valor da chave 'age'

res.sendFile(`${basePath}/userform.html`)
console.log(name)
console.log(age)

console.log(`Você está em /save. Seu nome é ${name} e sua idade é ${age}.`);
})

// antes do /
router.get('/:id', (req, res) => {
console.log(`Carregando usuário: ${req.params.id}`)

res.sendFile(`${basePath}/users.html`)
})

module.exports = router // exporta todos os routers (rotas)

No código acima, não precisamos adicionar /users/add e sim somente /add. Isso acontece porque vamos usar o código abaixo:

const users = require('./users')
app.use('/users', users)

A primeira linha informa que estamos importando um módulo dentro do diretório ./users (o ponto informa ser o diretório atual).


Na segunda linha estamos usando um middleware (app.use), o /users é o caminho de rota ou prefixo de rota na URL; Por isso não precisamos colocar isso quando usamos o route. Já users é o módulo contendo router (nossas rotas), o prefixo /users será usada em conjunto com as rotas declaradas em users.


O que acontece se usar /users no route?

Se fizermos o código assim:

router.get('/users/add', (req, res) => {
res.sendFile(`${basePath}/userform.html`)
})

Quando formos acessar, ele vai interpretar /users/users/add, já que vamos usar a chamada do middleware informando o prefixo da rota.


index.js
const express = require('express')
const app = express()
const port = 8080

const path = require('path')

const basePath = path.join(__dirname, 'templates')

const users = require('./users') // Como não tem nome de arquivo, o NodeJS vai procurar por um arquivo chamado index.js

// ler o body
app.use(
express.urlencoded({
extended: true,
}),
)

app.use(express.json()) // exporta o body da requisição como json

var checkAuth = function (req, res, next) {
req.authStatus = true

if (req.authStatus) {
console.log('Está logado, pode continuar')
next()
} else {
console.log('Não está logado, faça o login para continuar!')
}
}

app.use(checkAuth)

app.use('/users', users) // middleware usando a var 'users', '/users' é uma parte da URL.

app.get('/', (req, res) => {
res.sendFile(`${basePath}/index.html`)
})

app.listen(port, () => {
console.log(`App rodando na porta:${port}`)
})


Usando CSS nas páginas


Vamos começar a usar CSS para deixar o HTML mais "bonitinho". Vamos usar os mesmo arquivos que usamos mais acima, qualquer nova alteração deixarei os arquivos abaixo.

# Crie o diretório abaixo para colocarmos os arquivos css.
mkdir -p public/css

public/css/styles.css
body {
background-color: #dfdfdf;
text-align: center;
padding: 2em;
}

index.js
const express = require('express')
const app = express()
const port = 8080

const path = require('path')

const basePath = path.join(__dirname, 'templates')

const users = require('./users') // Como não tem nome de arquivo, o NodeJS vai procurar por um arquivo chamado index.js

// ler o body
app.use(
express.urlencoded({
extended: true,
}),
)

app.use(express.json()) // exporta o body da requisição como json

app.use(express.static('public')) // informa o diretório onde está o css.

var checkAuth = function (req, res, next) {
req.authStatus = true

if (req.authStatus) {
console.log('Está logado, pode continuar')
next()
} else {
console.log('Não está logado, faça o login para continuar!')
}
}

app.use(checkAuth)

app.use('/users', users) // middleware usando a var 'users', '/users' é uma parte da URL.

app.get('/', (req, res) => {
res.sendFile(`${basePath}/index.html`)
})

app.listen(port, () => {
console.log(`App rodando na porta:${port}`)
})

templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Home</title>
<link rel="stylesheet" href="/css/styles.css" />
</head>
<body>
<h1>Home Express! Atualização</h1>
</body>
</html>


Erro 404


É usado quando uma URL não existe.

index.js
const express = require('express')
const app = express()
const port = 8080

const path = require('path')

const basePath = path.join(__dirname, 'templates')

const users = require('./users') // Como não tem nome de arquivo, o NodeJS vai procurar por um arquivo chamado index.js

// ler o body
app.use(
express.urlencoded({
extended: true,
}),
)

app.use(express.json()) // exporta o body da requisição como json

app.use(express.static('public')) // informa o diretório onde está o css.

var checkAuth = function (req, res, next) {
req.authStatus = true

if (req.authStatus) {
console.log('Está logado, pode continuar')
next()
} else {
console.log('Não está logado, faça o login para continuar!')
}
}

app.use(checkAuth)

app.use('/users', users) // middleware usando a var 'users', '/users' é uma parte da URL.

app.get('/', (req, res) => {
res.sendFile(`${basePath}/index.html`)
})

// Fica no final, para ser executado se não der match com nenhuma outra rota
app.use(function (req, res, next) {
res.status(404).sendFile(`${basePath}/404.html`)
})

app.listen(port, () => {
console.log(`App rodando na porta:${port}`)
})

templates/404.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>404</title>
<link rel="stylesheet" href="/css/styles.css" />
</head>
<body>
<h1>Ops! Página não encontrada.</h1>
</body>
</html>


Template Engine


Uma template engine é uma ferramenta que facilita a geração de documentos (geralmente HTML) ao combinar dados dinâmicos com modelos estáticos.


Deixa o HTML mais dinâmico, inserindo variáveis do backend no frontend, ou seja, as template engines são frequentemente usadas para criar páginas da web dinâmicas, permitindo a inserção de dados específicos do usuário, do banco de dados ou de outras fontes de dados em modelos HTML pré-definidos.



Handlebars


O Handlebars é um template engine mais popular. Com ele podemos inserir variáveis diretamente nos templates, usando a sintaxe {{ variavel }}. Essas variáveis são substituídas pelos valores correspondentes quando o template é renderizado no navegador.

O template é em HTML.


Ainda podemos criar estruturas de decisão com {{#if}} e {{else}}, fechando o esse IF com {{/if}} e podemos até criar loops com {{#each}}, tudo diretamente nos templates.


O Handlebars permite a definição de parciais, que são pequenos trechos de templates reutilizáveis, e a criação de layouts mestres. Isso facilita a construção de interfaces de usuário consistentes e a reutilização de código em toda a aplicação.



Instalando


Vamos começar instalando:

# Após criar o projeto!!
npm install express-handlebars

Houve uma mudança na invocação do método!!

app.engine('handlebars', exphbs.engine())



Projetinho simples


index.js
const express = require('express')
const exphbs = require('express-handlebars')

const app = express()

// Configuração padrão para usar handlebars
app.engine('handlebars', exphbs.engine()) // define handlebars como template engine
app.set('view engine', 'handlebars')

app.get('/', function (req, res) {
res.render('home', { layout: false }) // define como false porque não temos layout ainda.
})

app.listen(8080)

views/home.handlebars
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Handlebars</title>
</head>
<body>
<h1>Olá Handlebars!</h1>
</body>
</html>


Projetinho simples - Criando layouts


index.js
const express = require('express')
const exphbs = require('express-handlebars')

const app = express()

// Configuração padrão para usar handlebars
app.engine('handlebars', exphbs.engine()) // define handlebars como template engine
app.set('view engine', 'handlebars')

app.get('/', function (req, res) {
res.render('home') // agora vamos ter layout
})

app.listen(8080)

A linha res.render('home') fará o Express procurar um arquivo denominado home dentro do diretório views.


Por padrão, o Express espera que você tenha uma pasta chamada views na raiz do seu projeto, onde você armazena seus arquivos de template. Dentro desta pasta views, ele procurará pelo arquivo 'home.handlebars' (ou 'home.hbs', dependendo da extensão configurada) para renderizar.


Quando a gente configurou app.set('view engine', 'handlebars'), estamos definindo o Express para usar Handlebars como sua template engine padrão e configurando-o para esperar arquivos de template com a extensão .handlebars.


Portanto, a linha res.render('home') refere-se ao arquivo de template que você deseja renderizar, nesse caso, um arquivo chamado home com a extensão .handlebars.


Para o Express saber qual arquivo de layout usar, você pode especificá-lo explicitamente em cada chamada res.render() usando o segundo argumento para fornecer opções de renderização, como este exemplo: res.render('home', { layout: 'banana' });.


views/home.handlebars
<h1>
Olá Handlebars com Layout!
</h1>

views/layouts/main.handlebars
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Handlebars</title>
</head>
<body>
{{{ body }}}
</body>
</html>

Como o express tem conhecimento de main.handlebars?

Como a gente não especificou explicitamente um layout padrão nem forneceu um layout específico em cada chamada res.render(), o Express vai procurar por um arquivo de layout com o nome padrão main.handlebars.



Enviando dados


index.js
const express = require('express')
const exphbs = require('express-handlebars')

const app = express()

// Configuração padrão para usar handlebars
app.engine('handlebars', exphbs.engine()) // define handlebars como template engine
app.set('view engine', 'handlebars')

app.get('/', function (req, res) {
const user = {
name: 'fulano',
surname: 'sicrano',
}

res.render('home', { user: user })
})
app.listen(8080)

views/home.handlebars
<h1>
Olá {{ user.name }} {{ user.surname }}, seja bem-vindo!
</h1>


Usando estrutura de decisão - Condicionais


Em uma template engine, como Handlebars, a estrutura básica do if e else é semelhante à da linguagem de programação subjacente (geralmente JavaScript), mas simplificada para ser usada em templates HTML. Um detalhe que muda em uma template engine como Handlebars, é que o bloco if é executado apenas se a condição dentro dele for avaliada como verdadeira. Se a condição for avaliada como falsa, o bloco if não será renderizado.


Estrutura IF simples:

{{#if condition}}
<!-- conteúdo a ser renderizado se a condição for verdadeira -->
{{/if}}

Estrutura IF com ELSE:

{{#if condition}}
<!-- conteúdo a ser renderizado se a condição for verdadeira -->
{{else}}
<!-- conteúdo a ser renderizado se a condição for falsa -->
{{/if}}

Estrutura IF com ELSE IF e ELSE:

{{#if condition1}}
<!-- conteúdo a ser renderizado se a condition1 for verdadeira -->
{{else if condition2}}
<!-- conteúdo a ser renderizado se a condition1 for falsa e a condition2 for verdadeira -->
{{else}}
<!-- conteúdo a ser renderizado se todas as condições anteriores forem falsas -->
{{/if}}

Agora vamos ao nosso código.

index.js
const express = require('express')
const exphbs = require('express-handlebars')

const app = express()

// Configuração padrão para usar handlebars
app.engine('handlebars', exphbs.engine()) // define handlebars como template engine
app.set('view engine', 'handlebars')

app.get('/', function (req, res) {
const user = {
name: 'fulano',
surname: 'sicrano',
}

res.render('home', { user: user, auth: true });
})

app.get("/dashboard", function (req, res) {
res.render("dashboard");
});

app.listen(8080)

views/home.handlebars
<h1>
Olá {{ user.name }} {{ user.surname }}, seja bem-vindo!
</h1>
{{#if auth}}
<p>
Você está autenticado no sistema, <a href="/dashboard">clique aqui</a> para acessar a área de membros
</p>
{{/if}}

views/dashboard.handlebars
<h1>
Dashboard de usuários!
</h1>


Usando ELSE


index.js
const express = require('express')
const exphbs = require('express-handlebars')

const app = express()

// Configuração padrão para usar handlebars
app.engine('handlebars', exphbs.engine()) // define handlebars como template engine
app.set('view engine', 'handlebars')

app.get('/', function (req, res) {
const user = {
name: 'fulano',
surname: 'sicrano',
}

const approved = false;

res.render("home", { user: user, auth: true, approved });
})

app.get("/dashboard", function (req, res) {
res.render("dashboard");
});

app.listen(8080)

views/home.handlebars
<h1>
Olá {{ user.name }} {{ user.surname }}, seja bem-vindo!
</h1>
{{#if approved}}
<p>Parabéns, o seu cadastro está aprovado!</p>
{{else}}
<p>O seu cadastro ainda necessita de aprovação!</p>
{{/if}}

{{#if auth}}
<p>
Você está autenticado no sistema, <a href="/dashboard">clique aqui</a> para acessar a área de membros
</p>
{{/if}}

Para testar mude approved = false; e auth: false.



Usando loops


Em uma template engine como Handlebars, a sintaxe para um loop é simplificada em comparação com linguagens de programação, mas ainda permite iterar sobre arrays ou objetos para renderizar repetidamente o conteúdo.


Veja a sintaxe do loop em Handlebars:

{{#each array}}
<!-- Conteúdo a ser repetido -->
{{/each}}

Em Handlebars, a palavra-chave this é usada dentro de um bloco {{#each}} para se referir ao item atual sendo iterado durante o loop. Dentro do contexto de um bloco {{#each}}, this representa o valor do item atual sendo iterado.


Pode-se pensar no this como o equivalente ao índice i em um loop for em linguagens de programação como JavaScript, Python, etc.


Vamos ao código:

index.js
const express = require('express')
const exphbs = require('express-handlebars')

const app = express()

// Configuração padrão para usar handlebars
app.engine('handlebars', exphbs.engine()) // define handlebars como template engine
app.set('view engine', 'handlebars')

app.get('/', function (req, res) {
const user = {
name: 'fulano',
surname: 'sicrano',
}

res.render("home", { user: user, auth: true });
})

app.get("/dashboard", function (req, res) {
const items_array = ["Item a", "Item b", "Item c"];

res.render("dashboard", { items: items_array });
});

app.listen(8080)

views/home.handlebars
<h1>
Olá {{ user.name }} {{ user.surname }}, seja bem-vindo!
</h1>
{{#if auth}}
<p>
Você está autenticado no sistema, <a href="/dashboard">clique aqui</a> para acessar a área de membros
</p>
{{/if}}

views/dashboard.handlebars
<h1>
Dashboard de usuários!
</h1>

<ul>
{{#each items}}
<li>{{this}}</li>
{{/each}}
</ul>


Usando WITH


Em Handlebars, a declaração {{#with}} é usada para alterar o contexto de dados dentro de um bloco, permitindo acessar propriedades de um objeto de forma mais concisa.


Resumidamente, a sintaxe é a seguinte:

{{#with objeto}}
<!-- Conteúdo -->
{{/with}}

Dentro do bloco {{#with}}, objeto é o contexto de dados que você deseja usar. Isso significa que você pode acessar as propriedades desse objeto diretamente dentro do bloco {{#with}} sem precisar repetir o nome do objeto.


Por exemplo, considere o seguinte código Handlebars:

{{#with user}}
<p>Nome: {{nome}}</p>
<p>Email: {{email}}</p>
{{/with}}

Neste exemplo, user é um objeto que contém as propriedades nome e email. Dentro do bloco {{#with user}}, você pode acessar essas propriedades diretamente como {{nome}} e {{email}} em vez de {{user.nome}} e {{user.email}}. Isso torna o código mais limpo e legível.


Vamos ao código:

index.js
const express = require('express')
const exphbs = require('express-handlebars')

const app = express()

// Configuração padrão para usar handlebars
app.engine('handlebars', exphbs.engine()) // define handlebars como template engine
app.set('view engine', 'handlebars')

app.get('/', function (req, res) {
const user = {
name: 'fulano',
surname: 'sicrano',
}

res.render("home", { user: user, auth: true });
})

app.get("/dashboard", function (req, res) {
const items_array = ["Item a", "Item b", "Item c"];

res.render("dashboard", { items: items_array });
});

app.get("/post", function (req, res) {
const post = {
title: "Aprender Node.js",
category: "Node.js",
body: "Node.js é muito utilizado na programação hoje em dia",
comments: 4,
};

res.render("blogpost", { post });
});


app.listen(8080)

views/blogpost.handlebars
{{#with post}}
<h1>{{title}}</h1>
<p>{{category}}</p>
<p>{{body}}</p>
<p>Número de comentários: {{comments}}</p>
{{/with}}


Partials


Partials em Handlebars são trechos de templates reutilizáveis que podem ser incluídos em outros templates para evitar repetição de código e promover a modularidade.


Eles são definidos usando a função registerPartial ou por meio de arquivos separados. São incluídos em outros templates usando a sintaxe {{> partialName}}.


Vamos ao código:

index.js
const express = require('express')
const exphbs = require('express-handlebars')

const app = express()

// Configuração padrão para usar handlebars
app.engine('handlebars', exphbs.engine()) // define handlebars como template engine
app.set('view engine', 'handlebars')

app.get('/', function (req, res) {
const user = {
name: 'fulano',
surname: 'sicrano',
}

res.render("home", { user: user, auth: true });
})

app.get("/dashboard", function (req, res) {
const items_array = ["Item a", "Item b", "Item c"];

res.render("dashboard", { items: items_array });
});

app.get("/post", function (req, res) {
const post = {
title: "Aprender Node.js",
category: "Node.js",
body: "Node.js é muito utilizado na programação hoje em dia",
comments: 4,
};

res.render("blogpost", { post });
});

app.get("/blog", function (req, res) {
const posts = [
{
title: "Aprender Node.js",
category: "Node.js",
body: "Node.js é muito utilizado na programação hoje em dia",
comments: 4,
},
{
title: "PHP ainda vale a pena?",
category: "PHP",
body: "",
comments: 12,
},
{
title: "Os segredos de JavaScript",
category: "JavaScript",
body: "",
comments: 5,
},
];

res.render("blog", { posts });
});

app.listen(8080)

views/blog.handlebars
<h1>Veja nossos posts</h1>
{{#each posts}}
{{> post}}
{{/each}}

No contexto em que {{> post}} é usado dentro do arquivo blog.handlebars, ele está referenciando o partial chamado post.handlebars. Portanto, o Handlebars procurará por um arquivo chamado post.handlebars na pasta de partials (geralmente definida como views/partials ou similar) e renderizará o conteúdo desse arquivo no local onde {{> post}} é usado.


views/partials/post.handlebars
<h2>{{this.title}}</h2>
<p>Categoria: {{this.category}}</p>
<p><a href="#">Ver post</a></p>


Renderizando CSS com Handlebars


index.js
const express = require('express')
const exphbs = require('express-handlebars')

const app = express()

// Configuração padrão para usar handlebars
app.engine('handlebars', exphbs.engine()) // define handlebars como template engine
app.set('view engine', 'handlebars')

app.use(express.static("public"));

app.get('/', function (req, res) {
const user = {
name: 'fulano',
surname: 'sicrano',
}

res.render("home", { user: user, auth: true });
})

app.get("/dashboard", function (req, res) {
const items_array = ["Item a", "Item b", "Item c"];

res.render("dashboard", { items: items_array });
});

app.get("/post", function (req, res) {
const post = {
title: "Aprender Node.js",
category: "Node.js",
body: "Node.js é muito utilizado na programação hoje em dia",
comments: 4,
};

res.render("blogpost", { post });
});

app.get("/blog", function (req, res) {
const posts = [
{
title: "Aprender Node.js",
category: "Node.js",
body: "Node.js é muito utilizado na programação hoje em dia",
comments: 4,
},
{
title: "PHP ainda vale a pena?",
category: "PHP",
body: "",
comments: 12,
},
{
title: "Os segredos de JavaScript",
category: "JavaScript",
body: "",
comments: 5,
},
];

res.render("blog", { posts });
});

app.listen(8080)

public/css/styles.css
h1 {
color: red;
}

h2 {
color: blue;
}

p {
color: gray;
}
views/layouts/main.handlebars
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Handlebars</title>
<link rel="stylesheet" href="/css/styles.css">
</head>
<body>
{{{ body }}}
</body>
</html>


Integrando NodeJS com MYSQL


Um banco de dados relacional é um tipo de banco de dados que organiza os dados em tabelas relacionadas. Cada tabela consiste em linhas e colunas, onde cada linha representa uma entrada de dados específica e cada coluna representa um atributo ou característica desses dados.


As tabelas em um banco de dados relacional podem ter ligações entre si, formando relacionamentos. Esses relacionamentos são estabelecidos através de chaves estrangeiras, que são colunas em uma tabela que referenciam a chave primária de outra tabela.


Quando uma tabela tem uma coluna que faz referência à chave primária de outra tabela, ela é chamada de chave estrangeira. Isso cria um relacionamento entre as duas tabelas, onde os registros em uma tabela podem estar relacionados aos registros em outra tabela.



Instalando o Mysql


Para instalar é bem simples:

# Primeiro atualize o repo:
sudo apt update

# Instale o Mysql:
sudo apt install -y mysql-server

# Aplique as configurações de segurança:
sudo mysql_secure_installation

# Minhas resposta para um ambiente de test:
y|0|y|y|y|y

# Crie o projeto e dentro dele instale os itens necessários:
npm install express express-handlebars nodemon mysql

# Crie os diretórios:
mkdir -p views/layouts

index.js
const express = require('express')
const exphbs = require('express-handlebars')
const mysql = require('mysql')

const app = express()

// Configuração padrão para usar handlebars
app.engine('handlebars', exphbs.engine()) // define handlebars como template engine
app.set('view engine', 'handlebars')

//app.use(express.static('public'))

app.get('/', function (req, res) {
res.render('home')
})

const conn = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'zO2*2D6.4sn;F9#9>e}*E!',
database: 'nodemysql',
})

conn.connect(function (err) {
if (err) {
console.log(err)
}

console.log('Conectado ao MySQL!')

app.listen(8080)
})

views/home.handlebars
<h1>Homepage</h1>

views/layouts/main.handlebars
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Node.js + MySQL</title>
<link rel="stylesheet" href="/css/styles.css">
</head>
<body>
{{{ body }}}
</body>
</html>

Antes de executar, vamos criar o banco de dados:

# Conecte no mysql (esteja logado como root):
mysql

# Agora vamos criar o banco:
create database nodemysql;

# Corrija um erro que vai aparecer no cliente do node:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'zO2*2D6.4sn;F9#9>e}*E!';

# Agora recarregue os privilégios:
flush privileges;


Criando tabelas


Vamos criar uma tabela chamada 'books', a primeira coluna será denominada id e será uma primary_key do tipo int, não aceitará valores nulos (null) e ainda terá um auto incrimento, assim a gente não precisa ficar se preocupando em colocar um valor nessa tabela sempre que adicionar um novo item.


A segunda coluna será title, o tipo de dados será varchar(255). A terceira será pagesn, ela será do tipo int.

# Conecte no mysql (esteja logado como root):
mysql

# Entre no banco criado:
use nodemysql;

# Crie a tabela:
CREATE TABLE books (
id INT NOT NULL AUTO_INCREMENT,
title VARCHAR(255) NULL,
pagesn INT NULL,
PRIMARY KEY (id));


Inserindo dados no banco


index.js
const express = require('express')
const exphbs = require('express-handlebars')
const mysql = require('mysql')

const app = express()

// Configuração padrão para usar handlebars
app.engine('handlebars', exphbs.engine()) // define handlebars como template engine
app.set('view engine', 'handlebars')

app.use(
express.urlencoded({
extended: true,
}),
)

app.use(express.json())

//app.use(express.static('public'))

app.get('/', function (req, res) {
res.render('home')
})

app.post('/books/insertbook', function (req, res) {
const title = req.body.title
const pagesn = req.body.pagesn

// pagesn e title que estão no json body, são definidos no home.handlebars


const query = `INSERT INTO books (title, pagesn) VALUES ('${title}', ${pagesn})`

conn.query(query, function (err) {
if (err) {
console.log(err)
}

res.redirect('/')
})
})

const conn = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'zO2*2D6.4sn;F9#9>e}*E!',
database: 'nodemysql',
})

conn.connect(function (err) {
if (err) {
console.log(err)
}

console.log('Conectado ao MySQL!')

app.listen(8080)
})

views/home.handlebars
<h1>Cadastre o seu livro:</h1>
<form class="form" action="/books/insertbook" method="POST">
<div class="form-control">
<label for="title">Título:</label>
<input type="text" name="title" placeholder="Título do livro">
</div>
<div class="form-control">
<label for="pagesn">Número de páginas:</label>
<input type="number" name="pagesn" placeholder="Quantas páginas o livro tem?">
</div>
<input type="submit" value="Cadastrar">
</form>

public/css/styles.css
* {
font-family: Helvetica, sans-serif;
}

h1 {
text-align: center;
}

.form {
width: 450px;
margin: 0 auto;
}

.form-control {
display: flex;
flex-direction: column;
margin-bottom: 0.5em;
}

.form-control label {
font-weight: bold;
margin-bottom: 0.3em;
}

.form-control input {
padding: 5px;
}

views/layouts/main.handlebars
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Node.js + MySQL</title>
<link rel="stylesheet" href="/css/styles.css">
</head>
<body>
{{{ body }}}
</body>
</html>

Execute o npm start e antes de acessar o browser, veja se existe algo na tabela:

No mysql, dentro do banco 'books'
select * from books;
Empty set (0.01 sec)

Agora acesse o browser e insira informações de livros. Depois faça um novo teste:

No mysql, dentro do banco 'books'
select * from books;
+----+------------------------------+--------+
| id | title | pagesn |
+----+------------------------------+--------+
| 1 | How Linux Works, 3rd Edition | 464 |
+----+------------------------------+--------+
1 row in set (0.00 sec)


Obtendo dados do banco


Adicione ao seu código.

index.js

app.use(express.static('public'))

app.get('/books', function (req, res) {
const query = `SELECT * FROM books`

conn.query(query, function (err, data) {
if (err) {
console.log(err)
}

const books = data // recebe os dados da query acima

console.log(data)

res.render('books', { books }) // renderiza o /books, usando books.handlebars e passa para ele os dados da var 'books'
})
})

views/books.handlebars
<h1>Livros disponíveis:</h1>
<div class="books-container">
{{#each books}}
<div>#{{this.id}} - <span class="bold">{{this.title}}</span> - {{this.pagesn}} páginas</div>
{{/each}}
</div>

public/css/styles.css
* {
font-family: Helvetica, sans-serif;
}

h1 {
text-align: center;
}

.form {
width: 450px;
margin: 0 auto;
}

.form-control {
display: flex;
flex-direction: column;
margin-bottom: 0.5em;
}

.form-control label {
font-weight: bold;
margin-bottom: 0.3em;
}

.form-control input {
padding: 5px;
}

/* Books */
.books-container div {
border-bottom: 1px solid #CCC;
padding: 5px;
margin: 5px;
}

.bold {
font-weight: bold;
}


Usando WHERE


Adicione ao seu código.

index.js
app.get('/books/:id', function (req, res) {
const id = req.params.id

const query = `SELECT * FROM books WHERE id = ${id}`

conn.query(query, function (err, data) {
if (err) {
console.log(err)
}

const book = data[0] // recebe os dados da query acima posição 0 = remove [], ficando apenas o json.

console.log(data[0])

res.render('book', { book }) // renderiza o /books/:id, usando book.handlebars e passa para ele os dados da var 'book'.
})
})


views/book.handlebars
<div id="book-page">
<a href="/books">Voltar</a>
<div class="container">
<h1>{{book.title}}</h1>
<p>Este livro tem {{book.pagesn}} páginas.</p>
</div>
</div>

views/books.handlebars
<h1>Livros disponíveis:</h1>
<div class="books-container">
{{#each books}}
<div>#{{this.id}} - <span class="bold"><a href="/books/{{this.id}}">{{this.title}}</a></span> - {{this.pagesn}} páginas</div>
{{/each}}
</div>

views/layouts/main.handlebars
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Node.js + MySQL</title>
<link rel="stylesheet" href="/css/styles.css">
</head>
<body>
<nav id="navbar">
<ul>
<li><a href="/">Cadastrar</a></li>
<li><a href="/books">Livros</a></li>
</ul>
</nav>
{{{ body }}}
</body>
</html>

public/css/styles.css
* {
font-family: Helvetica, sans-serif;
margin: 0;
padding: 0;
}

#navbar {
border-bottom: 1px solid #ccc;
padding: 1em;
margin-bottom: 1em;
}

#navbar ul {
display: flex;
list-style: none;
}

#navbar ul li {
margin-left: 1em;
}

h1 {
text-align: center;
margin-bottom: 1em;
}

.form {
width: 450px;
margin: 0 auto;
}

.form-control {
display: flex;
flex-direction: column;
margin-bottom: 0.5em;
}

.form-control label {
font-weight: bold;
margin-bottom: 0.3em;
}

.form-control input {
padding: 5px;
}

/* BOOKS */
.books-container {
width: 450px;
margin: 0 auto;
}

.books-container div {
border-bottom: 1px solid #ccc;
padding: 5px;
margin: 5px;
}

.bold {
font-weight: bold;
}

/* BOOK */
#book-page {
padding: 2em;
}

#book-page .container {
text-align: center;
}


Editar um dado do banco


Essa primeira parte é para ver como buscar os dados e exibir, depois faremos como modificar.

index.js
app.get('/books/edit/:id', function (req, res) {
const id = req.params.id

const query = `SELECT * FROM books WHERE id = ${id}`

conn.query(query, function (err, data) {
if (err) {
console.log(err)
}

const book = data[0]

console.log(data[0])

res.render('editbook', { book })
})
})


views/editbook.handlebars
<h1>Cadastre o seu livro:</h1>
<form class="form" action="/books/insertbook" method="POST">
<input type="hidden" name="id" value="{{book.id}}">
<div class="form-control">
<label for="title">Título:</label>
<input type="text" name="title" placeholder="Título do livro" value="{{book.title}}">
</div>
<div class="form-control">
<label for="pagesn">Número de páginas:</label>
<input type="number" name="pagesn" placeholder="Quantas páginas o livro tem?" value="{{book.pagesn}}">
</div>
<input type="submit" value="Cadastrar">
</form>


Agora vamos fazer a edição


Agora vamos editar os dados retornados acima.

index.js
app.post('/books/updatebook', function (req, res) {
const id = req.body.id
const title = req.body.title
const pagesn = req.body.pagesn

const query = `UPDATE books SET title = '${title}', pagesn = ${pagesn} WHERE id = ${id}`

conn.query(query, function (err) {
if (err) {
console.log(err)
}

res.redirect(`/books/edit/${id}`)
})
})

views/editbook.handlebars
<h1>Edite o seu livro:</h1>
<form class="form" action="/books/updatebook" method="POST">
<input type="hidden" name="id" value="{{book.id}}">
<div class="form-control">
<label for="title">Título:</label>
<input type="text" name="title" placeholder="Título do livro" value="{{book.title}}">
</div>
<div class="form-control">
<label for="pagesn">Número de páginas:</label>
<input type="number" name="pagesn" placeholder="Quantas páginas o livro tem?" value="{{book.pagesn}}">
</div>
<input type="submit" value="Editar">
</form>


Deletando


index.js
app.post('/books/remove/:id', function (req, res) {
const id = req.params.id

const query = `DELETE FROM books WHERE id = ${id}`

conn.query(query, function (err) {
if (err) {
console.log(err)
}

res.redirect(`/books`)
})
})

views/book.handlebars
<div id="book-page">
<a href="/books">Voltar</a>
<div class="container">
<h1>{{book.title}}</h1>
<p>Este livro tem {{book.pagesn}} páginas.</p>
<div class="actions">
<a href="/books/edit/{{book.id}}">Editar</a>
<form action="/books/remove/{{book.id}}" method="POST">
<input type="submit" value="Remover">
</form>
</div>
</div>
</div>


public/css/styles.css
* {
font-family: Helvetica, sans-serif;
margin: 0;
padding: 0;
}

#navbar {
border-bottom: 1px solid #ccc;
padding: 1em;
margin-bottom: 1em;
}

#navbar ul {
display: flex;
list-style: none;
}

#navbar ul li {
margin-left: 1em;
}

h1 {
text-align: center;
margin-bottom: 1em;
}

.form {
width: 450px;
margin: 0 auto;
}

.form-control {
display: flex;
flex-direction: column;
margin-bottom: 0.5em;
}

.form-control label {
font-weight: bold;
margin-bottom: 0.3em;
}

.form-control input {
padding: 5px;
}

/* BOOKS */
.books-container {
width: 450px;
margin: 0 auto;
}

.books-container div {
border-bottom: 1px solid #ccc;
padding: 5px;
margin: 5px;
}

.bold {
font-weight: bold;
}

/* BOOK */
#book-page {
padding: 2em;
}

#book-page .container {
text-align: center;
}

.actions {
margin-top: 1em;
display: flex;
justify-content: center;
}

.actions a {
margin-right: 1em;
}


Connection Pool


Um Connection Pool é um recurso usado para otimizar as conexões de banco de dados, criando um cache que pode ser reutilizado por aplicativos para acessar um banco de dados de forma eficiente.


index.js
const express = require('express')
const exphbs = require('express-handlebars')

const pool = require('./db/conn') // é necessária para criar uma conexão com o banco de dados usando um pool de conexões.

console.log(pool)

const app = express()

app.engine('handlebars', exphbs.engine())
app.set('view engine', 'handlebars')

app.use(
express.urlencoded({
extended: true,
}),
)

app.use(express.json())

app.use(express.static('public'))

app.get('/', function (req, res) {
res.render('home')
})

app.post('/books/insertbook', function (req, res) {
const title = req.body.title
const pagesn = req.body.pagesn
// pagesn e title que estão no json body, são definidos no home.handlebars

const query = `INSERT INTO books (title, pagesn) VALUES ('${title}', ${pagesn})`

pool.query(query, function (err) {
if (err) {
console.log(err)
}

res.redirect('/')
})
})

app.get('/books', function (req, res) {
const query = `SELECT * FROM books`

console.log('Teste')

pool.query(query, function (err, data) {
if (err) {
console.log(err)
}

const books = data

console.log(data)

res.render('books', { books })
})
})

app.get('/books/:id', function (req, res) {
const id = req.params.id

const query = `SELECT * FROM books WHERE id = ${id}`

pool.query(query, function (err, data) {
if (err) {
console.log(err)
}

const book = data[0]

console.log(data[0])

res.render('book', { book })
})
})

app.get('/books/edit/:id', function (req, res) {
const id = req.params.id

const query = `SELECT * FROM books WHERE id = ${id}`

pool.query(query, function (err) {
if (err) {
console.log(err)
}

const book = data[0]

console.log(data[0])

res.render('editbook', { book })
})
})

app.post('/books/updatebook', function (req, res) {
const id = req.body.id
const title = req.body.title
const pagesn = req.body.pagesn

const query = `UPDATE books SET title = '${title}', pagesn = ${pagesn} WHERE id = ${id}`

pool.query(query, function (err) {
if (err) {
console.log(err)
}

res.redirect(`/books/edit/${id}`)
})
})

app.post('/books/remove/:id', function (req, res) {
const id = req.params.id

const query = `DELETE FROM books WHERE id = ${id}`

pool.query(query, function (err) {
if (err) {
console.log(err)
}

res.redirect(`/books`)
})
})

app.listen(8080)

db/conn.js
const mysql = require('mysql')

const pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
user: 'root',
password: 'zO2*2D6.4sn;F9#9>e}*E!',
database: 'nodemysql',
})

module.exports = pool


Query Inpect


É uma forma de preparar a query (requisição) que será enviada para o Mysql, dessa forma, podemos nos defender de Mysql Injection. Deve ser usada sempre que for para produção e é bom até em test.


Vamos analisar um trecho de código como estavamos usando:

index.js
app.post('/books/insertbook', function (req, res) {
const title = req.body.title
const pagesn = req.body.pagesn
// pagesn e title que estão no json body, são definidos no home.handlebars

const query = `INSERT INTO books (title, pagesn) VALUES ('${title}', ${pagesn})`

pool.query(query, function (err) {
if (err) {
console.log(err)
}

res.redirect('/')
})
})

Agora podemos usar ?, como abaixo:

index.js
app.post('/books/insertbook', function (req, res) {
const title = req.body.title
const pagesn = req.body.pagesn
// pagesn e title que estão no json body, são definidos no home.handlebars

const sql = `INSERT INTO books (??, ??) VALUES (?, ?)`
const data = ['title', 'pageqty', title, pagesn]

pool.query(sql, data, function (err) {
if (err) {
console.log(err)
}

res.redirect('/')
})
})

Para dados usamos dois simbolos de interrogação (??), para colunas, usamos apenas um único simbolo (?).