12. Déploiement
En développement, l'application tourne sous le contrôle de [ng serve] : compilée à la volée, avec les vérifications et les messages d'[Angular] pour le développeur, et rechargée à chaque modification. Pour la livrer, on la construit : [ng build] produit une application web optimisée, sans outils de développement. C'est elle qu'on dépose sur un serveur web, et c'est ELLE AUSSI que [Capacitor] emballe dans l'application [Android] : il n'y a qu'une construction, pour les deux plates-formes.
12.1. La version web
- Construire le client. Dans le dossier [rdvmedecins_ionic] :
La console affiche (la liste des fichiers a été raccourcie) :
Sans option, [ng build] utilise la configuration [production] d'[angular.json] : le code est optimisé (les noms raccourcis, les espaces et les commentaires retirés, le code inutilisé éliminé), et les vérifications réservées au développement disparaissent. Le compilateur a traduit l'application ([TypeScript], gabarits, [Angular], [Ionic]) en [JavaScript], dans le dossier [dist/rdvmedecins_ionic/browser/] :
- [index.html] : LA page de l'application, qui charge les fichiers [JavaScript] et la feuille de style. Contrairement au client [Flutter], l'application produit du HTML ordinaire, que le navigateur affiche lui-même ;
- les « morceaux » (chunks) initiaux, chargés au démarrage : [Angular], [Ionic], le routeur, les services, le gabarit. 1,1 Mo, soit environ 220 ko une fois compressés par le serveur : c'est l'estimation de la dernière colonne ;
- les morceaux « paresseux » (lazy) : un par écran ([connexion], [mon-compte]...), grâce au [loadComponent] des routes (cf. [app.routes.ts]), et quelques autres pour les composants d'[Ionic] chargés à la demande. Chacun n'est téléchargé qu'à la première visite de l'écran ;
- le nom de chaque fichier contient une empreinte de son contenu ([main-LY7QVQC3.js]) : le serveur peut demander au navigateur de les garder en cache indéfiniment, puisqu'une nouvelle version aura de nouveaux noms ;
- [i18n/] et [favicon.ico] : le contenu du dossier [public/], copié tel quel ;
- [angular.json] fixe des « budgets » à la configuration [production] : un avertissement au-delà de 2 Mo pour les morceaux initiaux, une erreur au-delà de 5 Mo. L'application en est loin.
L'option [--define "SERVEUR='...'"] n'est pas nécessaire ici : servie par le serveur JSON, l'application utilise l'adresse relative de [config.ts] ([/api/...]).
-
La déposer dans le serveur. Le serveur JSON sait servir un client web compilé (fichier [src/web/client-spa.ts] du serveur) : ses fichiers sont cherchés dans le dossier [client/], à la racine du serveur, ou dans le dossier désigné par la variable d'environnement [DOSSIER_CLIENT] (fichier [src/config/chemins.ts] du serveur ; elle peut être écrite dans le fichier [.env]). Deux possibilités :
-
copier le CONTENU du dossier [dist/rdvmedecins_ionic/browser/] dans un dossier [client/] à la racine du serveur JSON ([rdvmedecins-nestjs-json/client/index.html], [rdvmedecins-nestjs-json/client/main-LY7QVQC3.js]...) ;
- ou désigner directement le dossier produit par [ng build] :
(un chemin relatif à la racine du serveur, ou un chemin absolu). Puis on relance le serveur. Il affiche :
L'application complète est alors à l'adresse http://localhost:8080. Le serveur sert les fichiers du client, et renvoie [index.html] pour toute autre adresse hors de /api : recharger la page /agenda?idMedecin=1&jour=2026-10-13 fonctionne (c'est le routeur du client qui affiche l'écran de cette adresse). La page et l'API viennent de la même origine : pas de CORS, et le cookie du jeton circule.
Pour l'essayer sans arrêter le serveur de développement, on peut lancer une SECONDE instance du serveur JSON sur un autre port, en donnant les deux variables dans la ligne de commande (ici sous [Linux] ou [macOS] ; sous [Windows], avec [PowerShell], on les définit d'abord par [$env:PORT=8090] et [$env:DOSSIER_CLIENT='...']) :
L'application est alors à l'adresse http://localhost:8090 ; les deux instances partagent la même base de données. C'est ainsi que la version web de ce client a été vérifiée : connexion, agenda, rechargement de la page sur une adresse /agenda?..., titre de l'onglet traduit.
On peut aussi déposer le client sur n'importe quel serveur web ([nginx], [Apache]...), à condition de lui faire transmettre les requêtes /api au serveur JSON (un « reverse proxy »), et de lui faire renvoyer [index.html] pour les adresses inconnues : c'est ce que fait, en développement, [ng serve].
12.2. La version Android
La construction de l'application [Android] demande [Android Studio] et le SDK d'[Android] (cf. chapitre « Mise en place de l'environnement de travail »). [Capacitor] 8 exige un JDK 21 ou plus récent : celui qui est livré avec [Android Studio] convient.
- Créer le projet [Android], une seule fois :
La commande fabrique le dossier [android/] : un projet [Android Studio] complet (un projet [Gradle]), qui contient une activité [Android] affichant une [WebView], et les greffons de [Capacitor] utilisés par l'application ([@capacitor/app], [@capacitor/preferences]). Ce dossier fait partie du projet : on le garde (et on l'enregistre avec les sources, dans [git]), car c'est là qu'on règle le nom, l'icône, la signature de l'application.
Si le JDK est un [Java] 25 (celui des versions récentes d'[Android Studio]), la version de [Gradle] choisie par [Capacitor] (8.14) ne sait pas le faire fonctionner ([Unsupported class file major version 69]) : on passe à [Gradle] 9.1 dans le fichier [android/gradle/wrapper/gradle-wrapper.properties] (cf. exemple 01) :
- Construire l'application web, puis la copier dans le projet [Android] :
- ligne 1 : l'application web, dans [dist/rdvmedecins_ionic/browser/] ;
- ligne 2 : [cap sync] copie ce dossier (le [webDir] de [capacitor.config.ts]) dans [android/app/src/main/assets/public/], ainsi que la configuration de [Capacitor] ([capacitor.config.json]), et met à jour les greffons. Avec [server.cleartext] à [true], elle ajoute aussi l'attribut [android:usesCleartextTraffic="true"] au manifeste des greffons, fusionné à la construction avec celui de l'application : les requêtes [http://] (non chiffrées) vers le serveur de développement sont permises.
Il faut refaire ces deux commandes après chaque modification du code de l'application.
- La lancer :
La commande liste les appareils disponibles (les émulateurs créés dans [Android Studio], les téléphones branchés en USB avec le « débogage USB » activé), construit l'application, l'installe sur l'appareil choisi, et la démarre. On peut aussi ouvrir le projet dans [Android Studio] ([npx cap open android]) et utiliser son bouton « Run ».
Sur l'émulateur, l'application joint le serveur à l'adresse [http://10.0.2.2:8080] (cf. [config.ts]). Sur un vrai téléphone, relié au même réseau Wi-Fi que l'ordinateur, il faut lui donner l'adresse de l'ordinateur sur ce réseau ([ipconfig] sous [Windows]), à la construction :
Le pare-feu de l'ordinateur doit laisser passer les connexions entrantes sur le port 8080.
-
La mettre au point. L'application est une page web, affichée par la [WebView] : les outils de développement de [Chrome], sur l'ordinateur, peuvent l'inspecter. On ouvre l'adresse [chrome://inspect] : l'application de l'émulateur ou du téléphone y apparaît ; « inspect » ouvre la console, l'inspecteur d'éléments, et l'onglet « Réseau » (les requêtes vers l'API passant par [CapacitorHttp], c'est-à-dire par le code natif, elles y sont moins visibles que sur le web : la console de [Logcat], dans [Android Studio], les affiche).
-
Un fichier d'installation (APK) de mise au point. Dans [Android Studio] : menu « Build > Generate App Bundles or APKs > Generate APKs ». Ou, en ligne de commande, dans le dossier [android/] :
(sous [Windows] : [gradlew assembleDebug]). Le fichier est produit dans [android/app/build/outputs/apk/debug/app-debug.apk]. Il est signé avec la clé de débogage d'[Android Studio] : il suffit pour des essais. On l'installe sur un téléphone branché par [adb install app-debug.apk], ou en copiant le fichier sur le téléphone et en l'ouvrant ([Android] demande alors d'autoriser l'installation d'applications « de sources inconnues »).
-
Une version de production, signée. Une application publiée doit être signée avec une clé propre au développeur, gardée précieusement (toutes les mises à jour devront être signées avec la même clé) :
-
dans [Android Studio] : menu « Build > Generate Signed App Bundle or APK... », qui permet aussi de créer la clé (un fichier « keystore », protégé par un mot de passe) ;
- ou en ligne de commande : on crée la clé avec l'outil [keytool] du JDK, puis on construit la version signée :
- ligne 1 : crée la clé, dans le fichier [rdvmedecins.jks] (la commande demande le nom du développeur, son organisation...) ;
- ligne 2 : [cap build] lance la construction [Gradle] de la version « release » ([assembleRelease]), et signe le fichier produit avec cette clé (le chemin de la clé est relatif au dossier [android/]). [--signing-type apksigner] : la signature est faite par l'outil [apksigner] du SDK, dont le dossier, [build-tools/<version>], doit être dans le PATH ; sans cette option, [Capacitor] utilise [jarsigner], dont la signature n'est plus acceptée par les versions récentes d'[Android] pour un APK. [--androidreleasetype AAB] produirait un « Android App Bundle », le format qu'attend le [Google Play Store].
On peut aussi décrire la signature dans le fichier [android/app/build.gradle] (un bloc [signingConfigs], cf. la documentation d'[Android] « Sign your app »), et lancer directement [./gradlew assembleRelease] (un APK, dans [android/app/build/outputs/apk/release/]) ou [./gradlew bundleRelease] (un App Bundle). Le mot de passe de la clé ne doit pas être écrit dans un fichier enregistré avec les sources.
-
Pour une vraie publication :
-
le nom de l'application se règle dans [capacitor.config.ts] ([appName]) et dans [android/app/src/main/res/values/strings.xml] ; son icône, dans les dossiers [android/app/src/main/res/mipmap-*/] (l'outil [@capacitor/assets] les fabrique toutes à partir d'une seule image) ;
- le serveur doit être en HTTPS (les mots de passe circulent) : on retire [server.cleartext] de [capacitor.config.ts], et l'on construit l'application avec l'adresse publique du serveur ([--define "SERVEUR='https://rdvmedecins.exemple.fr'"]).
12.3. Le serveur en production
En production réelle, on n'oubliera pas le fichier [.env] du serveur :
- un [JWT_SECRET] long et aléatoire ;
- [CAPTCHA_TEST] vide ;
- [COOKIE_SECURE=true] si le site est servi en HTTPS (il doit l'être) : le cookie ne circule alors que sur des connexions chiffrées ;
- [TRUST_PROXY=true] si le serveur est placé derrière un proxy ([nginx]...), pour que la limitation des tentatives voie l'adresse IP réelle des clients.
Le serveur n'a aucune règle CORS à déclarer : sur le web, le client est servi par le serveur lui-même (même origine) ; sur [Android], les requêtes partent du code natif ([CapacitorHttp]), auquel CORS ne s'applique pas.
On lance alors le serveur compilé : [npm run build], puis [npm run start:prod].