Entités
Une application PRISM dispose de deux façons de travailler avec des données relationnelles :
defineEntity: un générateur d'écrans d'administration piloté par configuration. Il associe une entité à un CRUD complet, adossé aux procédures de la Platform API.- Requêtes SQL : le tagged template
sqldu framework, pour les requêtes que vous écrivez vous-même.
defineEntity
defineEntity fait partie du kit @bext-stack/manage, et non du framework
(@bext-stack/framework). C'est la brique des écrans de console et d'administration.
defineEntity associe une entité à des écrans liste / détail / création / édition et au CRUD
correspondant, adossés aux procédures tRPC de la Platform API.
Les procédures de lecture sont typées : list.proc et getById.proc n'acceptent que des noms
du catalogue de procédures du kit, donc une faute de frappe y est une erreur de compilation.
Le type de la réponse de list.proc est aussi transmis aux adaptateurs rows et total. Les noms
de crud.create, crud.update et crud.delete sont de simples chaînes : vérifiez-les vous-même.
Configuration
Les lectures et les écritures passent par le kit, qui appelle lui-même la Platform API : vous
n'avez pas de client à écrire. L'adresse de l'API, le tenant et le site viennent de
configureManage(...), appelé une seule fois dans un module que chaque route importe pour son effet de bord :
// src/lib/manage-setup.ts
import { configureManage } from "@bext-stack/manage/config";
configureManage({
appId: "<app-id>",
cmBase: "…",
host: "…",
origin: "…",
tenantId: "…",
siteId: "…",
});
| Option | Rôle |
|---|---|
appId |
L'identifiant de votre application. |
cmBase |
L'adresse de base de la Platform API. |
host |
Le nom d'hôte du site administré, envoyé à l'API pour qu'elle résolve le bon tenant et le bon site. |
origin |
L'origine publique de host, utilisée pour le contrôle d'origine à la connexion. |
tenantId, siteId |
Le tenant et le site que ces écrans administrent. |
loginPath |
Facultatif. La page vers laquelle les écrans redirigent sans session. Par défaut /manage/login. |
brandName |
Facultatif. Le nom affiché dans la barre latérale et le titre des pages. |
Le ré-export mince
Pour publier une entité générée, ré-exportez le module d'entité du kit depuis un fichier de route, après l'import du module de configuration :
// src/app/manage/posts/page.tsx
import "../../../lib/manage-setup";
export { loader, default } from "@bext-stack/manage/entities/posts";
Une entité fournit un module par écran qu'elle possède (certaines n'ont qu'une liste) :
| Module | Écran | Route type |
|---|---|---|
@bext-stack/manage/entities/<entité> |
Liste | …/page.tsx |
@bext-stack/manage/entities/<entité>.detail |
Détail | …/[id]/page.tsx |
@bext-stack/manage/entities/<entité>.form |
Création et édition | …/new/page.tsx, …/[id]/edit/page.tsx |
Exemple : la base de connaissances du helpdesk
L'entité « articles » de la base de connaissances du helpdesk est une configuration defineEntity
reliée à ces procédures :
| Procédure | Usage |
|---|---|
helpdeskKb.getArticles |
Lister les articles (list.proc), filtrés par recherche, statut et visibilité |
helpdeskKb.getArticleById |
Lire un article (getById.proc) |
helpdeskKb.createArticle, helpdeskKb.updateArticle, helpdeskKb.deleteArticle |
Créer, modifier et supprimer (crud) |
helpdeskKb.getCategories |
Remplir la liste des catégories du formulaire (formOptions) |
Un extrait de cette configuration :
import { defineEntity } from "@bext-stack/manage/lib/entity";
export const helpdeskKbArticles = defineEntity({
name: "article",
namePlural: "articles",
icon: "book-open",
basePath: "/manage/helpdesk/knowledge-base",
list: {
proc: "helpdeskKb.getArticles",
rows: (r) => (Array.isArray(r) ? r : []),
total: (r) => (Array.isArray(r) ? r.length : 0),
input: (q) => ({
search: q.search || undefined,
status: q.filters.status || undefined,
}),
},
getById: { proc: "helpdeskKb.getArticleById" },
crud: {
create: "helpdeskKb.createArticle",
update: "helpdeskKb.updateArticle",
delete: "helpdeskKb.deleteArticle",
},
// colonnes, filtres et champs de formulaire omis
});
Voir Helpdesk.
Requêtes SQL
Pour écrire vos propres requêtes, le framework exporte un helper SQL en tagged template :
import { sql, database } from "@bext-stack/framework";
const db = database("data.sqlite");
const posts = db.all(sql`SELECT id, title FROM posts WHERE author_id = ${authorId}`);
| Export | Rôle |
|---|---|
sql |
Construit une requête à partir d'un tagged template. sql.id(name) (identifiant entre guillemets doubles), sql.raw(text), sql.if(cond, frag) et sql.join(frags, sep) composent des fragments. |
database(path) |
Lie un fichier de base SQLite. Retourne des fonctions synchrones : all (lignes, sous forme d'objets), get (première ligne ou null), run ({ changes, last_insert_rowid }) et compile ({ text, params }, sans exécution). |
SQLITE_DIALECT, POSTGRES_DIALECT |
Styles de paramètres (? ou $1) du texte compilé, passés en second argument de database ou à la méthode compile d'une requête. SQLite par défaut ; l'exécution passe toujours par SQLite. |
Un tableau interpolé devient une liste de paramètres, pratique pour IN (...). Les fragments se
combinent sans perdre la numérotation des paramètres :
const rows = db.all(sql`
SELECT id, title FROM ${sql.id("posts")}
WHERE id IN (${ids})
${sql.if(authorId, sql`AND author_id = ${authorId}`)}
ORDER BY created_at ${sql.raw(desc ? "DESC" : "ASC")}
`);
const conds = [sql`status = ${status}`, sql`author_id = ${authorId}`];
const count = db.get(sql`SELECT COUNT(*) AS n FROM posts WHERE ${sql.join(conds, " AND ")}`);
const { changes } = db.run(sql`UPDATE posts SET status = ${"archived"} WHERE id = ${id}`);
Chaque valeur interpolée avec ${...} devient un paramètre lié : elle n'est jamais concaténée au
texte SQL. Passez vos valeurs ainsi plutôt que de construire la chaîne de requête à la main.
sql.raw insère du texte sans échappement : réservez-le à des valeurs fixes comme ASC / DESC.
Quand sql.if reçoit une condition fausse, il n'insère rien : mettez le mot-clé (AND, WHERE)
dans le fragment lui-même.
Le client createSdk offre aussi sdk.db.all, sdk.db.get et sdk.db.exec avec des paramètres
? (voir la Présentation du SDK). Ces appels sont asynchrones ; exec retourne
{ changes, lastInsertId }. Chaque paramètre est envoyé sous forme de chaîne : les nombres sont
convertis, et null ou undefined deviennent une chaîne vide. Écrivez NULL dans le texte SQL
quand il vous faut une vraie valeur nulle.
Et ensuite
| Page | Ce qu'elle couvre |
|---|---|
| Modèle de données | Le schéma par tenant et les modèles clés |
| Helpdesk | L'entité de la base de connaissances en contexte |
| Loaders & Actions | Où s'exécutent les lectures et écritures |
| KV Store | Stocker un petit état clé/valeur |