Créez votre première application

Ce parcours construit une application bext PRISM minimale à partir de zéro : la générer, examiner les deux fichiers de configuration, ajouter un layout racine et une page dont le loader lit la session et appelle le SDK de la plateforme, puis l'exécuter localement avec rechargement à chaud. Quand vous serez prêt à la mettre sur un domaine, poursuivez avec Déployer un site PRISM.

Générer le squelette

Créez une nouvelle application à partir du modèle de démarrage PRISM :

bext new my-app --template @bext/starter-prism
cd my-app

Cela met en place un paquet de workspace : bext.config.toml, tsconfig.json, une arborescence de routes src/app/ et un répertoire public/ pour les ressources statiques. D'autres modèles de démarrage existent (-blog, -docs, -saas, -api) — voir la Référence CLI.

Les fichiers de configuration

bext.config.toml

Le modèle de démarrage fournit une configuration minimale. Elle déclare une application PRISM qui surveille ses répertoires source pour le rechargement à chaud et effectue le rendu avec l'ISR :

[server]
app_dir = "."
static_dir = "public"

[framework]
type = "prism"

[build]
watch_dirs = ["src/app", "src/components", "src/lib"]
live_reload = true

[rendering]
mode = "isr"
revalidate = 600

Voir la Référence de configuration pour chaque section.

tsconfig.json

PRISM compile le JSX directement en chaînes de caractères, la configuration TypeScript pointe donc le JSX vers le framework :

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "@bext-stack/framework",
    "moduleResolution": "bundler",
    "paths": { "@/*": ["./src/*"] }
  }
}

Le layout racine

Le layout.tsx racine possède le document <html> et reçoit la page en tant que children. Chaque fichier qui utilise du JSX commence par le pragma du framework :

/** @jsxImportSource @bext-stack/framework */

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <title>My App</title>
      </head>
      <body>{children}</body>
    </html>
  );
}

Un composant est simplement une fonction renvoyant une chaîne ((props) => string | Promise<string>) ; rien n'est envoyé au navigateur tant que vous n'ajoutez pas d'island.

Une page avec un loader

Un page.tsx effectue le rendu de la route. Placez le chargement des données dans un loader — le composant de page ne voit jamais request, les cookies et les en-têtes se lisent donc dans le loader. Ici, le loader lit la session et incrémente un compteur dans le magasin KV via le SDK loopback :

/** @jsxImportSource @bext-stack/framework */
import { createSdk } from "@bext-stack/framework";
import { readSession } from "@/lib/session";

const sdk = createSdk("my-app");

export async function loader({ request }) {
  const session = readSession(request);
  const visits = Number((await sdk.kv.get("visits")) ?? 0) + 1;
  await sdk.kv.set("visits", visits, 3600); // valeur + TTL (secondes)
  return { user: session?.user ?? null, visits };
}

export default function Page({ data }) {
  return (
    <main>
      <h1>Hello{data.user ? `, ${data.user.name}` : ""}</h1>
      <p>Page views: {data.visits}</p>
    </main>
  );
}

La valeur de retour du loader arrive sur la page en tant que props.data. Voir Loaders et actions pour le cycle de vie complet — y compris action pour les mutations, qui s'exécute avant le loader sur POST/PUT/PATCH/DELETE.

Note

createSdk("my-app") atteint le SDK de la plateforme via l'interface loopback de confiance en utilisant votre app id — aucun token admin requis. Il cantonne toutes les données à cet app id.

Appeler l'API de la plateforme depuis un loader

Un loader peut aussi appeler l'API de plateforme tRPC. Les requêtes sont en GET avec une enveloppe d'entrée superjson, et le résultat que vous voulez est imbriqué à result.data.json. Comme le loader détient request, il peut transférer le cookie de session de l'appelant (__Secure-authjs.session-token) pour s'authentifier, puis lire la session avec auth.getSession :

export async function loader({ request }) {
  const res = await fetch(
    "https://manage.inklura.fr/api/trpc/auth.getSession",
    { headers: { cookie: request.headers.get("cookie") ?? "" } },
  );
  const body = await res.json();
  return { me: body.result.data.json };
}

Pour un client autonome complet — @trpc/client typé, pagination et gestion des erreurs — voir Appeler l'API depuis un script.

Exécutez-la

Démarrez le serveur de développement avec rechargement à chaud :

bext dev

Il sert sur http://127.0.0.1:3000 par défaut. Options utiles : --port N, --listen host:port, --open, --no-hmr. Modifiez un fichier sous watch_dirs et le changement est pris en compte en une ou deux secondes. Pour voir les routes découvertes :

bext routes

Une note sur les paquets npm

Attention

Le bundler PRISM ne résout pas les spécificateurs npm nus dans l'isolate de rendu, et l'isolate n'a aucun module natif Node. Un simple import "some-pkg" pour un paquet non vendorisé échoue au moment du rendu. Vendorisez plutôt une build ESM en pur JS sous forme de fichiers .js relatifs et importez-la par chemin relatif — voir la Vue d'ensemble du SDK.

Étapes suivantes

Page Ce qu'elle couvre
Vue d'ensemble du SDK Le framework et le SDK loopback de la plateforme
Loaders et actions Chargement des données, mutations, accès à la session
Déployer un site PRISM Mettre l'application sur un domaine avec TLS automatique