Artigo original: How to Deploy a MERN Application to Heroku Using MongoDB Atlas

Introdução ao MERN

Neste artigo, construiremos e implantaremos uma aplicação criada com a stack MERN no Heroku.

MERN, que significa MongoDB, Express, React e Node.js, é uma stack (literalmente, em português, pilha – mas que podemos traduzir como "conjunto de tecnologias") popular usada na construção de aplicações para a web. Ela envolve trabalho de front-end (com React), trabalho de back-end (com Express e NodeJS) e um banco de dados (com MongoDB).

O Heroku, por outro lado, é uma plataforma como serviço (PaaS) que permite aos desenvolvedores criar, executar e operar aplicações inteiramente na nuvem.

Nota da tradução: até pouco tempo atrás, o Heroku dispunha de um espaço gratuito para essas implementações. Hoje em dia, mesmo a camada mais básica da plataforma vem com um custo.

Para o banco de dados, usaremos o MongoDB Atlas, que é um serviço global de banco de dados em nuvem para aplicações modernas. É mais seguro do que ter o MongoDB instalado localmente em nosso servidor e também nos dá espaço para mais recursos em nossos servidores.

Para o front-end, construiremos uma aplicação em React simples, que faz solicitações POST a uma API para adicionar um usuário, também podendo fazer solicitações GET para obter todos os usuários.

Você pode pular para qualquer etapa com o índice listado abaixo.

Índice

  • Introdução ao MERN
  • Vamos começar a criar
  • Construindo a aplicação em React
  • Criando o back-end
  • Conectando o banco de dados do MongoDB Atlas
  • Chamando APIs no front-end
  • Implantando no Heroku
  • Criando uma aplicação no Heroku
  • Configurando o package.json
  • Resumo

Vamos começar a criar

Construindo a aplicação em React

Nota: antes de começarmos nosso projeto, o node deve estar instalado em seu computador. O node também nos fornece o npm, que é usado para instalar pacotes.

Instalando o create-react-app

O create-react-app é usado para criar uma aplicação inicial em React.

Nota da tradução: mais recentemente, surgiram alternativas ao create-react-app que consomem menos espaço e são mais práticas para a criação de aplicações em React, como o Vite. O create-react-app, no entanto, segue sendo uma boa alternativa em termos didáticos para quem está começando a aprender.

Se você não tiver o create-react-app instalado, digite o seguinte na linha de comando:

npm i create-react-app -g

O parâmetro -g instala o pacote globalmente.

Criando o diretório do projeto

create-react-app my-project
cd my-project

O primeiro comando acima cria um diretório "my-project" e instala as dependências que serão usadas na aplicação inicial em React. Após terminar a instalação, o segundo comando muda para o diretório do projeto.

Iniciando a aplicação e fazendo as edições necessárias

npm start

O comando acima inicia a aplicação React, que fornece um URL onde você visualiza o projeto. Você pode, então, fazer as edições necessárias, como alterar imagens ou texto.

Instalando o axios

npm i axios --save

O axios é uma biblioteca do JavaScript usada para facilitar requisições HTTP. Ele será usado para enviar requisições do front-end (React) para as APIs fornecidas pelo back-end.

Criando o back-end

O back-end gerencia as APIs, lida com requisições e também se conecta ao banco de dados.

Instalando os pacotes de back-end

npm i express cors mongoose body-parser --save
  1. express: "O Express é uma infraestrutura para aplicações da web em Node.js mínima e flexível, que fornece um conjunto robusto de recursos para essas aplicações para a web" – documentação do Express (em inglês)
  2. cors: "O CORS é um pacote do Node.js para fornecer um middleware para Connect/Express que pode ser usado para habilitar o CORS com várias opções" – documentação do CORS (em inglês)
  3. mongoose: "O Mongoose é uma ferramenta de modelagem de objetos para MongoDB projetada para funcionar em um ambiente assíncrono. O Mongoose suporta promises e funções de callback (retornos de chamada)" – documentação do Mongoose
  4. body-parser: "O middleware de análise (e transformação dos dados) do body para o Node.js." – documentação do body-parser (em inglês)
Nota da tradução: body-parser foi descontinuado. A análise e a transformação dos dados, agora, pode ser feita diretamente pelo Express.

Criando a pasta de back-end

mkdir backend
cd backend

Configurando o back-end

Criando um ponto de entrada (entry point) server.js

Primeiro, crie um arquivo server.js, que será o ponto de entrada para o back-end.

touch server.js

