- open-source
- self-host
- Alternative to firebase and Supabase
- Better DX comparing to Supabase
Characteristics
- Backend in TypeScript
- Live queries by default
- In Supabase you have to do subscription manually and call UI update function.
- This is similar to firebase, but you define the backend endpoints (like tRPC)
- Full type safety with TypeScript
- Better than Supabase
YouTube | Web Dev Cody | # Why I use Convex over Supabase as my BaaS
Tips
- Tip: Use a separate project for dev and prod environment
- With convex auth, it uses
SITE_URL
environment variable as the redirect URL. Unlike Supabase which supports multiple within the same project, convex seems to support only one. So use separate project with different env vars.
- With convex auth, it uses
- It’s possible to integrate convex with Hono
- https://stack.convex.dev/hono-with-convex
- Could potentially reduce coupling, and help during migration away from Convex
Pros
- Generous free tier (unlike Supabase which provides only 2)
- You can create many projects as long as the total storage is not exceeded, this is nice for development (need dev, stage, prod env) and experiments
- I understand Supabase, their project requires hosting 10+ containers (look at their
docker-compose.yml
for self hosting). Their cost for a single project is so high.Services
- A Postgres DB
kong
for load balancingsupabase/studio
timberio/vector
: for logssupabase/supavisor
supabase/edge-runtime
for edge functionssupabase/logflare
: for analyticssupabase/postgres-meta
darthsim/imgproxy
: for resizing and converting imagessupabase/storage-api
: for storing filessupabase/realtime
postgrest/postgrest
: supabase-rest container for rest APIsupabase/gotrue
for supabase auth service
- Convex’s
docker-compose.yml
for self hosting has only 2 containers, one for backend DB and one for dashboard- There are more services in Supabase but Convex does most of them with only 2 containers
- Live queries with zero effort
- In the Cons Section below, I talked about Convex’s strong coupling issue, but there is a solution to that
- Convex provides client libraries for multiple major languages/frameworks: JS/Swift/Python/Android Kotlin/Rust and Svelte/React/TanStack Query/vue
- OpenAPI
- Client sdks are causing coupling but convex supports OpenAPI which helps providing supports for other languages like Go, Java, etc.
- If you really want to migrate away from Convex, try gradually swapping everything in client with OpenAPI generated client sdk, then swap out the backend, so you don’t need to migrate everything at once.
Cons
- Unknown max-capability
- Unlike mature Postgres, this is a new DB, we don’t know how well it can scale and handle extreme traffic
- Strong coupling (Harder to migrate)
- One drawback of using BaaS like Supabase and Firebase is strong coupling for frontend and backend
- You have to use BaaS’s client SDK in frontend, making it harder to migrate backend in the future if a bottleneck is reached. Convex is no exception.
- Supabase is using Postgres under the hood, to migrate away from Supabase
- I can gradually remove supabase client sdk usage in frontend and switch to use a conventional REST API backend, and use ORM like drizzle to connect to Supabase’s Postgres DB
- Finally the backend can be fully migrated to another backend framework or even DB, because the coupling between frontend and backend is replaced by the most common REST API/OpenAPI (or graphQL or gRPC).
- Convex has better DX than Supabase at the cost of even stronger coupling.
- There is pretty much no way to migrate to another design without rewriting everything. The DB is not like Postgres which you can host in other places. With Supabase, we can swap the Supabase’s Postgres with another Postgres gradually and migrate away from Supabase, but not with Convex.
- One drawback of using BaaS like Supabase and Firebase is strong coupling for frontend and backend
- Performance bottleneck
-
Suppose I have a table with 2 fields, id and data. I want to select only all ids from this table, and each data field has 1MB of data. There are 1000 rows. Will the backend fetch all data which is 1GB of data, extract IDs and return to frontend?
-
In Convex, when you query a table, the database always reads entire documents (including all fields) even if you only need specific fields in your result. Based on your example with a table containing 1000 rows where each row has an ID field and a 1MB data field, here’s what would happen: When you query to get only the IDs, Convex would:
- Read all 1000 complete documents (including the 1MB data fields)
- Extract just the IDs in your query function
- Return only those IDs to the frontend
As noted in the documentation: “Note that in Convex, even with QueryStreams, database queries always read entire documents. They can remove or modify fields in code before returning them to the client.” Translate SQL into Convex Queries
-
When?
When would I pick this BaaS?
- Most of the time I think I would pick this
- Very convenient for small projects with mostly CRUD operations
- Need live queries
When would I not pick this BaaS?
- The app could potentially get a huge amount of data, then this DB may not be as good as mature DB like Postgres
- Mature databases have known solutions for horizontal/vertically scaling through partitioning/sharding
- Need very complex queries
- I am not sure about the performance with this DB against complex queries
- Their filtering/joining/aggregation logic are written in JS/TS
- The queries code is executed within the DB environment though, so there is negligible network latency (which is the largest cost in most simple queries)
- In a traditional backend setup: Frontend → Backend → Database → Backend → Frontend
- If the backend and database are not placed in the same data center the network latency could be higher than actual computation time in DB
- But I doubt if it can be as performant as SQL queries as SQL has been optimized for years
- Convex’s backend is written in Rust BTW
- I am not sure about the performance with this DB against complex queries