A reactive backend framework for building real-time applications. Declare your schema, define queries that compile to materialized views, write mutations, and get live-updating data in React — all with end-to-end type safety.
Built on RisingWave, a streaming SQL database.
npm install surf// surf/schema.ts
import { defineTable, defineSchema, v } from "surf/server"
export default defineSchema({
todos: defineTable({
text: v.string(),
done: v.boolean(),
}),
})// surf/queries/allTodos.ts
import { query, v } from "surf/server"
export default query({
args: {},
query: (q) => q.from("todos").orderBy("createdAt", "desc"),
})// surf/mutations/addTodo.ts
import { mutation, v } from "surf/server"
export default mutation({
args: { text: v.string() },
handler: async (ctx, args) => {
await ctx.db.insert("todos", { text: args.text, done: false })
},
})import { useQuery, useMutation } from "surf/react"
function TodoList() {
const todos = useQuery("allTodos", {})
const addTodo = useMutation("addTodo")
return (
<div>
<button onClick={() => addTodo({ text: "New todo" })}>Add</button>
{todos?.map((t) => <div key={t.id}>{t.text}</div>)}
</div>
)
}- Schema definitions map to RisingWave tables
- Queries compile to materialized views that RisingWave maintains incrementally
- Mutations write to tables, which automatically update the materialized views
- Subscriptions stream changes from materialized views over WebSocket to React hooks
When a mutation inserts a row, RisingWave incrementally updates the materialized view, and every subscribed client receives the new data — no polling, no manual cache invalidation.
| Layer | Technology |
|---|---|
| Database | RisingWave (streaming SQL, PG-compatible) |
| Server | Node.js, TypeScript, ws, pg |
| Client | TypeScript, WebSocket |
| React | Hooks (useQuery, useMutation), Context provider |
| Testing | Vitest |
| Export | Contents |
|---|---|
surf/server |
defineTable, defineSchema, query, mutation, v, SurfServer |
surf/client |
SurfClient |
surf/react |
SurfProvider, useQuery, useMutation |
npm run build # Compile TypeScript to dist/
npm run dev # Watch mode
npm test # Run unit tests
npm run test:watch # Watch mode for testsIntegration tests require RisingWave running on :4566:
npx vitest run tests/integration/The demo/ directory contains a prediction market app that showcases real-time updates. The benchmark/ directory includes a trade generator and dashboard for comparing MV-based subscriptions vs re-query polling under load.
npm run demo:mv # Start with MV subscriptions (fast)
npm run demo:requery # Start with re-query polling (slow)
npm run benchmark # Generate trade loadSee ARCHITECTURE.md for a detailed breakdown of the system design.
MIT