DEV Community

Cover image for 💡 Build Along with Me: A Beginner’s Guide to Creating a Student API Using Flask
Emidowojo
Emidowojo

Posted on

💡 Build Along with Me: A Beginner’s Guide to Creating a Student API Using Flask

Today on my journey to gaining DevOps Mastery, I Built a Student REST API with Flask. In this guide, I’ll walk you through how I created a REST API from scratch. No fancy frameworks or prior knowledge is required—just some curiosity and a willingness to debug. Let’s build it together, step-by-step, with all the highs and lows I hit along the way.


Pre-requisites

Before we start, here’s what you’ll need:

  • A Computer: Any machine with Python 3 (Windows, Linux, or Mac) works fine.
  • Basic Terminal Skills: If you can type commands like cd or mkdir, you’re set.
  • Python Installed: I had Python 3.13—check with python3 --version in your terminal.
  • VS Code (Optional): It’s my editor of choice, but any text editor (like Notepad) will do.
  • Patience: I ran into errors (like “db not defined”), but I’ll show you how to fix them!

Table of Contents


Introduction to REST APIs

What is a REST API?

Think of a REST API as a librarian for a student database. You can ask it to add a student, list all students, update a record, or delete one—all through web commands. It’s like a website, but instead of pages, it gives you data (like {"name": "Alice", "age": 20}).

Why is this useful?

  • Flexibility: Use it from a browser, app, or script.
  • Learning: Teaches you web basics in a fun way.
  • Real-World Skills: APIs power tons of apps—like your favorite social media.

In this post, I’ll show you how I built the API to handle CRUD (Create, Read, Update, Delete) operations.


Step-by-Step Implementation

Here’s how I built my API, command by command.

Part 1: Setting Up the Project

Creating the Folder and Virtual Environment

On my terminal:

# Create a new folder for the project
mkdir student-api-new
# Move into the folder
cd student-api-new
# Create a virtual environment (a "toolbox" for project tools)
python3 -m venv .venv
# Activate the virtual environment to use its tools
source .venv/bin/activate
Enter fullscreen mode Exit fullscreen mode
  • What it does: Makes a folder and a “toolbox” (virtual environment) to keep my project’s tools separate. Activating it with source puts me in the toolbox.

Installing Tools

# Install Flask (web framework), Flask-SQLAlchemy (database), python-dotenv (settings), and pytest (testing)
pip install flask flask-sqlalchemy python-dotenv pytest
# Save the installed tools to a list for others to use
pip freeze > requirements.txt
Enter fullscreen mode Exit fullscreen mode
  • What it does: Adds Flask (web framework), Flask-SQLAlchemy (database helper), python-dotenv (settings loader), and pytest (testing tool). requirements.txt lists them for later.

Part 2: Building the Structure

Setting Up Files

I used VS Code to create this structure:

student-api-new/
├── Makefile          # Instructions for running the app
├── README.md         # Project guide
├── requirements.txt  # List of tools
├── .env              # Secret settings
├── .gitignore        # Files to ignore in Git
├── app/              # Main app code
│   ├── __init__.py   # App setup
│   ├── models.py     # Student definition
│   ├── routes.py     # Web commands
│   └── config.py     # App settings
├── tests/            # Test code
│   └── test_api.py   # Tests for the API
└── .venv/            # Virtual environment
Enter fullscreen mode Exit fullscreen mode
  • How: Right-click in VS Code’s Explorer, pick “New File” or “New Folder,” and name them.

Part 3: Coding the API

Configuration (app/config.py and .env)

.env:

# Set Flask to development mode for easier debugging
FLASK_ENV=development
# Define the database location (SQLite file)
DATABASE_URL=sqlite:///students.db
# A secret key for security
SECRET_KEY=mysecretkey
Enter fullscreen mode Exit fullscreen mode

app/config.py:

# Import os to access environment variables
import os
# Import dotenv to load .env file
from dotenv import load_dotenv

# Load environment variables from .env
load_dotenv()

# Define configuration settings for the app
class Config:
    # Set database location from .env
    SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL")
    # Disable a warning for performance
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    # Set secret key from .env for security
    SECRET_KEY = os.getenv("SECRET_KEY")
Enter fullscreen mode Exit fullscreen mode
  • Why: Keeps secrets (like the database location) safe and separate.

The App Setup (app/__init__.py)

# Import logging to track app events
import logging
# Import Flask to create the web app
from flask import Flask
# Import Config for settings
from .config import Config
# Import db for the database
from .models import db

# Set up logging to show info messages
logging.basicConfig(level=logging.INFO)
# Create a logger for this module
logger = logging.getLogger(__name__)

# Function to create and configure the Flask app
def create_app():
    # Initialize Flask app
    app = Flask(__name__)
    # Load settings from Config
    app.config.from_object(Config)
    # Connect database to app
    db.init_app(app)
    # Create database tables
    with app.app_context():
        db.create_all()
        # Log that tables were created
        logger.info("Database tables created")
    # Import and set up routes
    from .routes import init_routes
    init_routes(app)
    # Return the configured app
    return app
