Backend Step 4: API Endpoints
You'll create the actual chat API endpoints that the mobile app will call.
What You'll Learn
- POST and GET endpoints
- Reading request data with
request.get_json() - Saving and retrieving data from the database
- Using Blueprints to organize routes
Step 1: Create a Blueprint
A Blueprint lets you define routes in a separate file. Create backend/app/routes.py:
from flask import Blueprint, request, jsonify
from . import db
from .models import Message
api = Blueprint("api", __name__)
@api.route("/status")
def status():
return jsonify({"status": "ok"})
@api.route("/messages", methods=["GET"])
def get_messages():
messages = Message.query.order_by(Message.timestamp.asc()).all()
return jsonify([msg.to_dict() for msg in messages])
@api.route("/chat", methods=["POST"])
def chat():
data = request.get_json()
user_message = data.get("message", "")
if not user_message:
return jsonify({"error": "Message is required"}), 400
user_msg = Message(content=user_message, sender="user")
db.session.add(user_msg)
db.session.commit()
# For now, return a hardcoded reply
ai_reply = f"You said: '{user_message}'. I'm a basic bot for now!"
ai_msg = Message(content=ai_reply, sender="ai")
db.session.add(ai_msg)
db.session.commit()
return jsonify({
"user_message": user_msg.to_dict(),
"ai_message": ai_msg.to_dict(),
})
Step 2: Organize Tests with conftest.py
When you have multiple test files, they often share the same setup code. Instead of repeating it, use a conftest.py file that pytest loads automatically.
Create backend/tests/conftest.py:
import pytest
from app import create_app, db as _db
@pytest.fixture
def app():
app = create_app()
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:"
app.config["TESTING"] = True
return app
@pytest.fixture
def client(app):
return app.test_client()
@pytest.fixture(autouse=True)
def db(app):
with app.app_context():
_db.create_all()
yield _db
_db.session.rollback()
_db.drop_all()
This file:
- Creates a Flask app in memory (no files, no leftover data)
- Provides a client fixture that your tests use
- Sets up and tears down the database before and after every test
- The autouse=True means it runs automatically — you don't need to mention it
Now any test file in tests/ can use client and app without any setup code.
Step 3: Update init.py to Use the Blueprint
from flask import Flask
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 .routes import api
app.register_blueprint(api, url_prefix="/api")
with app.app_context():
db.create_all()
return app
if __name__ == "__main__":
app = create_app()
app.run(debug=True, port=5000)
Step 4: Write Automated Tests for Endpoints
Create backend/tests/test_chat.py:
def test_chat_with_message_returns_two_messages(client):
response = client.post("/api/chat", json={"message": "Hello"})
assert response.status_code == 200
data = response.get_json()
assert "user_message" in data
assert "ai_message" in data
assert data["user_message"]["sender"] == "user"
assert data["ai_message"]["sender"] == "ai"
def test_chat_empty_message_returns_400(client):
response = client.post("/api/chat", json={})
assert response.status_code == 400
assert "error" in response.get_json()
Create backend/tests/test_messages.py:
def test_get_messages_returns_list(client):
response = client.get("/api/messages")
assert response.status_code == 200
assert isinstance(response.get_json(), list)
def test_delete_nonexistent_message_returns_404(client):
response = client.delete("/api/messages/999")
assert response.status_code == 404
def test_delete_message_works(client):
client.post("/api/chat", json={"message": "Test"})
messages = client.get("/api/messages").get_json()
msg_id = messages[0]["id"]
response = client.delete(f"/api/messages/{msg_id}")
assert response.status_code == 200
assert response.get_json()["deleted"] is True
Run all tests:
cd aiproject/backend
python -m pytest tests/ -v
Expected output (5 tests):
tests/test_chat.py::test_chat_with_message_returns_two_messages PASSED
tests/test_chat.py::test_chat_empty_message_returns_400 PASSED
tests/test_messages.py::test_get_messages_returns_list PASSED
tests/test_messages.py::test_delete_nonexistent_message_returns_404 PASSED
tests/test_messages.py::test_delete_message_works PASSED
What these tests cover: - Happy path: sending a message works correctly - Edge case: empty message returns error - GET endpoint: returns a list - DELETE: successful and not-found cases
This is called boundary testing — testing both the happy path and the error cases.
Step 5: Test the Endpoints (Manual)
Test GET /api/status
curl http://127.0.0.1:5000/api/status
# → {"status":"ok"}
Test POST /api/chat
curl -X POST http://127.0.0.1:5000/api/chat \
-H "Content-Type: application/json" \
-d '{"message": "Hello!"}'
Expected response:
{
"user_message": {"id": 1, "content": "Hello!", "sender": "user", "timestamp": "..."},
"ai_message": {"id": 2, "content": "You said: 'Hello!'. I'm a basic bot for now!", "sender": "ai", "timestamp": "..."}
}
Test GET /api/messages
curl http://127.0.0.1:5000/api/messages
Expected response — list of all messages so far.
Test POST with empty message
curl -X POST http://127.0.0.1:5000/api/chat \
-H "Content-Type: application/json" \
-d '{}'
Expected: {"error":"Message is required"} with status 400.
Step 6: How It Works
POST /api/chat flow:
1. Mobile app sends JSON {"message": "Hello!"}
2. Flask receives the request
3. request.get_json() parses the JSON body
4. Creates a new Message with sender="user" and saves to database
5. Generates a placeholder AI reply
6. Saves the AI reply to database
7. Returns both messages as JSON
GET /api/messages flow:
1. Mobile app requests all messages
2. Flask queries the database, ordered by timestamp
3. Converts each Message to a dict via to_dict()
4. Returns the list as JSON
Step 7: Understanding HTTP Methods
| Method | Endpoint | What it does |
|---|---|---|
| GET | /api/messages |
Read all messages |
| POST | /api/chat |
Send a message, get reply |
POST is used when creating data. GET is used when reading data.
Step 8: Verification
Run all three tests above and confirm you get JSON responses. If POST returns a 400 error, check that you included the -H "Content-Type: application/json" header.