DEV Community

Ayaz
Ayaz

Posted on

How to Build a REST API with Python and Flask (Step-by-Step)

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
Enter fullscreen mode Exit fullscreen mode

Activate the virtual environment:

On Mac/Linux:

source venv/bin/activate
Enter fullscreen mode Exit fullscreen mode

On Windows:

venv\Scripts\activate
Enter fullscreen mode Exit fullscreen mode

Now install Flask:

pip install flask
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Run it:

python app.py
Enter fullscreen mode Exit fullscreen mode

Open your browser and go to http://127.0.0.1:5000/. You should see:

{
  "message": "Welcome to my REST API!"
}
Enter fullscreen mode Exit fullscreen mode

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"}
]
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"}'
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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"})
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)