Flask REST API Tutorial: Build a Simple Python Backend

Spread the love

Flask REST API Tutorial: Build a Simple Python Backend

Flask REST API Tutorial: Build a Simple Python Backend

Hey there, fellow coder! If you’ve ever wanted to build a REST API but had no idea where to start, you’re in the right place. Today, we’re going to build your very first Flask REST API using Python. It’s super cool to see your backend respond to requests. We’ll even connect it to a simple frontend page. Get ready to power up your web development skills!

What We Are Building: A Mini Book Inventory

We’re creating a simple web application today. It will have two main parts. First, a backend API. This API will manage a small list of books. It will let us get all books or add new ones. Second, we’ll build a basic HTML page. This page will use JavaScript to talk to our API. You can see the books and even add more right from your browser. Think of it as your very own digital bookshelf manager!

Setting Up Your Environment

Before we write any code, we need a good workspace. This setup makes sure our project runs smoothly. It also keeps things neat and tidy.

The Mighty Virtual Environment

Using a virtual environment is a crucial first step. It keeps your project’s dependencies separate. This stops conflicts with other Python projects. It’s like giving your project its own isolated sandbox. Let’s make one quickly! We’ll call it venv.


python -m venv venv
source venv/bin/activate  # On macOS/Linux
# venv\Scripts\activate   # On Windows

Pro Tip: Always activate your virtual environment! It ensures your project uses the right versions. Learn more about Python Virtual Environments: Isolate Projects & Dependencies.

Installing Flask

Now that our virtual environment is active, we can install Flask. Flask is our powerful web framework. It makes building APIs much easier. We just need one simple command for this.


pip install Flask

Great! You are now ready for the real coding fun. We have all the tools we need.

Crafting Your Flask REST API (Backend)

This is where we build the heart of our application. We’ll create a Python file named app.py. This file will contain all our API logic. It will handle requests and send back data. Let’s break down the code for our backend.

Here’s the complete Python code for your app.py file:


from flask import Flask, jsonify, request

app = Flask(__name__)

# Sample data (in-memory, not persistent)
books = [
    {'id': 1, 'title': 'The Hitchhiker\'s Guide to the Galaxy', 'author': 'Douglas Adams'},
    {'id': 2, 'title': 'Pride and Prejudice', 'author': 'Jane Austen'},
    {'id': 3, 'title': '1984', 'author': 'George Orwell'}
]

# Helper to find the next available ID
def get_next_id():
    return max([book['id'] for book in books]) + 1 if books else 1

@app.route('/books', methods=['GET'])
def get_all_books():
    return jsonify(books)

@app.route('/books', methods=['POST'])
def add_book():
    if not request.json or not 'title' in request.json or not 'author' in request.json:
        return jsonify({'error': 'Missing title or author'}), 400

    new_book = {
        'id': get_next_id(),
        'title': request.json['title'],
        'author': request.json['author']
    }
    books.append(new_book)
    return jsonify(new_book), 201 # 201 Created status

# You can uncomment and expand this for more functionality later!
# @app.route('/books/<int:book_id>', methods=['GET'])
# def get_book(book_id):
#     book = next((book for book in books if book['id'] == book_id), None)
#     if book is None:
#         return jsonify({'error': 'Book not found'}), 404
#     return jsonify(book)


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

Our Frontend: HTML Structure

Now, let’s create the basic structure for our web page. This HTML file will provide the layout. It will include a list to display books. Also, it will have a form to add new books. Save this as index.html in the same folder as app.py.

Styling Our Page with CSS

A little bit of CSS makes our page look much nicer. We’ll add some simple styles. This will improve readability and make the layout cleaner. Create a file named style.css in the same directory.

Connecting Frontend to Backend with JavaScript

This is the exciting part! Our JavaScript code will fetch data from the Flask REST API. It will display existing books. Also, it will handle adding new books. Save this as script.js in the same folder.

app.py

# app.py
from flask import Flask, jsonify, request

# Initialize the Flask application
app = Flask(__name__)

# --- In-memory database (for demonstration purposes) ---
# In a real application, you would connect to a database like PostgreSQL, MongoDB, etc.
items = [
    {"id": 1, "name": "Item A", "description": "This is the first item."},
    {"id": 2, "name": "Item B", "description": "This is the second item."},
    {"id": 3, "name": "Item C", "description": "This is the third item."},
]
current_id = 3 # To ensure unique IDs for new items

# --- Helper function for error responses ---
def create_error_response(message, status_code):
    return jsonify({"error": message}), status_code

# --- API Endpoints ---

@app.route('/items', methods=['GET'])
def get_all_items():
    """
    GET /items
    Returns a list of all items.
    Example: curl http://127.0.0.1:5000/items
    """
    return jsonify(items)

