Extensions JavaScript dans PocketBase : JSVM, Hooks et TypeScript en 2026
PocketBase est un backend open-source léger qui intègre une base de données SQLite, une API REST et des outils d'authentification, tout en un seul fichier exécutable. Pour les petites entreprises et startups qui cherchent à développer rapidement sans une équipe backend dédiée, c'est un outil idéal. Mais que faire quand les fonctionnalités par défaut ne suffisent pas ? C'est là qu'interviennent les extensions JavaScript via JSVM (JavaScript Virtual Machine).
Dans cet article, nous explorons comment étendre PocketBase avec du code JavaScript pur, sans avoir besoin de compiler en Go. Nous couvrons la configuration, les variables globales, les hooks disponibles, le support TypeScript et des exemples concrets. Si vous êtes débutant en backend ou si votre startup protège un MVP (Minimum Viable Product), ces extensions vous permettront d'ajouter de la logique métier personnalisée en quelques minutes. Tout est basé sur la documentation officielle de PocketBase, mise à jour en 2025.
1. Le dossier pb_hooks et la JSVM : Les bases pour démarrer
Pour activer les extensions JavaScript, commencez par créer un dossier spécifique. C'est simple et direct, sans configuration complexe.
Créez un dossier nommé pb_hooks à côté de votre exécutable PocketBase. Par exemple, si votre fichier pocketbase.exe (ou pocketbase sur Linux/Mac) est dans /mon-projet/, placez pb_hooks au même niveau : /mon-projet/pb_hooks/.
Dans ce dossier, ajoutez vos fichiers JavaScript avec l'extension .pb.js. Par exemple, main.pb.js pour le fichier principal. PocketBase charge ces fichiers dans l'ordre alphabétique des noms de fichiers. Cela signifie que si vous avez 01-init.pb.js et 02-hooks.pb.js, le premier s'exécutera avant le second.
La JSVM est intégrée à PocketBase depuis la version 0.17. Elle utilise l'engine Goja, compatible ES5, pour exécuter du JavaScript côté serveur. Pas besoin de Node.js ou d'un environnement externe : tout tourne dans l'exécutable PocketBase.
Pour lancer : redémarrez simplement votre serveur PocketBase. Sur les systèmes Unix (Linux, Mac), modifiez un fichier .pb.js et le serveur se recharge automatiquement – parfait pour le développement rapide en startup.
Un point important : chaque hook ou route s'exécute dans un contexte isolé. Les variables déclarées en dehors d'un handler (comme const maVar = "test";) ne sont pas visibles à l'intérieur. Pour partager du code, utilisez des modules CommonJS avec require().
Exemple basique pour tester :
Dans pb_hooks/test.pb.js :
console.log("Extensions JS activées !");
Redémarrez PocketBase et vérifiez les logs : vous verrez le message. C'est prêt en deux minutes.
Pour les performances, ajustez le pool de runtimes avec le flag --hooksPool=50 au démarrage (défaut : 15). Plus de runtimes pour plus de concurrence, mais surveillez la mémoire – utile si votre SMB gère plusieurs utilisateurs simultanés.
2. Les variables globales : $app, $http, $os et plus
Une fois les hooks activés, vous accédez à des objets globaux puissants. Ils exposent les fonctionnalités de PocketBase en JavaScript, sans réinventer la roue.
Voici les principaux :
- __hooks : Le chemin absolu vers votre dossier
pb_hooks. Utile pour charger des modules locaux, commerequire(__hooks + "/utils.js"). - $app : L'instance principale de l'application PocketBase. Vous pouvez y accéder pour des opérations comme trouver un record (
$app.findRecordById("users", "id")), valider des données ou gérer les settings. - $apis : Des helpers pour créer des routes API personnalisées ou des middlewares. Par exemple,
$apis.requireSuperuser()pour vérifier les droits admin. - $os : Des primitives système, comme supprimer un dossier (
$os.removeAll("/tmp/fichiers")) ou exécuter une commande shell ($os.exec("ls", ["-la"])). Attention : limitez à des opérations sécurisées en production. - $security : Outils de sécurité low-level, comme générer un JWT (
$security.makeJWT(secret, claims)), chiffrer en AES ou créer des strings aléatoires. Idéal pour des tokens custom sans dépendre de libs externes.
Tous les noms sont en camelCase (par exemple, une méthode Go App.FindRecordById devient $app.findRecordById). Si une opération échoue, une exception JavaScript est levée – gérez-la avec try/catch.
Ces globals sont disponibles dans tous les handlers. Pour les startups, c'est un gain de temps : au lieu d'écrire une API complète, modifiez une réponse existante avec $app.
Exemple : Logger l'IP d'un utilisateur connecté.
onRecordAuthRequest((e) => {
console.log("Connexion depuis IP:", e.httpContext.clientIP());
e.next();
});
3. Tous les hooks disponibles : Plus de 80 façons d'étendre votre backend
Les hooks sont les cœurs des extensions JS. Ce sont des fonctions qui interceptent des événements du cycle de vie de PocketBase : création de records, authentification, envoi d'emails, etc. En 2025, il y en a plus de 80, regroupés par catégorie.
Chaque hook suit la signature function(e) { ... e.next(); }. L'objet e contient le contexte (app, record, request). Appelez toujours e.next() pour continuer la chaîne ; sinon, l'exécution s'arrête. Vous pouvez scoped les hooks à une collection spécifique, comme onRecordAfterCreateSuccess((e) => { ... }, "users").
Hooks au niveau App
Ces hooks gèrent l'initialisation et la maintenance globale.
onBootstrap: Au démarrage, pour charger des ressources (ex. : config DB).onSettingsReload: Quand les settings changent.onBackupCreateetonBackupRestore: Avant/après backups.onTerminate: À l'arrêt du serveur.onMailerSend: Pour tout email envoyé.
Exemple pour customiser un email :
app.on("onMailerSend", (e) => {
e.message.subject = "Votre mise à jour PocketBase";
e.next();
});
Hooks Mailer spécifiques à l'auth
Pour personnaliser les emails d'authentification (alertes, resets, OTP).
onMailerRecordAuthAlertSend: Alerte de connexion.onMailerRecordPasswordResetSend: Reset mot de passe.onMailerRecordVerificationSend: Vérification email.onMailerRecordEmailChangeSend: Changement d'email.onMailerRecordOTPSend: Envoi OTP.
Exemple pour ajouter un lien custom dans un reset :
app.on("onMailerRecordPasswordResetSend", (e) => {
e.message.htmlBody += "<p>Cliquez ici pour reset : <a href='https://votre-site.com/reset'>Lien</a></p>";
e.next();
});
Hooks Realtime
Pour les connexions SSE (Server-Sent Events) en temps réel.
onRealtimeConnectRequest: Nouvelle connexion client.onRealtimeSubscribeRequest: Abonnement à un channel.onRealtimeMessageSend: Envoi d'un message.
Exemple pour logger les abonnements :
app.on("onRealtimeSubscribeRequest", (e) => {
console.log("Abonnement à:", e.subscriptions);
e.next();
});
Hooks pour Records et Collections
Les plus utilisés pour CRUD. Il y en a des dizaines : avant/après validation, exécution, succès/erreur.
Pour Records (CRUD sur un enregistrement) :
- Création :
onRecordCreate,onRecordCreateExecute,onRecordAfterCreateSuccess,onRecordAfterCreateError. - Mise à jour :
onRecordUpdate,onRecordUpdateExecute,onRecordAfterUpdateSuccess, etc. - Suppression :
onRecordDelete, etc. - Spécial :
onRecordValidatepour valider les données,onRecordEnrichpour enrichir une réponse (ajouter des champs calculés).
Pour Collections (gestion des schémas) :
- Similaires :
onCollectionCreate,onCollectionAfterUpdateSuccess, etc.
Exemple pour valider l'âge lors de la création d'un user :
app.on("onRecordCreateRequest", (e) => {
if (e.record.get("age") < 18) {
return e.invalid("Âge minimum : 18 ans");
}
e.next();
}, "users");
Hooks pour Requêtes API
Interceptent les endpoints spécifiques.
- List/View/Create/Update/Delete pour records et collections :
onRecordsListRequest,onRecordViewRequest, etc. - Auth :
onRecordAuthRequest,onRecordAuthWithPasswordRequest,onRecordAuthWithOAuth2Request,onRecordRequestPasswordResetRequest, etc. - Autres :
onFileDownloadRequest,onBatchRequest,onSettingsUpdateRequest.
Exemple pour restreindre l'accès à un fichier :
app.on("onFileDownloadRequest", (e) => {
if (!e.httpContext.authRecord()) {
return e.error(401, "Authentification requise");
}
e.next();
});
Hooks pour Modèles Généraux
Appliqués à tout modèle DB (records, collections, logs) : onModelValidate, onModelCreate, etc. Utilisez-les pour du code générique, mais préférez les hooks spécifiques pour la sécurité des types.
Ces hooks couvrent 100% des interactions courantes. Pour une startup, commencez par les hooks records et auth – ils résolvent 80% des besoins custom sans toucher au code Go.
4. Support TypeScript : Développez avec autocomplétion et sécurité
PocketBase supporte TypeScript nativement depuis les versions récentes, sans bundler externe. C'est un atout pour les SMB qui veulent du code maintenable.
PocketBase fournit un fichier de déclarations ambient pb_data/types.d.ts (généré automatiquement dans votre dossier data). Ajoutez cette ligne au début de vos fichiers .pb.js :
/// <reference path="../pb_data/types.d.ts" />
Votre éditeur (VS Code, WebStorm) gagne alors l'autocomplétion, la documentation inline et la vérification des types. Par exemple, tapez $app.findRecordById et voyez les params et retours.
Si vous préférez, renommez en .pb.ts et compilez manuellement en JS (avec tsc), puis placez le .js résultant dans pb_hooks. L'engine exécute du JS pur, donc TypeScript est pour le dev seulement.
Exemple avec types :
/// <reference path="../pb_data/types.d.ts" />
app.on("onRecordAfterCreateSuccess", (e: RecordHookEvent) => {
const email: string = e.record.getString("email");
console.log(`Nouveau user : ${email}`);
e.next();
}, "users");
C'est transparent et gratuit – idéal pour les juniors en équipe qui évitent les erreurs de typage.
5. Exemples puissants : Applications concrètes pour votre startup
Passons à la pratique. Ces exemples montrent comment valider des données, intégrer des APIs externes, customiser des réponses et gérer des workflows. Tous testables en local.
Exemple 1 : Validation avancée et envoi d'email custom
Pour une app de e-commerce, validez un ordre et envoyez une confirmation via une API tierce (comme SendGrid, mais ici simulé avec $http).
Dans pb_hooks/orders.pb.js :
/// <reference path="../pb_data/types.d.ts" />
app.on("onRecordCreateRequest", (e: RecordCreateEvent) => {
const total = e.record.getFloat("total");
if (total <= 0) {
return e.invalid("Total doit être positif");
}
e.next();
}, "orders");
app.on("onRecordAfterCreateSuccess", (e: RecordAfterCreateEvent) => {
const orderId = e.record.id;
const email = e.record.getString("customerEmail");
// Appel API externe pour email
const response = $http.send({
method: "POST",
url: "https://api.sendgrid.com/v3/mail/send",
headers: { Authorization: "Bearer VOTRE_API_KEY" },
body: {
to: email,
subject: "Commande confirmée",
body: `Votre ordre ${orderId} est validé.`
}
});
if (response.statusCode !== 202) {
console.error("Erreur envoi email:", response.body);
}
e.next();
}, "orders");
Ça intercepte la création, valide, et envoie un email sans alourdir l'API de base.
Exemple 2 : Intégration OAuth2 custom et enrichissement de records
Pour une startup SaaS, ajoutez un hook OAuth pour tracker les logins GitHub et enrichir le profil user avec des données GitHub.
Dans pb_hooks/auth.pb.js :
app.on("onRecordAuthWithOAuth2Request", (e: RecordAuthWithOAuth2RequestEvent) => {
if (e.oauth2User.provider === "github") {
// Fetch infos GitHub via leur API
const ghResponse = $http.send({
url: `https://api.github.com/users/${e.oauth2User.userId}`,
headers: { Authorization: "token VOTRE_GITHUB_TOKEN" }
});
if (ghResponse.ok) {
const ghData = JSON.parse(ghResponse.body);
e.record.set("githubAvatar", ghData.avatar_url);
e.record.set("githubRepos", ghData.public_repos);
}
}
e.next();
}, "users");
app.on("onRecordEnrich", (e: RecordEnrichEvent) => {
if (e.record.collection().name === "users") {
e.record.set("fullName", `${e.record.getString("firstName")} ${e.record.getString("lastName")}`);
}
e.next();
}, "users");
Résultat : les users ont un avatar GitHub et un nom complet calculé, sans modifier la collection.
Exemple 3 : Route API personnalisée et cleanup automatique
Créez une route /api/stats pour dashboard, et un hook pour nettoyer les logs anciens.
Dans pb_hooks/routes.pb.js :
// Route custom
routerAdd("GET", "/api/stats", (e: ApiRequest) => {
const stats = $app.stats();
return e.json(200, { users: stats.recordCount("users"), uptime: $os.hostname() });
});
// Cleanup logs toutes les heures (simulé via cron, mais hook sur terminate pour exemple)
app.on("onTerminate", (e: TerminateEvent) => {
const oldLogs = $app.dao().findRecordsByFilter("logs", "created < datetime('now', '-7 days')");
oldLogs.forEach(log => $app.dao().deleteRecord(log));
console.log("Cleanup terminé.");
e.next();
});
Pour les cron réels, utilisez un hook sur onBootstrap pour scheduler, mais PocketBase n'a pas de cron built-in – intégrez via $os.exec.
Exemple 4 : Sécurité renforcée avec MFA custom
Ajoutez une vérification extra pour les admins.
Dans pb_hooks/security.pb.js :
app.on("onRecordAuthRequest", (e: RecordAuthRequestEvent) => {
const record = e.record;
if (record && record.getBool("isAdmin")) {
// Vérifiez MFA custom (simulé)
const mfaToken = e.httpContext.formData().get("mfa_token");
if (!mfaToken || mfaToken !== $security.randomString(6, record.id)) { // Token stocké en DB
return e.error(401, "MFA requis pour admins");
}
}
e.next();
});
Ces exemples totalisent moins de 100 lignes, mais couvrent validation, intégrations, routes et sécurité.
Limites et bonnes pratiques pour une utilisation en production
JSVM est puissante, mais rappelez-vous les limites : pas d'APIs Node comme fs ou fetch (utilisez $http et $os). Pas de setTimeout pour la concurrence – tout est synchrone. Pour les calculs lourds (crypto), préférez $security.
Bonnes pratiques :
- Chargez les modules avec
require(__hooks + "/mon-module.js")pour l'isolation. - Utilisez TypeScript pour tout, même les petits scripts.
- Testez en dev avec hot-reload, puis monitorez la mémoire en prod.
- Pour ESM, bundlez avec Rollup avant require().
- Évitez les mutations globales : chaque hook est isolé.
En 2025, JSVM reste ES5-compliant avec la plupart des ES6 features, mais vérifiez les quirks Goja (comme les maps Go en JS).
Conclusion : Étendez PocketBase sans effort pour booster votre startup
Les extensions JavaScript transforment PocketBase en un backend flexible, sans les overheads d'un framework full comme Express. Avec le dossier pb_hooks, les globals comme $app, plus de 80 hooks pour intercepter tout, et le support TypeScript, vous prototypez en heures ce qui prendrait des jours ailleurs.
Pour une SMB ou startup, c'est l'outil parfait : ajoutez de la validation, des emails custom, des intégrations APIs ou des routes sans rebuild. Commencez par un hook simple sur auth, et scalez. Téléchargez PocketBase, créez pb_hooks, et testez un exemple ci-dessus. Votre backend sera customisé en un après-midi.
Si vous avez des questions sur un hook spécifique, la doc officielle reste la référence. Prêt à étendre ? Lancez-vous ! 1win iniciar sesión