No arquivo server.js, digite o seguinte:

const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const path = require('path')
const app = express();
require('./database');
-----
app.use(bodyParser.json());
app.use(cors());
-----
// API
const users = require('/api/users');
app.use('/api/users', users);
-----
app.use(express.static(path.join(__dirname, '../build')))
app.get('*', (req, res) => {
    res.sendFile(path.join(__dirname, '../build'))
})
-----
const port = process.env.PORT || 5000;
app.listen(port, () => {
    console.log(`Server started on port ${port}`);
});

express.static fornece arquivos estáticos que são aqueles construídos quando npm run build é executado em um projeto do React. Lembre-se de que o arquivo compilado está na pasta build.

A partir da nossa configuração, qualquer requisição enviada para /api/users será enviada para a API users que estamos prestes a configurar.

Configurando a API users

mkdir api
touch api/users.js

No arquivo api/users.js, adicione o seguinte:

const express = require('express');
const router = express.Router()
-----
const User = require('../models/User');
-----
router.get('/', (req, res) => {
    User.find()
        .then(users => res.json(users))
        .catch(err => console.log(err))
})
-----
router.post('/', (req, res) => {
    const { name, email } = req.body;
    const newUser = new User({
        name: name, email: email
    })
    newUser.save()
        .then(() => res.json({
            message: "Created account successfully"
        }))
        .catch(err => res.status(400).json({
            "error": err,
            "message": "Error creating account"
        }))
})
module.exports = router 

No código acima, criamos um manipulador de requisições GET e POST, que busca todos os usuários e os publica. Buscar e adicionar um usuário ao banco de dados é auxiliado pelo modelo User que criaremos.

Criando o modelo User

mkdir models
touch models/user.js

No arquivo models/user.js, adicione o seguinte:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
-----
const userSchema = new Schema({
    name: {
        type: String,
        required: true
    },
    email: {
        type: String,
        required: true
    }
})
module.exports = mongoose.model("User", userSchema, "users")

No código acima, é criado um esquema para o usuário que contém os campos do usuário. Ao final do arquivo, o modelo ("User") é exportado com o esquema e a coleção ("users").

Conectando o banco de dados do MongoDB Atlas

De acordo com a documentação (em inglês), "o MongoDB Atlas é o serviço global de banco de dados em nuvem para aplicações modernas".

Primeiro, precisamos nos registrar no MongoDB Cloud. Consulte esta documentação (em inglês) para criar uma conta do Atlas e criar seu cluster.

Uma coisa que vale a pena observar é colocar o endereço IP de sua conexão na lista de permissões. Se você ignorar essa etapa, não terá acesso ao cluster, portanto preste atenção nessa parte.

O cluster é um pequeno servidor que gerenciará nossas coleções (semelhante às tabelas em bancos de dados do SQL). Para conectar seu back-end ao cluster, crie um arquivo database.js, que, como você pode ver, é necessário em server.js. Em seguida, digite o seguinte:

const mongoose = require('mongoose');
const connection = "mongodb+srv://username:<password>@<cluster>/<database>?retryWrites=true&w=majority";
mongoose.connect(connection,{ useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false})
    .then(() => console.log("Database Connected Successfully"))
    .catch(err => console.log(err));

Na variável de conexão, insira seu nome de usuário (para o MongoDB Cloud), sua senha (senha do cluster), seu cluster (endereço do seu cluster) e o banco de dados (nome do seu banco de dados). Tudo isso pode ser facilmente descoberto se você seguir a documentação.

Chamando APIs no front-end

Todas as APIs estarão disponíveis localmente em localhost:5000, assim como configuramos no server.js. Quando implantado no Heroku, o servidor usará a porta fornecida pelo servidor (process.env.PORT).

Para facilitar as coisas, o React nos permite especificar um proxy para o qual as solicitações serão enviadas.

Abra o package.json e, logo antes da última chave, adicione o seguinte:

"proxy": "http://localhost:5000"

Desse modo, podemos enviar solicitações diretamente para api/users. Quando nosso site for implantado e criado, a porta padrão da nossa aplicação será usada com a mesma API.

Abra App.js para o React e adicione o seguinte:

