Skip to main content

Documentation 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.

Baseflare has six server function constructors. The public trio — query, mutation, and action — defines functions callable from client SDKs via the RPC routes. The internal trio — internalQuery, internalMutation, and internalAction — defines server-only functions that are never exposed over HTTP but can be composed together using ctx.runQuery, ctx.runMutation, and ctx.runAction. All six share the same FunctionDefinitionConfig shape and are exported from baseflare/server.

FunctionDefinitionConfig<TArgs, TReturns, TCtx>

Every function constructor accepts a single config object:
args
ValidatorShape
required
A plain object mapping argument names to validators from v.*. The inferred object type becomes the args parameter of handler. All arguments are validated at the RPC boundary before the handler is called.
returns
AnyValidator
Optional validator for the handler’s return value. When provided, the return value is validated at runtime before being sent back to the caller. If omitted, the return value is passed through as-is.
handler
(ctx: TCtx, args: InferredArgs) => Promise<T>
required
The async function that implements the operation. Receives a typed context object and the validated args. May be synchronous or async.

query(config)

Defines a public, read-only server function. Queries have access to ctx.db as a DatabaseReader but cannot write. They are invoked by the client via POST /api/query/:name.
function query<
  TArgs extends ValidatorShape,
  TReturns extends AnyValidator | undefined = undefined,
>(
  config: FunctionDefinitionConfig<TArgs, TReturns, QueryCtx>
): QueryDefinition<TArgs, TReturns>
Example:
import { query } from 'baseflare/server'
import { v } from 'baseflare/values'

export const getTodo = query({
  args: { id: v.string() },
  handler: async (ctx, { id }) => {
    return ctx.db.get('todos', id)
  },
})

mutation(config)

Defines a public, read-write server function. Mutations run inside a serializable transaction and have access to ctx.db as a DatabaseWriter. They are invoked by the client via POST /api/mutation/:name.
function mutation<
  TArgs extends ValidatorShape,
  TReturns extends AnyValidator | undefined = undefined,
>(
  config: FunctionDefinitionConfig<TArgs, TReturns, MutationCtx>
): MutationDefinition<TArgs, TReturns>
Mutation handlers should be deterministic and retry-safe. If a mutation is retried due to a transient D1 error, the handler will be called again with the same arguments.
Example:
import { mutation } from 'baseflare/server'
import { v } from 'baseflare/values'

export const createTodo = mutation({
  args: {
    text: v.string().min(1).max(280),
    ownerId: v.string(),
  },
  handler: async (ctx, args) => {
    return ctx.db.insert('todos', {
      text: args.text,
      ownerId: args.ownerId,
      completed: false,
    })
  },
})

action(config)

Defines a public server function that can run arbitrary async logic, call third-party APIs, and orchestrate other functions. Actions do not have direct database access — all DB work must go through ctx.runQuery() or ctx.runMutation(). They are invoked by the client via POST /api/action/:name.
function action<
  TArgs extends ValidatorShape,
  TReturns extends AnyValidator | undefined = undefined,
>(
  config: FunctionDefinitionConfig<TArgs, TReturns, ActionCtx>
): ActionDefinition<TArgs, TReturns>
Action handlers do not have ctx.db. All database access from an action must go through ctx.runQuery() or ctx.runMutation().
Example:
import { action, internalMutation } from 'baseflare/server'
import { v } from 'baseflare/values'

export const sendInvite = action({
  args: { email: v.string(), workspaceId: v.string() },
  handler: async (ctx, { email, workspaceId }) => {
    // Call a third-party API
    await fetch('https://api.email.example/send', {
      method: 'POST',
      body: JSON.stringify({ to: email, template: 'invite' }),
    })
    // Persist the result via a mutation
    await ctx.runMutation(recordInviteSent, { email, workspaceId })
  },
})

internalQuery, internalMutation, internalAction

The internal variants have identical signatures to their public counterparts but are registered with visibility: "internal". They are never reachable via the /api/query/, /api/mutation/, or /api/action/ RPC routes — they can only be called server-side using ctx.runQuery, ctx.runMutation, or ctx.runAction. Use internal functions to decompose complex logic, share database access patterns, or expose helper functions that should not be callable by clients.
import { internalQuery, internalMutation, internalAction } from 'baseflare/server'

// Only callable from another server function via ctx.runQuery(getUser, { id })
export const getUser = internalQuery({
  args: { id: v.string() },
  handler: async (ctx, { id }) => ctx.db.get('users', id),
})

export const archiveWorkspace = internalMutation({
  args: { workspaceId: v.string() },
  handler: async (ctx, { workspaceId }) => {
    // ...
  },
})

export const syncExternalData = internalAction({
  args: { source: v.string() },
  handler: async (ctx, { source }) => {
    // ...
  },
})

Context Types

QueryCtx

Available inside query and internalQuery handlers.
ctx.db
DatabaseReader
Read-only database access. See DatabaseReader below.
ctx.auth
Auth
Authentication context. Call ctx.auth.getUserIdentity() to retrieve the caller’s identity from the incoming request.
ctx.storage
StorageReader
Read-only file storage. Call ctx.storage.getUrl(id) to retrieve a presigned download URL for a stored object.

MutationCtx

