Salta el contingut

Creació d'una API REST amb Flask

Flask és ideal per construir APIs REST lleugeres. Amb l'extensió Flask-RESTful o simplement amb les rutes de Flask i jsonify, es pot crear una API funcional en poques línies.


Instal·lació

pip install flask
pip install flask-restful   # opcional, però recomanat

API bàsica amb Flask i jsonify

La manera més senzilla de crear una API és retornar JSON directament des de les rutes:

from flask import Flask, jsonify, request, abort

app = Flask(__name__)

# Dades en memòria (simulació de base de dades)
pelicules = [
    {"id": 1, "titol": "El Padrí", "any": 1972, "puntuacio": 9.2},
    {"id": 2, "titol": "Schindler's List", "any": 1993, "puntuacio": 8.9},
    {"id": 3, "titol": "Pulp Fiction", "any": 1994, "puntuacio": 8.9},
]

@app.route('/api/pelicules', methods=['GET'])
def get_pelicules():
    return jsonify(pelicules)

@app.route('/api/pelicules/<int:id>', methods=['GET'])
def get_pelicula(id):
    pelicula = next((p for p in pelicules if p['id'] == id), None)
    if pelicula is None:
        abort(404)
    return jsonify(pelicula)

@app.route('/api/pelicules', methods=['POST'])
def create_pelicula():
    data = request.get_json()
    nova = {
        "id": max(p['id'] for p in pelicules) + 1,
        "titol": data['titol'],
        "any": data['any'],
        "puntuacio": data.get('puntuacio', 0)
    }
    pelicules.append(nova)
    return jsonify(nova), 201

@app.route('/api/pelicules/<int:id>', methods=['PUT'])
def update_pelicula(id):
    pelicula = next((p for p in pelicules if p['id'] == id), None)
    if pelicula is None:
        abort(404)
    data = request.get_json()
    pelicula.update(data)
    return jsonify(pelicula)

@app.route('/api/pelicules/<int:id>', methods=['DELETE'])
def delete_pelicula(id):
    global pelicules
    pelicula = next((p for p in pelicules if p['id'] == id), None)
    if pelicula is None:
        abort(404)
    pelicules = [p for p in pelicules if p['id'] != id]
    return '', 204

if __name__ == '__main__':
    app.run(debug=True)

Mètodes HTTP i operacions CRUD

Mètode HTTP Operació CRUD Exemple d'endpoint
GET Read (llegir) GET /api/pelicules
POST Create (crear) POST /api/pelicules
PUT Update (actualitzar) PUT /api/pelicules/1
DELETE Delete (eliminar) DELETE /api/pelicules/1

Codis d'estat HTTP habituals

Codi Significat Quan usar-lo
200 OK Petició correcta GET, PUT reeixits
201 Created Recurs creat POST reeixit
204 No Content Sense contingut DELETE reeixit
400 Bad Request Petició incorrecta Dades invàlides
404 Not Found No trobat Recurs no existeix
500 Internal Server Error Error intern Error no controlat

Provar l'API amb curl

# Obtenir totes les pel·lícules
curl http://127.0.0.1:5000/api/pelicules

# Obtenir una pel·lícula per ID
curl http://127.0.0.1:5000/api/pelicules/1

# Crear una nova pel·lícula
curl -X POST http://127.0.0.1:5000/api/pelicules \
     -H "Content-Type: application/json" \
     -d '{"titol": "Interstellar", "any": 2014, "puntuacio": 8.6}'

# Actualitzar una pel·lícula
curl -X PUT http://127.0.0.1:5000/api/pelicules/1 \
     -H "Content-Type: application/json" \
     -d '{"puntuacio": 9.5}'

# Eliminar una pel·lícula
curl -X DELETE http://127.0.0.1:5000/api/pelicules/1

API amb Flask-RESTful

Flask-RESTful organitza la lògica en classes per cada recurs:

from flask import Flask, request
from flask_restful import Api, Resource, abort

app = Flask(__name__)
api = Api(app)

pelicules = {
    1: {"titol": "El Padrí", "any": 1972},
    2: {"titol": "Pulp Fiction", "any": 1994},
}

class PeliculaList(Resource):
    def get(self):
        return list(pelicules.values())

    def post(self):
        data = request.get_json()
        nou_id = max(pelicules.keys()) + 1
        pelicules[nou_id] = data
        return pelicules[nou_id], 201

class Pelicula(Resource):
    def get(self, id):
        if id not in pelicules:
            abort(404, message=f"Pel·lícula {id} no trobada")
        return pelicules[id]

    def delete(self, id):
        if id not in pelicules:
            abort(404, message=f"Pel·lícula {id} no trobada")
        del pelicules[id]
        return '', 204

api.add_resource(PeliculaList, '/api/pelicules')
api.add_resource(Pelicula, '/api/pelicules/<int:id>')

if __name__ == '__main__':
    app.run(debug=True)

Gestió d'errors personalitzada

from flask import Flask, jsonify

app = Flask(__name__)

@app.errorhandler(404)
def not_found(error):
    return jsonify({"error": "Recurs no trobat"}), 404

@app.errorhandler(400)
def bad_request(error):
    return jsonify({"error": "Petició incorrecta"}), 400

Recursos