Back to blog

Reactive TypeScript Backends: Convex and Pylon Are Changing the Game

May 29, 2026 Dedimarco
typescript backend convex pylon realtime fullstack
Reactive TypeScript Backends: Convex and Pylon Are Changing the Game

Reactive TypeScript Backends: Convex and Pylon Are Changing the Game

For the past decade, backend architecture has followed a well-established pattern: a database (PostgreSQL, MySQL, MongoDB), an API server (Express, Fastify, NestJS), and an optional real-time layer (Socket.IO, WebSockets). Each layer is a separate system with its own configuration, bugs, and maintenance burden. The result? Lots of glue code, complex state management, and cache invalidation strategies that belong in a fantasy novel.

Convex and Pylon propose a radically different approach: the reactive TypeScript backend. No more separate layers — the schema, database, server functions, and real-time sync are fused into a single system, fully typed and driven from your TypeScript code.

The Problem These Frameworks Solve

Consider a typical collaborative application — a CRM, shared document editor, or Kanban board. In a traditional architecture:

// Classic REST API
// 1. Define the SQL schema
// 2. Create migrations
// 3. Write CRUD endpoints
// 4. Add authentication middleware
// 5. Wire up WebSockets for real-time
// 6. Manage cache invalidation
// 7. Sync types between frontend and backend

At every step, there’s a risk of desynchronization. The type defined in the SQL schema doesn’t match what the frontend uses. A successful database mutation doesn’t trigger the right WebSocket update. Users see stale data because the cache wasn’t invalidated properly.

Convex and Pylon solve this with a simple premise: if the backend can tell the frontend exactly what changed, why bother with glue code at all?

Convex: The Reactive Database

Convex is an open-source backend built around a reactive database. Server code is written in pure TypeScript, right inside your project:

// convex/schema.ts — the schema IS the code
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema({
  tasks: defineTable({
    text: v.string(),
    isCompleted: v.boolean(),
    authorId: v.string(),
  }).index("by_author", ["authorId"]),
});

Queries are server functions that read the database. The system automatically tracks which documents and indexes each query touches:

// convex/tasks.ts
import { query, mutation } from "./_generated/server";
import { v } from "convex/values";

export const list = query({
  args: { authorId: v.string() },
  handler: async (ctx, args) => {
    return await ctx.db
      .query("tasks")
      .withIndex("by_author", (q) => q.eq("authorId", args.authorId))
      .order("desc")
      .collect();
  },
});

export const add = mutation({
  args: { text: v.string(), authorId: v.string() },
  handler: async (ctx, args) => {
    await ctx.db.insert("tasks", {
      text: args.text,
      isCompleted: false,
      authorId: args.authorId,
    });
  },
});

On the React client, usage is seamless:

import { useQuery, useMutation } from "convex/react";
import { api } from "./_generated/api";

function TaskList({ userId }) {
  // Automatically re-renders when data changes
  const tasks = useQuery(api.tasks.list, { authorId: userId });
  const addTask = useMutation(api.tasks.add);

  if (tasks === undefined) return <Spinner />;

  return (
    <ul>
      {tasks.map(task => (
        <li key={task._id}>{task.text}</li>
      ))}
    </ul>
  );
}

The result is striking: useQuery automatically re-executes on every mutation affecting the tracked documents. Zero manual cache management, zero WebSocket configuration. Typical latency is 50–100ms from mutation commit to client update.

What Convex Offers

  • End-to-end typing — the schema generates TypeScript types automatically, usable on the client without duplication
  • Transactional mutations — guaranteed atomicity with optimistic concurrency control (OCC)
  • Automatic dependency tracking — Convex knows exactly which queries each mutation impacts
  • Full-text search and vector search for AI applications
  • Scheduled functions (cron) and long-running actions
  • Optimistic updates built-in for instant UI feedback
  • Self-hostable (via Docker or prebuilt binary) or managed cloud

Convex is developed by a well-funded YC startup with rich documentation and a large community. The source code (Rust + TypeScript) is available under the FSL license (converts to Apache 2.0 after 2 years).

Pylon: The Single Binary Runtime

Pylon (by PylonSync) takes the concept even further. Where Convex is a multi-service Docker stack, Pylon is a single Rust binary that you scp to a VPS and run. One process does everything: database, API, WebSocket, auth, file storage.

// apps/api/app.ts — declarative schema
import { entity, field, policy, buildManifest } from "@pylonsync/sdk";

const Note = entity("Note", {
  title: field.string(),
  body: field.richtext(),
  authorId: field.string(),
  updatedAt: field.datetime(),
}, {
  indexes: [{ name: "by_author", fields: ["authorId"], unique: false }],
});

