DEV Community

Alex Spinov
Alex Spinov

Posted on

Convex Has a Free Real-Time Backend — Here's Why It's Replacing Firebase for React Apps

Firebase is complex. Supabase is PostgreSQL (not real-time by default). Convex gives you a real-time reactive database with TypeScript functions.

What is Convex?

Convex is a reactive backend-as-a-service. Write TypeScript functions, get a real-time database, and your UI updates automatically when data changes.

Free Tier

  • 1M function calls/month
  • 1 GB storage
  • 16 MB file storage
  • Automatic real-time sync

Quick Start

npm create convex@latest
Enter fullscreen mode Exit fullscreen mode

Define Your Schema

// convex/schema.ts
import { defineSchema, defineTable } from 'convex/server';
import { v } from 'convex/values';

export default defineSchema({
  tasks: defineTable({
    text: v.string(),
    completed: v.boolean(),
    userId: v.id('users'),
    createdAt: v.number(),
  }).index('by_user', ['userId']),

  users: defineTable({
    name: v.string(),
    email: v.string(),
  }),
});
Enter fullscreen mode Exit fullscreen mode

Queries (Real-Time by Default)

// convex/tasks.ts
import { query } from './_generated/server';
import { v } from 'convex/values';

export const list = query({
  args: { userId: v.id('users') },
  handler: async (ctx, args) => {
    return await ctx.db
      .query('tasks')
      .withIndex('by_user', q => q.eq('userId', args.userId))
      .order('desc')
      .collect();
  },
});
Enter fullscreen mode Exit fullscreen mode

Mutations

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

export const create = mutation({
  args: {
    text: v.string(),
    userId: v.id('users'),
  },
  handler: async (ctx, args) => {
    return await ctx.db.insert('tasks', {
      text: args.text,
      completed: false,
      userId: args.userId,
      createdAt: Date.now(),
    });
  },
});

export const toggle = mutation({
  args: { id: v.id('tasks') },
  handler: async (ctx, args) => {
    const task = await ctx.db.get(args.id);
    if (!task) throw new Error('Task not found');
    await ctx.db.patch(args.id, { completed: !task.completed });
  },
});
Enter fullscreen mode Exit fullscreen mode

React Integration (Real-Time!)

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

function TaskList({ userId }) {
  // This automatically re-renders when tasks change!
  const tasks = useQuery(api.tasks.list, { userId });
  const createTask = useMutation(api.tasks.create);
  const toggleTask = useMutation(api.tasks.toggle);

  if (tasks === undefined) return <p>Loading...</p>;

  return (
    <div>
      <button onClick={() => createTask({ text: 'New task', userId })}>
        Add Task
      </button>
      <ul>
        {tasks.map(task => (
          <li key={task._id} onClick={() => toggleTask({ id: task._id })}>
            {task.completed ? 'Done' : 'Todo'}: {task.text}
          </li>
        ))}
      </ul>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Open two browser tabs — changes appear in both instantly. No WebSocket setup, no polling.

Actions (External APIs)

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

export const sendEmail = action({
  args: { to: v.string(), subject: v.string(), body: v.string() },
  handler: async (ctx, args) => {
    await fetch('https://api.resend.com/emails', {
      method: 'POST',
      headers: { Authorization: `Bearer ${process.env.RESEND_API_KEY}` },
      body: JSON.stringify({ from: 'app@example.com', ...args }),
    });
  },
});
Enter fullscreen mode Exit fullscreen mode

Convex vs Firebase vs Supabase

Feature Convex Firebase Supabase
Real-time Automatic Listeners Manual
TypeScript Full Partial Good
Server Logic TypeScript functions Cloud Functions Edge Functions
Schema Typed Schemaless PostgreSQL
Transactions ACID Limited Full SQL
File Storage Built-in Built-in Built-in
Auth Plugin Built-in Built-in

Need real-time data feeds? Check out my Apify actors — real-time web data for your Convex app. For custom solutions, email spinov001@gmail.com.

Top comments (0)