본문으로 건너뛰기
Previous
Next
Python REST APIs | Build API Servers with Flask and Django

Python REST APIs | Build API Servers with Flask and Django

Python REST APIs | Build API Servers with Flask and Django

이 글의 핵심

Design RESTful APIs in Python: HTTP verbs, resource URLs, Flask CRUD examples, Django REST Framework, JWT auth, error handling, and production-minded tips.

Introduction

“The backbone of backend work”

REST APIs are the standard way to connect frontends and backends.

1. REST API fundamentals

RESTful design

Resource-oriented routes:
GET    /api/users       - list users
GET    /api/users/1     - get user 1
POST   /api/users       - create user
PUT    /api/users/1     - replace user 1
DELETE /api/users/1     - delete user 1

2. REST API with Flask

CRUD API

from flask import Flask, jsonify, request
app = Flask(__name__)
users = [
    {'id': 1, 'name': 'Alice', 'email': '[email protected]'},
    {'id': 2, 'name': 'Bob', 'email': '[email protected]'}
]
# List (GET)
@app.route('/api/users', methods=['GET'])
def get_users():
    return jsonify({'users': users, 'count': len(users)})
# Single resource (GET)
@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
    user = next((u for u in users if u['id'] == user_id), None)
    if user:
        return jsonify(user)
    return jsonify({'error': 'User not found'}), 404
# Create (POST)
@app.route('/api/users', methods=['POST'])
def create_user():
    data = request.get_json()
    
    if not data or 'name' not in data or 'email' not in data:
        return jsonify({'error': 'Invalid data'}), 400
    
    new_user = {
        'id': max(u['id'] for u in users) + 1 if users else 1,
        'name': data['name'],
        'email': data['email']
    }
    users.append(new_user)
    return jsonify(new_user), 201
# Update (PUT)
@app.route('/api/users/<int:user_id>', methods=['PUT'])
def update_user(user_id):
    user = next((u for u in users if u['id'] == user_id), None)
    if not user:
        return jsonify({'error': 'User not found'}), 404
    
    data = request.get_json()
    user.update(data)
    return jsonify(user)
# Delete (DELETE)
@app.route('/api/users/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):
    global users
    users = [u for u in users if u['id'] != user_id]
    return ', 204
if __name__ == '__main__':
    app.run(debug=True)

3. Django REST Framework

Installation

pip install djangorestframework

Serializers

# blog/serializers.py
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ['id', 'title', 'content', 'author', 'created_at']
        read_only_fields = ['id', 'created_at']

ViewSet

# blog/views.py
from rest_framework import viewsets
from .models import Post
from .serializers import PostSerializer
class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

URL routing

# blog/urls.py
from rest_framework.routers import DefaultRouter
from .views import PostViewSet
router = DefaultRouter()
router.register(r'posts', PostViewSet)
urlpatterns = router.urls

4. Authentication and permissions

JWT authentication

pip install djangorestframework-simplejwt
# settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
}
# urls.py
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)
urlpatterns = [
    path('api/token/', TokenObtainPairView.as_view()),
    path('api/token/refresh/', TokenRefreshView.as_view()),
]

5. Error handling

Custom JSON errors

from flask import jsonify
@app.errorhandler(404)
def not_found(error):
    return jsonify({
        'error': 'Not Found',
        'message': 'The requested resource could not be found'
    }), 404
@app.errorhandler(500)
def internal_error(error):
    return jsonify({
        'error': 'Internal Server Error',
        'message': 'An unexpected server error occurred'
    }), 500

6. Practical example

Full Flask API sketch

from flask import Flask, jsonify, request
from functools import wraps
app = Flask(__name__)
# Simple API key check
API_KEY = "secret-key-123"
def require_api_key(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        api_key = request.headers.get('X-API-Key')
        if api_key != API_KEY:
            return jsonify({'error': 'Unauthorized'}), 401
        return f(*args, **kwargs)
    return decorated
# In-memory store instead of a database
posts = []
@app.route('/api/posts', methods=['GET'])
def get_posts():
    page = request.args.get('page', 1, type=int)
    per_page = request.args.get('per_page', 10, type=int)
    
    start = (page - 1) * per_page
    end = start + per_page
    
    return jsonify({
        'posts': posts[start:end],
        'page': page,
        'total': len(posts)
    })
@app.route('/api/posts', methods=['POST'])
@require_api_key
def create_post():
    data = request.get_json()
    
    required_fields = ['title', 'content']
    if not all(field in data for field in required_fields):
        return jsonify({'error': 'Missing required fields'}), 400
    
    post = {
        'id': len(posts) + 1,
        'title': data['title'],
        'content': data['content'],
        'author': data.get('author', 'Anonymous')
    }
    posts.append(post)
    
    return jsonify(post), 201
if __name__ == '__main__':
    app.run(debug=True)

Practical tips

API design best practices

# Use nouns, not verbs
GET /api/users  # OK
GET /api/getUsers  # Avoid
# Prefer plural collection names
GET /api/users  # OK
GET /api/user  # Avoid
# Hierarchical resources
GET /api/users/1/posts  # posts belonging to user 1
# Meaningful HTTP status codes
200 OK           # success
201 Created      # created
400 Bad Request  # bad input
401 Unauthorized # auth required
404 Not Found    # missing resource
500 Server Error # server failure

Summary

Key takeaways

  1. REST: HTTP plus resource-oriented URLs
  2. Flask: lightweight, flexible API servers
  3. Django REST Framework: batteries-included API toolkit
  4. Auth: JWT, API keys
  5. Errors: consistent JSON bodies and correct status codes

Next steps

  • [Databases in Python](/en/blog/python-series-14-database/
  • [Python web deployment](/en/blog/python-series-15-deployment/

  • [Flask basics | Get started with the Python web framework](/en/blog/python-series-11-flask-basics/

자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. Design RESTful APIs in Python: HTTP verbs, resource URLs, Flask CRUD examples, Django REST Framework, JWT auth, error ha… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. Python 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

  • [Flask Basics | Get Started with the Python Web Framework](/en/blog/python-series-11-flask-basics/
  • [Django Basics](/en/blog/python-series-12-django-basics/
  • [Express REST API Tutorial for Node.js | Routing](/en/blog/nodejs-express-rest-api-tutorial/
  • [C++ REST API 서버 완벽 가이드 | Beast 라우팅·JSON·미들웨어 [#31-2]](/en/blog/cpp-series-31-2-rest-api-server/

이 글에서 다루는 키워드 (관련 검색어)

Python, REST API, Flask, Django, Backend, HTTP, JSON 등으로 검색하시면 이 글이 도움이 됩니다.