By the end of this guide you will have a working Cloudflare Worker powered by Baseflare. You will define aDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/nickruigrok/baseflare/llms.txt
Use this file to discover all available pages before exploring further.
todos collection with a schema, write a listTodos query and a createTodo mutation, wire them into the Worker entry point, and call both endpoints over HTTP. No Cloudflare account is required to follow along locally — you can run the Worker with Wrangler’s local mode or deploy it manually when you are ready.
Add the
baseflare package to your project. It ships as a single package with focused subpath exports for baseflare/values and baseflare/server.Create a
schema.ts file in your functions directory. Import defineSchema and defineTable from baseflare/server and the v validator namespace from baseflare/values. Each table definition describes the shape of its documents; .index() creates a SQLite index on the named fields so queries on those fields are fast.import { defineSchema, defineTable } from "baseflare/server";
import { v } from "baseflare/values";
export const schema = defineSchema({
todos: defineTable({
ownerId: v.string(),
text: v.string().min(1).max(280),
completed: v.boolean().default(false),
tags: v.array(v.string()).default([]),
}).index("by_owner", ["ownerId"]),
});
Baseflare uses a document model: every table stores a single
_data JSON column alongside its _id. The field definitions above are enforced at write time by the Worker runtime — no database-level constraints are required.Create a
rules.ts file and export your access rules with defineRules. Rules are deny-by-default: any table or operation that has no matching rule will refuse access. The ctx.auth.getUserIdentity() call resolves the caller’s identity from the request token.import { defineRules } from "baseflare/server";
export const rules = defineRules({
todos: {
read: async ({ ctx, doc }) =>
doc.ownerId === (await ctx.auth.getUserIdentity()),
insert: async ({ ctx, value }) =>
value.ownerId === (await ctx.auth.getUserIdentity()),
update: async ({ ctx, existingDoc }) =>
existingDoc.ownerId === (await ctx.auth.getUserIdentity()),
delete: async ({ ctx, existingDoc }) =>
existingDoc.ownerId === (await ctx.auth.getUserIdentity()),
},
});
Each rule is an
async function that receives typed context and returns a boolean. Returning false — or having no rule at all — denies the operation and surfaces a PERMISSION_DENIED error to the caller.Queries read documents and return typed data. They never write to the database. Create a
queries.ts file and export a listTodos function using the query wrapper from baseflare/server.import { query } from "baseflare/server";
import { v } from "baseflare/values";
export const listTodos = query({
args: {
ownerId: v.string(),
},
returns: v.array(v.any()),
async handler(ctx, args) {
return await ctx.db
.query("todos")
.filter({ ownerId: args.ownerId })
.order("_createdAt", "desc")
.limit(50)
.collect();
},
});
The
ctx.db.query() call returns a fluent query builder. .filter() accepts a plain object of field matchers, .order() sorts by any field (including the derived _createdAt), .limit() caps the result set, and .collect() executes and returns all matching documents.Mutations are the atomic write primitive. Create a
mutations.ts file and export a createTodo function using the mutation wrapper. The runtime validates the return value against the returns validator before committing any writes.import { mutation } from "baseflare/server";
import { v } from "baseflare/values";
export const createTodo = mutation({
args: {
ownerId: v.string(),
text: v.string().min(1).max(280),
},
returns: v.string(),
async handler(ctx, args) {
return await ctx.db.insert("todos", {
ownerId: args.ownerId,
text: args.text,
completed: false,
tags: [],
});
},
});
ctx.db.insert() validates the document against the schema, serializes it to JSON, writes it to D1, and returns the new document’s _id string — a plain UUIDv7.Wire your schema, rules, and functions into a Cloudflare Worker using
createWorker from baseflare/server. The manifest requires a schema and typed function entry arrays. Each entry provides the function definition, an exportName (the name used in the source file), a modulePath (the module identifier), and a name that combines them as modulePath:exportName. That combined name becomes the RPC route segment.import { createWorker } from "baseflare/server";
import { schema } from "./schema";
import { rules } from "./rules";
import { listTodos } from "./queries";
import { createTodo } from "./mutations";
export default createWorker({
schema,
rules,
queryEntries: [
{
definition: listTodos,
exportName: "listTodos",
modulePath: "queries",
name: "queries:listTodos",
},
],
mutationEntries: [
{
definition: createTodo,
exportName: "createTodo",
modulePath: "mutations",
name: "mutations:createTodo",
},
],
});
createWorker builds an internal function index and exposes RPC routes at /api/query/:name, /api/mutation/:name, and /api/action/:name, where :name is the modulePath:exportName identifier you provided. The Worker entry point also needs a D1 database binding named APP_DB — configure this in your wrangler.toml or via the Cloudflare dashboard.Once your Worker is running — locally via Wrangler or deployed to Cloudflare — you can call your functions over HTTP using a simple JSON body.
curl -X POST http://localhost:8787/api/query/queries:listTodos \
-H "Content-Type: application/json" \
-d '{"args": {"ownerId": "user_123"}}'
{
"result": [
{
"_id": "019078e5-d29f-7b00-8000-1a2b3c4d5e6f",
"_createdAt": 1709000000000,
"ownerId": "user_123",
"text": "Ship the Baseflare quickstart",
"completed": false,
"tags": []
}
]
}
curl -X POST http://localhost:8787/api/mutation/mutations:createTodo \
-H "Content-Type: application/json" \
-d '{"args": {"ownerId": "user_123", "text": "Ship the Baseflare quickstart"}}'
The CLI deploy workflow (
npx baseflare deploy) is in active development and is not yet available. For now, you can run the Worker locally with wrangler dev or deploy it manually using wrangler deploy or the Cloudflare dashboard. Point Wrangler at your worker.ts entry file and bind a D1 database named APP_DB.