import React, {useState, useEffect} from 'react'
import axios from 'axios';
-----
const App = function () {
	const [users, setUsers] = useState(null);

	const [username, setUsername] = useState("");
	const [email, setEmail] = useState("");
	useEffect(() => {
		axios
			.get("/api/users")
			.then((users) => setUsers(users))
			.catch((err) => console.log(err));
	}, []);

	function submitForm() {
		if (username === "") {
			alert("Please fill the username field");
			return;
		}
		if (email === "") {
			alert("Please fill the email field");
			return;
		}
		axios
			.post("/api/users", {
				username: username,
				email: email,
			})
			.then(function () {
				alert("Account created successfully");
				window.location.reload();
			})
			.catch(function () {
				alert("Could not creat account. Please try again");
			});
	}
	return (
		<>
			<h1>My Project</h1>
			{users === null ? (
				<p>Loading...</p>
			) : users.length === 0 ? (
				<p>No user available</p>
			) : (
				<>
					<h2>Available Users</h2>
					<ol>
						{users.map((user, index) => (
							<li key={index}>
								Name: {user.name} - Email: {user.email}
							</li>
						))}
					</ol>
				</>
			)}

			<form onSubmit={submitForm}>
				<input
					onChange={(e) => setUsername(e.target.value)}
					type="text"
					placeholder="Enter your username"
				/>
				<input
					onChange={(e) => setEmail(e.target.value)}
					type="text"
					placeholder="Enter your email address"
				/>
				<input type="submit" />
			</form>
		</>
	);
};
export default App

Os hooks useState e useEffect são usados ​​para manipular estados e efeitos colaterais (sideEffects). O que basicamente está acontecendo é que o primeiro estado dos usuários é null e 'Loading...' (carregando) é mostrado no navegador.

No useEffect[], [] é usado para especificar que, no estágio componentDidMount (quando o componente é montado), deve-se fazer uma solicitação do Axios para a API, que está sendo executada em localhost:5000. Se obtiver o resultado e não houver nenhum usuário, "No user available" (nenhum usuário disponível) será exibido. Caso contrário, será exibida uma lista numerada dos usuários.

Se você quiser saber mais sobre useState e useEffect, confira este artigo – O que diabos são React Hooks? (em inglês)

Implantando no Heroku

Para implantar sua aplicação no Heroku, você deve ter uma conta por lá.

Vá para a página do Heroku para criar uma conta. Em seguida, consulte a documentação sobre como criar uma aplicação no Heroku. Verifique também a documentação do Heroku CLI. (links em inglês)

Nota da tradução: reforçando, o Heroku não é mais gratuito. Caso seja sua intenção dar sequência a esse processo por lá, saiba que mesmo as alternativas iniciais agora são pagas.

Criando uma aplicação no Heroku

Primeiro, faça login no Heroku:

heroku login

Isso vai redirecionar você para um URL no navegador onde você poderá fazer login. Quando terminar, você poderá continuar no terminal.

No mesmo diretório do projeto em React, execute o seguinte:

heroku create

Isso criará uma aplicação no Heroku e também fornecerá o URL para acessar o aplicativo.

Configurando o package.json

O Heroku usa seu arquivo package.json para saber quais scripts executar e quais dependências instalar para que seu projeto seja executado com sucesso.

No seu arquivo package.json, adicione o seguinte:

{
    ...
    "scripts": {
        ...
        "start": "node backend/server.js",
        "heroku-postbuild": "NPM_CONFIG_PRODUCTION=false npm install npm && run build"
    },
    ...
    "engines": {
        "node": "10.16.0"
    }
}

O Heroku executa uma pós-compilação que, como você poderá ver, instalará suas dependências e executará uma compilação do seu projeto em React. Em seguida, ele iniciará seu projeto com o script start que, basicamente, inicia seu servidor. Depois disso, seu projeto deverá funcionar bem.

engines especifica as versões das engines, como node e npm, a serem instaladas.

Enviando para o Heroku

git push heroku master

O comando acima envia seu código para o Heroku. Lembre-se de incluir arquivos desnecessários em .gitignore.

Após alguns segundos, seu site estará pronto. Se houver algum erro, você pode verificar seu terminal ou acessar seu painel no navegador para visualizar os logs (registros) de criação (da build).

Agora, você pode visualizar seu site no URL que o Heroku enviou quando você executou heroku create.

Isso é tudo que há para fazer. Que bom que você chegou até aqui.

Resumo

É claro que há mais sobre aplicações da stack MERN.

Este artigo não se aprofundou em autenticações, login, sessões e tudo mais. Abordou apenas como implantar aplicações da stack MERN no Heroku e como trabalhar com MongoDB Atlas.

Você pode encontrar outros artigos como este no blog do autor: dillionmegida.com

Obrigado pela leitura.