aguarde...

10 de agosto de 2019

Crie e implemente uma API REST do Node.js, Express e PostgreSQL para Heroku

Crie e implemente uma API REST do Node.js, Express e PostgreSQL para Heroku

Recentemente, eu quis criar e hospedar um servidor Node, e descobri que o Heroku é um excelente serviço de plataforma de nuvem que tem hospedagem gratuita de passatempo para Node e PostgreSQL, entre muitos outros idiomas e bancos de dados.

Este tutorial aborda a criação de uma API REST local com o Node usando um servidor Express e um banco de dados PostgreSQL. Também lista as instruções para implantar no Heroku.

[block]1[/block]Pré-requisitos

Este guia usa instruções de instalação para o macOS e supõe um conhecimento prévio de:

[block]2[/block]Objetivos

Vamos criar uma API REST simples e local em Node.js que é executada em um servidor Express e utiliza o PostgreSQL para um banco de dados. Então vamos implantá-lo para Heroku.

Eu também tenho algumas dicas de produção para validação e limitação de taxa.

[block]3[/block]Configurar banco de dados PostgreSQL

Estava indo para:

  • Instalar o PostgreSQL
  • Crie um usuário
  • Crie um banco de dados, tabela e entrada para a tabela

Essa será uma solução muito rápida – se for a primeira vez que você usa o PostgreSQL ou o Express, recomendo ler Configurando uma API RESTful com Node.js e PostgreSQL .

Instale e inicie o PostgreSQL.

brew install postgresql
brew services start postgresql

Login para postgres.

psql postgres

Crie um usuário e senha e dê a eles acesso ao banco de dados.

CREATE ROLE api_user WITH LOGIN PASSWORD 'password';
ALTER ROLE api_user CREATEDB;

Efetue logout do usuário root e efetue login no usuário recém-criado.

\q
psql -d postgres -U api_user

Crie um books_apibanco de dados e conecte-se a ele.

CREATE DATABASE books_api;
\c books_api

Criar uma bookstabela com IDauthortitle.

CREATE TABLE books (
  ID SERIAL PRIMARY KEY,
  author VARCHAR(255) NOT NULL,
  title VARCHAR(255) NOT NULL
);

Insira uma entrada na nova tabela.

INSERT INTO books (author, title)
VALUES  ('J.K. Rowling', 'Harry Potter')

[block]4[/block]Criar API expressa

A API Express configurará um servidor expresso e roteará para dois pontos de extremidade GETPOST.

Crie os seguintes arquivos:

  • .env – arquivo contendo variáveis ​​de ambiente (não é controlado por versão)
  • package.json – informações sobre o projeto e dependências
  • init.sql – arquivo para inicializar a tabela do PostgreSQL
  • config.js – criará a conexão do banco de dados
  • index.js – o servidor expresso
touch .env package.json init.sql config.js index.js

[block]5[/block]Variáveis ​​ambientais

Defina seu nome de banco de dados, senha, host, porta e banco de dados..env

DB_USER=api_user
DB_PASSWORD=password
DB_HOST=localhost
DB_PORT=5432
DB_DATABASE=books_api

[block]6[/block]Inicialização do banco de dados

Crie um arquivo para inicializar a tabela com uma entrada. Nós vamos usar isso para o banco de dados Heroku.init.sql

CREATE TABLE books (
  ID SERIAL PRIMARY KEY,
  author VARCHAR(255) NOT NULL,
  title VARCHAR(255) NOT NULL
);

INSERT INTO books (author, title)
VALUES  ('J.K. Rowling', 'Harry Potter')

[block]7[/block]Configure a conexão do PostgreSQL

Use o pacote node-postgres para criar um Pool , que será usado para fazer consultas ao banco de dados.

Crie uma string de conexão que siga o padrão de postgresql://USER:PASSWORD@HOST:PORT/DATABASE. Vou usar as variáveis ​​de ambiente do .envuso process.env.VARIABLE. Inicializando com require('dotenv').config()permitirá que você use essas variáveis ​​de ambiente.

Eu também criei uma inProductionstring – em um ambiente como o HerokuNODE_ENVserá configurado para productionque você possa ter um comportamento diferente entre os ambientes. Heroku nos fornecerá uma string chamada DATABASE_URLconnectionString, então não teremos que construir uma nova.config.js

require('dotenv').config()

const { Pool } = require('pg')
const isProduction = process.env.NODE_ENV === 'production'

const connectionString = `postgresql://${process.env.DB_USER}:${process.env.DB_PASS}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_DATABASE}`

const pool = new Pool({
  connectionString: isProduction ? process.env.DATABASE_URL : connectionString,
  ssl: isProduction,
})

Configure o servidor expresso. Configurar uma API RESTful com Node.js e PostgreSQL vai entrar em muito mais detalhes sobre este processo, e ir através da criação de todos os endpoints CRUD mais importantes – GETPOSTPUT, e DELETE. Eu propositadamente fiz este exemplo muito simples apenas para obter um produto viável mínimo em funcionamento.