Available inside mutation and internalMutation handlers. Extends QueryCtx.
ctx.db
DatabaseWriter
Read and write database access. See DatabaseWriter below.
ctx.auth
Auth
Authentication context.
ctx.runQuery(ref, args)
Promise<TResult>
Run a query within the mutation context. Useful for reading data as part of a read-modify-write pattern.
ctx.scheduler
Scheduler
Schedule a function to run in the future. Call ctx.scheduler.runAfter(delayMs, ref, args) or ctx.scheduler.runAt(timestamp, ref, args). Returns a job ID string. (Planned)
ctx.storage
StorageWriter
Read and write file storage. Extends StorageReader with generateUploadUrl() and delete(id). (Planned)

ActionCtx

Available inside action and internalAction handlers. Does not include ctx.db.
ctx.runQuery(ref, args)
Promise<TResult>
Run a query (read-only database access) from within an action.
ctx.runMutation(ref, args)
Promise<TResult>
Run a mutation from within an action. Each runMutation call is its own serializable transaction.
ctx.runAction(ref, args)
Promise<TResult>
Run another action from within an action.
ctx.auth
Auth
Authentication context.
ctx.scheduler
Scheduler
Schedule a function to run in the future. (Planned)
ctx.storage
StorageActionWriter
Read, write, and directly store blobs. Extends StorageWriter with store(blob: Blob). (Planned)

DatabaseReader

Returned by ctx.db in a QueryCtx. Provides read-only access to the D1 database.
get(table, id)
Promise<Doc | null>
Fetch a single document by its _id. Returns null if no document exists with that ID. Permission rules are applied — a document that fails the read rule is treated as if it does not exist.
const todo = await ctx.db.get('todos', id)
query(table)
QueryBuilder
Begin a fluent query against the given table. Chain .filter(), .order(), .limit(), then terminate with .collect(), .first(), .paginate(), etc. See the QueryBuilder reference.
const todos = await ctx.db.query('todos')
  .filter({ ownerId: userId, completed: false })
  .order('asc')
  .collect()

DatabaseWriter

Returned by ctx.db in a MutationCtx. Extends DatabaseReader with write operations.
insert(table, doc)
Promise<string>
Insert a new document. The doc object must conform to the table’s field validators. Returns the new document’s _id string.
const id = await ctx.db.insert('todos', {
  text: 'Buy milk',
  ownerId: userId,
  completed: false,
})
patch(table, id, partial)
Promise<void>
Shallow-merge partial into the existing document. Fields set to undefined in the patch are deleted from the document (only allowed if the field is optional in the schema). Fields not mentioned in partial are unchanged.
await ctx.db.patch('todos', id, { completed: true })
// Remove an optional field:
await ctx.db.patch('todos', id, { editedAt: undefined })
replace(table, id, doc)
Promise<void>
Fully replace the document. The replacement doc must pass complete schema validation — all required fields must be present.
await ctx.db.replace('todos', id, {
  text: 'Buy oat milk',
  ownerId: userId,
  completed: false,
})
delete(table, id)
Promise<void>
Delete the document with the given _id. The delete permission rule is evaluated before deletion.
await ctx.db.delete('todos', id)

FunctionReference

A lightweight branded type used to pass a function definition as a reference to ctx.runQuery, ctx.runMutation, or ctx.runAction. Function definitions created by query(), mutation(), action(), and their internal variants all satisfy this interface automatically.
interface FunctionReference<TArgs, TResult> {
  readonly __baseflareArgs?: TArgs
  readonly __baseflareResult?: TResult
}

Document Utilities

The following utilities are exported from baseflare/server for use in custom tooling, testing, or lower-level integrations.

serialize(doc)

Converts a plain document object into the { _data: string } storage form written to the D1 _data column. Reserved fields (_id, _createdAt) are stripped. Uint8Array values are base64-encoded under a $bytes marker; keys starting with $ are escaped.
import { serialize } from 'baseflare/server'

function serialize(doc: Record<string, unknown>): { _data: string }

deserialize(row)

Reconstructs a full document object from the raw { _id, _data } row returned by D1. Decodes base64 byte arrays, un-escapes $-prefixed keys, and synthesizes _createdAt from the ID.
import { deserialize } from 'baseflare/server'

function deserialize(row: {
  _id: string
  _data: string
}): Record<string, unknown> & { _id: string; _createdAt: number }

validateInsertData(table, data)

Validates data against a TableDefinition for an insert operation. Throws a ValidationError if any required field is missing, a field value fails its validator, or a reserved field is present.
import { validateInsertData } from 'baseflare/server'

function validateInsertData(
  table: TableDefinition,
  data: DocumentData
): DocumentData

validatePatchData(table, current, patch)

Validates a partial patch against the stored document. Only the keys present in patch are re-validated against their individual field validators. Fields absent from patch are preserved unchanged from current. Setting a field to undefined deletes it (only permitted for optional fields).
import { validatePatchData } from 'baseflare/server'

function validatePatchData(
  table: TableDefinition,
  current: DocumentData,
  patch: DocumentPatch
): DocumentData

validateReplaceData(table, data)

Validates data for a full-document replacement. Identical to validateInsertData — all required fields must be present and pass their validators.
import { validateReplaceData } from 'baseflare/server'

function validateReplaceData(
  table: TableDefinition,
  data: DocumentData
): DocumentData

Build docs developers (and LLMs) love