Say this:
Welcome everyone to the Database Evolution Tutorial! Today, we’ll walk through how data storage has evolved from traditional relational databases to NoSQL and finally to vector databases — the foundation of AI-powered search. We’ll be using a single Docker Compose setup that ties together SQLite, Redis, and PostgreSQL with pgvector, all managed through a FastAPI backend.
Say this:
Every type of database solves a different kind of problem.
- Relational databases like SQLite give us structured, reliable, and strongly consistent data.
- NoSQL databases like Redis provide incredible speed and flexibility when structure isn’t fixed.
- And vector databases — such as Postgres with the pgvector extension — enable semantic, AI-driven search that understands meaning, not just keywords. This tutorial helps you see all three running side by side, understand their use cases, and learn how modern systems often use them together.
Say this:
Let’s begin with SQLite — a lightweight, file-based relational database. It follows a strict schema with tables, columns, and data types. Every record must fit into this structure, and SQL queries let us filter, join, and update data with precision. Relational databases are perfect for transactions, accounting, and core business logic where data integrity matters. In this project, you can test it using endpoints like
/sqlite/itemsfor CRUD operations. It’s simple but powerful — an ideal starting point.
Say this:
Now we move to Redis, which represents the NoSQL family. Unlike relational databases, Redis doesn’t require a schema — it stores key-value pairs directly in memory. This makes it lightning-fast. Redis is great for caching, real-time analytics, message queues, and even temporary document storage. In this tutorial, we show both styles:
- A simple key/value example through
/redis/kv.- And a small document-style storage using Redis hashes via
/redis/doc. This demonstrates how flexibility and performance come together in NoSQL databases.
Say this:
Finally, the modern frontier — Vector Databases. Here, we use Postgres extended with
pgvector. It allows us to store embeddings — numeric vector representations of text — and perform similarity searches. Instead of exact matches, we can find conceptually similar items. For example, when you search for “Python web API,” it can find documents that talk about FastAPI even if that phrase never appears literally. This kind of search powers chatbots, recommendation systems, and semantic search engines. You can try it using/vector/docsto add documents and/vector/searchto query similar ones.
Say this:
Let’s look at how it all fits together.
- FastAPI acts as the single entry point — a unified API service.
- Docker Compose orchestrates three containers: SQLite, Redis, and Postgres with pgvector.
- Each one exposes specific endpoints handled by FastAPI routes.
- The API automatically waits for databases to start, and it initializes tables and extensions on startup. In short, this architecture demonstrates a multi-database, microservice-friendly setup — perfect for education and production prototypes.
Say this:
The best way to understand this system is to test it directly using
curl.
- Create and list items in SQLite with
/sqlite/items.- Set and retrieve keys or small documents in Redis with
/redis/kvand/redis/doc.- Add and semantically search vector embeddings in Postgres via
/vector/docsand/vector/search. Each command can be executed directly in your terminal, so you can see how each database behaves differently in response to the same kind of operation.
Say this:
The future of data is hybrid. Real-world applications no longer rely on a single database type. Instead, they combine relational reliability, NoSQL performance, and vector intelligence. Imagine a workflow like this:
- User and order data in a relational DB,
- Session state and cache in Redis,
- Semantic recommendations powered by pgvector. That’s exactly how AI-enabled platforms work today. And with this Docker Compose stack, you now understand the foundation of that evolution.
Awesome—here’s a copy-paste lab sheet with curl commands to test every feature in your stack. These assume the API is at http://localhost:8000 and you have jq installed for pretty output.
curl -s -X POST http://localhost:8000/sqlite/items \
-H "Content-Type: application/json" \
-d '{"name":"Pen","description":"Blue ink"}' | jqcurl -s http://localhost:8000/sqlite/items | jqcurl -s http://localhost:8000/sqlite/items/1 | jqcurl -s -X PUT http://localhost:8000/sqlite/items/1 \
-H "Content-Type: application/json" \
-d '{"name":"Marker","description":"Black permanent marker"}' | jqcurl -s -X DELETE http://localhost:8000/sqlite/items/1 | jqcurl -s -X POST http://localhost:8000/redis/kv \
-H "Content-Type: application/json" \
-d '{"key":"greeting","value":"hello world"}' | jqcurl -s http://localhost:8000/redis/kv/greeting | jqcurl -s "http://localhost:8000/redis/keys" | jq
# or with a pattern
curl -s "http://localhost:8000/redis/keys?pattern=doc:*" | jqcurl -s -X POST http://localhost:8000/redis/doc \
-H "Content-Type: application/json" \
-d '{"doc_id":"a1","title":"Reminder","body":"Drink water"}' | jqcurl -s http://localhost:8000/redis/doc/a1 | jqcurl -s -X POST http://localhost:8000/vector/docs \
-H "Content-Type: application/json" \
-d '{"text":"FastAPI is a modern Python web framework"}' | jq
curl -s -X POST http://localhost:8000/vector/docs \
-H "Content-Type: application/json" \
-d '{"text":"Redis is an in-memory data store used for caching"}' | jq
curl -s -X POST http://localhost:8000/vector/docs \
-H "Content-Type: application/json" \
-d '{"text":"Postgres with pgvector enables semantic search"}' | jqcurl -s http://localhost:8000/vector/docs/all | jq--data-urlencode)# Option A: encode spaces
curl -s "http://localhost:8000/vector/search?query=python%20web%20api&top_k=2" | jq
# Option B: safer (lets curl encode)
curl -sG http://localhost:8000/vector/search \
--data-urlencode "query=python web api" \
--data-urlencode "top_k=2" | jqbash -lc '
set -e
BASE=http://localhost:8000
echo "== SQLite =="
curl -s -X POST $BASE/sqlite/items -H "Content-Type: application/json" -d "{\"name\":\"Pen\",\"description\":\"Blue ink\"}" | jq
curl -s $BASE/sqlite/items | jq
echo "== Redis KV =="
curl -s -X POST $BASE/redis/kv -H "Content-Type: application/json" -d "{\"key\":\"greeting\",\"value\":\"hello world\"}" | jq
curl -s $BASE/redis/kv/greeting | jq
echo "== Redis Doc =="
curl -s -X POST $BASE/redis/doc -H "Content-Type: application/json" -d "{\"doc_id\":\"a1\",\"title\":\"Reminder\",\"body\":\"Drink water\"}" | jq
curl -s $BASE/redis/doc/a1 | jq
curl -s \"$BASE/redis/keys?pattern=doc:*\" | jq
echo "== pgvector =="
curl -s -X POST $BASE/vector/docs -H "Content-Type: application/json" -d "{\"text\":\"FastAPI is a modern Python web framework\"}" | jq
curl -s -X POST $BASE/vector/docs -H "Content-Type: application/json" -d "{\"text\":\"Redis is an in-memory data store used for caching\"}" | jq
curl -s -X POST $BASE/vector/docs -H "Content-Type: application/json" -d "{\"text\":\"Postgres with pgvector enables semantic search\"}" | jq
curl -s $BASE/vector/docs/all | jq
curl -sG $BASE/vector/search --data-urlencode "query=python web api" --data-urlencode "top_k=2" | jq
'