Architecting the Application's Memory
Choosing the Right Tool for the Job
Our in-memory array was a great learning tool, but it had a fatal flaw: the data was not persistent. When the server restarts, all data is lost.
A database provides persistent storage, ensuring our data survives restarts, crashes, and deployments.
There are two major families of databases.
Data is stored in tables with rows and columns. A strict
schema defines the structure.
Relationships between tables are enforced with keys.
Analogy: A well-organized spreadsheet or filing
cabinet.
Examples: PostgreSQL, MySQL, SQL Server
A broad category of databases that do not use the traditional
table structure. Data can be stored as documents (JSON), key-value
pairs, or graphs. Schemas are flexible or non-existent.
Analogy: A folder of miscellaneous documents or a flexible
graph.
Examples: MongoDB, Redis, Cassandra
For most applications with structured data (users, products, orders), a relational database is the correct, reliable choice.
PostgreSQL is the world's most advanced open-source relational database. It is powerful, extensible, and trusted by companies of all sizes.
We will install PostgreSQL and a GUI client to interact with it.
Installation:
# macOS (with Homebrew)
brew install postgresql
# Ubuntu/Debian
sudo apt update && sudo apt install postgresql postgresql-contrib
GUI Client:
We recommend DBeaver (cross-platform) or Postico (macOS). A GUI makes it much easier to visualize your tables and data.
Defining the Blueprint of Your Data
SQL is composed of several sublanguages. We will focus on two today:
This is the primary DDL command. It defines a new table, its columns, and their data types.
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
Each column has a data type and can have constraints.
Relational databases are powerful because they model relationships. A foreign key is a column (or columns) that refers to the primary key of another table.
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT,
-- This column will store the ID of the user who wrote the post
user_id INTEGER NOT NULL,
-- This constraint links user_id to the id column in the users table
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
);
`ON DELETE CASCADE` means that if a user is deleted, all of their posts will be automatically deleted too. This maintains data integrity.
Used to modify an existing table structure.
-- Add a new column to the users table
ALTER TABLE users ADD COLUMN is_active BOOLEAN DEFAULT true;
-- Remove a column
ALTER TABLE users DROP COLUMN is_active;
-- Add a constraint
ALTER TABLE users ADD CONSTRAINT username_length_check CHECK (char_length(username) > 3);
Bringing Your Schema to Life
The `INSERT INTO` statement is used to add new rows of data into a table.
INSERT INTO users (username, email, password_hash)
VALUES ('alex_architect', 'alex@example.com', 'some_bcrypt_hash_here');
INSERT INTO posts (title, content, user_id)
VALUES ('My First Post', 'This is the content of my first post.', 1);
Note that we don't provide the `id` or `created_at` values; the database handles those automatically because of `SERIAL` and `DEFAULT`.
The `SELECT` statement is used to query the database and retrieve data.
-- Select all columns from the users table
SELECT * FROM users;
-- Select only specific columns
SELECT username, email FROM users;
-- Use the WHERE clause to filter rows
SELECT * FROM users WHERE id = 1;
SELECT * FROM posts WHERE user_id = 1 AND title = 'My First Post';
The `UPDATE` statement is used to modify existing records in a table.
The `WHERE` clause is CRITICAL. If you forget it, you will update every single row in the table.
UPDATE users
SET email = 'alex.arch@newdomain.com'
WHERE id = 1;
The `DELETE` statement is used to remove existing records from a table.
Like `UPDATE`, the `WHERE` clause is CRITICAL. If you omit it, you will delete all data in the table.
DELETE FROM posts
WHERE id = 1;
Using a SQL client (like DBeaver), you will write and execute raw SQL commands to create, populate, and query a new table.