Backend Step 3: Database Setup with SQLAlchemy
Now you'll add a database to store chat messages.
What You'll Learn
- Setting up SQLAlchemy with Flask
- Defining database models
- Creating tables
- Inserting and querying data
Step 1: Update init.py with Database Setup
Edit backend/app/__init__.py:
from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///chat.db"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db.init_app(app)
@app.route("/")
def home():
return jsonify({"message": "Hello Flask!"})
@app.route("/api/status")
def status():
return jsonify({"status": "ok", "version": "1.0"})
with app.app_context():
db.create_all()
return app
if __name__ == "__main__":
app = create_app()
app.run(debug=True, port=5000)
Key new lines:
- db = SQLAlchemy() — creates the database object
- app.config["SQLALCHEMY_DATABASE_URI"] — tells Flask to use a SQLite file called chat.db
- db.init_app(app) — connects the database to the Flask app
- db.create_all() — creates tables based on your models
Step 2: Create the Message Model
Edit backend/app/models.py:
from . import db
from datetime import datetime, timezone
class Message(db.Model):
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.String(1000), nullable=False)
sender = db.Column(db.String(20), nullable=False)
timestamp = db.Column(
db.DateTime, default=lambda: datetime.now(timezone.utc)
)
def to_dict(self):
return {
"id": self.id,
"content": self.content,
"sender": self.sender,
"timestamp": self.timestamp.isoformat()
if self.timestamp
else None,
}
What each column does:
- id — unique identifier, auto-incremented
- content — the message text (up to 1000 characters, required)
- sender — either "user" or "ai", required
- timestamp — when the message was created (defaults to now)
- to_dict() — converts the model to a dictionary for JSON response
Step 3: Import Models in init.py
Add the import at the top of __init__.py:
from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy
from dotenv import load_dotenv
load_dotenv()
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///chat.db"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db.init_app(app)
from .models import Message # Import models after db init
@app.route("/")
def home():
return jsonify({"message": "Hello Flask!"})
@app.route("/api/status")
def status():
return jsonify({"status": "ok", "version": "1.0"})
with app.app_context():
db.create_all()
return app
if __name__ == "__main__":
app = create_app()
app.run(debug=True, port=5000)
Step 4: Add a Test for the Model
Before running the server, write a test that checks your model works:
Create backend/tests/test_model.py:
from app.models import Message
def test_create_message(client):
"""A message can be created and read back."""
response = client.post("/api/chat", json={"message": "Hello!"})
assert response.status_code == 200
def test_message_has_required_fields(app):
"""Message model has the right fields."""
with app.app_context():
from app import db
from app.models import Message
msg = Message(content="Test", sender="user")
db.session.add(msg)
db.session.commit()
saved = Message.query.first()
assert saved.content == "Test"
assert saved.sender == "user"
assert saved.id is not None
assert saved.timestamp is not None
Run it:
cd aiproject/backend
python -m pytest tests/ -v
You should see both tests pass. The second test directly creates a Message object without going through an API endpoint — it tests the database model in isolation.
Step 5: Run and Verify
Option A — Virtual Environment:
cd aiproject/backend
source venv/bin/activate
python app/__init__.py
Option B — Docker:
cd aiproject/backend
docker compose up
Stop the server (Ctrl+C) and check that instance/chat.db was created:
Venv:
ls -la instance/
# → chat.db
Docker: The database file is stored inside a Docker volume. To check it exists:
docker compose run app ls -la instance/
# → chat.db
To reset the database (delete all messages):
docker compose down -v
docker compose up
Step 5: Test the Database with Python Shell
cd aiproject/backend
source venv/bin/activate
python3
>>> from app import create_app, db
>>> from app.models import Message
>>> app = create_app()
>>> with app.app_context():
... # Add a test message
... msg = Message(content="Hello database!", sender="user")
... db.session.add(msg)
... db.session.commit()
... print(f"Message ID: {msg.id}")
... # Query all messages
... messages = Message.query.all()
... for m in messages:
... print(m.to_dict())
...
Expected output:
Message ID: 1
{'id': 1, 'content': 'Hello database!', 'sender': 'user', 'timestamp': '2025-01-15T10:30:00+00:00'}
Explanation: What is SQLAlchemy?
SQLAlchemy is an ORM (Object-Relational Mapper). It lets you:
- Define database tables as Python classes (
class Message(db.Model)) - Work with rows as Python objects (
msg.content) - Use Python methods instead of SQL (
Message.query.all()instead ofSELECT * FROM message)
Checking Your Work
Your database now:
- Has a message table with 4 columns (id, content, sender, timestamp)
- Contains at least one test message
- Is ready to receive data from API endpoints