Backend Solutions
Knowledge Check Answers
-
jsonify()converts a Python dictionary to a JSON response and sets the correctContent-Type: application/jsonheader. Without it, Flask may return unexpected formats or errors. -
A virtual environment isolates your project's dependencies from the system Python, preventing version conflicts between projects.
-
db.session.commit()saves all pending changes to the database. Without it, changes (adds, updates, deletes) are lost when the session ends. -
nullable=Falsemeans the column cannot beNULL/empty in the database. An error is raised if you try to save a record without providing a value. -
.envfiles contain secrets (API keys, passwords). If committed to git and pushed to a public repository, anyone can see and steal your keys.
Challenge 1 Solution
Add to routes.py:
@api.route("/messages", methods=["DELETE"])
def delete_all_messages():
count = Message.query.delete()
db.session.commit()
return jsonify({"deleted": count})
Test:
curl -X DELETE http://localhost:5000/api/messages
# → {"deleted": 5}
Challenge 2 Solution
In-memory approach (simple):
Add near the top of routes.py:
CURRENT_SYSTEM_PROMPT = "You are a helpful assistant."
Add two new routes:
@api.route("/system-prompt", methods=["GET"])
def get_system_prompt():
global CURRENT_SYSTEM_PROMPT
return jsonify({"current_prompt": CURRENT_SYSTEM_PROMPT})
@api.route("/system-prompt", methods=["POST"])
def set_system_prompt():
global CURRENT_SYSTEM_PROMPT
data = request.get_json()
CURRENT_SYSTEM_PROMPT = data.get("prompt", CURRENT_SYSTEM_PROMPT)
return jsonify({"current_prompt": CURRENT_SYSTEM_PROMPT})
Then update the chat route to use CURRENT_SYSTEM_PROMPT:
messages = [
{"role": "system", "content": CURRENT_SYSTEM_PROMPT},
{"role": "user", "content": user_message},
]
Test:
curl -X POST http://localhost:5000/api/system-prompt \
-H "Content-Type: application/json" \
-d '{"prompt": "You speak like a pirate."}'
curl -X POST http://localhost:5000/api/chat \
-H "Content-Type: application/json" \
-d '{"message": "Hello!"}'
# → "Ahoy, matey!"
Challenge 3 Solution
Modify the chat route to include conversation history:
@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()
try:
# Get last 10 messages for context
recent = Message.query.order_by(Message.timestamp.desc()).limit(10).all()
recent.reverse()
messages = [{"role": "system", "content": "You are a helpful assistant."}]
for msg in recent:
role = "user" if msg.sender == "user" else "assistant"
messages.append({"role": role, "content": msg.content})
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
)
ai_reply = response.choices[0].message.content
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(),
})
except Exception as e:
return jsonify({"error": str(e)}), 500
Open-Ended Project: Todo List API
models.py — add Todo model
class Todo(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
completed = db.Column(db.Boolean, default=False)
created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc))
def to_dict(self):
return {
"id": self.id,
"title": self.title,
"completed": self.completed,
"created_at": self.created_at.isoformat() if self.created_at else None,
}
routes.py — add todo routes
@api.route("/todos", methods=["GET"])
def get_todos():
completed = request.args.get("completed")
sort = request.args.get("sort", "created_at")
order = request.args.get("order", "desc")
query = Todo.query
if completed is not None:
val = completed.lower() == "true"
query = query.filter_by(completed=val)
if order == "desc":
query = query.order_by(getattr(Todo, sort).desc())
else:
query = query.order_by(getattr(Todo, sort).asc())
todos = query.all()
return jsonify([t.to_dict() for t in todos])
@api.route("/todos", methods=["POST"])
def create_todo():
data = request.get_json()
if not data or not data.get("title"):
return jsonify({"error": "Title is required"}), 400
todo = Todo(title=data["title"])
db.session.add(todo)
db.session.commit()
return jsonify(todo.to_dict()), 201
@api.route("/todos/<int:todo_id>", methods=["PUT"])
def update_todo(todo_id):
todo = Todo.query.get(todo_id)
if not todo:
return jsonify({"error": "Not found"}), 404
data = request.get_json()
if "completed" in data:
todo.completed = data["completed"]
if "title" in data:
todo.title = data["title"]
db.session.commit()
return jsonify(todo.to_dict())
@api.route("/todos/<int:todo_id>", methods=["DELETE"])
def delete_todo(todo_id):
todo = Todo.query.get(todo_id)
if not todo:
return jsonify({"error": "Not found"}), 404
db.session.delete(todo)
db.session.commit()
return jsonify({"deleted": True})
Test the full flow:
# Create
curl -X POST http://localhost:5000/api/todos \
-H "Content-Type: application/json" \
-d '{"title": "Learn Flask"}'
# List
curl http://localhost:5000/api/todos
# Complete
curl -X PUT http://localhost:5000/api/todos/1 \
-H "Content-Type: application/json" \
-d '{"completed": true}'
# Delete
curl -X DELETE http://localhost:5000/api/todos/1