DEV Community

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

Posted on

8 4 4 5 4

💡 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!

Heroku

Built for developers, by developers.

Whether you're building a simple prototype or a business-critical product, Heroku's fully-managed platform gives you the simplest path to delivering apps quickly — using the tools and languages you already love!

Learn More

Top comments (0)

Image of DataStax

AI Agents Made Easy with Langflow

Connect models, vector stores, memory and other AI building blocks with the click of a button to build and deploy AI-powered agents.

Get started for free

đź‘‹ Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay