REST APIs with Flask: Build a JSON API in 30 Minutes

Flask is excellent for REST APIs. Here's how to build one that's actually production-ready.

Setup

pip install flask flask-cors

Application Structure

from flask import Flask, jsonify, request, abort
from flask_cors import CORS
import sqlite3

app = Flask(__name__)
CORS(app)  # allow cross-origin requests

def get_db():
    db = sqlite3.connect('api.db')
    db.row_factory = sqlite3.Row
    return db

CRUD Endpoints

# GET /api/v1/posts
@app.route('/api/v1/posts')
def list_posts():
    page     = request.args.get('page', 1, type=int)
    per_page = min(request.args.get('per_page', 10, type=int), 100)
    offset   = (page - 1) * per_page

    db    = get_db()
    posts = db.execute(
        'SELECT * FROM posts ORDER BY created_at DESC LIMIT ? OFFSET ?',
        (per_page, offset)
    ).fetchall()
    total = db.execute('SELECT COUNT(*) FROM posts').fetchone()[0]

    return jsonify({
        'data': [dict(p) for p in posts],
        'meta': {'page': page, 'per_page': per_page, 'total': total},
    })

# GET /api/v1/posts/<id>
@app.route('/api/v1/posts/<int:post_id>')
def get_post(post_id):
    post = get_db().execute(
        'SELECT * FROM posts WHERE id=?', (post_id,)
    ).fetchone()
    if not post:
        return jsonify({'error': 'Not found'}), 404
    return jsonify(dict(post))

# POST /api/v1/posts
@app.route('/api/v1/posts', methods=['POST'])
def create_post():
    data  = request.get_json(silent=True) or {}
    title = data.get('title', '').strip()
    body  = data.get('body', '').strip()

    if not title or not body:
        return jsonify({'error': 'title and body are required'}), 422

    db = get_db()
    cur = db.execute(
        'INSERT INTO posts (title, body) VALUES (?,?)', (title, body)
    )
    db.commit()
    return jsonify({'id': cur.lastrowid, 'title': title}), 201

# DELETE /api/v1/posts/<id>
@app.route('/api/v1/posts/<int:post_id>', methods=['DELETE'])
def delete_post(post_id):
    db = get_db()
    db.execute('DELETE FROM posts WHERE id=?', (post_id,))
    db.commit()
    return '', 204

API Key Authentication

import functools

VALID_KEYS = {'sk-test-abc123', 'sk-prod-xyz789'}

def require_api_key(f):
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        key = request.headers.get('X-API-Key') or request.args.get('api_key')
        if key not in VALID_KEYS:
            return jsonify({'error': 'Unauthorized'}), 401
        return f(*args, **kwargs)
    return wrapper

# Apply to protected routes:
@app.route('/api/v1/posts', methods=['POST'])
@require_api_key
def create_post():
    ...

Error Handling

@app.errorhandler(404)
def not_found(e):
    return jsonify({'error': 'Resource not found'}), 404

@app.errorhandler(405)
def method_not_allowed(e):
    return jsonify({'error': 'Method not allowed'}), 405

Your API is now ready to power a mobile app, a frontend SPA, or integrate with third-party services.