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ó¶
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