Back to articles
Backend

Convex, the ultimate backend

Why I Ditched Prisma and Supabase for Convex (And Why I Won't Go Back).

Daryl Ngako

Convex, the ultimate backend

I spent hours debugging broken stuff with Prisma. I struggled to get realtime to work with Supabase. And each time, I felt like I was spending more time running the mechanics than building my project.

Then I discovered Convex.

In this article, I tell you my real journey from Prisma and Supabase to Convex. Why I switched, when it's the right tool to use, how it compares to others, and also what's wrong (because nothing is perfect).

What made me run away from Prisma and Supabase

Before I talk about Convex, I have to be honest about what made me look for something else.

The hassle of migrations with Prisma

Prisma is a very good tool for reading and writing data from your app. TypeScript autocompletion is great, everything is well organized. But as soon as you work in a team or in several environments (local, prod), it becomes painful.

You update your database locally, everything is fine. You deploy, and there... it crashes. One change conflicts with another, or worse, it fails without telling you why. You find yourself repairing files you didn't really write, trying to figure out what happened. If you work alone, the real disadvantage remains having to execute the npx prisma migrate dev command each time the diagram is modified.

Realtime with Supabase: possible, but not simple

Supabase is solid. A real PostgreSQL database, integrated user management, file storage. For a classic app where you read and write data, it does the job very well.

But when I wanted multiple users to see each other's changes in real time, I quickly realized that it wasn't automatic.

With Supabase, to display live data, you must:

  • Write your query to retrieve the data.
  • Write a "subscription" separately to be notified when something changes.
  • Keep the two in sync on the client side.

Basically, you're doing the same job twice, and you're crossing your fingers that it stays consistent. On a simple project it works. On something more complex, things quickly go haywire.

What is Convex?

Convex is an all-in-one backend platform, focused on TypeScript and real-time synchronization.

Imagine that you no longer have to worry about servers, databases to configure or updates to synchronize by hand.

How does it work?

You write your backend functions (queries, mutations, actions) in TypeScript in a convex/ folder, you call them from the frontend, and Convex takes care of the rest, including keeping the data up to date in real time for all connected users.

It's a bit like Firebase, but made for TypeScript developers, with real guarantees on data reliability.

Concretely, Convex gives you:

  • A database that updates itself: when a piece of data changes, everyone who looks at it sees it change instantly.
  • Server-side functions in TypeScript: no SQL to write, just TypeScript.
  • Reliable transactions: no inconsistent state, all or nothing.
  • A built-in scheduled task system for background jobs.
  • File storage included with nothing to plug in.

What made me switch: The creation of a social network

The heart of the project: that everything be instantaneous. A user posts something, everyone sees it. Immediately. Not “after a refresh”.

With Supabase, that would have meant manually managing WebSockets, syncing state between clients, dealing with data conflicts... Basically, building an entire subsystem before even starting the app.

With Convex, this is all I wrote:

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

export const createPost = mutation({
  args: { content: v.string(), attachmentIds: v.array(v.string()) },
  handler: async (ctx, args) => {
    const userId = await getAuthUserId(ctx);
    const postId = await ctx.db.insert("posts", {
      content: args.content,
      medias: args.attachmentIds,
      authorId: userId,
      createdAt: Date.now(),
    });
  },
});

export const getPosts = query({
  handler: async (ctx) => {
    const posts = await ctx.db.query("posts").order("desc").collect();
    return Promise.all(
      posts.map(async (post) => ({
        ...post,
        author: await ctx.db.get(post.authorId),
      })),
    );
  },
});
PostList.tsx
// app/src/components/PostList.tsx
import { useQuery } from "convex/react";
import { api } from "@/convex/_generated/api";

export function PostList() {
  const posts = useQuery(api.posts.getPosts);

  return (
    <div className="space-y-4">
      {posts?.map((post) => (
        <div key={post._id} className="p-4 border rounded">
          <p className="font-bold">{post.author?.name}</p>
          <p>{post.content}</p>
        </div>
      ))}
    </div>
  );
}

That's all. No WebSocket to configure. No synchronization to manage by hand. Convex knows all by itself what data your component uses. When someone adds a post, everyone sees it appear immediately because the list automatically refreshes.

Quick setup of Convex with Next.js

Enough talking. Here's how to integrate Convex into a Next.js project in less than 5 minutes, no nonsense, just enough to get up and running.

1. Install Convex

In your existing Next.js project, just one command is enough:

npm install convex

2. Initialize Convex

npx convex dev

This command will:

  1. Log in to your Convex account (via GitHub)
  2. Create a project
  3. Create the convex/ folder at the root of your project
  4. Launch a development server that synchronizes your backend code in real time

It will also automatically create the environment variable in your .env.local file. Just make sure it's there:

.env.local
NEXT_PUBLIC_CONVEX_URL=https://your-project.convex.cloud

You let it run while you develop, just like next dev.

3. Create the Provider

Convex needs a client-side provider to connect your app to the backend. Creates a ConvexClientProvider.tsx file:

ConvexClientProvider.tsx
// app/src/providers/ConvexClientProvider.tsx
"use client";

import { ConvexProvider, ConvexReactClient } from "convex/react";
import { ReactNode } from "react";

const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);

