02-Claude Finished Notes ..
Learning Goals
- Build RESTful APIs that are easy to navigate and use in applications.
Key Vocab
| Term | Definition |
|---|---|
| REST | A convention for developing applications that use HTTP in a consistent, human-readable, machine-readable way. |
| API | A software application that allows two or more software applications to communicate with one another. |
| GET | The most common HTTP request method. Signifies the client is attempting to view a resource. |
| POST | Signifies the client is attempting to create a new resource. |
| PATCH | Signifies the client is attempting to partially update a resource. |
| PUT | Signifies the client is attempting to fully update a resource. |
| DELETE | Signifies the client is attempting to delete a resource. |
Introduction
Flask is already great for building RESTful APIs, but Flask-RESTful makes it even easier. It's an extension that provides the Api and Resource classes, reducing boilerplate and enforcing RESTful structure.
Flask-RESTful: Core Concepts
Api and Resource
Api— the constructor for your RESTful API. Initialized with a Flask app instance, then resources are added to it.Resource— the base class for all your endpoints. Each HTTP verb (get,post,patch,delete) becomes an instance method inside aResourcesubclass.- Resources are registered with
api.add_resource(ResourceClass, '/url').
What's Different from Vanilla Flask?
| Vanilla Flask | Flask-RESTful |
|---|---|
@app.route('/path', methods=['GET']) decorator | class MyResource(Resource) + api.add_resource() |
| One function per route | One class per URL, methods as instance methods |
| Manual JSON responses | Still manual, but structure is cleaner |
⚠️ If you add non-RESTful views, you still need
@app.route()for those.
Getting Started
1. Environment Setup
# From project root
pipenv shell
# Move into server directory
cd server
# Set Flask env vars
export FLASK_APP=app.py
export FLASK_RUN_PORT=5555
2. Initial app.py — Bare Bones Setup + Home Resource
Open server/app.py and add:
#!/usr/bin/env python3
from flask import Flask, request, make_response
from flask_migrate import Migrate
from flask_restful import Api, Resource
from models import db, Newsletter
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///newsletters.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.json.compact = False
migrate = Migrate(app, db)
db.init_app(app)
api = Api(app)
class Home(Resource):
def get(self):
response_dict = {
"message": "Welcome to the Newsletter RESTful API",
}
response = make_response(response_dict, 200)
return response
api.add_resource(Home, '/')
if __name__ == '__main__':
app.run(port=5555, debug=True)
Run the app:
flask run
# or
python app.py
Visit http://127.0.0.1:5555/ — you should see:
{
"message": "Welcome to the Newsletter RESTful API"
}
3. Create the DB and Seed It
flask db upgrade
python seed.py
Retrieving Records — GET /newsletters
Add the Newsletters resource below Home in app.py:
class Newsletters(Resource):
def get(self):
response_dict_list = [n.to_dict() for n in Newsletter.query.all()]
response = make_response(response_dict_list, 200)
return response
api.add_resource(Newsletters, '/newsletters')
Visit http://127.0.0.1:5555/newsletters — you should see all newsletter records:
[
{
"body": "Create southern girl news...",
"edited_at": null,
"id": 1,
"published_at": "2022-09-21 18:35:17",
"title": "Establish they."
},
{
"body": "Really attack we ground production game...",
"edited_at": null,
"id": 2,
"published_at": "2022-09-21 18:35:17",
"title": "Plan wonder manage."
}
]
Creating Records — POST /newsletters
Add a post method inside the existing Newsletters class:
class Newsletters(Resource):
def get(self):
response_dict_list = [n.to_dict() for n in Newsletter.query.all()]
response = make_response(response_dict_list, 200)
return response
def post(self):
new_record = Newsletter(
title=request.form['title'],
body=request.form['body'],
)
db.session.add(new_record)
db.session.commit()
response_dict = new_record.to_dict()
response = make_response(response_dict, 201)
return response
api.add_resource(Newsletters, '/newsletters') # only call add_resource ONCE
✅ You do NOT call
api.add_resource()twice. BothGETandPOSTlive in the same class under the same URL.
Test in Postman:
- URL:
http://127.0.0.1:5555/newsletters - Method:
POST - Body:
form-datatitle→My Newsletterbody→Some content here
⚠️
form-datadoesn't use quotes for strings — it'll throw an error if you include them.
Expected response (201):
{
"body": "Some content here",
"edited_at": null,
"id": 21,
"published_at": "2022-09-21 18:35:17",
"title": "My Newsletter"
}
Retrieving a Single Record — GET /newsletters/<id>
You need a new Resource class for this because:
- A
GETalready exists at/newsletters - This endpoint needs an
idin the URL
Add below Newsletters:
class NewsletterByID(Resource):
def get(self, id):
response_dict = Newsletter.query.filter_by(id=id).first().to_dict()
response = make_response(response_dict, 200)
return response
api.add_resource(NewsletterByID, '/newsletters/<int:id>')
Test in Postman:
- URL:
http://127.0.0.1:5555/newsletters/20 - Method:
GET
Expected response:
{
"body": "College tax head change. Claim exactly because choose. Church edge center across test stock.",
"edited_at": null,
"id": 20,
"published_at": "2022-09-21 18:35:17",
"title": "Court probably not."
}
Full app.py — Complete Solution
#!/usr/bin/env python3
from flask import Flask, request, make_response
from flask_migrate import Migrate
from flask_restful import Api, Resource
from models import db, Newsletter
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///newsletters.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.json.compact = False
migrate = Migrate(app, db)
db.init_app(app)
api = Api(app)
class Home(Resource):
def get(self):
response_dict = {
"message": "Welcome to the Newsletter RESTful API",
}
response = make_response(response_dict, 200)
return response
api.add_resource(Home, '/')
class Newsletters(Resource):
def get(self):
response_dict_list = [n.to_dict() for n in Newsletter.query.all()]
response = make_response(response_dict_list, 200)
return response
def post(self):
new_record = Newsletter(
title=request.form['title'],
body=request.form['body'],
)
db.session.add(new_record)
db.session.commit()
response_dict = new_record.to_dict()
response = make_response(response_dict, 201)
return response
api.add_resource(Newsletters, '/newsletters')
class NewsletterByID(Resource):
def get(self, id):
response_dict = Newsletter.query.filter_by(id=id).first().to_dict()
response = make_response(response_dict, 200)
return response
api.add_resource(NewsletterByID, '/newsletters/<int:id>')
if __name__ == '__main__':
app.run(port=5555, debug=True)
Route Summary
| Resource Class | URL | Methods | Description |
|---|---|---|---|
Home | / | GET | Welcome message |
Newsletters | /newsletters | GET | Return all newsletters |
Newsletters | /newsletters | POST | Create a new newsletter |
NewsletterByID | /newsletters/<int:id> | GET | Return one newsletter by ID |
Key Patterns to Remember
# 1. Always import Api and Resource from flask_restful
from flask_restful import Api, Resource
# 2. Initialize the Api with the Flask app
api = Api(app)
# 3. Each URL group = one Resource class
class MyResource(Resource):
def get(self): # handles GET
pass
def post(self): # handles POST
pass
# 4. Register the class with a URL — only once per class
api.add_resource(MyResource, '/my-url')
# 5. Dynamic URLs use <type:param> syntax, matched in method args
class MyResourceByID(Resource):
def get(self, id): # id comes from the URL
pass
api.add_resource(MyResourceByID, '/my-url/<int:id>')