Spunto CMS

Démarrage rapide

D'un compte vide à la lecture de votre propre contenu par l'API REST du CMS headless, en une dizaine de minutes.

Voici le chemin le plus court : créer un espace de travail, y poser une carte, émettre une clé API, relire le tout. Tout se fait ici sur https://cms.spunto.net ; une instance auto-hébergée fonctionne de la même façon, avec son propre hôte.

1. Obtenir un compte

Connectez-vous sur /login. Cette page porte les deux moitiés — la connexion et l'inscription — et sait laquelle cette instance autorise.

La possibilité de s'inscrire se règle par instance. Par défaut, le code n'accepte que les inscriptions sur invitation ; cms.spunto.net est ouvert. Si l'inscription est fermée, il vous faut un jeton d'invitation émis par un admin d'un espace de travail ; le lien qu'il vous envoie le contient.

2. Créer un espace de travail et une liste

Un espace de travail est la frontière du tenant : membres, contenu, propriétés, clés API et journal d'événements vivent tous dans un seul et unique espace. Créez-en un depuis /app.

À l'intérieur, créez une collection — une liste de cartes. Donnez-lui un préfixe de clé comme DOC ; ses cartes seront numérotées DOC-1, DOC-2, et ainsi de suite.

Ajoutez une carte ou deux. Une carte a un titre, un corps, un statut, et toutes les propriétés que vous déclarez.

3. Créer une clé API

Allez sur /w/{workspaceId}/settings/api et créez une clé — il faut être admin ou owner de cet espace de travail. Vous obtenez un jeton qui commence par spk_, affiché une seule fois : l'API n'en conserve que le SHA-256, il est donc impossible de le récupérer plus tard.

Une clé porte son propre espace de travail et son propre rôle, tous deux fixés à l'émission. Elle ne suit pas l'appartenance de la personne qui l'a créée : si celle-ci quitte l'espace ou perd son rôle, la clé continue de faire exactement ce pour quoi elle a été émise, jusqu'à ce que quelqu'un la révoque.

Donnez-lui le plus petit rôle qui suffit. viewer lit, member écrit du contenu, admin modifie en plus le registre.

4. Relire votre contenu

La racine publique de l'API est https://cms.spunto.net/api. Commencez par la santé du service, pour vérifier la tuyauterie avant que quoi que ce soit d'autre ne s'en mêle :

curl https://cms.spunto.net/api/health

Listez maintenant vos espaces de travail avec la clé :

curl https://cms.spunto.net/api/workspaces \
  -H "Authorization: Bearer $SPUNTO_TOKEN"

Puis lisez une collection — cet unique appel renvoie la liste, ses propriétés, ses statuts, ses vues sauvegardées et votre rôle, pour qu'un client puisse afficher tout un écran sans second aller-retour :

curl "https://cms.spunto.net/api/workspaces/$WORKSPACE/collections/$COLLECTION" \
  -H "Authorization: Bearer $SPUNTO_TOKEN"

Et interrogez ses cartes. Lire une vue se fait en POST, parce qu'une définition de vue est un arbre de filtres, et qu'un arbre ne tient pas dans une query string :

curl -X POST \
  "https://cms.spunto.net/api/workspaces/$WORKSPACE/collections/$COLLECTION/query" \
  -H "Authorization: Bearer $SPUNTO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"definition":{"sort":[{"field":{"fieldId":"sys:order"},"direction":"asc"}]},"limit":50}'

Vous obtenez { "rows": [...], "nextCursor": "..." }. Renvoyez nextCursor comme cursor pour la page suivante ; null signifie que vous êtes au bout.

5. Passer à un client typé

Dans un projet TypeScript, générez les types depuis la spécification et appelez l'API avec openapi-fetch. Vos filtres sont alors vérifiés à la compilation contre la spécification même avec laquelle le serveur les valide :

npm install openapi-fetch
npx openapi-typescript https://cms.spunto.net/openapi.json -o spunto-api.d.ts
import createClient from "openapi-fetch"
import type { paths } from "./spunto-api"

const api = createClient<paths>({
  baseUrl: "https://cms.spunto.net/api",
  headers: { Authorization: `Bearer ${process.env.SPUNTO_TOKEN}` },
})

const { data, error } = await api.POST(
  "/workspaces/{workspaceId}/collections/{collectionId}/query",
  {
    params: { path: { workspaceId, collectionId } },
    body: { definition: { sort: [], includeSecondaryLists: false }, limit: 50 },
  },
)

Les valeurs par défaut sont explicites dans les types

sort et includeSecondaryLists ont des valeurs par défaut côté serveur, et les types générés les réclament quand même. Passez-les explicitement ; la requête est la même.

Vous construisez un site ? Le guide Next.js part d'ici pour aller jusqu'aux pages, aux corps rendus, aux images et à la revalidation par webhook.

Pour aller plus loin

Sur cette page