Appendix B

Databases Locally

Installing and running SQLite, PostgreSQL, and MongoDB

Appendix B — Databases Locally

“And he said to them, ‘Therefore every scribe who has been trained for the kingdom of heaven is like a master of a house, who brings out of his treasure what is new and what is old.’” — Matthew 13:52 (ESV)

Phase 2 persists data in three different engines — SQLite, PostgreSQL, and MongoDB — on purpose. You will build the same kind of system three ways and be required to justify which engine a given problem deserves. You can’t make that call honestly until you’ve run all three on your own machine and felt the difference. This appendix gets each one installed, started, and connected.

Three engines, three personalities:

  • SQLite — a file. No server, nothing to start. The easiest one. (Week 11)
  • PostgreSQL — a real client/server relational database. A server process you start, connect to over a port, and manage. (Week 12)
  • MongoDB — a document store. Also a server, also over a port, but it stores JSON-shaped documents instead of rows. (Week 13)

Install only what each week needs — but installing all three now means you never lose momentum mid-week. Every section ends with a verify command. Run it.

Coach’s Note — Don’t confuse “I installed it” with “it’s running.” SQLite has nothing to run. Postgres and Mongo are server processes — if the server isn’t running, every connection fails with “connection refused,” and that error has tripped up every engineer alive. When in doubt, ask first: is the server actually up?


B.1 — SQLite (the easy one)

SQLite is serverless: the entire database is a single file on disk, and the engine is a library your program links against. There is no server process, no port, no user accounts. You open a file and you’re talking to a database. This is exactly why it’s the right tool for an enormous number of jobs — and exactly why it’s the wrong tool for others (no concurrent writers at scale). You’ll learn the line in Week 11.

Is it already there?

It almost certainly is. The sqlite3 command-line tool ships with macOS and most Linux distros, and — crucially — Python’s sqlite3 module is built into the standard library, so you need nothing extra to use SQLite from Python.

Install (only if missing)

  • macOS: present by default; brew install sqlite for the newest CLI.
  • Windows: the CLI isn’t bundled, but you don’t need it — Python’s built-in sqlite3 is enough. If you want the command-line shell, download the “sqlite-tools” bundle from sqlite.org/download.html and put sqlite3.exe on your PATH.
  • Linux: sudo apt install -y sqlite3 (Debian/Ubuntu) or your distro’s equivalent.

Verify it worked

The CLI:

sqlite3 --version

But the version that matters for this course is Python’s built-in module. This is the real test:

python3 -c "import sqlite3; c=sqlite3.connect(':memory:'); print(sqlite3.sqlite_version)"

If that prints a version number (e.g. 3.45.0), SQLite works from Python with zero installs. That :memory: connection is a throwaway database that lives in RAM — handy for tests.


B.2 — PostgreSQL (the real relational server)

PostgreSQL (“Postgres”) is a full client/server relational database with MVCC, real concurrency, strong types, and decades of hardening. Unlike SQLite, it runs as a server process that listens on a port (default 5432), and you connect to it with a client (psql, or your Python driver). This is the database that bears real weight.

Install

macOS — Option 1 (easiest): Postgres.app Download, drag to Applications, open it, click Initialize. It runs a Postgres server you start/stop with a menu-bar icon. Add its command-line tools to your PATH per the app’s instructions so psql works in your terminal.

macOS — Option 2: Homebrew

brew install postgresql@16
brew services start postgresql@16

brew services start runs the server in the background and relaunches it on reboot.

Windows Download the installer from EnterpriseDB and run it. It installs the server, psql, and pgAdmin (a GUI), and registers Postgres as a Windows service that starts automatically. It asks you to set a password for the postgres superuser — remember it. (Or: winget install PostgreSQL.PostgreSQL.16.)

Linux (Debian/Ubuntu)

sudo apt update
sudo apt install -y postgresql
sudo systemctl enable --now postgresql

systemctl enable --now starts it now and on every boot. (Fedora: sudo dnf install -y postgresql-server && sudo postgresql-setup --initdb && sudo systemctl enable --now postgresql.)

Starting the server

  • Postgres.app: click the menu-bar icon.
  • Homebrew: brew services start postgresql@16 (or stop).
  • Windows: it’s a service — already running; manage it in Services.msc if needed.
  • Linux: sudo systemctl start postgresql.

Create a database and a user