export function ConvexClientProvider({ children }: { children: ReactNode }) {
  return <ConvexProvider client={convex}>{children}</ConvexProvider>;
}

4. Plug the Provider into your layout

Last step: wrap your app with the provider in the main layout.tsx.

layout.tsx
// app/layout.tsx
import { ConvexClientProvider } from "./ConvexClientProvider";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="fr">
      <body>
        <ConvexClientProvider>{children}</ConvexClientProvider>
      </body>
    </html>
  );
}

And there you have it. Your Next.js app is connected to Convex.

When to use Convex?

Convex is not suitable for everything. But in these situations, that's really where he shines.

When several people work at the same time

Project management tools, collaborative documents, shared tables. If several people need to see each other's changes live, Convex makes it natural.

When you need a live dashboard

Figures that update, statuses that change, real-time alerts. You retrieve your data once, Convex takes care of keeping it up to date.

To launch a project quickly

No server to configure, no database to install, no cache to set up. You start with npx create-convex@latest and you're up and running in just a few minutes. Ideal for testing an idea quickly.

For a team that lives in TypeScript and React

If your team is comfortable with TypeScript and React but doesn't want to deal with infrastructure, Convex is made for that. Anyone can touch the backend without learning SQL or managing servers.

For projects with AI

Convex handles long running background tasks and AI agents well. If you're building something around AI, it's a solid foundation.

Another advantage of Convex: forgetting the client/server components distinction.

With Convex, everything runs client-side by default, the Next.js backend never comes into play. No more wondering where is running what.

Convex facing others: what it really gives

CriterionConvexSupabaseFirebasePrisma + DB
Realtime without configYesNo (manual)YesNo
Auto types in the frontendYesTo generateNoTo generate
SQLNoYesNoYes
Database updatesAutomaticFiles to manageNoFiles to manage
Ideal forReal-time apps, MVPsApps with lots of relationshipsMobile appsExisting base

In summary: take Convex if real time is at the heart of what you are building. Take Supabase if you need complex SQL queries with lots of relationships between your data. Keep Prisma if you already have a database in place that you don't want to touch.

The real disadvantages of Convex

I showed you what I like. But there are things that hurt, and you deserve to know them.

You can't make complex queries

Convex does not use SQL. If your app needs to cross a lot of tables, to do advanced calculations on your data or advanced statistics, you will quickly find yourself stuck. This is really where Supabase remains much better.

Fewer resources than elsewhere

Supabase or Firebase have thousands of tutorials, questions on Stack Overflow, plugins. Convex is newer, the community is smaller. When you get stuck on something specific, you will often end up on the official doc or the Discord.

Bad choice if you already have a backend

Convex is made to start from scratch. If you try to piggyback it on an existing project with a lot of server-side logic already, you'll create more problems than you solve.

My honest review

Convex is truly the ultimate tool for creating real-time applications without any configuration.

No more hassle with broken migrations, no more duplicate code to manage synchronization.

Convex manages everything: the backend, real-time, synchronization, and TypeScript typing.

Is it the perfect tool for everything? No. If your app has a lot of relationships between its data and you need complex queries, Supabase is more suitable. If you already have a Prisma project running, don't touch anything.

But if you're starting from scratch on a TypeScript and React project, you want real time without hassle, and you want to go quickly? Convex is exactly what you need.

Don't hesitate to test it and tell me what you think about it on Linkedin!