A pilha FARM é uma pilha de desenvolvimento web moderna que combina três tecnologias poderosas: FastAPI, React e MongoDB. Esta solução de pilha completa fornece aos desenvolvedores um conjunto robusto de ferramentas para construir aplicações web escaláveis, eficientes e de alto desempenho.
Neste artigo, eu irei apresentar uma introdução a cada uma das tecnologias chave, e depois construiremos um projeto usando a pilha FARM e o Docker para você ver como tudo funciona juntos.
Este artigo é baseado em um curso que criei no canal YouTube freeCodeCamp.org. Assista aqui:
Introdução à Pilha FARM
O FARM na pilha FARM significa:
-
F: FastAPI (Backend)
-
R: React (Frontend)
-
M: MongoDB (Database)
A pilha FARM é projetada para aproveitar as vantagens de cada componente, permitindo que os desenvolvedores criem aplicações ricas em recursos com uma experiência de desenvolvimento suave.
Componentes da Pilha FARM
-
FastAPI: FastAPI é um framework web Python moderno e de alto desempenho para construir APIs. Ele é projetado para ser fácil de usar, rápido de codificar e pronto para ambientes de produção. FastAPI é construído sobre Starlette para as partes da web e Pydantic para as partes de dados, tornando-o uma escolha poderosa para construir serviços de backend robustos.
-
React: O React é uma biblioteca JavaScript popular para construir interfaces de usuário. Desenvolvida e mantida pelo Facebook, o React permite que os desenvolvedores criem componentes UI reutilizáveis que se atualizam e renderizam eficientemente conforme os dados mudam. Sua arquitetura baseada em componentes e o DOM virtual tornam-no uma escolha excelente para construir aplicações de frontend dinâmicas e responsivas.
-
MongoDB: O MongoDB é um banco de dados NoSQL orientado a documentos. Ele armazena dados em documentos flexíveis, semelhantes a JSON, o que significa que os campos podem variar de documento para documento e a estrutura de dados pode ser alterada ao longo do tempo. Esta flexibilidade torna o MongoDB uma escolha ideal para aplicações que precisam evoluir rapidamente e manipular tipos de dados diversificados.
Advantages of using FARM Stack
-
High Performance: FastAPI é um dos frameworks Python mais rápidos disponíveis, enquanto o DOM virtual do React garante atualizações eficientes da IU. O modelo de documento do MongoDB permite leituras e gravações rápidas.
-
Scalabilidade: Todos os componentes da pilha FARM são projetados para escalar. O FastAPI pode lidar com requisições concorrentes de forma eficiente, os aplicativos React podem gerenciar IUs complexas, e o MongoDB pode distribuir dados em vários servidores.
-
Comunidade e Ecosistema: Todas as três tecnologias têm comunidades grandes e ativas e ecossistemas ricos de bibliotecas e ferramentas.
-
Flexibilidade: A pilha FARM é flexível o suficiente para abrigar vários tipos de aplicações web,从简单的CRUD应用到复杂的、数据密集型的系统。
Ao combinar essas tecnologias, a pilha FARM fornece uma solução abrangente para a construção de aplicações web modernas. Permite que os desenvolvedores criem backends rápidos e escaláveis com FastAPI, frontends intuitivos e responsivos com React, e armazenamento de dados flexíveis e eficientes com MongoDB. Esta pilha é particularmente bem-sucedida para aplicações que exigem atualizações em tempo real, modelos de dados complexos e alta performance.
Visão geral do projeto: Aplicação de Tarefas
No curso de vídeo, eu cover mais sobre cada tecnologia individual na pilha FARM. Mas neste artigo, vamos mergulhar diretamente the project to put everything together.
Vamos criar uma aplicação de tarefas para ajudar a entender a pilha FARM. Antes de começar a criar a aplicação, vamos discutir mais sobre as funcionalidades e arquitetura do software.
Funcionalidades da aplicação de tarefas
A nossa aplicação de tarefas da pilha FARM incluirá as seguintes funcionalidades:
-
Múltiplas Listas de Tarefas:
-
Usuários podem criar, visualizar, atualizar e excluir múltiplas listas de tarefas.
-
Cada lista tem um nome e contém vários itens de tarefa.
-
-
Itens de Tarefa:
-
Dentro de cada lista, os usuários podem adicionar, visualizar, atualizar e excluir itens de tarefa.
-
Cada item tem uma etiqueta, um status marcado/não marcado e pertence a uma lista específica.
-
-
Atualizações em Tempo Real:
- A interface gráfica é atualizada em tempo real quando há mudanças nas listas ou itens.
-
Design Responsivo:
- A aplicação será responsiva e funcionará bem tanto em dispositivos desktop quanto em dispositivos móveis.
Arquitetura do Sistema
Nossa aplicação de tarefas terá uma arquitetura de stack FARM típica:
-
Frontend (React):
-
Fornece a interface de usuário para interagir com listas de tarefas e itens.
-
Comunica com o backend através de chamadas API RESTful.
-
-
Backend (FastAPI):
-
Trata das solicitações API do frontend.
-
Implementa a lógica de negócios para gerenciar listas de tarefas e itens.
-
Interage com o banco de dados MongoDB para a persistência de dados.
-
-
Database (MongoDB):
-
Armazena listas de tarefas e itens.
-
Fornece consultas e atualizações eficientes de dados de tarefas.
-
-
Docker:
- Contêm cada componente (frontend, backend, banco de dados) em um container para facilitar o desenvolvimento e implantação.
Modelo de dados
Nosso modelo de dados MongoDB será composto por duas estruturas principais:
- Lista de Tarefas:
{
"_id": ObjectId,
"name": String,
"items": [
{
"id": String,
"label": String,
"checked": Boolean
}
]
}
- Resumo da Lista (para exibição na lista de todas as listas de tarefas):
{
"_id": ObjectId,
"name": String,
"item_count": Integer
}
Projeto de ponto final de API
Nosso backend FastAPI exporá os seguintes pontos finais RESTful:
-
Listas de Tarefas:
-
GET /api/lists: Recupera todas as listas de tarefas (visão resumida)
-
POST /api/lists: Cria uma nova lista de tarefas
-
GET /api/lists/{list_id}: Recupera uma lista de tarefas específica com todos seus itens
-
DELETE /api/lists/{list_id}: Exclui uma lista de tarefas específica
-
-
Itens de Tarefas:
-
POST /api/listas/{list_id}/itens: Adiciona um novo item à lista específica
-
PATCH /api/listas/{list_id}/estado_verificado: Atualiza o estado verificado de um item
-
DELETE /api/listas/{list_id}/itens/{item_id}: Exclui um item específico de uma lista
-
Este projeto fornecerá uma base sólida no desenvolvimento de pilha FARM e containerização Docker, que você pode expandir para aplicativos mais complexos no futuro.
Vamos começar o projeto.
Tutorial do Projeto
Configuração do Projeto e Desenvolvimento do Backend
Passo 1: Configure a estrutura do projeto
Crie um novo diretório para o seu projeto:
mkdir farm-stack-todo
cd farm-stack-todo
Crie subdiretórios para o backend e frontend:
mkdir backend frontend
Passo 2: Configure o ambiente de backend
Navegue até o diretório de backend:
cd backend
Crie um ambiente virtual e ative-o:
python -m venv venv
source venv/bin/activate # On Windows, use: venv\Scripts\activate
Crie os seguintes arquivos na pasta de backend:
-
-
Dockerfile
- pyproject.toml
-
No seu terminal, instale os pacotes necessários:
pip install "fastapi[all]" "motor[srv]" beanie aiostream
Gerar o arquivo requirements.txt:
pip freeze > requirements.txt
Após criar o arquivo requirements.txt (tanto com pip-compile quanto manualmente), você pode instalar as dependências usando:
pip install -r requirements.txt
Adicione o seguinte conteúdo ao Dockerfile:
FROM python:3
WORKDIR /usr/src/app
COPY requirements.txt ./
RUN pip install --no-cache-dir --upgrade -r ./requirements.txt
EXPOSE 3001
CMD [ "python", "./src/server.py" ]
Adicione o seguinte conteúdo ao pyproject.toml:
[tool.pytest.ini_options]
pythonpath = "src"
Etapa 4: Configure a estrutura do backend
Crie um diretório src dentro da pasta de backend:
mkdir src
Crie os seguintes arquivos dentro do diretório src:
Etapa 5: Implemente a Camada de Acesso a Dados (DAL)
Abra src/dal.py e adicione o seguinte conteúdo:
from bson import ObjectId
from motor.motor_asyncio import AsyncIOMotorCollection
from pymongo import ReturnDocument
from pydantic import BaseModel
from uuid import uuid4
class ListSummary(BaseModel):
id: str
name: str
item_count: int
@staticmethod
def from_doc(doc) -> "ListSummary":
return ListSummary(
id=str(doc["_id"]),
name=doc["name"],
item_count=doc["item_count"],
)
class ToDoListItem(BaseModel):
id: str
label: str
checked: bool
@staticmethod
def from_doc(item) -> "ToDoListItem":
return ToDoListItem(
id=item["id"],
label=item["label"],
checked=item["checked"],
)
class ToDoList(BaseModel):
id: str
name: str
items: list[ToDoListItem]
@staticmethod
def from_doc(doc) -> "ToDoList":
return ToDoList(
id=str(doc["_id"]),
name=doc["name"],
items=[ToDoListItem.from_doc(item) for item in doc["items"]],
)
class ToDoDAL:
def __init__(self, todo_collection: AsyncIOMotorCollection):
self._todo_collection = todo_collection
async def list_todo_lists(self, session=None):
async for doc in self._todo_collection.find(
{},
projection={
"name": 1,
"item_count": {"$size": "$items"},
},
sort={"name": 1},
session=session,
):
yield ListSummary.from_doc(doc)
async def create_todo_list(self, name: str, session=None) -> str:
response = await self._todo_collection.insert_one(
{"name": name, "items": []},
session=session,
)
return str(response.inserted_id)
async def get_todo_list(self, id: str | ObjectId, session=None) -> ToDoList:
doc = await self._todo_collection.find_one(
{"_id": ObjectId(id)},
session=session,
)
return ToDoList.from_doc(doc)
async def delete_todo_list(self, id: str | ObjectId, session=None) -> bool:
response = await self._todo_collection.delete_one(
{"_id": ObjectId(id)},
session=session,
)
return response.deleted_count == 1
async def create_item(
self,
id: str | ObjectId,
label: str,
session=None,
) -> ToDoList | None:
result = await self._todo_collection.find_one_and_update(
{"_id": ObjectId(id)},
{
"$push": {
"items": {
"id": uuid4().hex,
"label": label,
"checked": False,
}
}
},
session=session,
return_document=ReturnDocument.AFTER,
)
if result:
return ToDoList.from_doc(result)
async def set_checked_state(
self,
doc_id: str | ObjectId,
item_id: str,
checked_state: bool,
session=None,
) -> ToDoList | None:
result = await self._todo_collection.find_one_and_update(
{"_id": ObjectId(doc_id), "items.id": item_id},
{"$set": {"items.$.checked": checked_state}},
session=session,
return_document=ReturnDocument.AFTER,
)
if result:
return ToDoList.from_doc(result)
async def delete_item(
self,
doc_id: str | ObjectId,
item_id: str,
session=None,
) -> ToDoList | None:
result = await self._todo_collection.find_one_and_update(
{"_id": ObjectId(doc_id)},
{"$pull": {"items": {"id": item_id}}},
session=session,
return_document=ReturnDocument.AFTER,
)
if result:
return ToDoList.from_doc(result)
Este conclui a Parte 1 do tutorial, onde configuramos a estrutura do projeto e implementamos a Camada de Acesso a Dados para nossa aplicação de tarefas FARM stack todo. Na próxima parte, implementaremos o servidor FastAPI e criaremos os pontos de extremidade da API.
Implementando o Servidor FastAPI
Etapa 6: Implementar o servidor FastAPI
Abra src/server.py e adicione o seguinte conteúdo:
from contextlib import asynccontextmanager
from datetime import datetime
import os
import sys
from bson import ObjectId
from fastapi import FastAPI, status
from motor.motor_asyncio import AsyncIOMotorClient
from pydantic import BaseModel
import uvicorn
from dal import ToDoDAL, ListSummary, ToDoList
COLLECTION_NAME = "todo_lists"
MONGODB_URI = os.environ["MONGODB_URI"]
DEBUG = os.environ.get("DEBUG", "").strip().lower() in {"1", "true", "on", "yes"}
@asynccontextmanager
async def lifespan(app: FastAPI):
# Inicialização:
client = AsyncIOMotorClient(MONGODB_URI)
database = client.get_default_database()
# Certifique-se de que o banco de dados esteja disponível:
pong = await database.command("ping")
if int(pong["ok"]) != 1:
raise Exception("Cluster connection is not okay!")
todo_lists = database.get_collection(COLLECTION_NAME)
app.todo_dal = ToDoDAL(todo_lists)
# Devolver ao Aplicativo FastAPI:
yield
# Encerramento:
client.close()
app = FastAPI(lifespan=lifespan, debug=DEBUG)
@app.get("/api/lists")
async def get_all_lists() -> list[ListSummary]:
return [i async for i in app.todo_dal.list_todo_lists()]
class NewList(BaseModel):
name: str
class NewListResponse(BaseModel):
id: str
name: str
@app.post("/api/lists", status_code=status.HTTP_201_CREATED)
async def create_todo_list(new_list: NewList) -> NewListResponse:
return NewListResponse(
id=await app.todo_dal.create_todo_list(new_list.name),
name=new_list.name,
)
@app.get("/api/lists/{list_id}")
async def get_list(list_id: str) -> ToDoList:
"""Get a single to-do list"""
return await app.todo_dal.get_todo_list(list_id)
@app.delete("/api/lists/{list_id}")
async def delete_list(list_id: str) -> bool:
return await app.todo_dal.delete_todo_list(list_id)
class NewItem(BaseModel):
label: str
class NewItemResponse(BaseModel):
id: str
label: str
@app.post(
"/api/lists/{list_id}/items/",
status_code=status.HTTP_201_CREATED,
)
async def create_item(list_id: str, new_item: NewItem) -> ToDoList:
return await app.todo_dal.create_item(list_id, new_item.label)
@app.delete("/api/lists/{list_id}/items/{item_id}")
async def delete_item(list_id: str, item_id: str) -> ToDoList:
return await app.todo_dal.delete_item(list_id, item_id)
class ToDoItemUpdate(BaseModel):
item_id: str
checked_state: bool
@app.patch("/api/lists/{list_id}/checked_state")
async def set_checked_state(list_id: str, update: ToDoItemUpdate) -> ToDoList:
return await app.todo_dal.set_checked_state(
list_id, update.item_id, update.checked_state
)
class DummyResponse(BaseModel):
id: str
when: datetime
@app.get("/api/dummy")
async def get_dummy() -> DummyResponse:
return DummyResponse(
id=str(ObjectId()),
when=datetime.now(),
)
def main(argv=sys.argv[1:]):
try:
uvicorn.run("server:app", host="0.0.0.0", port=3001, reload=DEBUG)
except KeyboardInterrupt:
pass
if __name__ == "__main__":
main()
Esta implementação configura o servidor FastAPI com o middleware CORS, conecta ao MongoDB e define os pontos de extremidade da API para nossa aplicação de tarefas.
Etapa 7: Configurar variáveis de ambiente
Crie um arquivo .env no diretório raiz com o seguinte conteúdo. Certifique-se de adicionar o nome do banco de dados (“todo”) no final de “.mongodb.net/”.
MONGODB_URI='mongodb+srv://beau:codecamp@cluster0.ji7hu.mongodb.net/todo?retryWrites=true&w=majority&appName=Cluster0'
Etapa 8: Criar um arquivo docker-compose
No diretório raiz do seu projeto (farm-stack-todo), crie um arquivo chamado compose.yml com o seguinte conteúdo:
name: todo-app
services:
nginx:
image: nginx:1.17
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
ports:
- 8000:80
depends_on:
- backend
- frontend
frontend:
image: "node:22"
user: "node"
working_dir: /home/node/app
environment:
- NODE_ENV=development
- WDS_SOCKET_PORT=0
volumes:
- ./frontend/:/home/node/app
expose:
- "3000"
ports:
- "3000:3000"
command: "npm start"
backend:
image: todo-app/backend
build: ./backend
volumes:
- ./backend/:/usr/src/app
expose:
- "3001"
ports:
- "8001:3001"
command: "python src/server.py"
environment:
- DEBUG=true
env_file:
- path: ./.env
required: true
Etapa 9: Configurar a configuração do Nginx
Crie um diretório chamado nginx na raiz do seu projeto:
mkdir nginx
Crie um arquivo chamado nginx.conf dentro do diretório nginx com o seguinte conteúdo:
server {
listen 80;
server_name farm_intro;
location / {
proxy_pass http://frontend:3000;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /api {
proxy_pass http://backend:3001/api;
}
}
Isso conclui a Parte 2 do tutorial, onde implementamos o servidor FastAPI, configuramos variáveis de ambiente, criamos um arquivo docker-compose e configuramos o Nginx. Na próxima parte, vamos concentrar na configuração do frontend React para nossa aplicação todo FARM stack.
Configuração do Frontend React
Passo 10: Criar o aplicativo React
Navegue até o diretório frontend:
cd ../frontend
Crie um novo aplicativo React usando Create React App:
npx create-react-app .
Instale dependências adicionais:
npm install axios react-icons
Passo 11: Configurar o componente principal App
Substitua o conteúdo de src/App.js pelo seguinte:
import { useEffect, useState } from "react";
import axios from "axios";
import "./App.css";
import ListToDoLists from "./ListTodoLists";
import ToDoList from "./ToDoList";
function App() {
const [listSummaries, setListSummaries] = useState(null);
const [selectedItem, setSelectedItem] = useState(null);
useEffect(() => {
reloadData().catch(console.error);
}, []);
async function reloadData() {
const response = await axios.get("/api/lists");
const data = await response.data;
setListSummaries(data);
}
function handleNewToDoList(newName) {
const updateData = async () => {
const newListData = {
name: newName,
};
await axios.post(`/api/lists`, newListData);
reloadData().catch(console.error);
};
updateData();
}
function handleDeleteToDoList(id) {
const updateData = async () => {
await axios.delete(`/api/lists/${id}`);
reloadData().catch(console.error);
};
updateData();
}
function handleSelectList(id) {
console.log("Selecting item", id);
setSelectedItem(id);
}
function backToList() {
setSelectedItem(null);
reloadData().catch(console.error);
}
if (selectedItem === null) {
return (
<div className="App">
<ListToDoLists
listSummaries={listSummaries}
handleSelectList={handleSelectList}
handleNewToDoList={handleNewToDoList}
handleDeleteToDoList={handleDeleteToDoList}
/>
</div>
);
} else {
return (
<div className="App">
<ToDoList listId={selectedItem} handleBackButton={backToList} />
</div>
);
}
}
export default App;
Passo 12: Criar o componente ListTodoLists
Crie um novo arquivo src/ListTodoLists.js com o seguinte conteúdo:
import "./ListTodoLists.css";
import { useRef } from "react";
import { BiSolidTrash } from "react-icons/bi";
function ListToDoLists({
listSummaries,
handleSelectList,
handleNewToDoList,
handleDeleteToDoList,
}) {
const labelRef = useRef();
if (listSummaries === null) {
return <div className="ListToDoLists loading">Loading to-do lists ...</div>;
} else if (listSummaries.length === 0) {
return (
<div className="ListToDoLists">
<div className="box">
<label>
New To-Do List:
<input id={labelRef} type="text" />
</label>
<button
onClick={() =>
handleNewToDoList(document.getElementById(labelRef).value)
}
>
New
</button>
</div>
<p>There are no to-do lists!</p>
</div>
);
}
return (
<div className="ListToDoLists">
<h1>All To-Do Lists</h1>
<div className="box">
<label>
New To-Do List:
<input id={labelRef} type="text" />
</label>
<button
onClick={() =>
handleNewToDoList(document.getElementById(labelRef).value)
}
>
New
</button>
</div>
{listSummaries.map((summary) => {
return (
<div
key={summary.id}
className="summary"
onClick={() => handleSelectList(summary.id)}
>
<span className="name">{summary.name} </span>
<span className="count">({summary.item_count} items)</span>
<span className="flex"></span>
<span
className="trash"
onClick={(evt) => {
evt.stopPropagation();
handleDeleteToDoList(summary.id);
}}
>
<BiSolidTrash />
</span>
</div>
);
})}
</div>
);
}
export default ListToDoLists;
Crie um novo arquivo src/ListTodoLists.css com o seguinte conteúdo:
.ListToDoLists .summary {
border: 1px solid lightgray;
padding: 1em;
margin: 1em;
cursor: pointer;
display: flex;
}
.ListToDoLists .count {
padding-left: 1ex;
color: blueviolet;
font-size: 92%;
}
Passo 13: Criar o componente ToDoList
Crie um novo arquivo src/ToDoList.js com o seguinte conteúdo:
import "./ToDoList.css";
import { useEffect, useState, useRef } from "react";
import axios from "axios";
import { BiSolidTrash } from "react-icons/bi";
function ToDoList({ listId, handleBackButton }) {
let labelRef = useRef();
const [listData, setListData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const response = await axios.get(`/api/lists/${listId}`);
const newData = await response.data;
setListData(newData);
};
fetchData();
}, [listId]);
function handleCreateItem(label) {
const updateData = async () => {
const response = await axios.post(`/api/lists/${listData.id}/items/`, {
label: label,
});
setListData(await response.data);
};
updateData();
}
function handleDeleteItem(id) {
const updateData = async () => {
const response = await axios.delete(
`/api/lists/${listData.id}/items/${id}`
);
setListData(await response.data);
};
updateData();
}
function handleCheckToggle(itemId, newState) {
const updateData = async () => {
const response = await axios.patch(
`/api/lists/${listData.id}/checked_state`,
{
item_id: itemId,
checked_state: newState,
}
);
setListData(await response.data);
};
updateData();
}
if (listData === null) {
return (
<div className="ToDoList loading">
<button className="back" onClick={handleBackButton}>
Back
</button>
Loading to-do list ...
</div>
);
}
return (
<div className="ToDoList">
<button className="back" onClick={handleBackButton}>
Back
</button>
<h1>List: {listData.name}</h1>
<div className="box">
<label>
New Item:
<input id={labelRef} type="text" />
</label>
<button
onClick={() =>
handleCreateItem(document.getElementById(labelRef).value)
}
>
New
</button>
</div>
{listData.items.length > 0 ? (
listData.items.map((item) => {
return (
<div
key={item.id}
className={item.checked ? "item checked" : "item"}
onClick={() => handleCheckToggle(item.id, !item.checked)}
>
<span>{item.checked ? "✅" : "⬜️"} </span>
<span className="label">{item.label} </span>
<span className="flex"></span>
<span
className="trash"
onClick={(evt) => {
evt.stopPropagation();
handleDeleteItem(item.id);
}}
>
<BiSolidTrash />
</span>
</div>
);
})
) : (
<div className="box">There are currently no items.</div>
)}
</div>
);
}
export default ToDoList;
Crie um novo arquivo src/ToDoList.css com o seguinte conteúdo:
.ToDoList .back {
margin: 0 1em;
padding: 1em;
float: left;
}
.ToDoList .item {
border: 1px solid lightgray;
padding: 1em;
margin: 1em;
cursor: pointer;
display: flex;
}
.ToDoList .label {
margin-left: 1ex;
}
.ToDoList .checked .label {
text-decoration: line-through;
color: lightgray;
}
Passo 14: Atualizar o arquivo CSS principal
Substitua o conteúdo de src/index.css pelo seguinte:
html, body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-size: 12pt;
}
input, button {
font-size: 1em;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
.box {
border: 1px solid lightgray;
padding: 1em;
margin: 1em;
}
.flex {
flex: 1;
}
Isso conclui a Parte 3 do tutorial, onde configuramos o frontend React para nossa aplicação todo FARM stack. Nós criamos o componente principal App, o componente ListTodoLists para exibir todas as listas de tarefas e o componente ToDoList para listas de tarefas individuais. Na próxima parte, vamos concentrar na execução e teste do aplicativo.
Executando e Testando a Aplicação
Passo 18: Executar a aplicação usando Docker Compose
-
Certifique-se de que você tem Docker e Docker Compose instalados no seu sistema
-
Abra um terminal no diretório raiz do seu projeto (farm-stack-todo)
-
Construir e iniciar os contêineres:
docker-compose up --build
- Uma vez que os contêineres estão rodando, abra o seu navegador web e vá para http://localhost:8000
Passo 19: Parando a aplicação
-
Se você estiver executando a aplicação sem Docker:
-
Parar o servidor de desenvolvimento React pressionando Ctrl+C em seu terminal
-
Parar o servidor FastAPI pressionando Ctrl+C em seu terminal
-
Parar o servidor MongoDB pressionando Ctrl+C em seu terminal
-
-
Se você está executando o aplicativo com Docker Compose:
-
Pressione Ctrl+C no terminal onde executou o docker-compose up
-
Execute o seguinte comando para parar e remover os contêineres:
-
docker-compose down
“`
Parabéns! Você construiu e testou com sucesso uma aplicação de stack FARM de tarefas. Esta aplicação demonstra a integração de FastAPI, React e MongoDB em uma aplicação web de pilha completa.
Veja algumas próximas etapas potenciais para melhorar sua aplicação:
-
Adicionar autenticação e autorização de usuário
-
Implementar validação de dados e tratamento de erros
-
Adicionar mais recursos como datas de vencimento, prioridades ou categorias para itens de tarefas
-
Melhorar o UI/UX com um design mais polido
-
Escrever testes unitários e de integração para ambos o frontend e o backend
-
Configurar integração contínua e implantação (CI/CD) para sua aplicação
Lembre-se de manter suas dependências atualizadas e seguir as melhores práticas de segurança e performance enquanto desenvolve sua aplicação.
Conclusão e Próximas etapas
Parabéns por completar este tutorial completo sobre a pilha FARM! Construindo esta aplicação de tarefas, você adquiriu experiência prática com algumas das tecnologias mais poderosas e populares em desenvolvimento web moderno. Você aprendera a criar um robusto backend API com FastAPI, construir um frontend dinâmico e responsivo com React, persistir dados com MongoDB, e containerizar toda sua aplicação usando Docker. Este projeto demonstra como essas tecnologias funcionam juntas de forma harmoniosa para criar uma aplicação web completa e escalável.
Source:
https://www.freecodecamp.org/news/use-the-farm-stack-to-develop-full-stack-apps/