@app.route('/items/<int:item_id>', methods=['GET'])
def get_item(item_id):
    """
    GET /items/<id>
    Returns a specific item by its ID.
    Example: curl http://127.0.0.1:5000/items/1
    """
    item = next((item for item in items if item["id"] == item_id), None)
    if item:
        return jsonify(item)
    return create_error_response("Item not found", 404)

@app.route('/items', methods=['POST'])
def create_item():
    """
    POST /items
    Creates a new item.
    Requires JSON data with 'name' and 'description'.
    Example: curl -X POST -H "Content-Type: application/json" -d '{"name": "New Item", "description": "A newly created item."}' http://127.0.0.1:5000/items
    """
    if not request.json or not 'name' in request.json:
        return create_error_response("Missing 'name' field in request body", 400)

    global current_id
    current_id += 1
    new_item = {
        "id": current_id,
        "name": request.json['name'],
        "description": request.json.get('description', "") # Description is optional
    }
    items.append(new_item)
    return jsonify(new_item), 201 # 201 Created

@app.route('/items/<int:item_id>', methods=['PUT'])
def update_item(item_id):
    """
    PUT /items/<id>
    Updates an existing item by its ID.
    Requires JSON data with 'name' and/or 'description'.
    Example: curl -X PUT -H "Content-Type: application/json" -d '{"name": "Updated Item A", "description": "Description has changed."}' http://127.0.0.1:5000/items/1
    """
    item = next((item for item in items if item["id"] == item_id), None)
    if not item:
        return create_error_response("Item not found", 404)

    if not request.json:
        return create_error_response("Request body must be JSON", 400)

    # Update fields if they exist in the request
    item['name'] = request.json.get('name', item['name'])
    item['description'] = request.json.get('description', item['description'])
    
    return jsonify(item)

@app.route('/items/<int:item_id>', methods=['DELETE'])
def delete_item(item_id):
    """
    DELETE /items/<id>
    Deletes an item by its ID.
    Example: curl -X DELETE http://127.0.0.1:5000/items/1
    """
    global items
    original_len = len(items)
    items = [item for item in items if item["id"] != item_id]
    if len(items) < original_len:
        return jsonify({"message": "Item deleted successfully"}), 200
    return create_error_response("Item not found", 404)

# --- How to run the application ---
if __name__ == '__main__':
    # Before running:
    # 1. Ensure you have Python installed.
    # 2. Install Flask: pip install Flask
    #
    # To run this API:
    # python app.py
    #
    # The API will be accessible at http://127.0.0.1:5000
    #
    # Use tools like curl, Postman, or your browser to test the endpoints.
    app.run(debug=True) # debug=True enables reloader and debugger

How It All Works Together

You’ve now got three key files: app.py, index.html, and script.js. Let’s see how these pieces interact. This will bring our book inventory to life. Each part plays a vital role.

The Flask API Logic

Our app.py file sets up the API. We import Flask to create our web application. We also use jsonify to turn Python dictionaries into JSON responses. This is a standard for REST APIs. Our books list acts as a temporary database. We have two main routes, also called endpoints. The /books endpoint with a GET method sends back all books. The /books endpoint with a POST method lets us add a new book. It takes data from the request body. If successful, it sends back the new book details. We use status codes like 200 (OK) or 201 (Created) to tell the client what happened. Also, 400 (Bad Request) if something is wrong. You can learn more about HTTP methods on MDN Web Docs.

Did you know? In Flask, the @app.route decorator connects a URL path to a Python function. Each function is like a handler for that specific route and HTTP method. It’s a key concept in web development! This is similar to how Python functions generally organize code blocks.

Fetching Books from the API

Our script.js file has a function called fetchBooks(). This function uses the Fetch API to make an HTTP GET request. It targets our /books API endpoint. When the API responds, the JavaScript code takes the JSON data. It then creates list items for each book. Finally, it adds these items to the #book-list on our index.html page. It updates the display dynamically.

Adding New Books

The form on our index.html page is connected to the addBook() function in script.js. When you submit the form, this function gathers the title and author. It then sends this data to our API using a POST request. The data is sent as JSON. After the API confirms the new book, the frontend clears the form. It then calls fetchBooks() again. This refreshes the list with the newly added book. This creates a smooth user experience.

Running Everything

To see your amazing project in action, follow these steps:

  1. First, open your terminal or command prompt.
  2. Navigate to the folder where you saved all your files.
  3. Make sure your virtual environment is active.
  4. Run your Flask application: python app.py.
  5. You should see output like * Running on http://127.0.0.1:5000/.
  6. Open your index.html file in your web browser. You can usually just double-click it.

Your browser will load the HTML. The JavaScript will then talk to your running Flask API. You’ll see the books and can add more! How cool is that?