[block]10[/block]Configurar o servidor expresso

index.js

const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const { pool } = require('./config')

const app = express()

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.use(cors())

const getBooks = (request, response) => {
  pool.query('SELECT * FROM books', (error, results) => {
    if (error) {
      throw error
    }
    response.status(200).json(results.rows)
  })
}

const addBook = (request, response) => {
  const { author, title } = request.body

  pool.query('INSERT INTO books (author, title) VALUES ($1, $2)', [author, title], error => {
    if (error) {
      throw error
    }
    response.status(201).json({ status: 'success', message: 'Book added.' })
  })
}

app
  .route('/books')
  // GET endpoint
  .get(getBooks)
  // POST endpoint
  .post(addBook)

// Start server
app.listen(process.env.PORT || 3002, () => {
  console.log(`Server listening`)
})

[block]11[/block]Dependências

package.jsonarquivo listará suas dependências / devDependencies e outras informações.

  • express – estrutura do servidor da web
  • pg – cliente PostgreSQL para Node
  • dotenv – permite que você carregue variáveis ​​de ambiente do .envarquivo
  • cors – ativar o CORS

Também instalaremos o nodemon para desenvolvimento, que reinicia automaticamente o servidor toda vez que você fizer uma alteração.

Não esqueça de incluir a enginespropriedade para a versão do Node.package.json

