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 apresentarei uma introdução a cada uma das tecnologias chave, e então 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 forças 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 é uma estrutura de web moderna e de alto desempenho em Python para construir APIs. Ela é projetada para ser fácil de usar, rápida de codificar e pronta para ambientes de produção. FastAPI é construída sobre Starlette para as partes web e Pydantic para as partes de dados, tornando-se uma escolha poderosa para construir serviços backend robustos.
-
React: React é uma biblioteca de JavaScript popular para construir interfaces de usuário. Desenvolvida e mantida pela Facebook, React permite que desenvolvedores criem componentes de UI reutilizáveis que atualizam e renderizam eficientemente conforme os dados mudam. Seu arquitetura baseada em componentes e o DOM virtual tornam React uma excelente opção para construir aplicações frontend dinâmicas e responsivas.
-
MongoDB: MongoDB é uma base de dados não-relacional orientada a documentos. Ela 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 mudar ao longo do tempo. Esta flexibilidade torna MongoDB uma opção ideal para aplicações que precisam evoluir rapidamente e lidar com tipos de dados diversos.
Vantagens de usar a Stack FARM
-
Alto Desempenho: FastAPI é um dos frameworks Python mais rápidos disponíveis, enquanto o DOM virtual do React garante atualizações eficientes da interface gráfica. O modelo de documento do MongoDB permite leituras e gravações rápidas.
-
Escalabilidade: Todos os componentes da Stack FARM são projetados para escalar. FastAPI pode lidar com pedidos concorrentes eficientemente, as aplicações React podem gerenciar interfaces gráficas complexas, e o MongoDB pode distribuir dados entre vários servidores.
-
Comunidade e Ecosistema: Todas as três tecnologias possuem grandes comunidades ativas e ecosistemas abrangentes de bibliotecas e ferramentas.
-
Flexibilidade: O conjunto FARM é flexível o suficiente para abrigar vários tipos de aplicações web, desde simples aplicações CRUD até sistemas complexos e intensivos em dados.
Ao combinar essas tecnologias, o conjunto FARM fornece uma solução abrangente para a construção de aplicações web modernas. Permite que 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. Este conjunto é particularmente bem-sucedido para aplicações que exigem atualizações em tempo real, modelos de dados complexos e alto desempenho.
Visão geral do projeto: Aplicação de Tarefas
No curso de vídeo, eu cover mais sobre cada tecnologia individual no conjunto FARM. Mas nesta article, vamos começar um projeto para juntar tudo.
Vamos criar uma aplicação de tarefas para ajudar a entender o conjunto FARM. Antes de começar a criar a aplicação, vamos discutir mais sobre as funcionalidades e arquitetura de software.
Funcionalidades da aplicação de tarefas
Nossa aplicação de tarefas do conjunto FARM incluirá as seguintes funcionalidades:
-
Várias Listas de Tarefas:
-
Usuários podem criar, visualizar, atualizar e excluir várias listas de tarefas.
-
Cada lista tem um nome e contém vários itens de tarefa.
-
-
Itens de Tarefas:
-
Dentro de cada lista, os usuários podem adicionar, visualizar, atualizar e excluir itens de tarefas.
-
Cada item tem uma etiqueta, um status marcado/desmarcado e pertence a uma lista específica.
-
-
Atualizações em Tempo Real:
- A interface do usuário atualiza em tempo real quando há alterações nas listas ou itens.
-
Design Responsivo:
- A aplicação será responsiva e funcionará bem tanto em computadores quanto em dispositivos móveis.
Arquitetura do sistema
Nossa aplicação de tarefas seguirá uma arquitetura típica da pilha FARM:
-
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 a base de dados MongoDB para 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 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
}
API endpoint design
Nosso backend FastAPI exporá os seguintes pontos de extremidade RESTful:
-
Listas de tarefas:
-
GET /api/lists: Recupera todas as listas de tarefas (visão resumida)
-
POST /api/lists: Criar 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/lists/{list_id}/items: Adiciona um novo item à lista específica
-
PATCH /api/lists/{list_id}/checked_state: Atualiza o estado marcado de um item
-
DELETE /api/lists/{list_id}/items/{item_id}: Exclui um item específico da lista
-
Este projeto fornecerá uma base sólida no desenvolvimento da pilha FARM e na containerização Docker, que você pode expandir para aplicações mais complexas no futuro.
Então, vamos começar com o projeto.
Tutorial do Projeto
Configuração do Projeto e Desenvolvimento do Backend
Passo 1: Defina 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 do backend
Navegue até o diretório do 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 no diretório backend:
-
-
Dockerfile
- pyproject.toml
-
No seu terminal, instale as pacotes necessários:
pip install "fastapi[all]" "motor[srv]" beanie aiostream
Gere o arquivo requirements.txt:
pip freeze > requirements.txt
Após criar o arquivo requirements.txt (ou usando pip-compile ou 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"
Passo 4: Configurar a estrutura de backend
Crie um diretório src dentro do diretório backend:
mkdir src
Crie os seguintes arquivos dentro do diretório src:
Passo 5: Implementar a Camada de Acesso a Dados (DAL)
Abra o arquivo 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)
Isso 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 todo do FARM stack. Na próxima parte, implementaremos o servidor FastAPI e criaremos as rotas da API.
Implementando o Servidor FastAPI
Passo 6: Implementar o servidor FastAPI
Abra o arquivo 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 que o banco de dados estiver 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)
# Retorne para a Aplicação 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 com o MongoDB e define as rotas da API para nossa aplicação de todo.
Passo 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'
Passo 8: Criar um arquivo docker-compose
No diretório raiz de 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
Passo 9: Configurar a configuração Nginx
Crie um diretório chamado nginx no root de 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;
}
}
Este conclui a Parte 2 do tutorial, onde implementamos o servidor FastAPI, configuramos as variáveis de ambiente, criamos um arquivo docker-compose e configuramos o Nginx. Na próxima parte, vamos nos concentrar em configurar o frontend React para nossa aplicação de lista de tarefas FARM stack.
Configurando o Frontend React
Etapa 10: Criar a aplicação React
Navegue até o diretório frontend:
cd ../frontend
Crie uma nova aplicação React usando o Create React App:
npx create-react-app .
Instale dependências adicionais:
npm install axios react-icons
Etapa 11: Configure o componente principal App
Substitua o conteúdo de src/App.js com o 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;
Etapa 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%;
}
Etapa 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;
}
Etapa 14: Atualize o arquivo CSS principal
Substitua o conteúdo de src/index.css com o 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;
}
Este conclui a Parte 3 do tutorial, onde configuramos o frontend React para nossa aplicação de lista de tarefas FARM stack. 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 nos concentrar em executar e testar a aplicação.
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 containers:
docker-compose up --build
- Uma vez que os containers estiverem rodando, abra 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 a aplicação com Docker Compose:
-
Pressione Ctrl+C no terminal onde você 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 lista de tarefas FARM stack. 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
-
Escreva testes de unidade e integração para o frontend e o backend
-
Configure a integração e implantação contínua (CI/CD) para sua aplicação
Remember to keep your dependencies updated and follow best practices for security and performance as you continue to develop your application.
Conclusão e Próximos Passos
Parabéns por concluir este tutorial abrangente do FARM stack! Ao construir esta aplicação de tarefas, você ganhou experiência prática com algumas das tecnologias mais poderosas e populares no desenvolvimento web moderno. Você aprendeu como criar uma API de backend robusta com o FastAPI, construir um frontend dinâmico e responsivo com o React, persistir dados com o MongoDB e containerizar toda sua aplicação usando o Docker. Este projeto demonstrou como essas tecnologias funcionam juntas perfeitamente 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/