Databases for React Developers: What Serverless Actually Changed

There's a specific fear a lot of frontend developers have, and I had it too: the database is where I'm not supposed to go. Tables, indexes, connection pools, migrations, the DBA who guards it all — it reads like someone else's territory, and the safe move is to stay in the browser where I belong.
That fear made sense a decade ago. It doesn't anymore, and holding onto it now quietly caps what you can build alone. Because "add a database" used to be a genuinely heavy sentence — provision a server, keep it running, manage a pool of connections, hand-run migrations, worry about it at 3am. Serverless rewrote every clause of that sentence, and most of the weight is just gone.
So this article is the database explainer I wish someone had handed me when I still thought data storage was off-limits. Not how to be a DBA — how to be a React developer who can pick a database, reason about it, and ship a full app without asking permission.
The Fear Was Really About Operations
Here's the thing I got wrong for years: my fear of databases wasn't about SQL. SQL is learnable in a weekend. It was about operations — the running, scaling, backing-up, not-losing-everyone's-data part. That's the genuinely hard, genuinely scary part, and it's exactly the part serverless takes off your plate.
A serverless database is one you don't run. No server to provision, no connection pool to babysit, no capacity to plan. It scales when you're busy, costs almost nothing when you're idle, and someone else owns the 3am pager. What's left for you is the part that was never scary: describing your data and querying it.
Once I understood that the wall was operational, not intellectual, databases stopped being forbidden and started being just another tool I could reach for. That reframe is the whole point of this article.
The Types, Quickly and Honestly
You need a rough map before names mean anything. There are more categories than this, but for building a React app, three matter.
Relational (SQL) — Postgres, MySQL, SQLite. Data in tables with defined relationships. This is the default, and it should be. It's proven, it's flexible, and the guarantees are strong. When you don't have a specific reason to pick something else, pick this.
Document (NoSQL) — MongoDB, Firestore. Data as flexible JSON-ish documents, no rigid schema up front. Genuinely useful when your data is loosely structured or you're moving fast before the shape settles. My honest take: teams reach for it to "avoid schemas" and later discover the schema didn't disappear, it just moved into their application code where the database can't help enforce it.
Key-value — Redis. Dead-simple pairs, blazing fast, usually a complement to a real database for caching and sessions, not your source of truth.
If you're building a typical app and unsure, the boring answer is right: a relational database. The interesting question in 2026 isn't SQL versus NoSQL — it's what serverless did to the relational option.
Why SQLite Came Back
For years SQLite was filed under "the little embedded database" — great for a phone app or a local file, not something you'd run a real web product on. That mental filing is now out of date, and it's worth understanding why, because it's the shift this whole article turns on.
SQLite is spectacularly simple and fast — the entire database is a file. What it historically lacked was a way to be that simple and live in the cloud serving a distributed app. That's the gap platforms like Turso fill: SQLite's simplicity, rebuilt for the edge, replicated close to your users, with the operational weight removed.
import { createClient } from '@libsql/client'; const db = createClient({ url: process.env.DATABASE_URL!, authToken: process.env.DATABASE_AUTH_TOKEN, }); // This is the whole "connect to your production database" story. const policies = await db.execute('SELECT * FROM policies WHERE user_id = ?', [userId]);
Look at what isn't there. No pool configuration, no connection lifecycle, no server to have provisioned first. A URL and a token, and you're talking to a replicated production database. The thing that scared me — the operations — has been compressed into two environment variables. That's the serverless shift made concrete.
I'm not claiming Turso is the only right answer; serverless Postgres options like Neon do the same trick for the Postgres world, and if your heart is set on Postgres, reach for one of those instead. The point isn't the specific vendor. It's that the operational barrier that made databases feel off-limits has genuinely fallen, whichever ecosystem you pick.
Don't Write SQL Strings by Hand — Use an ORM
The one place I'll push you toward a specific practice: put a typed query layer between your React-adjacent code and the raw database. In the TypeScript world that means an ORM like Drizzle or Prisma, and the reason is the same thread running through this entire series — types.
Raw SQL strings are stringly-typed. A typo in a column name is a runtime error you find in production. A typed ORM turns that into a compile error you find while typing.
import { drizzle } from 'drizzle-orm/libsql'; import { eq } from 'drizzle-orm'; const db = drizzle(client); // Fully typed: 'policies.userId' is checked, the result is typed, // a renamed column breaks the build, not production. const rows = await db.select().from(policies).where(eq(policies.userId, userId)); // ^? Policy[] — inferred from the schema
This should feel deeply familiar by now. It's the exact move from the tRPC and server-functions articles: define the shape once, let the compiler carry it everywhere. Your database schema becomes a TypeScript source of truth, the same types flow from the table into your query into your TanStack Query cache and finally into your component. One definition, enforced end to end, from the database row to the rendered pixel. That's the coherence this series keeps chasing, now extended all the way down to storage.
Migrations Aren't Scary Either
The last piece of the old fear: migrations — changing your schema without breaking the running app. This used to be hand-written SQL run carefully against production, and it's where "I might delete everything" lives.
Modern ORMs largely defuse it. You change your schema definition in TypeScript, and the tool generates the migration for you.
# Change the schema in code, then: npx drizzle-kit generate # generates the migration from the diff npx drizzle-kit migrate # applies it
You still have to think — a migration that drops a column is still destructive, and no tool saves you from a bad decision. But the mechanical, error-prone part of hand-writing schema changes is handled, and the migration lives in your repo as a reviewable, version-controlled file instead of a command someone typed into a production shell and forgot.
What a React Developer Should Actually Take Away
Strip it down:
- The wall was operational, not intellectual. Serverless removed the running-and-scaling part that was actually hard.
- Default to relational. Reach for document or key-value only when you have a specific reason.
- SQLite is a real option now. Turso (or serverless Postgres like Neon) gives you production data storage behind a URL and a token.
- Use a typed ORM. Drizzle or Prisma make your schema a TypeScript source of truth, and the types flow all the way to your components.
- Migrations are generated, not hand-written — but you still own the judgment.
I spent too long treating the database as someone else's job, and all it did was make me dependent on someone else to ship anything real. The serverless shift didn't just make databases easier — it made them mine, and yours. You don't need a DBA and a provisioning ticket to store data anymore. You need a schema, a typed client, and the willingness to walk through a wall that isn't there. That, more than any single tool, is what changed.
With storage handled, the app needs to actually run somewhere the world can reach it. The next article is about hosting a React app — how modern platforms turned deployment from a DevOps project into a git push, and where the limits still bite.
If you're sitting on an app idea and the database is the part that's stopping you, that's the exact fear I'm describing — tell me what you're trying to store and I'll help you pick where it goes.