Au Cœur du Népal
Plateforme de réservation et d’exploitation pour une agence de trek francophone
- Performance
- 93
- Accessibility
- 96
- Best Practices
- 100
- SEO
- 100
- Année
- 2022
- Type
- Projet client
- Rôle
- Ingénierie full-stack et infrastructure
- Statut
- En ligne

Une plateforme en français pour une agence de trek basée à Katmandou : catalogue de treks et d’ascensions, réservations sur la capacité réelle des départs, espace d’équipe pour les guides et les affectations, et blog éditorial. Je l’ai construite de bout en bout, du site Next.js et de l’API Django à la pile Docker de production.
Contexte
Au Cœur du Népal est une agence de trek fondée à Katmandou par des Népalais francophones. Sa clientèle est francophone : tout le site public, le parcours de réservation et les outils de l’équipe fonctionnent en français.
L’agence avait besoin de plus qu’une vitrine : un catalogue de treks, d’ascensions et de programmes culturels avec des départs datés, des réservations qui respectent le nombre réel de places, et un espace privé où l’équipe, les guides et les porteurs voient leurs affectations.
Mon rôle
J’ai conçu le modèle de données et construit l’ensemble du système : le frontend Next.js, l’API REST Django, les interfaces d’administration et d’exploitation, et l’infrastructure de production (pile Compose, Nginx, sauvegardes, CI et déploiement).
Approche
Le backend est découpé en applications qui suivent le métier : catalogue (treks, ascensions, programmes), réservations, exploitation (équipe, disponibilités, affectations) et contenu (articles, témoignages, FAQ). Chaque application confine les écritures aux services et les lectures aux sélecteurs : les vues restent minces et chaque règle métier a une seule place.
Le site public est rendu statiquement et revalidé à la publication : quand un rédacteur enregistre, Django met en file une tâche Celery qui indique à Next.js les étiquettes à reconstruire. Les pages restent aussi rapides que des fichiers statiques, sans délai de cache pour les rédacteurs.
Architecture du système
Client
Navigateur · Voyageurs et équipe
Les voyageurs consultent les treks et réservent ; l’équipe et les guides utilisent l’administration privée. Tout est pensé en français.
Appelle Nginx
Périphérie
Nginx · TLS · deux domaines
Termine le TLS pour le site et l’API, fixe les en-têtes de cache des images optimisées et limite la taille des envois par route avant que la requête n’atteigne l’application.
Appelle Next.js, Django · DRF · Appelé par Navigateur
Application
Next.js · SSR · ISR · BFF
Rend les pages publiques de façon statique et les revalide à chaque publication. Les jetons d’authentification restent dans des cookies HttpOnly derrière des routes de même origine.
Appelle Django · DRF · Appelé par Nginx
Django · DRF · Gunicorn · API REST
API du catalogue, des réservations, de l’exploitation et du contenu, avec séparation services/sélecteurs. La capacité d’un départ est revérifiée sous verrou.
Appelle PostgreSQL 17, Redis 7, Cloudflare R2, LanguageTool · Appelé par Nginx, Next.js, GitHub Actions
Worker Celery · E-mails · revalidation
Envoie les e-mails transactionnels et indique à Next.js quelles pages reconstruire, hors du chemin de la requête, avec reprises.
Appelé par Redis 7
Beat · Planificateur
Planification en base pour le nettoyage des sessions et jetons expirés.
Appelle Redis 7 (asynchrone)
Données et services
PostgreSQL 17 · Stockage principal
Catalogue, départs, réservations, affectations et contenu. L’application s’y connecte avec un rôle restreint.
Appelé par Django · DRF, Sauvegardes
Redis 7 · Cache · file
Cache, broker Celery, état de révocation et de verrouillage, dans des bases logiques séparées pour qu’un vidage du cache ne perde aucune tâche.
Appelle Worker Celery (asynchrone) · Appelé par Django · DRF, Beat
Cloudflare R2 · media.aucoeurdunepal.com
Stockage objet des photos et vidéos. Les envois sont décodés dans des limites de pixels, réorientés, ramenés à 2560 px et débarrassés de leurs métadonnées.
Appelé par Django · DRF
LanguageTool · Instance privée
Un correcteur grammatical français auto-hébergé pour les rédacteurs. Les textes sont relus sans quitter le serveur.
Appelé par Django · DRF
Exploitation
Sauvegardes · Dump · restauration vérifiée
Sauvegardes planifiées de la base, dont la restauration est éprouvée par la suite de validation de production plutôt que supposée.
Appelle PostgreSQL 17 (asynchrone)
GitHub Actions · Tests · publication · déploiement
Les vérifications backend, frontend et conteneurs conditionnent la publication des images sur GHCR ; le déploiement utilise exactement les images validées, immuables.
Appelle Django · DRF (asynchrone)
Décisions techniques
Pourquoi Django REST Framework derrière un frontend Next.js ?
- Décision
Une API Django distincte détient toutes les données et règles métier ; Next.js se charge du rendu et ne touche jamais la base.
- Pourquoi
Les problèmes les plus durs de l’agence sont relationnels : départs, capacité, affectations, permissions. L’ORM de Django, ses migrations, ses transactions et son admin y répondent directement, et la même API sert le site public, les outils de l’équipe et l’administration.
- Alternatives envisagées
Des route handlers Next.js avec un ORM comme Prisma, ou un CMS headless hébergé.
- Compromis
Deux environnements d’exécution à construire, déployer et surveiller, et un contrat d’API à maintenir. En échange, la logique métier est testable en un seul endroit, indépendamment du rendu.
Pourquoi des jetons en cookies HttpOnly derrière un BFF ?
- Décision
Les requêtes du navigateur passent par des route handlers Next.js de même origine, qui conservent les jetons dans des cookies HttpOnly et appellent Django côté serveur.
- Pourquoi
Aucun script de la page ne peut lire un jeton, et la rotation des jetons avec détection de rejeu fonctionne sans que le navigateur manipule de secret.
- Alternatives envisagées
Des jetons dans localStorage avec un en-tête Authorization.
- Compromis
Un saut supplémentaire pour les appels authentifiés, et une vérification d’origine sur chaque mutation.
Interface


