Construindo Aplicações Web com a Poderosa Stack MERN
Tags: mongodb, express, react, node
Published: 2023-08-24 16:47:24


_Guia Passo a Passo da Stack MERN: Do Início ao Uso em Projetos Reais_

A Stack MERN é um conjunto de tecnologias amplamente utilizadas para desenvolver aplicações web modernas e eficientes. Nada mais é do que um acrônimo composta por essas 4 tecnologias: [MongoDB](https://www.mongodb.com/pt-br), [Express.js](https://expressjs.com/pt-br/), [React](https://react.dev/) e [Node.js](https://nodejs.org/pt-br), essa stack oferece uma abordagem completa, para a criação de aplicações full-stack. Neste artigo, exploraremos cada componente da stack MERN, suas características distintas e os prós e contras de estudá-la seguindo minha percepção, que tive ao usa-lá, logicamente como sempre falo, cada um pode ter suas próprias vantagens ou desvantagens, além de deixar um exemplo de caso de uso real de uma aplicação web utilizando dessa stack.

Essa Stack ela é dividida da seguinte maneira, o React fica responsável por toda a parte no lado do cliente, o front-end da aplicação, deixando o Express.js e o Node.js na parte do servidor, juntamente com o MongoDB no back-end, onde é usado como o banco de dados da aplicação.

Existe outra stack muito similar a essa, que é a MEAN, fazendo basicamente a mesma coisa, porém no lugar do React, se utiliza do [Angular](https://angular.io/) para gerir a parte do front-end da aplicação.

Eu vou falar um pouco de cada uma aqui de forma resumida, trazendo alguns pontos fortes e fracos de se usar cada tecnologia, pretendo depois trazer um artigo a parte, falando detalhadamente sobre cada tecnologia em questão, para não alongar muito aqui.


#### [1. MongoDB](https://www.mongodb.com/pt-br)
O MongoDB é um banco de dados NoSQL orientado a documentos, que oferece flexibilidade e escalabilidade para as aplicações.

_Vantagens:_

- Flexibilidade de Esquema: Os documentos são armazenados em formato JSON-like (BSON), permitindo que os esquemas se adaptem às mudanças ao longo do desenvolvimento.
- Escalabilidade Horizontal: O MongoDB suporta a distribuição de dados em clusters, facilitando o aumento de capacidade à medida que o tráfego aumenta.
- Desenvolvimento Rápido: A ausência de esquema rígido acelera o processo de desenvolvimento, permitindo iterações mais ágeis.

_Desvantagens:_

- Complexidade de Modelagem: A ausência de um esquema definido pode resultar em modelos de dados mal estruturados, exigindo uma abordagem cuidadosa na modelagem.
- Menos Adequado para Dados Relacionais: Aplicações que dependem fortemente de relações complexas entre os dados podem encontrar desafios ao usar um banco de dados NoSQL, como é o caso do MongoDB.


#### [2. Express.js](https://expressjs.com/pt-br/)
O Express.js é um framework para criação de aplicativos web em Node.js. Ele simplifica a criação de APIs e o manuseio de rotas e requisições.

_Vantagens:_

- Simplicidade: O Express oferece uma abordagem minimalista, permitindo aos desenvolvedores criar rapidamente endpoints e configurar middleware.
- Ampla Comunidade e Documentação: Sendo um dos frameworks mais populares para Node.js, o Express tem uma comunidade ativa e vasta documentação.

_Desvantagens:_

- Estrutura Flexível: A falta de estrutura rígida pode levar a inconsistências na organização do código, especialmente em projetos maiores.

#### [3. React](https://react.dev/)

O React é uma biblioteca JavaScript desenvolvida pelo Facebook para construir interfaces de usuário interativas e reativas, no lado do cliente.

_Vantagens:_

- Componentização: O React promove a criação de componentes reutilizáveis, agilizando o desenvolvimento e a manutenção.
- Renderização Eficiente: Utilizando o conceito de Virtual DOM, o React atualiza apenas as partes necessárias da interface, melhorando o desempenho.

_Desvantagens:_

- Curva de Aprendizado Inicial: Para desenvolvedores novos no conceito de componentes e JSX, pode haver uma curva de aprendizado inicial.

#### [4. Node.js](https://nodejs.org/pt-br)

O Node.js é um ambiente de execução JavaScript do lado do servidor. Ele permite que os desenvolvedores construam aplicativos web escaláveis e em tempo real.

_Vantagens:_

- JavaScript Unificado: Usar a mesma linguagem (JavaScript) em todo o stack simplifica a comunicação e a colaboração entre os desenvolvedores.
- Não Bloqueante: O modelo de E/S não bloqueante do Node.js permite um alto desempenho e escalabilidade em aplicações que dependem de muitas operações de I/O.

_Desvantagens:_

- Gerenciamento de Eventos: O modelo baseado em eventos pode tornar o código complexo em casos de fluxos de controle complexos.

Essas foram algumas vantagens e desvantagens que encontrei ao longo dos anos utilizando dessa stack, teria muitas outras, mas é assunto para um próximo artigo. Sinta-se a vontade para concordar ou discordar sobre os pontos que citei até aqui.


## Vantagens de Estudar a Stack MERN

1. Tecnologias Modernas: A stack MERN é composta por tecnologias atualizadas e amplamente adotadas na indústria.
2. Flexibilidade: A flexibilidade das tecnologias permite adaptar-se a diferentes tipos de projetos e requisitos.
3. Comunidades Ativas: Cada componente da stack possui uma comunidade ativa, facilitando a obtenção de suporte e aprendizado contínuo.
4. Linguagem Unificada: Você pode escrever tudo com Javascript, sem a necessidade de ter a curva de aprendizado de utilizar outra linguagem em alguma tecnologia do acrônimo.

## Desvantagens de Estudar a Stack MERN

1. Curva de Aprendizado: Cada tecnologia possui sua própria curva de aprendizado, o que pode ser desafiador para desenvolvedores iniciantes.
2. Complexidade: A integração de quatro tecnologias diferentes pode aumentar a complexidade do desenvolvimento e da depuração.
3. Tecnologias em Evolução: As tecnologias podem evoluir rapidamente, exigindo que os desenvolvedores estejam atualizados constantemente.

Apontando aqui os prós e contras dessa Stack que achei, vou dar um exemplo prático de caso de uso de como utilizar ela em um projeto real.

## Utilizando a Stack MERN em um projeto real
Para este artigo, vou dar um exemplo simples de uma aplicação real, que será uma espécie de To Do List, onde o usuário consegue adicionar suas tarefas(To Do) e também atualizar caso tenha necessidade e posteriormente excluí-las, esses To Dos então são mandados para nossa API onde persistirá no nosso banco de Dados em questão.

#### Front-end da Aplicação
Vamos começar criando nossa aplicação no front-end, do lado do cliente utilizando do React, para isso execute o seguinte comando:

`npx create-react-app todo_list — template typescript`

Após isso vamos utilizar de algumas libs que vão facilitar a criação do nosso projeto como o [Axios](https://axios-http.com/), na hora de fazer nossa chamada a nossa API, e para estilizar eu não vou focar muito nisso, então optei por utilizar os componentes já prontos do [MaterialUI](https://mui.com/).

`npm install @mui/material axios @mui/icons-material — save`

Optei por usar o [CRA](https://create-react-app.dev/) afins de já vi os recursos prontos para startar nossa aplicação, porém vamos refatorar, já que tem muita coisa que não vamos usar nela, e deixar somente o que importa, ao fim de tudo isso a estrutura do seu projeto ficará algo assim.


![Arquitetura React front-end](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9gcw58xcv6815gekdnp1.png)

Como estou utilizando o [TypeScript](https://www.typescriptlang.org/) no projeto, tive que criar uma pasta chamada types para poder tipar manualmente a biblioteca de icons do material que até o momento deste artigo não estava tipada nativamente, então é só criar uma pasta com o nome de _types_ dentro de _src_, e dentro dela crie um arquivo chamado _mui-icons-material.d.ts_ e adicione o seguinte trecho.

```typescript
declare module '@mui/icons-material/Delete' {
    import { SvgIconProps } from '@mui/material/SvgIcon';
  
    const DeleteIcon: (props: SvgIconProps) => JSX.Element;
    export default DeleteIcon;
  }
```

Feito isso, o TypeScript vai parar de dar erro de tipagens, futuramente ao chamar essa lib no código.

Antes mesmo de criar nosso back-end utilizando das outras tecnologias da stack MERN, vamos já deixar nosso front-end preparado, para isso crie uma pasta chamada _server_ no _src_ da aplicação, e dentro dela crie um arquivo chamado _api.tsx_, dentro desse arquivo você fará a seguinte chamada a nossa API, para nossas rotas via Axios.

```typescript
import axios, { AxiosResponse } from "axios";

const baseUrl = "http://localhost:3001/toDos";

const api = axios.create({ baseURL: baseUrl });

export interface ToDo {
  _id: string;
  text: string;
  completed: boolean;
}

export const fetchToDos = async (): Promise<ToDo[]> => {
  try {
    const response: AxiosResponse<ToDo[]> = await api.get(baseUrl);
    return response.data;
  } catch (error) {
    console.error("Error fetching toDos:", error);
    throw error;
  }
};

export const addToDo = async (task: {
  text: string;
  completed: boolean;
}): Promise<void> => {
  try {
    await api.post(baseUrl, task);
  } catch (error) {
    console.error("Error adding toDo:", error);
    throw error;
  }
};

export const toggleToDo = async (
  toDoId: string,
  completed: boolean
): Promise<void> => {
  try {
    await api.put(`${baseUrl}/${toDoId}`, { completed: !completed });
  } catch (error) {
    console.error("Error toggling toDo:", error);
    throw error;
  }
};

export const deleteToDo = async (toDoId: string): Promise<void> => {
  try {
    await api.delete(`${baseUrl}/${toDoId}`);
  } catch (error) {
    console.error("Error deleting toDo:", error);
    throw error;
  }
};

```

Feito isto nossas futuras rotas já estará tudo configurada, chamadas via Axios na nossa aplicação e exportado todos os métodos do nosso CRUD, vamos agora importar ele no nosso arquivo principal _App_.

```typescript
import React, { useState, useEffect } from "react";
import {
  Container,
  TextField,
  Button,
  List,
  ListItem,
  ListItemText,
  Checkbox,
  IconButton,
} from "@mui/material";
import DeleteIcon from "@mui/icons-material/Delete";
import {
  ToDo,
  fetchToDos,
  addToDo,
  toggleToDo,
  deleteToDo,
} from "./server/api";

function App() {
  const [toDos, setToDos] = useState<ToDo[]>([]);
  const [toDoText, setToDoText] = useState<string>("");

  useEffect(() => {
    loadToDos();
  }, []);

  const loadToDos = async () => {
    const fetchedToDos = await fetchToDos();
    setToDos(fetchedToDos);
  };

  const handleAddToDo = async () => {
    if (toDoText.trim() === "") return;

    await addToDo({ text: toDoText, completed: false });
    setToDoText("");
    loadToDos();
  };

  const handleToggleToDo = async (toDoId: string, completed: boolean) => {
    await toggleToDo(toDoId, completed);
    loadToDos();
  };

  const handleDeleteToDo = async (toDoId: string) => {
    await deleteToDo(toDoId);
    loadToDos();
  };

  return (
    <Container>
      <h1>To Do List</h1>
      <TextField
        label="Adicionar To Do"
        variant="outlined"
        fullWidth
        value={toDoText}
        onChange={(e) => setToDoText(e.target.value)}
      />
      <Button variant="contained" onClick={handleAddToDo}>
        Adicionar
      </Button>
      <List>
        {toDos.map((toDo) => (
          <ListItem key={toDo._id}>
            <Checkbox
              checked={toDo.completed}
              onChange={() => handleToggleToDo(toDo._id, toDo.completed)}
            />
            <ListItemText primary={toDo.text} />
            <IconButton onClick={() => handleDeleteToDo(toDo._id)}>
              <DeleteIcon />
            </IconButton>
          </ListItem>
        ))}
      </List>
    </Container>
  );
}

export default App;
```

Pronto nosso front-end está finalizado por hora, dessa maneira você já consegue ver algo como o exemplo abaixo, porém ainda não tem como adicionar, pois as rotas ainda não foram criadas no nosso back-end, então vamos criá-las agora.


![Aplicação TO DO front-end](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ab8a2ipl6f4lnxgl078y.png)

#### Back-end da Aplicação
Para criar nosso back-end em NodeJS e Express.js vamos fazer o seguinte. Primeiro crie uma pasta chamada _todo_list_api_ e dentro dessa pasta rode o seguinte comando:

`npm init -y`

Isso irá criar nossa configuração do [package.json](https://docs.npmjs.com/cli/v9/configuring-npm/package-json), após isso rode o seguinte comando para instalar o express.js e o [mongoose](https://mongoosejs.com/), que é uma biblioteca de modelagem de objetos para Node.js, projetada para trabalhar com o MongoDB, isso vai facilitar em muito na hora de montar nossos schemas.

`npm install express mongoose`

Após isso também vamos instalar e configurar o TypeScript para trabalhar com ele.

`npm install typescript@latest — save-dev`

Esse comando vai instalar a versão mais atual do TypeScript até o momento, após isso é necessário ainda passar as configurações para o compilador do TypeScript, para isso crie um arquivo na raiz do projeto chamado _tsconfig.json_, e dentro dele cole o seguinte trecho de código, basicamente isto são algumas configurações básicas para utilizar o TypeScript no nosso projeto, existem muitas outras do que somente estas, por hora vão bastar somente elas.


```typescript
{
    "compilerOptions": {
      "target": "ES2018",
      "module": "commonjs",
      "outDir": "./dist",
      "rootDir": "./src",
      "strict": true,
      "esModuleInterop": true
    }
  }
```

Vamos precisar também dessa lib instalada no nosso código.

`npm install — save-dev ts-node`

O [ts-node](https://typestrong.org/ts-node/) é uma ferramenta que permite executar arquivos TypeScript diretamente no Node.js sem a necessidade de compilação prévia.

Pode ser que alguma lib que você venha instalar necessite de seu _@types_ por conta do TypeScript, então é só procurar na web ou na própria doc da documentação em questão, que você acha para instalar, caso necessite dependendo da versão usada.

Vamos já adiantar também e instalar outra biblioteca para salvar nossas variáveis de ambiente, na hora de fazer a chamada ao MongoDB Atlas, não ficar exposto no código nosso login e senha, já que isso seria uma falha crítica de segurança, para isso vamos utilizar essa lib chamada, [dotenv](https://www.npmjs.com/package/dotenv).

`npm install dotenv`

Feito isso vamos criar um arquivo na nossa raiz do nosso projeto chamado _.env_, a princípio pode deixá-lo vazio, que será lá que ficarão nossas variáveis de ambiente, onde quando for subir essas informações para algum repositório lembre-se de criar um arquivo chamado, _[.gitignore](https://git-scm.com/docs/gitignore)_ e colocar para ignorar esse .env, por default já vem, porém, sempre é bom olhar isso.

Após isso feito vamos criar a nossa estrutura de pastas, para trabalhar mais organizado na nossa API, no meu caso eu estou usando um padrão de Arquitetura em Camadas. Nesse padrão, o código é organizado em diferentes camadas, cada uma com uma responsabilidade específica. As camadas mais comuns e que vamos usar nesse exemplo são o Model, Controller, Repository e Service, breve vou criar um artigo explicando o porquê desse padrão é o que cada um faz por boas práticas, mas para não se alongar muito apenas siga essa estrutura por hora, para darmos prosseguimento ao projeto.


![Arquitetura Nodejs back-end](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fd8go3cwijfh9q4bxhj7.png)

Eu criei toda nossa estrutura que vamos precisar nesse exemplo acima, com seus respectivos arquivos vazio até o momento e que vamos começar a codificar, como pode ser visto a maioria das pastas tem um arquivo _index.ts_, isso costuma ser uma boa prática, pois na hora de fazer o import, por padrão o compilador sempre busca pelo valor index por default, caso contrário teria que especificar o nome do arquivo dentro da pasta, na hora do import.

Vamos começar agora modelar os nossos dados da nossa aplicação, para isso vamos criar nosso modelo, lá no nosso model e já vamos preparar nosso schema através do mongoose para falar o que vai ser mandado para o MongoDB.

```typescript
import { Document, Schema, model } from "mongoose";

export interface toDo extends Document {
  text: string;
  completed: boolean;
}

const toDoSchema: Schema = new Schema({
  text: { type: String, required: true },
  completed: { type: Boolean, required: false },
});

export default model<toDo>("toDo", toDoSchema);

```
Feito isto, já tendo o nosso modelo criado, vamos agora criar a nossa conexão com o nosso banco de dados, que no nosso caso eu vou utilizar o [Mongo Atlas](https://www.mongodb.com/cloud/atlas/register) que é a solução projetada para hospedar e operar bancos de dados MongoDB na nuvem.

Para isso você tem que ter uma conta no site do [MongoDB](https://account.mongodb.com/account/login), para poder criar um Cluster no Mongo Atlas.


![novo projeto mongo atlas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/olf36pjwjmpkaylfd2wc.png)

Você terá que criar um projeto para trabalhar, é só clicar no botão _New Project_, você vai inserir o nome do seu projeto e logo em seguida, ele vai pedir para você adicionar outros e-mails caso tenha mais alguém no projeto com você, no nosso caso só ir em next, já que o seu e-mail cadastrado na sua conta já será o Owner do projeto por default.


![novo database](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ux487kuvq7zihbnlf8k.png)

Aqui é a parte crucial, é onde você vai criar seu cluster, se atente algumas informações, na hora de definir o nome lembre-se que ele não poderá mais ser alterado, então escolha com sabedoria no nosso caso, vamos usar toDoList, o fornecedor eu deixei a AWS mesmo, mas fica a seu critério e a região, é outro ponto importante eu recomendo colocar sempre a região, mas próxima de você no meu caso eu coloquei o Brasil, escolhi também a versão gratuita e depois disso só clicar em _create_.


![create DB](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fa8wkoqtdydpxl545n86.png)

Feito isto, você será redirecionado para essa página onde será necessário criar seu usuário com sua chave única.


![criando usuario e senha para o mongo Atlas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/epy9girrelrn31e19y8y.png)


Após isso será necessário colocar seu IP para conectar a seu cluster, basta clicar no botão _Add My Current IP Address_, que ele vai adicionar seu IP atual a lista de permitidos para acessar seu cluster.

![Adicionando seu IP ao cluster](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ito8ugr0p9ck8l5nq7pb.png)


Feito isto, parabéns, você já tem seu cluster criado para conectar a sua API.


![overview database](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xfj8bxgyvgbuh47iowhq.png)

Mas para isso agora vamos precisar de alguns dados que serão adicionados a nossas variáveis de ambiente, para conectar com o Mongo Atlas. Ao clicar em _connect_ irá abrir essas opções:


![connect Mongo Atlas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ce39xf54ck0iuwsrqntd.png)

Você irá selecionar a opção _Drivers_ e irá abrir essa nova janela.


![connect Mongo Atlas com a API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cno4px48xjb62cvwku90.png)

Nesta janela você irá copiar toda essa linha que está selecionada na imagem, lembre-se isso são os dados sensíveis da sua aplicação, por isso eu deixei oculto aqui, e você vai substituir o password pela senha que você criou, la para seu usuário acessar seu cluster, tudo isso irá ficar em suas variáveis de ambientes, eu vou estar amostrando como eu fiz aqui com minhas variáveis de ambientes, é só replicar modificando com as suas próprias variáveis do seu banco.


![variaveis de ambiente](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/esukatqe5pzwjveto8ml.png)

Seu arquivo .env, deve ficar desse jeito e agora no nosso arquivo _db_ iremos fazer a nossa conexão com o Mongo Atlas criado.

```typescript
import mongoose from "mongoose";
import { config as dotenvConfig } from "dotenv";
dotenvConfig();

export async function connect() {
  const baseUrl = `mongodb+srv://${process.env.MONGODB_USER}:${process.env.MONGODB_PASSWORD}@${process.env.MONGODB_CLUSTER_URL}/${process.env.MONGODB_DATABASE}`;
  try {
    const options: any = {};
    options.useNewUrlParser = true;
    options.useUnifiedTopology = true;
    await mongoose.connect(baseUrl, options);
    console.log("MongoDB connected sucess");
  } catch (error) {
    console.error(error);
  }
}
```

Feito isto, já temos a nossa chamada com o nosso banco de Dados criado, a partir do nosso método _connect_, que será chamado posteriormente no nosso _server_, para conectar nosso banco de dados com nossa aplicação.

Agora vamos criar nosso _repository_, que é responsável por lidar com a camada de acesso a dados. Ele fornece uma abstração sobre como os dados são armazenados e recuperados. O principal objetivo é isolar o restante do código da lógica específica do armazenamento, tornando mais fácil a troca ou atualização do mecanismo de armazenamento sem afetar o restante do sistema, para isso vamos criar nosso CRUD básico aqui dividido nos nossos métodos de buscar, criar, atualizar e deletar um toDo.

```typescript
import toDo, { toDo as toDoModel } from "../model/toDo";

class ToDoRepository {
  async getAllToDos(): Promise<toDoModel[]> {
    return toDo.find();
  }

  async createToDo(toDoData: Partial<toDoModel>): Promise<toDoModel> {
    return toDo.create(toDoData);
  }

  async updateToDoData(
    toDoId: string,
    updateData: Partial<toDoModel>
  ): Promise<toDoModel | null> {
    return toDo.findByIdAndUpdate(toDoId, updateData, { new: true });
  }

  async deleteToDo(toDoId: string): Promise<void> {
    await toDo.findByIdAndDelete(toDoId);
  }
}

export default new ToDoRepository();
```
Criei uma classe com os métodos do CRUD e por fim exportei uma instância da classe para ser usada no meu _service_ que vou criar agora, poderia também ter usado de outro paradigma como o funcional, porém optei nesse projeto utilizar do paradigma de classes, seguindo, vamos criar agora nosso service.

No nosso service, ele é onde a lógica de negócios da aplicação é implementada, nele que vamos chamar os nossos métodos criados no nosso repository.

```typescript
import toDoRepository from "../repository";
import { toDo } from "../model/toDo";

class ToDoService {
  async getAllToDos(): Promise<toDo[]> {
    return toDoRepository.getAllToDos();
  }

  async createToDo(toDoData: Partial<toDo>): Promise<toDo> {
    return toDoRepository.createToDo(toDoData);
  }

  async updateToDo(
    toDoId: string,
    updateData: Partial<toDo>
  ): Promise<toDo | null> {
    return toDoRepository.updateToDoData(toDoId, updateData);
  }

  async deleteToDo(toDoId: string): Promise<void> {
    return toDoRepository.deleteToDo(toDoId);
  }
}

export default new ToDoService();
```
Após isso, já com nosso _repository_ e _service_ criados e a vez de criarmos nosso _controller_ que será responsável, como intermediário entre as solicitações do usuário e o restante do sistema. Ele que vai receber as solicitações, processar os dados necessários, interagir com os serviços apropriados e decidir qual visão (ou saída) deve ser apresentada ao usuário. Para isso vamos criar nosso CRUD básico aqui agora, da seguinte maneira.

```typescript
import { Request, Response } from "express";
import toDoService from "../service";

class ToDoController {
  async getAllToDos(req: Request, res: Response): Promise<void> {
    try {
      const toDos = await toDoService.getAllToDos();
      res.json(toDos);
    } catch (error) {
      res.status(500).json({ error: "Error fetching toDos" });
    }
  }

  async createToDo(req: Request, res: Response): Promise<void> {
    const { text } = req.body;

    try {
      const newToDo = await toDoService.createToDo({ text });
      res.status(201).json(newToDo);
    } catch (error) {
      res.status(500).json({ error: "Error creating toDo" });
    }
  }

  async updateToDo(req: Request, res: Response): Promise<void> {
    const { toDoId } = req.params;
    const { completed } = req.body;

    try {
      const updatedToDo = await toDoService.updateToDo(toDoId, { completed });
      res.json(updatedToDo);
    } catch (error) {
      res.status(500).json({ error: "Error updating toDo" });
    }
  }

  async deleteToDo(req: Request, res: Response): Promise<void> {
    const { toDoId } = req.params;

    try {
      await toDoService.deleteToDo(toDoId);
      res.status(204).send();
    } catch (error) {
      res.status(500).json({ error: "Error deleting toDo" });
    }
  }
}

export default new ToDoController();

```

Para deixar mais organizado eu utilizei alguns _status code http_, para que o nosso front consiga tratar essas informações.

Agora para organizar melhor nossa aplicação, vamos criar nosso arquivo de _routes_, onde ficarão nossas chamadas do nosso controller.

```typescript
import express from "express";
import toDoController from "../controller";

const router = express.Router();

router.get("/toDos", toDoController.getAllToDos);
router.post("/toDos", toDoController.createToDo);
router.put("/toDos/:toDoId", toDoController.updateToDo);
router.delete("/toDos/:toDoId", toDoController.deleteToDo);

export default router;

```
Desse jeito, utilizando do Express.js do MERN, podemos criar um roteador chamado _router_ usando `express.Router()`. Um roteador permite definir as rotas separadamente para modularizar o código e organizar as diferentes partes da aplicação.

Agora precisamos criar nosso _server_, que será nosso endpoint de nossa aplicação para chamar nossa porta e rodar ela.

Mas antes disso vamos instalar duas ultimas biblioteca para facilitar nossa vida, primeiro vamos instalar essa.

`npm install cors`

Isso vai resolver possíveis dores de cabeça no futuro, o pacote _cors_ serve para lidar com políticas de mesma origem (Same-Origin Policy) em navegadores e permite que um servidor conceda permissão para que recursos em um determinado domínio sejam acessados de outro domínio. Isso é particularmente útil quando você está construindo APIs que serão acessadas por diferentes origens. Certifique-se de configurá-lo adequadamente para suas necessidades de segurança e autenticação, no nosso caso esta configuração já vai resolver.

Logo após vamos instalar essa aqui.

`npm install -g nodemon`

A opção _-g_ indica que você está instalando o pacote globalmente no seu sistema, o que significa que o _nodemon_ ficará disponível para uso em qualquer projeto, em qualquer pasta.

O _nodemon_ é uma ferramenta que ajuda a desenvolver aplicativos com o Node.js reiniciando automaticamente o servidor toda vez que um arquivo do projeto é modificado. Isso é especialmente útil durante o desenvolvimento, pois permite que você veja as alterações imediatamente, sem precisar reiniciar o servidor manualmente a cada vez.

```typescript
import express from "express";
import cors from "cors";
import { connect } from "./config/db";
import toDoRoutes from "./routes";

const app = express();
const PORT = process.env.PORT || 3001;

app.use(cors());
app.use(express.json());

connect();

app.use(toDoRoutes);

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

```

Por último, não esqueça de alterar o endpoint, no seu _package.json_ no _main_ que vem por padrão _index.js_ para no nosso caso _server.ts_ e adicione esse comando ao nosso package.json nos seus scripts:

`“start”: “nodemon src/server.ts”`

E após isso execute esse comando para rodar nossa API.

`npm start`

Se você seguiu todo esse tutorial, sua API já vai estar rodando e conectada ao seu banco de dados deste jeito.


![API rodando](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b2dlmxm4cxtj4p5ntw8n.png)

Agora vamos voltar na nossa aplicação no front-end que deixamos rodando e vamos testar nossas funcionalidades.

Se você seguiu tudo até aqui, creio eu que esteja funcionando perfeitamente, basta deixar tanto o servidor da API ligado e deixar sua aplicação rodando na porta 3000, e você vai ver algo como isso, já sendo possível adicionar e excluir seus toDos.


![Aplicação Finalizada](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a9f0zoty0qj7evpo033r.png)

E você também pode ver seus toDos salvos no seu banco de dados lá no Mongo Atlas deste jeito.


![RO DOs salvos no Mongo Atlas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2tedvctaay00a6tmzhkh.png)

Pronto, feito isto já temos uma aplicação totalmente funcional full-stack construído com base na stack MERN, utilizando de todas as tecnologias do acrônimo.

Eu não foquei em estilização, ou nem na parte de segurança da nossa API, como também não foquei nos testes, apenas testei na minha máquina por um cliente como o [Postman](https://www.postman.com/) para vê se todas as rotas estavam funcionando normalmente, acho que isso é assunto para outro artigo, mas sinta-se livres para usar esse exemplo como base de algum projeto e publicar em seus portifólios, pode, por exemplo, evoluir deixar a API modularizada com login e cada usuário ter seus próprios toDos salvos no banco separado, enfim ideias é o que não faltam para continuar essa aplicação.

Em outro artigo futuro irei amostrar como fazer o deploy de sua aplicação back-end, ao invés de ter que utilizar em seu localhost, toda vez ligando seu servidor.

## Conclusão
A Stack MERN oferece um ambiente robusto para o desenvolvimento de aplicações web modernas e escaláveis. Ela combina a flexibilidade do MongoDB, a agilidade do Express.js, a interatividade do React e a eficiência do Node.js. Enquanto apresenta desafios de aprendizado e complexidade, os benefícios da MERN Stack são evidentes em projetos que exigem interfaces dinâmicas, escalabilidade e desenvolvimento ágil. Ao estudar essa stack, os desenvolvedores estarão bem equipados para enfrentar os desafios e demandas do desenvolvimento web, projetando uma aplicação de ponta a ponta, desde o front-end até o back-end do seu negócio, logicamente cada projeto tem sua especificação e não pode ser uma bala de prata de sempre utilizá-la para tudo, visto seus prós e contras, fica a seu critério de estudá-la ou utilizá-la em seus projetos.