Lunora Backend framework

A type-safe, realtime backend on your own Cloudflare account.

Four files and about forty lines below. Read them in order and you have seen the whole model, because there is not much more to it.

View the source
Step 1 of 4

Describe the data once.

The schema is the source of truth for the database, the server functions and the client types. There is no second definition to keep in step.

lunora/schema.ts
import { defineSchema, defineTable, v } from "@lunora/server";

export default defineSchema({
  messages: defineTable({
    author: v.string(),
    body: v.string(),
    ts: v.number(),
  }),
});

Next, write plain functions against it

Step 2 of 4

Write plain functions against it.

A query reads, a mutation writes, and both are ordinary TypeScript with a typed context. No controllers, no route table, no serialisation layer to hand-roll.

lunora/messages.ts
import { mutation, query, v } from "./_generated/server";

export const list = query.query(async ({ ctx }) =>
  ctx.db.query("messages").order("desc").take(50));

export const send = mutation
  .input({ author: v.string(), body: v.string() })
  .mutation(async ({ ctx, args }) => {
    await ctx.db.insert("messages", { ...args, ts: Date.now() });
  });

Next, read them from the client

Step 3 of 4

The list stays live.

Queries are subscriptions. The component re-renders when the data behind it changes, on every connected client, without a socket to wire up or a cache to invalidate.

src/App.tsx
import { useQuery, useMutation } from "@lunora/react";
import { api } from "../lunora/_generated/api";

const messages = useQuery(api.messages.list) ?? [];
const send = useMutation(api.messages.send);

Last, where all of this runs

Step 4 of 4

It is your account.

Managed backends ask you to move the data to their servers and price it later. Lunora deploys the same primitives to the Cloudflare account you already own, so idle projects cost roughly nothing and leaving costs you a redeploy.

Cloudflare

Workers

Your functions run as Workers, in the same runtime locally and in production.

Durable Objects

SQLite-backed state at the edge. Shard by tenant key or replicate reads globally.

D1

Relational storage for everything that outlives a single object.

R2

File and blob storage without egress fees.

Queues

Background work and scheduled jobs, without a second provider.

Where it sits

The tradeoff Lunora is built around: the developer experience of a managed backend, on infrastructure you control.

LunoraConvexFirebasePlain Cloudflare
Type-safe end to endpartialbuild it yourself
Realtime subscriptionsbuild it yourself
Runs on your own account
Scales past one Durable Objectn/an/abuild it yourself
Vite-first local developmentn/an/abuild it yourself

Start on the free tier.

The starter wires up the schema, the dev server on workerd, and a React client. Lunora is alpha software: install from the alpha tag and expect the API to move.

pnpm dlx lunorash@alpha init my-app
cd my-app && pnpm install
pnpm dev
214
Stars on GitHub
v1.0.0-alpha
Current release
FSL-1.1
Source-available, Apache-2.0 after two years

Build with us

Lunora is built in the open and moving fast. Issues, questions, and pull requests are all welcome.

Lunora on GitHub
Anolilab Jungle