The names matter — use exactly these. Week 12’s chapter, reps, and starter code all connect as the user verses_app to a database named verses. Create those exact names here and everything downstream just works. If you invent your own names, you’ll have to change them in every code file too — so don’t; match the course.

Open the psql shell. On macOS (Postgres.app/Homebrew), psql postgres connects as your own user — no admin password, no sudo — which is the simplest path and the one to prefer if you’re avoiding administrator setup. On Linux, the install creates a postgres system account: sudo -u postgres psql. On Windows, open “SQL Shell (psql)” from the Start menu and log in as postgres with the password you set.

Inside psql, create the dedicated course user and database (don’t use the superuser for app work):

CREATE USER verses_app WITH PASSWORD 'changeme';
CREATE DATABASE verses OWNER verses_app;
GRANT ALL PRIVILEGES ON DATABASE verses TO verses_app;
\q

\q quits psql. (Backslash commands are psql’s own; \l lists databases, \dt lists tables, \du lists users.)

Pick a real password and remember it. Replace 'changeme' with a password of your own. You will put that same password in an environment variable — PG_PASSWORD — that the Week 12 code reads with os.environ; it never goes in the source. Set it in your shell with export PG_PASSWORD='your-password' (macOS/Linux) or setx PG_PASSWORD "your-password" (Windows, then reopen the terminal). The course code reads this variable, so the password lives in exactly one place — your shell, not any file you commit.

The connection string

Everything that connects to Postgres uses a URL of this shape (here, with the names you just created):

postgresql://verses_app:changeme@localhost:5432/verses

Read it as: protocol postgresql://, user:password@, host:port/, database name. localhost means “this machine.” You’ll feed this string to your Python driver and your FastAPI config. The Week 12 code builds the same connection from its parts (host=localhost port=5432 dbname=verses user=verses_app) and reads the password from PG_PASSWORD — same coordinates, just written keyword-style.

The Python driver

Use psycopg (version 3), the standard Postgres driver for Python. The [binary] extra ships precompiled so you don’t need a C compiler:

pip install "psycopg[binary]"

(Do this inside your project’s .venv — see Appendix A.1.1.)

Verify it worked

From the command line (replace changeme with the password you set):

psql "postgresql://verses_app:changeme@localhost:5432/verses" -c "SELECT version();"

That should print a PostgreSQL 16.x ... line (16.x is the current series as of 2026; a newer major version is fine). Now confirm Python can connect — and read the password from the environment, the way the course code does, instead of typing it into the command:

export PG_PASSWORD='changeme'   # the password you chose; setx on Windows
python3 -c "import os, psycopg; print(psycopg.connect(f\"host=localhost port=5432 dbname=verses user=verses_app password={os.environ['PG_PASSWORD']}\").execute('SELECT 1').fetchone())"

If it prints (1,), your driver, server, user, and database all line up — and you’ve confirmed the exact connection shape (dbname=verses, user=verses_app, password from PG_PASSWORD) that every Week 12 file uses.

Coach’s Note — connection refused means the server isn’t running (or isn’t on 5432). password authentication failed means the server is running and your user/password is wrong — a completely different fix. Read the error word for word before you change anything. The error is telling you exactly where the wall is.


B.3 — MongoDB (the document store)

MongoDB stores documents — JSON-shaped records (BSON under the hood) grouped into collections — instead of rows in tables. It’s a server process listening on port 27017 by default, and you talk to it with mongosh (the shell) or a driver. It’s the right tool when your data is naturally nested and your access patterns are document-shaped; the wrong tool when you need multi-table joins and strict relational integrity. You’ll learn that line in Week 13.

Install MongoDB Community

macOS (Homebrew tap):

brew tap mongodb/brew
brew install mongodb-community
brew services start mongodb-community

Windows Download the MongoDB Community Server MSI from mongodb.com/try/download/community. Run it; choose the “Complete” setup and let it install MongoDB as a service (it then starts automatically). Optionally install MongoDB Compass (the GUI) when offered. Download mongosh separately from the same site if the installer doesn’t include it.

Linux (Ubuntu/Debian) Mongo isn’t in the default apt repos; add MongoDB’s official repo first. Follow the current steps at mongodb.com/docs/manual/administration/install-on-linux for your exact release (it has you import a GPG key and add a sources.list.d entry), then:

sudo apt update
sudo apt install -y mongodb-org
sudo systemctl enable --now mongod

