Software Engineering·July 21, 2026·9 min read

Database Fundamentals

A database stores and organises your app's data. This guide explains SQL vs NoSQL, when to use each, core concepts like indexes and transactions, and how to get started.

databasesqlnosqldataengineeringfundamentals

A database is an organised system for storing, retrieving, and managing data. Every app that remembers anything — user accounts, orders, messages, settings — uses a database.

If Cartara flagged this in your diff, you likely added database queries, defined a schema, connected to a database, or changed how data is stored or retrieved.


The Two Families

Almost every database falls into one of two families:

  • SQL (Relational) — data stored in structured tables with defined relationships
  • NoSQL (Non-relational) — data stored in more flexible formats: documents, key-value pairs, etc.

For most apps: start with SQL. A well-structured Postgres database handles millions of rows with ease and is flexible enough for almost any data model.


SQL Databases

How They Work

SQL databases store data in tables — rows and columns, like a spreadsheet. Each table has a defined schema: specific columns with specific data types.

Tables link to each other through relationships. A users table and an orders table connect via a user_id column:

```
users orders
───────────────── ──────────────────────────
id | name | email id | user_id | product | amount
1 | Matt | m@x.com 1 | 1 | Widget | 29.00
2 | Sara | s@x.com 2 | 1 | Gadget | 49.00
3 | 2 | Widget | 29.00
```

Querying with SQL

SQL (Structured Query Language) is how you talk to relational databases. It reads like English:

```sql
-- Get a specific user
SELECT name, email FROM users WHERE id = 1;

-- Get all orders for that user
SELECT * FROM orders WHERE user_id = 1;

-- Get the total spent by each user
SELECT user_id, SUM(amount) FROM orders GROUP BY user_id;
```

In practice, most apps use an ORM (Object Relational Mapper) like Prisma or Drizzle to write queries in TypeScript/JavaScript instead of raw SQL. But knowing SQL basics helps enormously when debugging.

Why SQL Is the Default Choice

  • Data integrity — the schema enforces structure; you can't store the wrong type of data
  • Relationships — joins let you query across related tables efficiently
  • ACID transactions — operations either fully succeed or fully fail (critical for financial data)
  • Flexibility — SQL is powerful enough to answer questions you haven't thought of yet
DatabaseNotes
PostgreSQLBest general-purpose choice. Open source, highly capable
SupabaseManaged Postgres with auth, storage, and realtime built in
MySQL / MariaDBVery widely used, slightly simpler than Postgres
SQLiteLocal development and small embedded use cases

PostgreSQL via Supabase is the recommended starting point for most small apps.


NoSQL Databases

NoSQL databases trade strict structure for flexibility, often at the cost of consistency or query power. Use them when you have a specific need, not by default.

Document Databases

Store data as JSON-like documents. No fixed schema — each document can have different fields.

```json
{
"id": "user_123",
"name": "Matt",
"preferences": {
"theme": "dark",
"notifications": ["email", "sms"]
}
}
```

Use when: Content with flexible or nested structure, user profiles, product catalogs.
Tools: MongoDB, Firestore (Firebase).

Key-Value Stores

A simple key maps to a value. Extremely fast for reads and writes.

Use when: Caching, session storage, rate limiting counters.
Tools: Redis, Memcached.

Vector Databases

Store numerical embeddings and support similarity search. Used in AI applications for semantic search and RAG systems.

Use when: AI-powered search, recommendation systems.
Tools: Pinecone, Weaviate, pgvector (a Postgres extension).


Core Concepts

Indexes

An index makes lookups faster — like a book's index pointing to page numbers. Without one, the database reads every row to find a match. With one, it jumps directly to the right rows.

Index the columns you filter or sort by most often. Too many indexes slow down writes; too few slow down reads.

Transactions

A transaction is a group of operations that succeed or fail together. When transferring money between accounts, both the debit and the credit must succeed — or neither should. Transactions guarantee this.

```sql
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- If anything fails between BEGIN and COMMIT, neither update happens
```

Schema Migrations

As your app grows, you need to change the database structure — adding columns, creating tables. A migration is a versioned script that applies these changes safely. See Database Migrations.


What You'll See in Your Code

Database code typically shows up in diffs as:

Schema definitions (Prisma example):
```prisma
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
orders Order[]
createdAt DateTime @default(now())
}
```

Queries using an ORM (Drizzle example):
```ts
// Fetch a user with all their orders
const user = await db.query.users.findFirst({
where: eq(users.id, userId),
with: { orders: true },
});
```

Raw SQL (in migrations or complex queries):
```sql
CREATE INDEX idx_orders_user_id ON orders(user_id);
```


Practical Tips

Use a managed database. Supabase, PlanetScale, Railway, and Neon handle backups, scaling, and maintenance. Don't run your own database server until you have a strong reason to.

Enable backups on day one. Before you have a single real user.

Use an ORM. Prisma or Drizzle lets you write type-safe queries in TypeScript and auto-generates types from your schema.

Don't optimise prematurely. A well-structured Postgres database handles millions of rows with basic indexing. Solve real performance problems when they arise.

Separate dev and prod databases. Always.


Related concepts

System Design Basics
System design is how the individual parts of your app — database, cache, queue, and APIs — fit together into something that's fast, reliable, and able to grow. This guide covers the core building blocks and the decisions that shape every system.
Understanding APIs
An API lets two pieces of software talk to each other. This guide explains REST APIs, webhooks, authentication, and status codes in plain English — with real examples.
API Design
How to design APIs that are predictable, consistent, and easy to build on — naming, versioning, errors, pagination, and more.

Turn shipping into understanding

Cartara measures what your team actually learns from every AI coding session.

Join the waitlist