@saastemly/better-faqs
v0.1.0
Published
Frequently asked questions for Better Auth — ordered, grouped, localized, as a plugin.
Maintainers
Readme
better-faqs
A help centre as a Better Auth plugin. Ordered, grouped, localized — no separate CMS, and every table becomes a screen in a generated admin panel.
import { betterAuth } from "better-auth";
import { faqs } from "@saastemly/better-faqs";
export const auth = betterAuth({
plugins: [faqs({ defaultGroup: "general", defaultLocale: "en" })],
});The hard part is the order
Nobody struggles to store a question and an answer. What goes wrong is the
sequence — an FAQ is hand-curated, the question everyone asks goes first, so
position is data an editor sets rather than something derived from a
timestamp. Three consequences follow, and they are most of this plugin.
Reordering is one call for the whole group. Not a PATCH per row: an editor dragging the ninth item to the top would issue nine writes, and a failure halfway leaves the list in an order nobody chose.
A partial list is refused. Accepting one would silently drop whatever it omitted to the end in an arbitrary order, and a reorder that loses entries is worse than a reorder that fails. The request must name every entry in the group, exactly once.
Positions are renumbered 0..n-1 on every write, including after a delete. Storing whatever the client sent is how two entries end up both claiming position 3 and the order becomes whichever row the database returned first. Leaving a gap works fine until something assumes the positions are an index.
Groups
One deployment can serve several help centres — shipping, returns, engraving —
without a second install. GET /faqs/groups lists what exists with counts, so
a nav can build itself.
Locales
One row per (entry, locale). da-DK falls back through da to the entry's own
locale, and the response says which it served.
Routes
| Route | Who |
|---|---|
| GET /faqs?group=&locale= | anyone — active entries, in order |
| GET /faqs/groups | anyone |
| GET /faqs/admin/entries | editors — inactive included |
| POST /faqs/admin/entries | editors — create, appended last |
| POST /faqs/admin/entries/update | editors |
| POST /faqs/admin/entries/reorder | editors — the whole group at once |
| POST /faqs/admin/entries/delete | editors — closes the gap |
| POST /faqs/admin/entries/translate | editors |
Who counts as an editor is canEdit, defaulting to a user whose role contains
admin.
bun install
bun run test # 10 tests
bun run typecheck