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
ormkdir
, 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
- Step-by-Step Guide to Building the Student API
- Key Concepts: Flask, SQLAlchemy, and Testing
- Troubleshooting Common Issues
- Conclusion
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
-
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
-
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
- 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
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")
- 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
- 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}
- 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...
- 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
- 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
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}'
- 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"
-
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
androutes.py
.
-
“db not defined” in Tests:
-
Issue: Missed importing
db
intest_api.py
. -
Solution: Added
from app.models import db
.
-
Issue: Missed importing
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)