| name | flask |
| description | Expert in Flask web framework - lightweight Python framework for REST APIs and web applications. Use this when building Flask routes, handling requests, error handling, or database integration. |
| license | MIT |
Flask Web Framework Expert
Expert in Flask 3.0+ for building REST APIs and web applications in Python.
When to Use This Skill
- Creating Flask routes and endpoints
- Handling HTTP requests (GET, POST, PUT, DELETE)
- Processing JSON requests/responses
- Error handling and validation
- Database integration (SQLite, SQLAlchemy)
- Testing Flask applications
- Production deployment
Flask Basics
Minimal Application
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello, World!'
if __name__ == '__main__':
app.run(debug=True)
Running Flask
python app.py
flask --app app run --debug
flask --app app run --host=0.0.0.0 --port=5000
Routing
URL Variables
@app.route('/user/<username>')
def show_user(username):
return f'User: {username}'
@app.route('/post/<int:post_id>')
def show_post(post_id):
return f'Post {post_id}'
@app.route('/user/<username>/post/<int:post_id>')
def user_post(username, post_id):
return f'{username} - Post {post_id}'
HTTP Methods
from flask import request
@app.route('/api/users', methods=['POST'])
def create_user():
return 'User created', 201
@app.route('/api/users/<int:id>', methods=['GET', 'PUT', 'DELETE'])
def handle_user(id):
if request.method == 'GET':
return jsonify({'id': id})
elif request.method == 'PUT':
return jsonify({'id': id, 'updated': True})
elif request.method == 'DELETE':
return '', 204
Request Handling
JSON Requests
from flask import request, jsonify
@app.route('/api/user', methods=['POST'])
def create_user():
data = request.get_json()
if not data:
return jsonify({'error': 'No data provided'}), 400
if 'name' not in data:
return jsonify({'error': 'Name is required'}), 400
user = {
'id': 123,
'name': data['name'],
'email': data.get('email', '')
}
return jsonify(user), 201
Query Parameters
@app.route('/search')
def search():
query = request.args.get('q', '')
page = request.args.get('page', 1, type=int)
return jsonify({
'query': query,
'page': page
})
Headers and Cookies
@app.route('/api/protected')
def protected():
auth_token = request.headers.get('Authorization')
session_id = request.cookies.get('session')
return jsonify({'authenticated': True})
Response Handling
JSON Responses
from flask import jsonify
@app.route('/api/user')
def get_user():
user = {'id': 1, 'name': 'John'}
return jsonify(user), 200
Custom Headers
from flask import make_response
@app.route('/api/data')
def get_data():
resp = make_response(jsonify({'data': []}))
resp.headers['X-Custom-Header'] = 'Value'
resp.set_cookie('session', 'abc123')
return resp
Status Codes
return jsonify({'message': 'OK'}), 200
return jsonify({'message': 'Created'}), 201
return '', 204
return jsonify({'error': 'Bad Request'}), 400
return jsonify({'error': 'Not Found'}), 404
return jsonify({'error': 'Server Error'}), 500
Error Handling
Custom Error Handlers
@app.errorhandler(404)
def not_found(error):
return jsonify({'error': 'Not found'}), 404
@app.errorhandler(500)
def internal_error(error):
return jsonify({'error': 'Internal server error'}), 500
class ValidationError(Exception):
pass
@app.errorhandler(ValidationError)
def handle_validation_error(error):
return jsonify({'error': str(error)}), 400
Try-Except in Routes
@app.route('/api/user/<int:id>')
def get_user(id):
try:
user = database.get_user(id)
if not user:
return jsonify({'error': 'User not found'}), 404
return jsonify(user), 200
except ValueError as e:
return jsonify({'error': 'Invalid ID'}), 400
except Exception as e:
app.logger.error(f'Error: {e}')
return jsonify({'error': 'Server error'}), 500
Database Integration
SQLite (Standard Library)
import sqlite3
from flask import g
DATABASE = 'database.db'
def get_db():
db = getattr(g, '_database', None)
if db is None:
db = g._database = sqlite3.connect(DATABASE)
db.row_factory = sqlite3.Row
return db
@app.teardown_appcontext
def close_connection(exception):
db = getattr(g, '_database', None)
if db is not None:
db.close()
@app.route('/users')
def list_users():
db = get_db()
cursor = db.execute('SELECT * FROM users')
users = [dict(row) for row in cursor.fetchall()]
return jsonify(users)
Flask-SQLAlchemy
from flask_sqlalchemy import SQLAlchemy
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), nullable=False)
def to_dict(self):
return {'id': self.id, 'username': self.username}
with app.app_context():
db.create_all()
@app.route('/users')
def list_users():
users = User.query.all()
return jsonify([u.to_dict() for u in users])
Input Validation
@app.route('/api/users', methods=['POST'])
def create_user():
data = request.get_json()
if not data:
return jsonify({'error': 'No data provided'}), 400
required = ['name', 'email']
for field in required:
if field not in data or not data[field]:
return jsonify({'error': f'{field} is required'}), 400
if '@' not in data['email']:
return jsonify({'error': 'Invalid email'}), 400
user = save_user(data)
return jsonify(user), 201
Testing
pytest
import pytest
from app import app
@pytest.fixture
def client():
app.config['TESTING'] = True
with app.test_client() as client:
yield client
def test_get_user(client):
response = client.get('/api/users/1')
assert response.status_code == 200
assert 'id' in response.json
def test_create_user(client):
response = client.post('/api/users', json={
'name': 'John',
'email': 'john@example.com'
})
assert response.status_code == 201
Best Practices
- Use Blueprints for modular apps
- Validate input before processing
- Handle errors with custom handlers
- Close database connections properly
- Use environment variables for config
- Don't use debug mode in production
- Return appropriate status codes
- Log errors for debugging
Configuration
import os
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY', 'dev-key')
DATABASE_URI = os.environ.get('DATABASE_URI', 'sqlite:///dev.db')
app.config.from_object('config.Config')
Project Context
In this project, Flask is used for the REST API at scripts/api.py:
Current Endpoints
GET /health - Health check
POST /api/ask - Ask questions
GET /api/questions - List all Q&A
GET /api/questions/<id> - Get specific Q&A
Running the API
poetry run python scripts/api.py
./scripts/start-api.sh
Database
- Uses SQLite at
db/thoughtful_ai.db
- Auto-initializes on startup
- Tables created from hardcoded data
Example Endpoint
@app.route('/api/ask', methods=['POST'])
def ask_question():
data = request.get_json()
if not data or 'question' not in data:
return jsonify({'error': 'Missing question'}), 400
question = data['question'].strip()
if not question:
return jsonify({'error': 'Question cannot be empty'}), 400
match = find_best_match(question)
if match:
return jsonify({
'question': question,
'answer': match['answer'],
'confidence': match['score']
}), 200
else:
return jsonify({
'question': question,
'answer': 'Fallback response...',
'confidence': 0.0
}), 200