La pile FARM est une suite de développement web moderne qui combine trois technologies puissantes : FastAPI, React et MongoDB. Cette solution de pilier complète fournit aux développeurs un ensemble robuste d’outils pour construire des applications web scalables, efficaces et performantes.
Dans cet article, je vais vous donner une introduction à chacune des technologies clés, puis nous construirons un projet en utilisant la pile FARM et Docker pour que vous puissiez voir comment tout fonctionne ensemble.
Cet article est basé sur un cours que j’ai créé sur la chaîne YouTube freeCodeCamp.org. Vous le pouvez regarder ici :
Introduction à la pile FARM
Le FARM dans la pile FARM signifie :
-
F : FastAPI (Backend)
-
R : React (Frontend)
-
M : MongoDB (Base de données)
La pile FARM est conçue pour tirer parti des avantages de chacun des composants, permettant aux développeurs de créer des applications riches en fonctionnalités avec une expérience de développement fluide.
Composants de la pile FARM
-
FastAPI : FastAPI est un framework web moderne pour Python de hautes performances destiné à la construction d’API. Il est conçu pour être facile à utiliser, rapide à coder et prêt pour les environnements de production. FastAPI est construit à partir de Starlette pour les parties web et de Pydantic pour les parties de données, ce qui en fait une solution puissante pour la construction de services backend robustes.
-
React : React est une bibliothèque JavaScript populaire pour la construction d’interfaces utilisateur. Développée et maintenue par Facebook, React permet aux développeurs de créer des composants UI réutilisables qui se mettent à jour et s’affichent efficacement au fur et à mesure des changements de données. Son architecture basée sur les composants et le DOM virtuel le rendent excellent pour la construction d’applications frontend dynamiques et réactives.
-
MongoDB : MongoDB est une base de données NoSQL orientée document. Elle stocke des données dans des documents flexibles, de type JSON, ce qui signifie que les champs peuvent varier d’un document à l’autre et que la structure des données peut évoluer au fil du temps. Cette flexibilité fait de MongoDB un choix idéal pour les applications qui doivent évoluer rapidement et gérer des types de données divers.
Avantages de l’utilisation de la pile FARM
-
Haute Performance : FastAPI est l’un des frameworks Python les plus rapides disponibles, tandis que le virtual DOM de React garantit des mises à jour de l’UI efficientes. Le modèle de document de MongoDB permet des lectures et écritures rapides.
-
Scalabilité : Tous les composants de la pile FARM sont conçus pour être scalables. FastAPI peut gérer efficacement des demandes concurrentes, les applications React peuvent gérer des UIs complexes, et MongoDB peut distribuer les données sur plusieurs serveurs.
-
Communauté et Écosystème : Chacune des trois technologies possède une grande communauté active et un riche écosystème de bibliothèques et d’outils.
-
Flexibilité : La pile FARM est suffisamment flexible pour accueillir divers types d’applications web, depuis des applications de CRUD simples jusqu’à des systèmes complexes et intensifs en données.
En combinant ces technologies, la pile FARM offre une solution complète pour la construction d’applications web modernes. Elle permet aux développeurs de créer des backends rapides et scalables avec FastAPI, des frontends intuitifs et réactifs avec React, et un stockage de données flexible et efficace avec MongoDB. Cette pile est particulièrement adaptée pour les applications qui nécessitent des mises à jour en temps réel, des modèles de données complexes et de hautes performances.
Aperçu du projet : Application Todo
Dans la formation vidéo, je couvre plus sur chacune des technologies individuelles de la pile FARM. Cependant, dans cet article, nous allons plutôt nous plonger directement dans un projet pour rassembler tout.
Nous créerons une application Todo pour mieux comprendre la pile FARM. Avant de commencer à créer l’application, discutons davantage des fonctionnalités et de l’architecture logicielle.
Fonctionnalités de l’application Todo
Notre application Todo de pile FARM comprendra les fonctionnalités suivantes :
-
Plusieurs Listes de Todo :
-
Les utilisateurs peuvent créer, afficher, modifier et supprimer plusieurs listes de todos.
-
Chaque liste a un nom et contient plusieurs éléments de todos.
-
-
Éléments de tâches :
-
Au sein de chaque liste, les utilisateurs peuvent ajouter, afficher, mettre à jour et supprimer des éléments de tâches.
-
Chaque élément a une étiquette, un statut de coché/non coché et appartient à une liste spécifique.
-
-
Mises à jour en temps réel :
- L’interface utilisateur est mise à jour en temps réel lorsque des modifications sont apportées aux listes ou aux éléments.
-
Conception responsive :
- L’application sera responsive et fonctionnera bien sur les ordinateurs de bureau et les appareils mobiles.
Architecture du système
Notre application de tâches à faire suivra une architecture de type FARM stack :
-
Frontend (React) :
-
Fournit l’interface utilisateur pour interagir avec les listes de tâches et les éléments.
-
Communique avec le backend par des appels d’API RESTful.
-
-
Backend (FastAPI) :
-
Gère les demandes d’API provenant du frontend.
-
Implémente la logique métier pour gérer les listes de tâches et les éléments.
-
Interagit avec la base de données MongoDB pour la persistance des données.
-
-
Base de données (MongoDB) :
-
Stocke les listes de tâches et les éléments.
-
Fournit une recherche et une mise à jour efficaces des données de listes de tâches.
-
-
Docker :
- Containerise chaque composant (frontend, backend, base de données) pour une développement et un déploiement faciles.
Conception du modèle de données
Notre modèle de données MongoDB se composera de deux structures principales :
- Liste des tâches à faire :
{
"_id": ObjectId,
"name": String,
"items": [
{
"id": String,
"label": String,
"checked": Boolean
}
]
}
- Résumé de la liste (pour afficher dans la liste de toutes les listes de tâches à faire) :
{
"_id": ObjectId,
"name": String,
"item_count": Integer
}
Conception des points d’accès API
Notre backend FastAPI exposera les points d’accès RESTful suivants :
-
Listes de tâches :
-
GET /api/lists : Récupérer toutes les listes de tâches (vue résumée)
-
POST /api/lists : Créer une nouvelle liste de tâches
-
GET /api/lists/{list_id} : Récupérer une liste de tâches spécifique avec tous ses éléments
-
DELETE /api/lists/{list_id} : Supprimer une liste de tâches spécifique
-
-
Éléments de tâches :
-
POST /api/lists/{list_id}/items : Ajoute un nouvel élément à une liste spécifique
-
PATCH /api/lists/{list_id}/checked_state : Met à jour l’état cochée d’un élément
-
DELETE /api/lists/{list_id}/items/{item_id} : Supprime un élément spécifique d’une liste
-
Ce projet fournira une base solide en développement du pile FARM et en containerisation Docker, que vous pourrez par la suite développer pour des applications plus complexes dans le futur.
Faisons donc débuter le projet.
Tutoriel du projet
Configuration du projet et développement du backend
Étape 1 : Configurer la structure du projet
Créez un nouveau répertoire pour votre projet :
mkdir farm-stack-todo
cd farm-stack-todo
Créez des sous-répertoires pour le backend et le frontend :
mkdir backend frontend
Étape 2 : Configurer l’environnement du backend
Naviguez vers le répertoire du backend :
cd backend
Créez un environnement virtuel et activez-le :
python -m venv venv
source venv/bin/activate # On Windows, use: venv\Scripts\activate
Créez les fichiers suivants dans le répertoire backend :
-
-
Dockerfile
- pyproject.toml
-
Sur votre terminal, installez les paquets requis :
pip install "fastapi[all]" "motor[srv]" beanie aiostream
Générez le fichier requirements.txt :
pip freeze > requirements.txt
Après avoir créé le fichier requirements.txt (soit via pip-compile, soit manuellement), vous pouvez installer les dépendances en utilisant :
pip install -r requirements.txt
Ajoutez le contenu suivant à 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" ]
Ajoutez le contenu suivant à pyproject.toml :
[tool.pytest.ini_options]
pythonpath = "src"
Étape 4 : Configurer la structure du backend
Créez un répertoire src à l’intérieur du répertoire backend :
mkdir src
Créez les fichiers suivants à l’intérieur du répertoire src :
Étape 5 : Implémentez la couche d’accès aux données (DAL)
Ouvrez src/dal.py et ajoutez le contenu suivant :
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)
Ceci conclut la première partie de ce tutoriel, où nous avons mis en place la structure du projet et implémenté la couche d’accès aux données pour notre application todo en utilisant le stack FARM. Dans la prochaine partie, nous implémenterons le serveur FastAPI et créerons les endpoints API.
Implémentation du serveur FastAPI
Étape 6 : Implémentez le serveur FastAPI
Ouvrez src/server.py et ajoutez le contenu suivant :
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):
# Démarrage :
client = AsyncIOMotorClient(MONGODB_URI)
database = client.get_default_database()
# Assurez-vous que la base de données est disponible :
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)
# Rendre la main à l'application FastAPI :
yield
# Arrêt :
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()
Cette implémentation met en place le serveur FastAPI avec le middleware CORS, se connecte à MongoDB, et définit les endpoints API pour notre application todo.
Étape 7 : Configurez les variables d’environnement
Créez un fichier .env dans le répertoire racine avec le contenu suivant. Assurez-vous d’ajouter le nom de la base de données (« todo ») à la fin de « .mongodb.net/ ».
MONGODB_URI='mongodb+srv://beau:codecamp@cluster0.ji7hu.mongodb.net/todo?retryWrites=true&w=majority&appName=Cluster0'
Étape 8 : Créez un fichier docker-compose
Dans le répertoire racine de votre projet (farm-stack-todo), créez un fichier nommé compose.yml avec le contenu suivant :
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
Étape 9 : Configurez Nginx
Créez un répertoire nommé nginx à la racine de votre projet :
mkdir nginx
Créez un fichier nommé nginx.conf dans le dossier nginx avec le contenu suivant :
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;
}
}
Ceci conclut la Partie 2 du tutoriel, où nous avons mis en œuvre le serveur FastAPI, configuré les variables d’environnement, créé un fichier docker-compose et configuré Nginx. Dans la prochaine partie, nous nous concentrerons sur la mise en place du frontend React pour notre application de todo FARM stack.
Configuration du frontend React
Étape 10 : Créer l’application React
Naviguez vers le dossier frontend :
cd ../frontend
Créer une nouvelle application React en utilisant Create React App :
npx create-react-app .
Installez les dépendances supplémentaires :
npm install axios react-icons
Étape 11 : Configurer le composant principal App
Remplacez le contenu de src/App.js par le suivant :
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;
Étape 12 : Créer le composant ListTodoLists
Créer un nouveau fichier src/ListTodoLists.js avec le contenu suivant :
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;
Créer un nouveau fichier src/ListTodoLists.css avec le contenu suivant :
.ListToDoLists .summary {
border: 1px solid lightgray;
padding: 1em;
margin: 1em;
cursor: pointer;
display: flex;
}
.ListToDoLists .count {
padding-left: 1ex;
color: blueviolet;
font-size: 92%;
}
Étape 13 : Créer le composant ToDoList
Créer un nouveau fichier src/ToDoList.js avec le contenu suivant :
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;
Créer un nouveau fichier src/ToDoList.css avec le contenu suivant :
.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;
}
Étape 14 : Mettre à jour le fichier CSS principal
Remplacez le contenu de src/index.css par le suivant :
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;
}
Ceci conclut la Partie 3 du tutoriel, où nous avons configuré le frontend React pour notre application de todo FARM stack. Nous avons créé le composant principal App, le composant ListTodoLists pour afficher toutes les listes de todo et le composant ToDoList pour les listes de todo individuelles. Dans la prochaine partie, nous se concentrerons sur l’exécution et la testation de l’application.
Exécution et test de l’application
Étape 18 : Exécutez l’application en utilisant Docker Compose
-
Assurez-vous que Docker et Docker Compose soient installés sur votre système
-
Ouvrez un terminal au répertoire racine de votre projet (farm-stack-todo)
-
Générez et démarrez les conteneurs :
docker-compose up --build
- Une fois les conteneurs en cours d’exécution, ouvrez votre navigateur web et allez à http://localhost:8000
Étape 19 : Arrêt de l’application
-
Si vous exécutez l’application sans Docker :
-
Arrêtez le serveur de développement React en appuyant sur Ctrl+C dans son terminal
-
Arrêtez le serveur FastAPI en appuyant sur Ctrl+C dans son terminal
-
Arrêtez le serveur MongoDB en appuyant sur Ctrl+C dans son terminal
-
-
Si vous exécutez l’application avec Docker Compose :
-
Appuyez sur Ctrl+C dans le terminal où vous avez lancé docker-compose up
-
Exécutez la commande suivante pour arrêter et supprimer les conteneurs :
-
docker-compose down
« `
Félicitations ! Vous avez réussi à construire et à tester une application de pile FARM todo. Cette application montre l’intégration de FastAPI, React et MongoDB dans une application web full-stack.
Voici quelques prochaines étapes potentielles pour améliorer votre application:
-
Ajouter l’authentification et l’autorisation des utilisateurs
-
Implémenter la validation des données et la gestion des erreurs
-
Ajouter plus de fonctionnalités comme des échéances, des priorités ou des catégories pour les éléments todo
-
Améliorer l’UI/UX avec un design plus soigné
-
Écrire des tests unitaires et d’intégration pour le frontend et le backend
-
Configurer l’intégration continue et le déploiement (CI/CD) pour votre application
N’oubliez pas de maintenir vos dépendances à jour et de suivre les meilleures pratiques en matière de sécurité et de performance au fur et à mesure de votre développement d’application.
Conclusion et Prochaines Étapes
Félicitations pour avoir terminé ce tutoriel complet sur le stack FARM ! En construisant cette application de tâches à faire, vous avez acquis une expérience pratique avec certaines des technologies les plus puissantes et les plus populaires dans le développement web moderne. Vous avez appris comment créer une API backend robuste avec FastAPI, construire un frontend dynamique et réactif avec React, persister des données avec MongoDB, et containeriser votre application entière à l’aide de Docker. Ce projet a montré comment ces technologies travaillent ensemble de manière transparente pour créer une application web pleinement fonctionnelle et scalable.
Source:
https://www.freecodecamp.org/news/use-the-farm-stack-to-develop-full-stack-apps/