# GoodGET /users
GET /users/123
POST /users
PUT /users/123
DELETE /users/123
# BadGET /getUsers
POST /createUser
POST /deleteUser/123
Nest for relationships:
1
2
3
4
5
6
7
# User's ordersGET /users/123/orders
GET /users/123/orders/456
# But don't go too deep# Bad: /users/123/orders/456/items/789/reviews# Better: /order-items/789/reviews
Use query params for filtering, sorting, searching:
1
2
3
4
GET /users?status=active
GET /users?sort=created_at&order=desc
GET /users?search=john
GET /users?status=active&role=admin&sort=-created_at
# Success200OK# GET, PUT, PATCH succeeded201Created# POST created a resource204NoContent# DELETE succeeded# Client errors400BadRequest# Malformed request401Unauthorized# No/invalid authentication403Forbidden# Authenticated but not allowed404NotFound# Resource doesn't exist409Conflict# State conflict (duplicate email, etc.)422Unprocessable# Validation failed# Server errors500Internal# Something broke503Unavailable# Service temporarily down
fromflaskimportFlask,jsonify,requestapp=Flask(__name__)@app.route('/users',methods=['POST'])defcreate_user():data=request.json# Validationifnotdata.get('email'):returnjsonify({'error':'validation_error','message':'Email is required'}),422# Check for duplicateifUser.query.filter_by(email=data['email']).first():returnjsonify({'error':'conflict','message':'Email already exists'}),409# Createuser=User(**data)db.session.add(user)db.session.commit()returnjsonify(user.to_dict()),201
{"error":{"code":"validation_error","message":"Request validation failed","details":[{"field":"email","message":"Invalid email format"},{"field":"age","message":"Must be a positive integer"}]}}
fromflask_limiterimportLimiterfromflask_limiter.utilimportget_remote_addresslimiter=Limiter(app=app,key_func=get_remote_address,default_limits=["100 per minute"])@app.route('/users')@limiter.limit("10 per second")deflist_users():...