Tips to Customise Your Flask REST API Project

Congratulations on building your first working Flask REST API! But don’t stop here. Here are some ideas to expand and personalize your project:

  • Add More Endpoints: Implement PUT (update a book) and DELETE (remove a book) methods. This will give your API full CRUD (Create, Read, Update, Delete) capabilities.
  • Persistence: Our current data is lost when the server restarts. Integrate a database like SQLite or PostgreSQL to store books permanently.
  • Error Handling: Implement more robust error handling. Return specific error messages for invalid inputs.
  • Validation: Add validation to ensure that incoming data is in the correct format. Flask-WTF or custom decorators can help.
  • Frontend Enhancements: Improve the UI! Add search functionality, sort options, or even a simple edit button for books.

Conclusion: You Did It!

Wow, what an accomplishment! You’ve just built your very first functional Flask REST API and connected it to a frontend. You’ve handled routing, JSON data, and HTTP requests. These are fundamental skills for any web developer. This project lays a strong foundation for more complex applications. Now, go show off your creation! Share it with friends or on social media. Keep experimenting, keep learning, and happy coding!

app.py

# app.py
from flask import Flask, jsonify, request

# Initialize the Flask application
app = Flask(__name__)

# --- In-memory database (for demonstration purposes) ---
# In a real application, you would connect to a database like PostgreSQL, MongoDB, etc.
items = [
    {"id": 1, "name": "Item A", "description": "This is the first item."},
    {"id": 2, "name": "Item B", "description": "This is the second item."},
    {"id": 3, "name": "Item C", "description": "This is the third item."},
]
current_id = 3 # To ensure unique IDs for new items

# --- Helper function for error responses ---
def create_error_response(message, status_code):
    return jsonify({"error": message}), status_code

# --- API Endpoints ---

@app.route('/items', methods=['GET'])
def get_all_items():
    """
    GET /items
    Returns a list of all items.
    Example: curl http://127.0.0.1:5000/items
    """
    return jsonify(items)

@app.route('/items/<int:item_id>', methods=['GET'])
def get_item(item_id):
    """
    GET /items/<id>
    Returns a specific item by its ID.
    Example: curl http://127.0.0.1:5000/items/1
    """
    item = next((item for item in items if item["id"] == item_id), None)
    if item:
        return jsonify(item)
    return create_error_response("Item not found", 404)

@app.route('/items', methods=['POST'])
def create_item():
    """
    POST /items
    Creates a new item.
    Requires JSON data with 'name' and 'description'.
    Example: curl -X POST -H "Content-Type: application/json" -d '{"name": "New Item", "description": "A newly created item."}' http://127.0.0.1:5000/items
    """
    if not request.json or not 'name' in request.json:
        return create_error_response("Missing 'name' field in request body", 400)

    global current_id
    current_id += 1
    new_item = {
        "id": current_id,
        "name": request.json['name'],
        "description": request.json.get('description', "") # Description is optional
    }
    items.append(new_item)
    return jsonify(new_item), 201 # 201 Created

@app.route('/items/<int:item_id>', methods=['PUT'])
def update_item(item_id):
    """
    PUT /items/<id>
    Updates an existing item by its ID.
    Requires JSON data with 'name' and/or 'description'.
    Example: curl -X PUT -H "Content-Type: application/json" -d '{"name": "Updated Item A", "description": "Description has changed."}' http://127.0.0.1:5000/items/1
    """
    item = next((item for item in items if item["id"] == item_id), None)
    if not item:
        return create_error_response("Item not found", 404)

    if not request.json:
        return create_error_response("Request body must be JSON", 400)

    # Update fields if they exist in the request
    item['name'] = request.json.get('name', item['name'])
    item['description'] = request.json.get('description', item['description'])
    
    return jsonify(item)

@app.route('/items/<int:item_id>', methods=['DELETE'])
def delete_item(item_id):
    """
    DELETE /items/<id>
    Deletes an item by its ID.
    Example: curl -X DELETE http://127.0.0.1:5000/items/1
    """
    global items
    original_len = len(items)
    items = [item for item in items if item["id"] != item_id]
    if len(items) < original_len:
        return jsonify({"message": "Item deleted successfully"}), 200
    return create_error_response("Item not found", 404)

# --- How to run the application ---
if __name__ == '__main__':
    # Before running:
    # 1. Ensure you have Python installed.
    # 2. Install Flask: pip install Flask
    #
    # To run this API:
    # python app.py
    #
    # The API will be accessible at http://127.0.0.1:5000
    #
    # Use tools like curl, Postman, or your browser to test the endpoints.
    app.run(debug=True) # debug=True enables reloader and debugger

Spread the love

Leave a Reply

Your email address will not be published. Required fields are marked *