const notePolicy = policy({
  name: "note_public",
  entity: "Note",
  allowRead: "true",
  allowInsert: "auth.userId == data.authorId",
  allowUpdate: "auth.userId == data.authorId",
  allowDelete: "auth.userId == data.authorId",
});

const manifest = buildManifest({
  name: "notes",
  version: "0.1.0",
  entities: [Note],
  policies: [notePolicy],
});

Server functions live in functions/:

// apps/api/functions/createNote.ts
import { mutation, v } from "@pylonsync/functions";

export default mutation({
  args: {
    title: v.string(),
    body: v.string(),
  },
  async handler(ctx, args) {
    const id = await ctx.db.insert("Note", {
      title: args.title,
      body: args.body,
      authorId: ctx.auth.userId,
      updatedAt: new Date().toISOString(),
    });
    return { id };
  },
});

Reactive queries work like Convex, with a slightly different API:

import { db } from "@pylonsync/react";

function Feed({ userId }) {
  const { data: notes, loading, error } = db.useReactiveQuery(
    "getNotes",
    { userId }
  );

  if (loading) return <Spinner />;
  return notes.map(note => <NoteCard key={note.id} note={note} />);
}

Pylon follows the same model as Convex — the server captures dependencies on first execution and re-runs on every mutation affecting that data.

What Sets Pylon Apart

  • Single binaryscp pylon to a VPS, systemctl start pylon, done
  • MIT/Apache 2.0 license — fully open-source, no restrictions
  • SQLite by default — one file, zero config; PostgreSQL available when needed
  • Native faceted search — FTS5 + roaring bitmaps, ideal for Algolia-style UIs
  • Game shards — tick-based primitives for multiplayer games and collaborative apps
  • 32 built-in plugins — TOTP, audit log, Stripe, MCP, webhooks, and more
  • Native CRDTs — Loro integration for conflict-free collaborative editing
  • Row-level policies — access rules declared in the schema, not in middleware

Pylon is newer than Convex but boasts a remarkably clean architecture. Its “self-host first” positioning appeals to developers who want control over their infrastructure.

Detailed Comparison

Architecture — Convex uses a Docker stack with a proprietary database (Rust). Pylon is a single binary running on SQLite or PostgreSQL. For a hobby project on a $5 VPS, Pylon installs in 30 seconds with no Docker.

License — Convex uses the Functional Source License (FSL), which restricts certain commercial uses without a paid license, converting to Apache 2.0 after 2 years. Pylon is MIT / Apache 2.0, free without restrictions.

Typing — Convex has the edge on type finesse. TypeScript inference is deeper, with no intermediate codegen step. Pylon requires a pylon codegen CLI command to generate types from the manifest.

Deployment — Convex offers a polished managed cloud with a generous free tier. Self-hosting is possible but more complex (multi-service Docker). Pylon is designed for self-hosting first, with VPS, Vercel, or optional cloud deployment.

Search — Convex has full-text and vector search. Pylon has full-text search (FTS5) and native faceted search with live facet counts.

Edge cases — Pylon offers game shards for multiplayer games and CRDTs for collaborative editing. Convex doesn’t have these natively.

When to Choose What

Choose Convex if:

  • You want the most polished TypeScript experience
  • Managed cloud or Docker are fine for you
  • You need vector search for an AI application
  • Large community and rich documentation matter

Choose Pylon if:

  • You want ultra-simple deployment on your own infrastructure
  • Unrestricted open-source licensing is important
  • You’re building a faceted search app (e-commerce, CRM)
  • You need multiplayer or collaborative editing features
  • You prefer SQLite over a proprietary database engine

Conclusion

Convex and Pylon represent the next evolution in backend development. The reactive model — where the server automatically pushes updates to clients without polling or manual WebSocket management — is a significant leap forward from traditional REST architectures.

What’s remarkable is that both projects are open-source and written in Rust on the infrastructure side, with TypeScript API surfaces on the developer side. This lets them deliver high performance while keeping JavaScript accessibility.

The competition between Convex and Pylon is healthy — it drives both projects to innovate. Convex invests in polish and developer experience; Pylon bets on deployment simplicity and license freedom. As a developer, you can’t go wrong — the hardest part will be choosing.

After years of cobbling together Express + Socket.IO + Redis pub/sub architectures, I can tell you that these frameworks genuinely change how you think about the backend. It’s one of those rare moments when a new abstraction actually simplifies a complex problem instead of adding to the pile.