all writing

Why I choose Convex for the right projects

Reactive data, TypeScript and clear backend functions: What attracts me to Convex, and when I would still choose PostgreSQL.

When starting a project, my first backend question is fairly simple: How well will this help me build the product I have in mind?

I enjoy working with React, Next.js, Bun, Prisma and PostgreSQL. I also find Convex compelling for certain applications. When several people work with shared data, or longer tasks run in the background, its model fits the interfaces I want to develop.

This is my assessment based on the documentation available in September 2026. Choosing Convex is always a decision about a particular project.

A backend that fits the interface

Convex separates three core function types: Queries read data and support subscriptions, mutations change data within a transaction, and actions call external services. The Convex functions overview explains this division.

I like the clear question this asks whenever I write a function: Am I reading something, changing application state, or crossing into an external system?

Consider an application where someone uploads documents and then watches their processing status. I do not want to manually refresh that status in five places. Showing the current state is part of the feature itself. A reactive query is a good fit for that workflow.

This also makes the interface easier to think about. The list can focus on states such as “waiting”, “processing” and “complete”. Deciding when to fetch data again should require as little separate product logic as possible.

External systems still need careful handling

An external request can succeed even if its response gets lost. Convex therefore does not automatically retry failed actions in the same way it handles database operations. The actions documentation explicitly explains this boundary.

For my document example, I would first store a job with a unique identifier. An action could then call the external service and save its result through a mutation. If that service supports idempotency keys, I would use the same job identifier.

Idempotency means that repeating the same request must not accidentally perform the job twice. I would also need an understandable failure state and a rule for deciding when another attempt makes sense. Choosing a backend platform does not remove those decisions.

I would want the interface to distinguish a confirmed failure from a result we have not received yet. Those situations can look similar to a person waiting at a screen, but they should lead to different recovery behaviour.

Authentication is only the beginning

A backend function can use ctx.auth.getUserIdentity() to access the signed-in user's identity. The authentication documentation shows the intended approach.

My actual permission checks begin after that. Does the document belong to this workspace? Can this person only read it, or also delete it? Is their membership still active?

I would put those checks into shared helpers and enforce them in every public function. Hiding a button in the frontend can communicate what is available, but it cannot replace the backend decision. In an application serving several organisations, I want that boundary to be obvious in the code.

PostgreSQL still has a place

Convex stores documents and supports relational modelling through references. JavaScript functions read and write the data; this is a different database model from a PostgreSQL backend. The database overview describes that foundation.

If a project already depends heavily on SQL, existing databases and reporting tools, I would examine a migration particularly carefully. Team experience, data exports, operational requirements and costs under realistic usage also matter to me.

A prototype helps more than a feature comparison. I would build a complete workflow: sign in, create a record, check permissions, start a background job and deliberately trigger a failure. That is where I can judge whether the model fits the project.

My selection rule

I like choosing Convex when an interactive application benefits from reactive data and I want to describe its backend logic consistently in TypeScript. PostgreSQL remains an equally deliberate option when SQL and existing systems shape the work.

Ultimately, I want to spend less time keeping interface state and backend state aligned. That is the part of Convex I find particularly interesting.