Introduction
If you've been writing Python for a while, you've probably heard the term "REST API" thrown around a lot. But building one yourself? That can feel intimidating.
In this tutorial, I'll walk you through building a fully functional REST API from scratch using Python and Flask — one of the simplest and most popular web frameworks in Python. By the end, you'll have a working API that can Create, Read, Update, and Delete (CRUD) data.
No databases needed for this one — we'll use in-memory data to keep things simple and focused.
What You'll Need
- Python 3.7 or higher installed
- Basic knowledge of Python (functions, dictionaries, lists)
- A terminal / command prompt
- A text editor (VS Code recommended)
Step 1: Set Up Your Project
First, create a new folder for your project and set up a virtual environment.
mkdir python-rest-api
cd python-rest-api
python -m venv venv
Activate the virtual environment:
On Mac/Linux:
source venv/bin/activate
On Windows:
venv\Scripts\activate
Now install Flask:
pip install flask
Step 2: Create Your First Flask App
Create a file called app.py in your project folder:
from flask import Flask, jsonify, request
app = Flask(__name__)
@app.route('/')
def home():
return jsonify({"message": "Welcome to my REST API!"})
if __name__ == '__main__':
app.run(debug=True)
Run it:
python app.py
Open your browser and go to http://127.0.0.1:5000/. You should see:
{
"message": "Welcome to my REST API!"
}
Congratulations — you just built your first API endpoint! 🎉
Step 3: Add Some Data
Let's create a simple in-memory "database" of books. Add this to app.py just below your imports:
books = [
{"id": 1, "title": "The Pragmatic Programmer", "author": "Andy Hunt"},
{"id": 2, "title": "Clean Code", "author": "Robert C. Martin"},
{"id": 3, "title": "You Don't Know JS", "author": "Kyle Simpson"}
]
Step 4: Build the GET Endpoints (Read)
Now let's add endpoints to fetch books.
Get all books:
@app.route('/books', methods=['GET'])
def get_books():
return jsonify(books)
Get a single book by ID:
@app.route('/books/<int:book_id>', methods=['GET'])
def get_book(book_id):
book = next((b for b in books if b['id'] == book_id), None)
if book is None:
return jsonify({"error": "Book not found"}), 404
return jsonify(book)
Test it by visiting http://127.0.0.1:5000/books — you'll see all three books returned as JSON.
Step 5: Build the POST Endpoint (Create)
This lets users add a new book by sending data to the API.
@app.route('/books', methods=['POST'])
def add_book():
data = request.get_json()
if not data or 'title' not in data or 'author' not in data:
return jsonify({"error": "Title and author are required"}), 400
new_book = {
"id": len(books) + 1,
"title": data['title'],
"author": data['author']
}
books.append(new_book)
return jsonify(new_book), 201
To test this, use a tool like Postman or curl:
curl -X POST http://127.0.0.1:5000/books \
-H "Content-Type: application/json" \
-d '{"title": "Atomic Habits", "author": "James Clear"}'
Step 6: Build the PUT Endpoint (Update)
This updates an existing book:
@app.route('/books/<int:book_id>', methods=['PUT'])
def update_book(book_id):
book = next((b for b in books if b['id'] == book_id), None)
if book is None:
return jsonify({"error": "Book not found"}), 404
data = request.get_json()
book['title'] = data.get('title', book['title'])
book['author'] = data.get('author', book['author'])
return jsonify(book)
Step 7: Build the DELETE Endpoint
This removes a book by ID:
@app.route('/books/<int:book_id>', methods=['DELETE'])
def delete_book(book_id):
global books
book = next((b for b in books if b['id'] == book_id), None)
if book is None:
return jsonify({"error": "Book not found"}), 404
books = [b for b in books if b['id'] != book_id]
return jsonify({"message": f"Book {book_id} deleted successfully"})
Step 8: We Complete app.py
Here's the full file with everything together:
from flask import Flask, jsonify, request
app = Flask(__name__)
books = [
{"id": 1, "title": "The Pragmatic Programmer", "author": "Andy Hunt"},
{"id": 2, "title": "Clean Code", "author": "Robert C. Martin"},
{"id": 3, "title": "You Don't Know JS", "author": "Kyle Simpson"}
]
@app.route('/')
def home():
return jsonify({"message": "Welcome to my REST API!"})
@app.route('/books', methods=['GET'])
def get_books():
return jsonify(books)
@app.route('/books/<int:book_id>', methods=['GET'])
def get_book(book_id):
book = next((b for b in books if b['id'] == book_id), None)
if book is None:
return jsonify({"error": "Book not found"}), 404
return jsonify(book)
@app.route('/books', methods=['POST'])
def add_book():
data = request.get_json()
if not data or 'title' not in data or 'author' not in data:
return jsonify({"error": "Title and author are required"}), 400
new_book = {
"id": len(books) + 1,
"title": data['title'],
"author": data['author']
}
books.append(new_book)
return jsonify(new_book), 201
@app.route('/books/<int:book_id>', methods=['PUT'])
def update_book(book_id):
book = next((b for b in books if b['id'] == book_id), None)
if book is None:
return jsonify({"error": "Book not found"}), 404
data = request.get_json()
book['title'] = data.get('title', book['title'])
book['author'] = data.get('author', book['author'])
return jsonify(book)
@app.route('/books/<int:book_id>', methods=['DELETE'])
def delete_book(book_id):
global books
book = next((b for b in books if b['id'] == book_id), None)
if book is None:
return jsonify({"error": "Book not found"}), 404
books = [b for b in books if b['id'] != book_id]
return jsonify({"message": f"Book {book_id} deleted successfully"})
if __name__ == '__main__':
app.run(debug=True)
What we Built
In this tutorial, we built a fully working REST API that supports all four CRUD operations:
| Method | Endpoint | What it does |
|---|---|---|
| GET | /books |
Get all books |
| GET | /books/<id> |
Get one book |
| POST | /books |
Add a new book |
| PUT | /books/<id> |
Update a book |
| DELETE | /books/<id> |
Delete a book |
Top comments (0)