Building RESTful APIs with Flask
Flask is a lightweight and powerful Python web framework perfect for building RESTful APIs. In this guide, we’ll create a complete REST API from scratch, implementing best practices for scalable and maintainable code.
Why Flask for APIs?
Flask offers several advantages for API development:
- Lightweight and minimalist - only what you need
- Flexible architecture - build it your way
- Extensive ecosystem of extensions
- Easy to learn and quick to prototype
- Production-ready with proper configuration
Setting Up Your Project
First, create a virtual environment and install Flask:
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install Flask
pip install Flask flask-sqlalchemy flask-cors
Creating Your First API
Let’s build a simple books API with CRUD operations:
from flask import Flask, jsonify, request
from flask_cors import CORS
app = Flask(__name__)
CORS(app) # Enable CORS for all routes
# In-memory database (use real DB in production)
books = [
{"id": 1, "title": "Python Crash Course", "author": "Eric Matthes"},
{"id": 2, "title": "Flask Web Development", "author": "Miguel Grinberg"}
]
# GET all books
@app.route('/api/books', methods=['GET'])
def get_books():
return jsonify({"books": books}), 200
# GET single book
@app.route('/api/books/<int:book_id>', methods=['GET'])
def get_book(book_id):
book = next((b for b in books if b["id"] == book_id), None)
if book:
return jsonify(book), 200
return jsonify({"error": "Book not found"}), 404
# POST new book
@app.route('/api/books', methods=['POST'])
def create_book():
data = request.get_json()
new_book = {
"id": len(books) + 1,
"title": data.get("title"),
"author": data.get("author")
}
books.append(new_book)
return jsonify(new_book), 201
# PUT update book
@app.route('/api/books/<int:book_id>', methods=['PUT'])
def update_book(book_id):
book = next((b for b in books if b["id"] == book_id), None)
if not book:
return jsonify({"error": "Book not found"}), 404
data = request.get_json()
book.update(data)
return jsonify(book), 200
# DELETE book
@app.route('/api/books/<int:book_id>', methods=['DELETE'])
def delete_book(book_id):
global books
books = [b for b in books if b["id"] != book_id]
return jsonify({"message": "Book deleted"}), 200
if __name__ == '__main__':
app.run(debug=True)
API Best Practices
1. Use Proper HTTP Methods
- GET - Retrieve resources
- POST - Create new resources
- PUT/PATCH - Update existing resources
- DELETE - Remove resources
2. Implement Error Handling
@app.errorhandler(404)
def not_found(error):
return jsonify({"error": "Resource not found"}), 404
@app.errorhandler(500)
def internal_error(error):
return jsonify({"error": "Internal server error"}), 500
3. Validate Input Data
def validate_book_data(data):
if not data.get("title") or not data.get("author"):
return False
return True
@app.route('/api/books', methods=['POST'])
def create_book():
data = request.get_json()
if not validate_book_data(data):
return jsonify({"error": "Invalid book data"}), 400
# ... rest of the code
Adding Database Integration
For production applications, use a real database with SQLAlchemy:
from flask_sqlalchemy import SQLAlchemy
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///books.db'
db = SQLAlchemy(app)
class Book(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
author = db.Column(db.String(50), nullable=False)
def to_dict(self):
return {
"id": self.id,
"title": self.title,
"author": self.author
}
# Create tables
with app.app_context():
db.create_all()
Authentication and Security
Protect your API endpoints with JWT tokens:
from flask_jwt_extended import JWTManager, create_access_token, jwt_required
app.config['JWT_SECRET_KEY'] = 'your-secret-key'
jwt = JWTManager(app)
@app.route('/api/login', methods=['POST'])
def login():
username = request.json.get('username')
password = request.json.get('password')
# Validate credentials
access_token = create_access_token(identity=username)
return jsonify(access_token=access_token)
@app.route('/api/protected', methods=['GET'])
@jwt_required()
def protected():
return jsonify(message="This is protected")
Testing Your API
Use pytest to write comprehensive tests:
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_books(client):
response = client.get('/api/books')
assert response.status_code == 200
assert 'books' in response.json
Deployment Tips
- Use environment variables for sensitive configuration
- Enable HTTPS in production
- Implement rate limiting to prevent abuse
- Use a production-grade server like Gunicorn
- Set up logging and monitoring
Conclusion
Flask makes it easy to build powerful RESTful APIs quickly. Start with the basics, follow best practices, and gradually add features like authentication, database integration, and comprehensive testing. Your API will be production-ready in no time!