{
  "name": "books-api",
  "version": "1.0.0",
  "private": true,
  "description": "Books API",
  "main": "index.js",
  "engines": {
    "node": "11.x"
  },
  "scripts": {
    "start": "node index.js",
    "start:dev": "nodemon index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}

Agora você só precisa instalar todas as dependências.

npm i cors dotenv express pg
npm i -D nodemon

Tudo está configurado, assim você pode executar npm start, iniciar o servidor uma vez ou npm run start:devreiniciar o servidor após cada alteração.

npm start

Você pode testar a API fazendo uma ligação para o Postman:

Crie e implemente uma API REST do Node.js, Express e PostgreSQL para Heroku

Ou usando cURL.

curl http://localhost:3002/books
# [{"id":1,"author":"J.K. Rowling","title":"Harry Potter"}]

Não sabe usar o carteiro ou o cURL? Leia Fazendo solicitações de API com o Postman ou o cURL .

[block]19[/block]Implante o aplicativo no Heroku

Agora, temos tudo funcionando localmente, para que possamos criar uma conta Heroku, instalar o Heroku CLI e configurar o servidor de aplicativos e o servidor de banco de dados.

[block]20[/block]Configurar o Heroku CLI e o aplicativo

Vá para Heroku e crie uma conta.

Instale o Heroku CLI.

brew install heroku/brew/heroku

Entre para o Heroku CLI. Isso abrirá uma janela do navegador, que você pode usar para efetuar login.

heroku login

Criar aplicativo

# this can be whatever you want, but must be unique
heroku create example-node-api
Creating app... done, ⬢ example-node-api
https://.herokuapp.com/ | https://git.heroku.com/.git

Se você não passar um nome, ele criará um nome aleatório para você.

heroku create # generates random name

[block]22[/block]Configurar o Heroku Postgres

Vá até Heroku Add-ons e selecione Heroku Postgres . Clique em “Instalar o Heroku Postgres”. Clique em “Aplicar ao aplicativo”.

Pode levar até 5 minutos para se propagar. Depois que o tempo passar, verifique se o seu add-on existe via Heroku CLI.

heroku addons
example-node-api  postgresql-whatever-00000  heroku-postgresql:hobby-dev  free   created

Você verá sua nova instância do PostgreSQL como algum nome gerado automaticamente postgresql-whatever-00000.

Faça o login na instância do Heroku PostgreSQL.

heroku pg:psql postgresql-whatever-00000 --app example-node-api

A partir da raiz do projeto onde você está init.sql, execute o seguinte comando para criar sua tabela e entradas no Heroku Postgres.

cat init.sql | heroku pg:psql postgresql-whatever-00000 --app example-node-api

[block]25[/block]Teste e implante

Neste ponto, tudo deve ser configurado e pronto para ir para Heroku. Você pode testar isso executando o seguinte comando:

heroku local web

Com isso, você pode http://localhost:5000/booksver como será seu aplicativo no Heroku.

Se tudo parece bom, adicione, envie e empurre para Heroku.

git add .
git commit -m "init"
git push heroku master
-----> Launching... done
       http://.herokuapp.com deployed to Heroku

[block]26[/block]Dicas de Produção

Vou listar algumas dicas para ajudar a tornar sua API do Node um pouco mais segura e eficiente na produção.

Vamos trazer todas as novas dependências.index.js

const helmet = require('helmet')
const compression = require('compression')
const rateLimit = require('express-rate-limit')
const { body, check } = require('express-validator')

[block]31[/block]Cabeçalhos HTTP e compactação

helmetcompressionnão requer configuração adicional – eles apenas adicionarão alguma segurança útil e segurança de cabeçalho HTTP.

const app = express()

...

app.use(compression())
app.use(helmet())

[block]32[/block]Protegendo o CORS

Vamos adicionar algumas opções à biblioteca de cors . Quando usamos cors(), disponibilizamos o aplicativo para uso em qualquer navegador. Isso é bom para o desenvolvimento, porque estaremos usando localhost, mas na produção, só queremos que nosso aplicativo seja acessado por meio de nosso próprio domínio.index.js

const isProduction = process.env.NODE_ENV === 'production'
const origin = {
  origin: isProduction ? 'https://www.example.com' : '*',
}

app.use(cors(origin))

Não que a proteção CORS se aplique somente aos navegadores – ela não protege seu aplicativo de ser acessado por meio de cURL e Postman.

[block]34[/block]Limitação de taxa

Para ajudar a proteger contra ataques de força bruta / DDoS, podemos limitar a quantidade de solicitações usando o limite de taxa expressa . O windowMsdetermina a quantidade de tempo e maxdetermina quantas solicitações. Aqui eu posso dizer que qualquer cliente só pode acessar qualquer endpoint 5 vezes em 1 minuto.index.js

const limiter = rateLimit({
  windowMs: 1 * 60 * 1000, // 1 minute
  max: 5, // 5 requests,
})

app.use(limiter)

Com app.use()isso, ele será aplicado a todos os endpoints, mas também podemos tornar certos endpoints mais restritos a outro rateLimit.

const postLimiter = rateLimit({
  windowMs: 1 * 60 * 1000
  max: 1,
})

app.post('/books', postLimiter, addBook)

[block]36[/block]Validação

Se alguém conseguir enviar dados inválidos para o banco de dados PostgreSQL, o aplicativo poderá travar – por exemplo, se mais de 255 caracteres forem enviados para uma VARCHAR(255)entrada do banco de dados. Podemos usar o express-validator para garantir que qualquer solicitação recebida seja válida, caso contrário, exibir um erro.

app.post(
  '/books',
  [
    check('author')
      .not()
      .isEmpty()
      .isLength({ min: 5, max: 255 })
      .trim(),
    check('title')
      .not()
      .isEmpty()
      .isLength({ min: 5, max: 255 })
      .trim(),
  ],
  postLimiter,
  (request, response) => {
    const errors = validationResult(request)

    if (!errors.isEmpty()) {
      return response.status(422).json({ errors: errors.array() })
    }

    const { author, title } = request.body

    pool.query('INSERT INTO books (author, title) VALUES ($1, $2)', [author, title], error => {
      if (error) {
        throw error
      }
      response.status(201).json({ status: 'success', message: 'Book added.' })
    })
  }
)

[block]38[/block]Chave API

Para proteger seu aplicativo, convém usar JSON Web Tokens (JWTs) para criar um token de acesso / atualização que você enviaria como um cabeçalho para a API. A implementação de JWTs é um pouco mais de um processo envolvido, mas a maneira mais simples de restringir o tráfego a um terminal é usar uma chave de API. Isso pode ser tão simples quanto um par chave / valor de cabeçalho e valor.

Em Heroku, podemos definir uma variável de ambiente com heroku config:set.

heroku config:set API_KEY=hunter2

Se a solicitação para o nó de extremidade não contiver o cabeçalho adequado, ele poderá retornar um erro não autorizado.

const deleteBook = (request, response) => {
  if (!request.header('apiKey') || request.header('apiKey') !== process.env.API_KEY) {
    return response.status(401).json({ status: 'error', message: 'Unauthorized.' })
  }
  // ...
}

Com esse código, seria necessário definir cabeçalhos com a solicitação por meio de Postman ou cURL.

curl -X DELETE \
  https://.herokuapp.com/books/1 \
  -H 'Content-Type: application/json' \
  -H 'ApiKey: hunter2'

[block]40[/block]Usando a API no front end

Se você não estiver familiarizado com a forma de trabalhar com uma API no front end, leia Como se conectar a uma API com JavaScript . Como uma revisão rápida, veja como você pode usar a API de busca integrada para fazer GETe as POSTsolicitações para os pontos de extremidade.

[block]41[/block]Pegue

try {
  const response = await fetch('https://.herokuapp.com/books')
  const books = await response.json()

  console.log(books)
} catch (error) {
  console.log(error)
}

[block]42[/block]Postar

const newBook = {
  title: 'Game of Thrones',
  author: 'George R. R. Martin',
}

try {
  const response = await fetch('https://.herokuapp.com/books', {
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    },
    method: 'post',
    body: JSON.stringify(newBook),
  })
} catch (error) {
  console.log(error)
}

[block]43[/block]Conclusão

Estas são todas as etapas necessárias para configurar um servidor de API REST do Node, Express e PostgreSQL muito básico e implementá-lo no Heroku. Se alguma coisa sobre o aplicativo não faz sentido, eu recomendo a leitura, eu recomendo a leitura Configurando uma API RESTful com Node.js e PostgreSQL 

Posted in Blog
Write a comment