DEV Community

Alex Spinov
Alex Spinov

Posted on

Convex Has a Free Reactive Database That Syncs in Real-Time — Like Firebase but With ACID Transactions

The Real-Time Database Problem

Firebase: no joins, no transactions, no relational queries. Supabase: real-time is an add-on. Building real-time from scratch: WebSockets, cache invalidation, conflict resolution.

Convex is a reactive database. Queries re-run automatically when data changes. Full ACID transactions. TypeScript end-to-end.

What Convex Gives You

Reactive Queries

// convex/messages.ts
import { query } from './_generated/server';

export const list = query({
  handler: async (ctx) => {
    return await ctx.db
      .query('messages')
      .order('desc')
      .take(50);
  },
});
Enter fullscreen mode Exit fullscreen mode
// React component — auto-updates when data changes
import { useQuery } from 'convex/react';
import { api } from '../convex/_generated/api';

function MessageList() {
  const messages = useQuery(api.messages.list);
  return (
    <ul>
      {messages?.map(m => <li key={m._id}>{m.text}</li>)}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

No WebSocket setup. No polling. No cache invalidation. When someone adds a message, every connected client sees it instantly.

Mutations (ACID Transactions)

import { mutation } from './_generated/server';
import { v } from 'convex/values';

export const send = mutation({
  args: { text: v.string(), author: v.string() },
  handler: async (ctx, args) => {
    await ctx.db.insert('messages', {
      text: args.text,
      author: args.author,
      createdAt: Date.now(),
    });
  },
});
Enter fullscreen mode Exit fullscreen mode

Server Functions (Actions)

import { action } from './_generated/server';

export const summarize = action({
  handler: async (ctx) => {
    const messages = await ctx.runQuery(api.messages.list);
    const summary = await fetch('https://api.openai.com/v1/chat/completions', {
      body: JSON.stringify({ messages: [{ role: 'user', content: messages.map(m => m.text).join('\n') }] }),
    });
    return summary;
  },
});
Enter fullscreen mode Exit fullscreen mode

Scheduled Functions

export const cleanup = mutation({
  handler: async (ctx) => {
    const old = await ctx.db.query('messages')
      .filter(q => q.lt(q.field('createdAt'), Date.now() - 86400000))
      .collect();
    for (const msg of old) {
      await ctx.db.delete(msg._id);
    }
  },
});
// Schedule: ctx.scheduler.runAfter(3600000, api.messages.cleanup);
Enter fullscreen mode Exit fullscreen mode

Free Tier

  • 1M function calls/month
  • 1GB database storage
  • 1GB file storage
  • Real-time sync included

Why This Matters

Real-time shouldn't be hard. Convex makes every query reactive by default, so you build real-time apps without thinking about real-time infrastructure.


Need real-time web data? Check out my web scraping actors on Apify Store — data feeds for your reactive apps. For custom solutions, email spinov001@gmail.com.

Top comments (0)