Enter fullscreen mode Exit fullscreen mode
  • What it does: Starts the Flask app and sets up the database.

Student Model (app/models.py)

# Import SQLAlchemy for database management
from flask_sqlalchemy import SQLAlchemy

# Create database object
db = SQLAlchemy()

# Define Student table structure
class Student(db.Model):
    # Primary key column for unique IDs
    id = db.Column(db.Integer, primary_key=True)
    # Name column, can't be empty
    name = db.Column(db.String(100), nullable=False)
    # Age column, can't be empty
    age = db.Column(db.Integer, nullable=False)

    # Convert student data to a dictionary for API responses
    def to_dict(self):
        return {"id": self.id, "name": self.name, "age": self.age}
Enter fullscreen mode Exit fullscreen mode
  • What it does: Defines what a “student” looks like in the database.

API Routes (app/routes.py)

# Import logging for tracking events
import logging
# Import Flask tools for handling requests
from flask import request, jsonify
# Import database and Student model
from .models import db, Student

# Create a logger for this module
logger = logging.getLogger(__name__)

# Function to define all API routes
def init_routes(app):
    # Healthcheck endpoint to test if API is running
    @app.route('/api/v1/healthcheck', methods=['GET'])
    def healthcheck():
        # Return a JSON response with status
        return jsonify({"status": "healthy"}), 200

    # Endpoint to add a new student
    @app.route('/api/v1/students', methods=['POST'])
    def add_student():
        # Get JSON data from request
        data = request.get_json()
        # Create a new student object
        student = Student(name=data['name'], age=data['age'])
        # Add student to database
        db.session.add(student)
        # Save changes to database
        db.session.commit()
        # Log the addition
        logger.info(f"Student {student.name} added with ID {student.id}")
        # Return student data as JSON with status 201 (created)
        return jsonify(student.to_dict()), 201
    # More routes for GET, PUT, DELETE...
Enter fullscreen mode Exit fullscreen mode
  • What it does: Adds web commands like “add a student.”

Makefile

# Define targets that aren't files
.PHONY: install run test

# Target to install dependencies
install:
    # Install tools listed in requirements.txt
    .venv/bin/pip install -r requirements.txt

# Target to run the app
run:
    # Start Flask server
    flask run

# Target to run tests
test:
    # Run pytest with verbose output
    .venv/bin/pytest -v
Enter fullscreen mode Exit fullscreen mode
  • Pitfall: Use tabs before commands, not spaces or it fails

Part 4: Testing the API

Manual Testing

# Tell Flask where the app is
export FLASK_APP=app
# Start the app
make run
Enter fullscreen mode Exit fullscreen mode

Then, in another terminal:

# Send a POST request to add a student
curl -X POST http://127.0.0.1:5000/api/v1/students -H "Content-Type: application/json" -d '{"name": "Alice", "age": 20}'
Enter fullscreen mode Exit fullscreen mode
  • Output: {"id": 1, "name": "Alice", "age": 20}

Automated Testing (tests/test_api.py)

# Import pytest for testing
import pytest
# Import sys and os for path adjustments
import sys
import os
# Add project root to Python path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
# Import app setup function
from app import create_app
# Import database object
from app.models import db

# Define a fixture to set up test client
@pytest.fixture
def client():
    # Create app instance
    app = create_app()
    # Enable testing mode
    app.config['TESTING'] = True
    # Create a test client
    with app.test_client() as client:
        # Use app context for database operations
        with app.app_context():
            # Create database tables
            db.create_all()
        # Yield client for tests
        yield client

# Test adding a student
def test_add_student(client):
    # Send POST request with student data
    response = client.post('/api/v1/students', json={"name": "Alice", "age": 20})
    # Check if status code is 201 (created)
    assert response.status_code == 201
    # Check if response name matches input
    assert response.json['name'] == "Alice"
Enter fullscreen mode Exit fullscreen mode
  • Run: make test
  • Pitfall: Forgot to import db—fixed it and it passed!

Key Concepts: Flask, SQLAlchemy, and Testing

  • Flask: A lightweight tool to build web apps. It’s like the frame of our “student library.”
  • SQLAlchemy: Manages the database, like a filing cabinet for student records.
  • Testing with pytest: Checks if the app works without manual effort.

Together, they make a simple, working API.


Troubleshooting Common Issues

  • Makefile Error (“missing separator”):
    • Issue: Used spaces instead of tabs.
    • Solution: Retyped with tabs—worked like magic!
  • “Not Found” on Healthcheck:
    • Issue: App wasn’t running or routes weren’t loaded.
    • Solution: Checked make run and routes.py.
  • “db not defined” in Tests:
    • Issue: Missed importing db in test_api.py.
    • Solution: Added from app.models import db.

Conclusion

I built a Student REST API from nothing—adding students, and testing it. It’s not perfect, but it works, and I learned so much. From “missing separator” to “db not defined,” every error taught me something new. I hope you learnt something insightful from this. Happy coding!

Top comments (0)