À la fin de ce chapitre, vous serez capables de :
Expliquer ce qu’est un Backend-as-a-Service (BaaS) et pourquoi il facilite le développement mobile
Comprendre l’intérêt du cloud dans une application moderne
Faire la différence entre Supabase, Firebase, Appwrite, AWS Amplify, etc.
Gérer des données distantes (CRUD), la synchro cloud ↔ local et la gestion des permissions
Intégrer dans une app mobile :
Les applications mobiles modernes doivent :
Cela implique généralement :
Créer tout cela à la main signifie :
💥 Résultat : long, coûteux, complexe… surtout pour un projet mobile “simple”.
Un backend cloud apporte :
💡 Pour un module pédagogique ou une app interne, un BaaS est souvent le meilleur compromis.
Exemple : une app où des élèves stockent leurs notes et photos de projets.
Un BaaS = un backend prêt à l’emploi, hébergé dans le cloud.
Il fournit les briques principales d’un backend moderne, sans :
Aucune infrastructure à gérer (backups, patchs, sécurité bas niveau)
Développement rapide grâce aux SDK et API prêtes à l’emploi
Sécurité intégrée : permissions, règles d’accès, JWT, etc.
Scalabilité automatique (le service s’adapte à la charge)
Particulièrement adapté aux apps mobiles :
Dépendance au fournisseur (vendor lock-in)
Coûts qui peuvent augmenter avec :
Moins flexible qu’un backend 100 % sur mesure
💬 Dans un contexte pédagogique ou de projet interne, ces limites sont souvent acceptables.
Cycle classique d’un échange entre une app mobile et un backend cloud :
L’utilisateur saisit : email + mot de passe (ou Google, Apple, etc.).
L’app envoie ces infos au backend :
POST /auth/v1/token HTTP/1.1
Host: api.mon-backend.com
Content-Type: application/json
{
"email": "user@example.com",
"password": "super-secret"
}
Le backend :
vérifie les identifiants
génère un JWT (JSON Web Token) avec :
user_idL’app stocke ensuite le token dans un stockage sécurisé :
// Exemple pseudo-code avec SecureStorage
await SecureStorage.set({
key: 'access_token',
value: jwtToken
})
🔐 À partir de là, l’app n’envoie plus le mot de passe, uniquement le token à chaque requête.
À chaque appel au backend, l’app ajoute :
GET /rest/v1/notes HTTP/1.1
Host: api.mon-backend.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
En TypeScript (Ionic + fetch) :
const token = await SecureStorage.get({ key: 'access_token' })
const res = await fetch('https://api.mon-backend.com/rest/v1/notes', {
headers: {
Authorization: `Bearer ${token.value}`,
'Content-Type': 'application/json'
}
})
const data = await res.json()
Côté backend, pour chaque requête :
Vérification du token (signature, expiration)
Lecture des claims (ex. user_id, role)
Application des règles de sécurité :
Exemple de règle (pseudo-SQL) :
-- Un utilisateur ne peut lire que ses propres notes
SELECT * FROM notes
WHERE user_id = auth.uid();
Si la permission est refusée →
401ou403.
Si tout est OK, le backend renvoie un JSON adapté à l’utilisateur.
Exemple Supabase :
GET /rest/v1/notes?user_id=eq.214 HTTP/1.1
Host: xyz.supabase.co
Authorization: Bearer <token>
apikey: <public-anon-key>
Réponse :
[
{
"id": 42,
"user_id": 214,
"title": "Note de cours",
"content": "Backend as a Service",
"created_at": "2025-11-19T10:15:00Z"
}
]
L’app :
Pour un usage fluide et offline-first, l’app garde une copie locale :
Exemple pseudo-code (Pinia) :
const useNotesStore = defineStore('notes', {
state: () => ({
notes: [] as Note[]
}),
actions: {
async fetchNotes() {
const token = await SecureStorage.get({ key: 'access_token' })
const res = await fetch('https://api.mon-backend.com/rest/v1/notes', {
headers: { Authorization: `Bearer ${token.value}` }
})
this.notes = await res.json()
// TODO: sauvegarder aussi en local (SQLite / Capacitor Storage)
}
}
})
✅ Interface réactive et responsive, même après redémarrage.
Quand l’app est hors ligne :
les actions utilisateur sont stockées localement (queue)
exemples :
CREATE_NOTEUPDATE_NOTEDELETE_NOTEExemple de type :
type PendingAction = {
type: 'CREATE_NOTE' | 'UPDATE_NOTE' | 'DELETE_NOTE'
payload: any
createdAt: string
}
La queue peut être stockée :
Dès que la connexion revient :
💡 Même modèle que l’on utilise avec Supabase, Firebase, Appwrite, etc. Auth → token → requêtes signées → règles de sécurité → JSON → cache local → sync offline.
Supabase = BaaS construit autour de PostgreSQL.
Objectif : une alternative open-source aux solutions propriétaires, basée sur des standards :
Supabase repose sur PostgreSQL :
données structurées dans des tables
relations, contraintes, clés étrangères
idéal pour des données cohérentes et liées :
Supabase génère automatiquement :
une API REST pour chaque table
une API GraphQL (optionnelle)
une console web pour :
Supabase propose un système d’auth complet :
Les règles de sécurité sont basées sur Row Level Security (RLS) :
Les RLS permettent de définir qui peut lire / écrire quelle ligne dans une table, en fonction :
- de l’utilisateur connecté
- de son rôle
- du contexte (
auth.uid()…)
Supabase inclut un service de stockage de fichiers :
Organisation en buckets (comme Amazon S3).
L’accès aux fichiers suit :
Supabase intègre :
un système realtime (basé sur PostgreSQL) :
des Edge Functions (fonctions serverless) pour :
Permet de créer des interactions dynamiques dans l’app mobile.
L’approche SQL permet d’apprendre :
const { data, error } = await supabase
.from('notes')
.insert([
{ user_id: '214', title: 'Note de cours', content: 'Contenu de la note' }
])
🎯 Une seule instruction côté client → Supabase gère l’auth, la permission, l’écriture en base, et le retour JSON.
Firebase = plateforme BaaS de Google.
Points forts :
Deux systèmes principaux :
Ce modèle NoSQL est :
très flexible
sans relations strictes (comme en SQL)
performant pour :
Lorsque des données changent :
Idéal pour :
Firebase propose :
Sessions :
Firebase propose :
🎯 Très pratique pour des apps simples qui doivent stocker des médias utilisateur.
Firebase est particulièrement adapté :
Le NoSQL nécessite :
un modèle de données réfléchi
éviter :
💬 Firebase convient mieux aux données peu structurées ou très dynamiques.
Deux approches :
Les deux offrent :
Mais la philosophie est différente.
Supabase → structure relationnelle, claire, avec contraintes → idéal pour applications avec modèle de données structuré
Firebase → documents & collections, très flexible → idéal pour données désynchronisées, temps réel
| Critère | Supabase | Firebase |
|---|---|---|
| Type de base | Relationnelle (PostgreSQL) | NoSQL (Firestore) – Documents / Collections |
| API | REST + GraphQL natifs | SDK propriétaires uniquement |
| Auth | JWT, flexible (Email, OAuth, Magic Links) | Auth Google (simple / robuste), Email, téléphone, social |
| Realtime | Oui | Oui (très optimisé) |
| Storage | Buckets type S3 | Cloud Storage |
| Open-source | Oui | Non |
| Facile pour débutants | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Pertinence pédagogique | ⭐⭐⭐⭐⭐ (concepts universels) | ⭐⭐ (NoSQL uniquement) |
| Idéal pour… | projets structurés, SQL | prototypes, apps temps réel |
💬 En résumé
- Firebase : parfait pour démarrer vite, temps réel, concepts NoSQL.
- Supabase : idéal pour apprendre SQL + API REST + sécurité.
✅ Souvent auto-hébergé → contrôle total des données.
Solution BaaS d’AWS :
⚠️ Puissant mais parfois complexe pour un débutant. Idéal pour projets à forte scalabilité dans l’écosystème AWS.
Backendless propose :
Atouts :
🎯 Adapté à ceux qui veulent une solution complète avec UI d’admin riche.
Back4app (basé sur Parse Server) propose :
Atouts :
🎯 Intéressant si vous aimez l’approche Parse et une doc accessible.
La synchronisation entre :
est centrale pour une app mobile moderne.
🎯 Objectif : Offrir une expérience fluide, continue, fiable, même avec un réseau instable.
Parce que les données doivent être :
Exemple : une app de notes avec sync entre smartphone, tablette et web.
L’app utilise SQLite pour enregistrer une note hors-ligne
Lorsque la connexion revient :
Au démarrage :
// Lecture depuis SQLite au démarrage
const notes = await db.selectFrom('notes').selectAll().execute()
store.notes = notes
✅ Ouverture instantanée + fonctionnement offline garanti.
Si le réseau est disponible :
const { data } = await supabase
.from('notes')
.select('*')
.eq('user_id', user.id)
await saveToLocalDB(data)
💡 L’UI reste fluide → les nouvelles données remplacent progressivement l’ancien cache.
Quand l’utilisateur agit sans réseau :
L’app :
type PendingAction = {
id: string
type: 'CREATE_NOTE' | 'UPDATE_NOTE' | 'DELETE_NOTE'
payload: any
timestamp: number
}
🧠 La queue mémorise “ce que l’utilisateur voulait faire”.
Avec Capacitor (Ionic) :
import { Network } from '@capacitor/network'
Network.addListener('networkStatusChange', status => {
if (status.connected) {
syncPendingActions()
}
})
Fonction de synchronisation :
async function syncPendingActions() {
for (const action of queue) {
await sendToBackend(action)
markActionSynced(action.id)
}
}
💡 Synchronisation automatique et silencieuse.
Conflit = même donnée modifiée :
Stratégies :
if (local.updated_at > remote.updated_at) {
// garder la version locale
upload(local)
} else {
// garder la version cloud
saveToLocalDB(remote)
}
🧩 Le choix dépend du type d’application (notes, todo, documents, finances, etc.).
Une app offline-first ne “poll” pas le réseau.
Avec Ionic-Vue + Capacitor Network :
🎯 L’app réagit aux changements de réseau, elle ne les surveille pas en boucle.
import { Network } from '@capacitor/network'
const status = await Network.getStatus()
console.log('Connected at startup:', status.connected)
if (!status.connected) {
enterOfflineMode()
}
Exemple de sortie :
{
"connected": false,
"connectionType": "none"
}
L’app sait tout de suite si elle doit charger :
- uniquement les données locales (offline)
- ou lancer une synchronisation cloud
iOS / Android envoient des événements :
import { Network } from '@capacitor/network'
Network.addListener('networkStatusChange', status => {
console.log('Network changed:', status)
if (status.connected) {
syncPendingActions()
} else {
enterOfflineMode()
}
})
C’est l’OS qui prévient l’app, pas l’inverse.
type PendingAction = {
id: string
type: 'CREATE' | 'UPDATE' | 'DELETE'
payload: any
timestamp: number
}
let queue: PendingAction[] = []
export function addOfflineAction(action: PendingAction) {
queue.push(action)
// TODO : sauvegarder en SQLite ou Capacitor Preferences
}
Puis :
// Exemple lorsqu’une note est éditée hors-ligne
addOfflineAction({
id: crypto.randomUUID(),
type: 'UPDATE',
payload: { id: 42, title: 'Nouvelle valeur' },
timestamp: Date.now()
})
L’UI se met à jour immédiatement → la sync viendra plus tard.
async function syncPendingActions() {
for (const action of queue) {
await sendToBackend(action) // requête API
action.synced = true
}
// Nettoyage de la queue
queue = queue.filter(a => !a.synced)
}
✅ L’utilisateur ne fait rien. La synchronisation est automatique et fiable.
L’app récupère l’état réseau initial
L’OS signale les changements (online / offline)
Les actions offline vont dans une queue
Quand le réseau revient :
🎯 Modèle utilisé par : WhatsApp, Notion, Google Drive, etc.
Documentation Supabase https://supabase.com/docs
Guides Supabase – Auth, Database, Storage https://supabase.com/docs/guides
API REST Supabase (PostgREST) https://supabase.com/docs/guides/api/rest
Documentation Firebase https://firebase.google.com/docs
Firestore – Data Modeling Guide https://firebase.google.com/docs/firestore/data-model
Firebase Authentication https://firebase.google.com/docs/auth
Appwrite Documentation https://appwrite.io/docs
AWS Amplify Documentation https://docs.amplify.aws/
PostgreSQL Documentation https://www.postgresql.org/docs/
Understanding JSON Web Tokens (JWT.io) https://jwt.io
Offline-first Architecture Principles https://offlinefirst.org
MDN – REST API Concepts https://developer.mozilla.org/docs/Glossary/REST