Build a notes feature — create, list, edit, and delete rich documents — with a single change to your backend.
In this tutorial you add a notes feature to an existing app: signed-in
users write rich documents (Quran, hadith, matn, full Arabic RTL), see them in
a list, reopen them, and delete them.The key architectural idea: Qirtaas is the notes backend. Documents are
created, listed, and deleted from the frontend over the SDK’s embed-token
channel, and each user’s embed identity scopes them to their own documents.
Your backend needs exactly one change — a token endpoint — and zero new
tables.What you’ll build
Backend: one token endpoint (the only backend change).
Frontend: a compose page with <QirtaasEditor>, a note list driven by
listDocuments(), reopen-to-edit, and delete.
Frontend samples are React; the token endpoint is tabbed Node (Express) and
Python (FastAPI). Prerequisites: a qrt_sk_… API key (see the
Quickstart) and
npm install @qirtaas/react.
The editor authenticates every request with a short-lived embed token, and
only your backend — holding the secret key — can mint one. Gate the endpoint
with your app’s own auth and pass the authenticated user’s id as
external_user_id: Qirtaas auto-provisions an identity per distinct id, and
that identity is what scopes each user to their own documents (creation,
listing, and deletion all happen within it).
Whoever can call this endpoint can edit that identity’s documents — always
gate it with your own authentication and derive external_user_id from the
session, never from the request body.
That’s the entire backend. Everything below is frontend.
Mount <QirtaasEditor>without a documentId: the SDK lazily creates the
document on the first keystroke and hands you the new id via
onDocumentCreated. There is nothing to persist yourself — the document is
already stored under the user’s identity, and autosave keeps it current as
they type.
src/ComposeNote.tsx
import { QirtaasEditor } from "@qirtaas/react";import { getToken } from "./qirtaas";export function ComposeNote({ onCreated }: { onCreated: (id: string) => void }) { return ( // The editor fills its container — give it a bounded height. <div style={{ height: "70vh" }}> <QirtaasEditor getToken={getToken} locale="ar" theme="light" // e.g. update the route from /notes/new to /notes/<id> onDocumentCreated={onCreated} onSaveStateChange={(state) => console.log("save:", state)} /> </div> );}
Use onDocumentCreated for navigation concerns (swap /notes/new for
/notes/<id> so a refresh reopens the same note) and onSaveStateChange for
a saved/saving badge.
listDocuments() returns the identity’s documents with server-derived
titles (the first words of the content), so the list is directly
displayable — no table of your own, no title field to maintain:
To continue an existing note, pass its id as documentId. The component
mounts the editor once, so when the user switches notes, force a remount
with a key:
The zero-model setup carries you as far as “each user manages their own
documents”. Add a table of your own the moment a document needs your
data attached to it:
Host metadata — covers, tags, summaries, or a curated ordering that
Qirtaas doesn’t store. Keep a row per note holding the qirtaas_doc_id
pointer plus your fields; the content itself stays in Qirtaas.
Cross-user access — letting other users read a document requires your
backend to know which document belongs to what, so it can decide whom to
authorize. That’s the pointer-table pattern, covered in
Access control.