describe the data once
One schema. Everything else follows.
QUESTPIE derives the typed API, the admin, the jobs and the client from one definition. In your codebase, on your servers.
MIT · v3.25.3 · Bun 1.3+ · Postgres 15+
import { collection } from "#questpie/factories";export const news = collection("news") .fields(({ f }) => ({ title: f.text(255).label("Title").required(), content: f.richText(), isPublished: f.boolean().default(false), })) .title(({ f }) => f.title);derived, not written
Four things you stop maintaining
Typed REST
GET /api/newsGET /api/news/:idPOST /api/newsPATCH /api/news/:idDELETE /api/news/:idFive routes per collection, typed end to end.
Client
const { docs } = await client.collections.news.find({ where: { isPublished: true }, limit: 10,});Method names come from your collection names. No SDK to keep in step.
Admin
No layout file: the schema is the layout. See the admin →
Jobs
export default job({ name: "digest", options: { cron: "0 8 * * *" }, handler: async ({ collections, email }) => { await email.send(await buildDigest(collections.news)); },});Queue, retries and a cron schedule, from one file convention.
one source of truth
Change the model once
The same model types your database, API, admin and client. You do not copy a field or its rules into four files.
the model is only the start
Write the parts that make the app yours
QUESTPIE handles the repeated work. Your code holds the decisions.
Routes
Typed handlers receive the same collections, database and services.
Jobs
Run work now, later or on a schedule. Keep retries in one place.
Services
Give hooks, routes and jobs one typed dependency.
Infrastructure
Add realtime, search, storage, mail and queues when you need them.
your application stays yours
Run it where you choose
QUESTPIE ships as open-source packages. Your code runs on your server. Your data stays in PostgreSQL.
two commands
Build the first app
Pick your runtime: TanStack Start, Next, Hono or Elysia. The generator writes your typed app.
bunx create-questpie my-appbun run dev