Starting mongod

mongod is the server daemon.

  • macOS (brew): brew services start mongodb-community.
  • Windows: it’s a service, already running.
  • Linux: sudo systemctl start mongod.

To run it manually (foreground) anywhere: mongod --dbpath /path/to/data.

The Python driver

Use PyMongo, the official driver:

pip install pymongo

Verify it worked

The shell:

mongosh --eval "db.runCommand({ ping: 1 })"

A response containing ok: 1 means the server answered. Now confirm Python:

python3 -c "from pymongo import MongoClient; c=MongoClient('mongodb://localhost:27017'); print(c.admin.command('ping'))"

{'ok': 1.0} means PyMongo reached the server. The connection string mongodb://localhost:27017 is Mongo’s equivalent of the Postgres URL — protocol, host, port.


B.4 — The Docker Shortcut (for the brave)

Installing three database servers natively is real work, and it scatters services across your machine. If you already use — or are willing to install — Docker, you can run Postgres and Mongo as disposable containers instead, with no native install of the database itself. The honest caveat: Docker itself must be installed and running, and that’s its own setup (Docker Desktop on macOS/Windows, the docker engine on Linux). If you don’t already want Docker for other reasons, the native installs above are simpler.

With Docker running, one line each:

PostgreSQL:

docker run -d --name pg-verses \
  -e POSTGRES_PASSWORD=changeme \
  -e POSTGRES_USER=verses_app \
  -e POSTGRES_DB=verses \
  -p 5432:5432 \
  -v pgdata:/var/lib/postgresql/data \
  postgres:16

This creates the same verses_app user and verses database the native install does, so the Week 12 code connects identically. (Replace changeme with your password and set PG_PASSWORD to match.)

MongoDB:

docker run -d --name mongo-coding3 \
  -p 27017:27017 \
  -v mongodata:/data/db \
  mongo:7

What the flags mean: -d runs it in the background; --name lets you docker stop/docker start it by name; -e sets environment variables (the password/user/db); -p host:container maps the container’s port to the same port on your machine, so localhost:5432 reaches it; -v namedvolume:/path stores the data in a named volume so it survives the container being deleted (without this, your data vanishes when the container does).

Verify exactly as in B.2 and B.3 — the connection strings (localhost:5432, localhost:27017) are identical to the native installs, which is the whole point. Stop a container with docker stop pg-verses; start it again with docker start pg-verses.

Coach’s Note — Docker is a fourth tool with its own cost. It’s a wonderful way to run a database without polluting your machine, and a terrible thing to be fighting at 11pm the night before a project is due because you’d never used it. Choose deliberately: if Docker is new to you, install the databases natively this term and learn Docker when it isn’t also exam week.


B.5 — Quick Reference

DatabaseDefault portHow to startHow to connect (shell)Python driver
SQLitenone (it’s a file)nothing to startsqlite3 mydata.dbbuilt in (import sqlite3)
PostgreSQL5432brew services start / systemctl start postgresql / Windows servicepsql "postgresql://verses_app:pw@localhost:5432/verses"psycopg[binary]
MongoDB27017brew services start mongodb-community / systemctl start mongod / Windows servicemongosh "mongodb://localhost:27017"pymongo

When Things Go Wrong

  • connection refused (Postgres or Mongo) — The server isn’t running, or it’s on a different port. Start the service (B.2 / B.3) and confirm the port matches your connection string.
  • password authentication failed (Postgres) — Server’s up; your user or password is wrong. Re-check the CREATE USER step and your connection string. They are different problems from “refused.”
  • psql: command not found on macOS — Postgres.app or Homebrew installed the server but its CLI tools aren’t on your PATH. Follow the app’s “add to PATH” note, then open a fresh terminal.
  • mongod won’t start on Linux — Usually a permissions or data-directory issue. Check sudo systemctl status mongod and the log it points to; the cause is almost always spelled out there.
  • Port already in use — Something else is on 5432 or 27017 (often a second copy of the same database, or a leftover Docker container). Stop the other process, or map the Docker container to a different host port (-p 5433:5432) and adjust your connection string.
  • Native install and a Docker container both running — They’ll fight over the port. Pick one.

Up next: Appendix C — The Agentic-AI Toolkit. With your toolchain (Appendix A) and your databases ready, you’re equipped for all of Phase 2. Head back to Chapter 11 when SQLite is your week.