Cette API Express.js permet de gérer un backoffice pour une application de gestion de pharmacies, produits, utilisateurs, commandes, gardes, etc. Elle utilise PostgreSQL avec PostGIS pour la gestion des données géographiques.
- Fonctionnalités
- Pré-requis
- Installation
- Configuration
- Lancement en local
- Déploiement sur Render
- Structure des endpoints
- Exemples de requêtes
- Sécurité
- Gestion des pharmacies (CRUD, coordonnées géographiques)
- Gestion des produits et catégories
- Gestion des utilisateurs (CRUD, authentification, coordonnées)
- Gestion des commandes (CRUD, jointures, filtrage)
- Gestion des gardes (CRUD, pharmacies de garde, statut actif)
- Gestion des pharmaciens (CRUD, authentification, hashage des mots de passe)
- Node.js >= 14
- PostgreSQL >= 12 avec l'extension PostGIS
- Un compte GitHub (pour le déploiement sur Render)
- Cloner le projet
git clone <url-du-repo> cd api_express_backoffice
- Installer les dépendances
npm install
- Créer la base de données
- Exécute le script
script.sqldans PostgreSQL pour créer les tables et extensions nécessaires.
- Exécute le script
Crée un fichier .env à la racine du projet avec le contenu suivant :
PORT=3000
PGUSER=ton_user
PGPASSWORD=ton_mot_de_passe
PGHOST=localhost
PGPORT=5432
PGDATABASE=ta_baseNe pousse jamais ce fichier sur GitHub !
npm startL'API sera accessible sur https://api-express-backoffice-bdd.onrender.com/pharmacies
- Pousse ton code sur GitHub.
- Va sur https://dashboard.render.com/, crée un Web Service et connecte ton repo.
- Renseigne les variables d'environnement (voir section Configuration).
- Build command :
npm install(ou laisse vide) - Start command :
npm start - Clique sur Create Web Service.
- L'API sera accessible à l'URL fournie par Render.
GET /pharmacies: liste toutes les pharmaciesGET /pharmacies/:id: détails d'une pharmacie (coordonnées en {longitude, latitude})POST /pharmacies: créer une pharmaciePUT /pharmacies/:id: modifier une pharmacieDELETE /pharmacies/:id: supprimer une pharmacie
GET /produits: liste tous les produitsGET /produits/:id: détails d'un produitPOST /produits: créer un produitPUT /produits/:id: modifier un produitDELETE /produits/:id: supprimer un produitGET /produits/categorie/:id_categorie: produits d'une catégorie
GET /categories: liste toutes les catégoriesPOST /categories: créer une catégorieGET /categories/:id: détails d'une catégoriePUT /categories/:id: modifier une catégorieDELETE /categories/:id: supprimer une catégorieGET /categories/nom/:nom: recherche par nomGET /categories/:id/produits: catégorie avec ses produitsGET /categories/stats/count: stats nombre de produits par catégorie
GET /utilisateurs: liste tous les utilisateursPOST /utilisateurs: créer un utilisateurGET /utilisateurs/:id: détails d'un utilisateurPUT /utilisateurs/:id: modifier un utilisateurDELETE /utilisateurs/:id: supprimer un utilisateurPOST /utilisateurs/login: authentification
GET /pharmaciens: liste tous les pharmaciensPOST /pharmaciens: créer un pharmacienGET /pharmaciens/:id: détails d'un pharmacienPUT /pharmaciens/:id: modifier un pharmacienDELETE /pharmaciens/:id: supprimer un pharmacienPOST /pharmaciens/login: authentification
GET /commandes: liste toutes les commandesPOST /commandes: créer une commandeGET /commandes/:id: détails d'une commandePUT /commandes/:id: modifier une commandeDELETE /commandes/:id: supprimer une commandeGET /commandes/pharmacie/:id_pharmacie: commandes d'une pharmacieGET /commandes/utilisateur/:id_utilisateur: commandes d'un utilisateurGET /commandes/statut/:statut: commandes par statut
GET /gardes: liste toutes les gardesPOST /gardes: créer une gardeGET /gardes/:id: détails d'une gardePUT /gardes/:id: modifier une gardeDELETE /gardes/:id: supprimer une gardeGET /gardes/actives: gardes activesGET /gardes/pharmacie/:id_pharmacie: gardes d'une pharmacieGET /gardes/pharmacies/list: liste des pharmacies (ID + nom)
{
"nom": "Pharmacie du Marché",
"emplacement": "Marché central",
"telephone1": "0123456789",
"telephone2": "0987654321",
"coordonnees": "POINT(1.2075 6.1287)"
}{
"id_pharmacie": 1,
"nom_utilisateur": "pharma2024",
"mot_de_passe": "monSuperMotDePasse"
}{
"debut_garde": "2024-06-10T20:00:00",
"fin_garde": "2024-06-11T08:00:00",
"pharmacies_garde": [1, 2, 3],
"isactive": true
}Body :
{
"nom": "Pharmacie du Marché",
"emplacement": "Marché central",
"telephone1": "0123456789",
"telephone2": "0987654321",
"coordonnees": "POINT(1.2075 6.1287)"
}Réponse (GET /pharmacies/1) :
{
"id_pharmacie": 1,
"nom": "Pharmacie du Marché",
"emplacement": "Marché central",
"telephone1": "0123456789",
"telephone2": "0987654321",
"coordonnees": {
"longitude": 1.2075,
"latitude": 6.1287
},
"produits": [],
"commandes_resume": []
}Body :
{
"nom": "Pharmacie du Centre",
"emplacement": "Avenue de la Santé",
"telephone1": "0123456789",
"telephone2": "0987654321",
"coordonnees": "POINT(1.2100 6.1300)"
}Body :
{
"id_categorie": 1,
"libelle": "Paracétamol 500mg",
"description": "Antalgique et antipyrétique",
"unite": "boîte",
"prix_unitaire": 150,
"sur_ordonnance": false
}Réponse (GET /produits/1) :
{
"id_produit": 1,
"id_categorie": 1,
"libelle": "Paracétamol 500mg",
"description": "Antalgique et antipyrétique",
"unite": "boîte",
"prix_unitaire": 150,
"sur_ordonnance": false,
"nom_categorie": "Antalgiques"
}Body :
{
"id_categorie": 1,
"libelle": "Paracétamol 1g",
"description": "Antalgique puissant",
"unite": "boîte",
"prix_unitaire": 250,
"sur_ordonnance": true
}Body :
{
"nom": "Antalgiques",
"description": "Médicaments contre la douleur"
}Réponse (GET /categories/1) :
{
"id_categorie": 1,
"nom": "Antalgiques",
"description": "Médicaments contre la douleur"
}Body :
{
"nom": "Antalgiques",
"description": "Médicaments pour soulager la douleur et la fièvre"
}Body :
{
"nom": "Dupont",
"prenoms": "Jean",
"adresse_courant": "POINT(1.2075 6.1287)",
"telephone": "0123456789",
"email": "jean.dupont@email.com",
"mot_de_passe": "motdepasse123"
}Réponse (GET /utilisateurs/1) :
{
"id_utilisateur": 1,
"nom": "Dupont",
"prenoms": "Jean",
"adresse_courant": "POINT(1.2075 6.1287)",
"telephone": "0123456789",
"email": "jean.dupont@email.com"
}Body :
{
"nom": "Dupont",
"prenoms": "Jean-Pierre",
"adresse_courant": "POINT(1.2100 6.1300)",
"telephone": "0123456789",
"email": "jean.dupont@email.com"
}Body :
{
"id_pharmacie": 1,
"nom_utilisateur": "pharma2024",
"mot_de_passe": "monSuperMotDePasse"
}Réponse (GET /pharmaciens/1) :
{
"id_pharmacien": 1,
"id_pharmacie": 1,
"nom_utilisateur": "pharma2024",
"mot_de_passe": "$2b$10$...hash..."
}Body :
{
"id_pharmacie": 1,
"nom_utilisateur": "pharma2024",
"mot_de_passe": "nouveauMotDePasse"
}Body :
{
"id_pharmacie": 1,
"id_utilisateur": 1,
"produits": [
{
"id_produit": 1,
"libelle": "Paracétamol 500mg",
"quantite": 2,
"prix_unitaire": 150,
"prix_total": 300
}
],
"statut": "en cours",
"code_commande": "CMD001"
}Réponse (GET /commandes/1) :
{
"id_commande": 1,
"id_pharmacie": 1,
"id_utilisateur": 1,
"produits": [
{
"id_produit": 1,
"libelle": "Paracétamol 500mg",
"quantite": 2,
"prix_unitaire": 150,
"prix_total": 300
}
],
"statut": "en cours",
"code_commande": "CMD001",
"created_at": "2024-06-10T20:00:00.000Z",
"nom_pharmacie": "Pharmacie du Marché",
"nom_utilisateur": "Dupont"
}Body :
{
"id_pharmacie": 1,
"id_utilisateur": 1,
"produits": [
{
"id_produit": 1,
"libelle": "Paracétamol 1g",
"quantite": 1,
"prix_unitaire": 250,
"prix_total": 250
}
],
"statut": "livrée",
"code_commande": "CMD001"
}Body :
{
"debut_garde": "2024-06-10T20:00:00",
"fin_garde": "2024-06-11T08:00:00",
"pharmacies_garde": [1, 2, 3],
"isactive": true
}Réponse (GET /gardes/1) :
{
"id_garde": 1,
"debut_garde": "2024-06-10T20:00:00.000Z",
"fin_garde": "2024-06-11T08:00:00.000Z",
"pharmacies_garde": [1, 2, 3],
"isactive": true
}Body :
{
"debut_garde": "2024-06-12T20:00:00",
"fin_garde": "2024-06-13T08:00:00",
"pharmacies_garde": [2, 3],
"isactive": false
}- Les mots de passe des pharmaciens sont hashés avec bcrypt.
- Ne jamais exposer les mots de passe en clair.
- Ne jamais pousser le fichier
.envsur GitHub. - Utiliser HTTPS en production (Render le fait automatiquement).
Pour toute question ou problème, ouvre une issue sur le dépôt GitHub ou contacte le mainteneur du projet.