Difficultés d’ingénierie
Une réservation pouvait tomber sur un départ tout juste fermé
Problème
La création d’une réservation vérifiait la capacité sous verrou, mais vérifiait avant de prendre ce verrou si le départ était encore ouvert (ni fermé, ni annulé, ni passé).
Contrainte
Les reprises devaient rester idempotentes : un client qui renvoie après un délai dépassé doit retrouver sa réservation, pas en créer une seconde.
Solution
La vérification d’éligibilité est passée dans la transaction, après la recherche de reprise idempotente et sous le même verrou d’inventaire que la vérification de capacité.
Résultat
Capacité et éligibilité sont désormais décidées de façon atomique, et une ancienne saisie malformée renvoie une erreur de validation au lieu d’une erreur de base.
Des envois légers sur disque mais énormes en mémoire
Problème
Une image compressée peut se décoder en un bitmap énorme, et les photos originales portaient des métadonnées privées (dont le GPS) et des dimensions bien supérieures à tout affichage du site.
Analyse
Des tests avec des fichiers construits pour l’occasion ont montré que le décodeur allouait la mémoire avant toute vérification de taille.
Solution
Les pixels et les images d’animation sont bornés avant décodage ; les images sont orientées, ramenées à 2560 px au plus, réencodées en WebP et débarrassées de leurs métadonnées.
Résultat
Des fichiers plus légers, aucune position GPS issue des téléphones des guides, et des tests sur la suppression EXIF, la transparence, les animations et les fichiers invalides. Les médias existants demandent une migration séparée et relue.
robots.txt cachait le site au moteur de rendu qu’il devait servir
Problème
Le robots.txt en ligne interdisait /_next/, qui contient les scripts et images optimisées nécessaires aux robots pour rendre une page Next.js ; par ailleurs, la collecte du plan de site s’arrêtait aux 100 premiers éléments.
Solution
Les ressources de rendu sont de nouveau accessibles, les routes privées sont exclues explicitement, et le plan de site comme le catalogue parcourent toutes les pages de l’API.
Résultat
Les moteurs de recherche voient les pages comme les visiteurs, et un grand catalogue ne disparaît plus du plan de site.
Performance, sécurité et accessibilité
Une revue qualité de production, en septembre 2026, a couvert la sécurité, la performance et l’accessibilité, sur le site en ligne comme sur les conteneurs de production. Elle a conduit à des mises à jour de dépendances fermant toutes les alertes ouvertes, à des limites de décodage des images envoyées, à un menu mobile qui retient le focus, à l’accès des robots aux ressources de rendu et à une pagination complète du plan de site. Les tests backend tournent sur PostgreSQL ; les tests navigateur, sur les vraies images de production en HTTPS.
Infrastructure et déploiement
La production est une pile Compose unique sur un VPS : Nginx, Next.js, Gunicorn, un worker Celery et Beat, PostgreSQL 17, Redis 7, une instance privée de LanguageTool et un service de sauvegarde. Les images sont construites une fois par commit, publiées sur GHCR seulement après la réussite des vérifications backend, frontend et conteneurs, puis déployées par référence immuable : ce qui tourne est exactement ce qui a été testé.
Enseignements
La plupart des défauts sérieux se trouvaient aux frontières (entre un verrou et une vérification, entre un envoi et un décodeur, entre robots.txt et le moteur de rendu), pas à l’intérieur d’un composant. C’est en testant les vraies images de production, et non un serveur de développement, qu’ils sont apparus.
Technologies
- TypeScript
- Next.js
- Django
- Django REST Framework
- Celery
- PostgreSQL
- Redis
- Cloudflare R2
- Docker
- Nginx
- GitHub Actions
- Playwright
- LanguageTool