Supabase
Before We Start
In the previous section, we connected Express to PostgreSQL using TypeORM. We had to install Postgres, create the database, manage credentials, and write the auth and file upload logic ourselves.
Supabase gives you a PostgreSQL database in the cloud, plus a set of services built around it:
- Database: a real PostgreSQL database (not a lookalike), with a web dashboard and SQL editor
- Auto-generated API: every table is instantly available through a REST API
- Auth: sign up, log in, magic links, OAuth (Google, GitHub, etc.), and JWTs out of the box
- Storage: S3-like file storage with access rules
- Row Level Security (RLS): access rules written in SQL, enforced by the database itself
Because it is plain PostgreSQL underneath, everything you learned about tables, columns, primary keys, and foreign keys still applies.
Two Ways to Use Supabase from Express
There are two ways to talk to a Supabase database from Node.js.
| Approach | How it works | Best for |
|---|---|---|
@supabase/supabase-js | HTTP calls to the Supabase API (supabase.from("books")...) | Using Supabase Auth, Storage, and RLS. Quick CRUD APIs. |
| TypeORM (or any Postgres ORM) | Direct Postgres connection using the database connection string | Complex queries, transactions, entities, and migrations. |
You can also mix them: use TypeORM for your data and supabase-js for auth and storage. Most of this section uses supabase-js, and the setup page shows how to point TypeORM at a Supabase database.
API Keys
Every Supabase project has two kinds of keys. You will find them in the dashboard under Project Settings > API Keys.
| Key | Where it can be used | What it can do |
|---|---|---|
Publishable key (sb_publishable_...) | Browser, mobile app, server | Only what your Row Level Security policies allow |
Secret key (sb_secret_...) | Server only | Everything. It bypasses Row Level Security. |
Older projects show an anon key and a service_role key instead. They map one to one: anon is the publishable key, and service_role is the secret key. The code in this section works with both.
Never send the secret key to the browser, never commit it to Git, and never log it. Anyone who has it has full access to your database. Keep it in an environment variable, as explained in Environment Variables.
Supabase vs Self-Managed Postgres with TypeORM
| Concern | Postgres + TypeORM | Supabase + supabase-js |
|---|---|---|
| Hosting | You run and back up the database | Managed for you, free tier available |
| Schema | Entities and migrations in your code | SQL editor, dashboard, or Supabase CLI migrations |
| Queries | Repository, QueryBuilder, raw SQL | Chainable filters (.eq(), .order(), ...) |
| Transactions | Yes (DataSource.transaction) | Not from the client. Use a Postgres function |
| Auth | Build it yourself (bcrypt + JWT) | Built in |
| File storage | Multer + disk or S3 | Built in |
| Access control | In your Express code | In your Express code and in RLS policies |
Pick Supabase when you want auth and storage without building them yourself, or when a hosted Postgres with a dashboard is enough. Pick plain Postgres with TypeORM when you need full control, heavy transactional logic, or want to stay independent of a vendor.
What's Next
In the next doc, we will create a Supabase project, create our first table, and connect to it from Express.
Conclusion
In this doc, we learned what Supabase is, the two ways to use it from Express, the difference between the publishable and secret keys, and how it compares to running PostgreSQL with TypeORM yourself.