Skip to content

intellisenseCodez/flask-student-management-app

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

48 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“˜ Project: Building a Three-Tier Architecture with Flask, MySQL, and Next.js in Docker

πŸ“Œ Overview

This project demonstrates a three-tier architecture using:

  • Backend: Flask (Python web framework), SQLAlchemy (ORM)
  • Database: MySQL (database)
  • Frontend: Next.js (React-based SSR framework)

It showcases full-stack application development, container orchestration, and production-aligned patterns like service health checks, environment-based configuration, and database migrations.

🎯 Objectives

  • Build a clean separation of concerns using a three-tier model.
  • Implement Flask APIs to handle core business logic and persistence.
  • Use MySQL as a relational database backend.
  • Serve a modern Next.js frontend.
  • Deploy all services using Docker Compose.
  • Add production-aware patterns like health checks, entrypoint wait scripts, and environment management.

πŸ“ Architecture

Here's a simplified architecture diagram:

flask-student-management-app
.github/workflows/
    β”œβ”€β”€ backend.yml.            # implement CICD workflow for the backend  codebase
    β”œβ”€β”€ frontend.yml.            # implement CICD workflow for the frontend  codebase
backend/
    β”œβ”€β”€ app/
    β”‚   β”œβ”€β”€ models/           # SQLAlchemy models for Student, Course, Enrollment
    β”‚   └── routes/           # Flask Blueprints (API endpoints)
    β”œβ”€β”€ migrations/           # Flask-Migrate scripts
    β”œβ”€β”€ .dockerignore         # dockerignore file
    β”œβ”€β”€ flask.dockerfile      # Dockerfile for Flask service
    β”œβ”€β”€ main.py               # App entrypoint
    β”œβ”€β”€ requirements.txt      # requirements for Flask app
    β”œβ”€β”€ wait-for-mysql.sh     # Wait script for MySQL readiness
frontend/
    β”œβ”€β”€ (Your Next.js app here)
.gitignore
compose.yml               # Docker Compose for all services
.env                      # Environment variables
README.md

πŸ§ͺ Running the Flask Backend Locally

This section guides you through setting up and running the Flask backend locally on your machine for development and testing purposes.

  1. Clone the Repository
git clone https://github.com/intellisenseCodez/flask-student-management-app.git
cd flask-student-management-app
  1. Configure Flask Environment Variables Inside the backend/ directory, create a .flaskenv file:
cd backend
touch .flaskenv

Add the following content to .flaskenv:

# Flask Environment Configuration
FLASK_APP=main.py
FLASK_ENV=development
FLASK_RUN_PORT=8080
FLASK_RUN_HOST=127.0.0.1
DEBUG=1

# SQLAlchemy connection URI 
DATABASE_URL=mysql+pymysql://root:your-password@localhost:3306/student_db
  • Replace your-password with your actual MySQL root password.
  • .flaskenv is automatically used by Flask when running locally if you have the python-dotenv package installed.

Note: Ensure that MySQL Server is installed and running on your local system. You can download it here: MySQL Download.

  1. Create a MySQL Database You can create the database using MySQL Workbench or the MySQL CLI:
CREATE DATABASE IF NOT EXISTS student_db;
  1. Set Up a Python Virtual Environment From inside the backend/ directory:
# Create a virtual environment
python3 -m venv venv

# Activate the virtual environment
source venv/bin/activate  # For Linux/macOS
# OR
venv\Scripts\activate     # For Windows

Reference: Learn more about Python virtual environments page.

  1. Install Python Dependencies After activating the virtual environment, install the required packages:
pip install -r requirements.txt
  1. Run Database Migrations Use Flask-Migrate to create the tables in your student_db:
flask db upgrade

Reference: Learn more about Flask Migration page.

database runing

  1. Start the Flask Development Server Finally, run the application:
flask run

application runing

By default, it will be available at: πŸ”— http://127.0.0.1:8080

🐳 Dockerizing the Flask Backend

1. Dockerfile:

In backend/, we have a file named flask.dockerfile:

# Python base image
FROM python:3.11-slim

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1
    

# Set working directory
WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y \
    build-essential \
    libpq-dev \
    gcc \
    curl \
    && rm -rf /var/lib/apt/lists/*

# Install mysqladmin
RUN apt-get update && \
    apt-get install -y default-mysql-client && \
    apt-get clean && rm -rf /var/lib/apt/lists/*

# Install Python dependencies
COPY requirements.txt .
RUN pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt

# Copy project files
COPY . .


# Copy the MySQL wait script into the container
COPY wait-for-mysql.sh .

# Make the wait script executable
RUN chmod +x wait-for-mysql.sh

# Set environment variables for Flask
ENV FLASK_APP=main.py
ENV FLASK_RUN_PORT=8080
ENV FLASK_RUN_HOST=0.0.0.0

# Expose the application port
EXPOSE 8080


# Python base image
FROM python:3.11-slim

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1
    

# Set working directory
WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y \
    build-essential \
    libpq-dev \
    gcc \
    curl \
    && rm -rf /var/lib/apt/lists/*

# Install mysqladmin
RUN apt-get update && \
    apt-get install -y default-mysql-client && \
    apt-get clean && rm -rf /var/lib/apt/lists/*

# Install Python dependencies
COPY requirements.txt .
RUN pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt

# Copy project files
COPY . .


# Copy the MySQL wait script into the container
COPY wait-for-mysql.sh .

# Make the wait script executable
RUN chmod +x wait-for-mysql.sh

# Set environment variables for Flask
ENV FLASK_APP=main.py
ENV FLASK_RUN_PORT=8080
ENV FLASK_RUN_HOST=0.0.0.0

# Expose the application port
EXPOSE 8080


# Run wait-for-mysql, apply migrations, then start Flask server
CMD ["sh", "-c", "./wait-for-mysql.sh && flask db upgrade && flask run --host=0.0.0.0 --port=8080"]

2. MySQL Wait Script

we also have backend/wait-for-mysql.sh: The wait-for-mysql.sh script ensures that your Flask backend only starts after the MySQL database is fully up and ready to accept connections.

#!/bin/bash

echo "Waiting for MySQL to become healthy..."

until mysqladmin ping -h"$MYSQL_HOST" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" --silent; do
    sleep 2
done

echo "MySQL is up!"
exec "$@"

2. Define Environment Variables

Create a .env file in the project root:

# MySQL Environment Variables
MYSQL_HOST="database" 
MYSQL_ROOT_PASSWORD="rootpassword"
MYSQL_DATABASE="studentdb"
MYSQL_USER="testuser"
MYSQL_PASSWORD="testpassword"

# SQLAlchemy connection URI 
DATABASE_URL=mysql+pymysql://testuser:testpassword@database:3306/student_db

# dockerhub username
DOCKERHUB_USERNAME=your-username

4. Docker Compose

In your root directory we have compose.yml:

services:

  # backend service
  backend:
    container_name: flask-student-api
    restart: always
    image: ${DOCKERHUB_USERNAME}/flask-student-api:v1.0
    build:
      context: ./backend
      dockerfile: flask.dockerfile
    ports:
      - 8080:8080
    env_file: ".env"
    environment:
      - DATABASE_URL=mysql+pymysql://${MYSQL_USER}:${MYSQL_PASSWORD}@database:3306/${MYSQL_DATABASE}
    depends_on:
      - database


  # database service
  database:
    container_name: mysql-database
    image: mysql:5.7
    env_file: ".env"
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - MYSQL_DATABASE=${MYSQL_DATABASE}
      - MYSQL_USER=${MYSQL_USER}
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
    ports:
      - 3307:3306
    volumes:
      - mysql_data:/var/lib/mysql
  

  # frontend service



volumes:
  mysql_data: {}

5. πŸš€ Run Your Backend with Docker

Ensure you are in the root project:

# run in non-detached mode
docker-compose up --build 
OR
# run in detached mode
docker-compose up -d --build

Once the services are up, your Flask API should be available at: πŸ”— http://localhost:8080

docker-compose runing

docker-compose runing

βœ… Testing the API πŸ§ͺ Testing the API Using Postman Once your Flask backend is up and running (either locally or via Docker), you can use Postman β€” a popular API testing tool β€” to interact with your API endpoints.

Endpoints to Test

Method Endpoint Description
POST /api/v1.0/student/create Create a new student
GET /api/v1.0/students/all Retrieve all students
GET /api/v1.0/students/<student-id> Retrieve student by ID
GET /api/v1.0/students/by-course?course_title="your course" Retrieve students by course title
PUT /api/v1.0/students/<student-id> Update student by ID
DELETE /api/v1.0/students/<student-id> Delete student by ID
POST /api/v1.0/course/create Create a new course
GET /api/v1.0/course/all Retrieve all courses
GET /api/v1.0/course/<course-id> Retrieve course by ID
PUT /api/v1.0/courses/<course-id> Update course by ID
DELETE /api/v1.0/courses/<course-id> Delete course by ID
POST /api/v1.0/course/add/<course-id> Enroll student for a course
GET /api/v1.0/students/<student-id>/courses Retrieve all enrolled courses by student

postman

Implementing a CICD Pipeline

This project includes a CI/CD pipeline that automatically builds, pushes, and deploys the Flask API as a Docker image whenever changes are made to the backend codebase. It is implemented using GitHub Actions and a self-hosted AWS runner.

The CI/CD workflow is defined in .github/workflows/backend.yml.yml. It contains the following jobs:

1. Build and Deploy Docker Image

  • Trigger:

    The workflow runs when:

    • A push is made to the master branch with changes in backend/**.

    • The workflow file itself (.github/workflows/backend.yml) is updated.

    • It can also be triggered manually using workflow_dispatch.

  • Steps:

    1. Checkout Code:

    Uses actions/checkout@v4 to pull the latest code from GitHub.

    1. Build Flask Docker Image:

      Builds the backend image using the flask.dockerfile:

      docker build --file backend/flask.dockerfile -t ${{ secrets.DOCKERHUB_USERNAME }}/flask-student-api:v1.0 backend
    2. Login to Docker Hub:

      Authenticates with Docker Hub using docker/login-action@v3 and secrets:

      • DOCKERHUB_USERNAME
      • DOCKERHUB_TOKEN
    3. Push Image to Docker Hub:

      Pushes the image to the Docker Hub registry:

      docker push ${{ secrets.DOCKERHUB_USERNAME }}/flask-student-api:v1.0

2. Email Notifications

Two notification jobs are configured to inform the team of pipeline status:

  • notify-success: Sends an email if the build-and-deploy job succeeds.

  • notify-fail: Sends an email if the build-and-deploy job fails.

Emails are sent using the dawidd6/action-send-mail@v6 action and the following secrets:

  • MAIL_USERNAME

  • MAIL_PASSWORD

  • RECEIVER_EMAIL_ADDERESS

  • SENDER_EMAIL_ADDERESS

3. Run Docker Compose (Deployment Stage)

Once the image is successfully pushed to Docker Hub, the pipeline:

  • Generates a .env file on the self-hosted AWS runner, using variables and secrets from GitHub:
cat <<EOF > .env
DOCKERHUB_USERNAME=${{ vars.DOCKERHUB_USERNAME }}
MYSQL_USER=${{ vars.MYSQL_USER }}
MYSQL_PASSWORD=${{ secrets.MYSQL_PASSWORD }}
MYSQL_DATABASE=${{ vars.MYSQL_DATABASE }}
MYSQL_ROOT_PASSWORD=${{ secrets.MYSQL_ROOT_PASSWORD }}
DATABASE_URL=mysql+pymysql://${{ vars.MYSQL_USER }}:${{ secrets.MYSQL_PASSWORD }}@database:3306/${{ vars.MYSQL_DATABASE }}
EOF
  • Runs Docker Compose to start the Flask API and MySQL containers:
docker compose up -d

Environment Variables and Secrets

The workflow depends on the following secrets and repository variables configured in the repository settings:

Secrets

  • DOCKERHUB_USERNAME – Docker Hub username.

  • DOCKERHUB_TOKEN – Docker Hub access token or password.

  • MYSQL_PASSWORD – Database user password.

  • MYSQL_ROOT_PASSWORD – MySQL root password.

  • MAIL_USERNAME – Email SMTP username.

  • MAIL_PASSWORD – Email SMTP password.

  • RECEIVER_EMAIL_ADDERESS – Email address to receive notifications.

  • SENDER_EMAIL_ADDERESS – Email address used as the sender.

Repository Variables

  • DOCKERHUB_USERNAME – Docker Hub username (can also be a secret).

  • MYSQL_USER – MySQL user.

  • MYSQL_DATABASE – MySQL database name.

How to Set Up an AWS Self-Hosted Runner

1. Launch an EC2 Instance

  • Go to AWS EC2 Console.

  • Launch an instance (e.g., Ubuntu 22.04 LTS or Amazon Linux 2).

  • Choose an instance type (e.g., t2.micro or larger).

  • Configure security groups:

    • Allow SSH (22).
    • Allow HTTP (80), HTTPS (443), and Custom TCP (8080) for your Flask API.
  • Download the .pem key for SSH access.

2. Install Dependencies

  • Open an SSH client.
  • Locate your private .pem key file.
  • Run this command, if necessary, to ensure your key is not publicly viewable.
chmod 400 "github-action-runner-login.pem"
  • Connect to your instance using its Public DNS:
ssh -i "your-private-pem-file.pem" ubuntu@your-public-DNS

(Log out and log back in for changes to take effect.)

3. Create a Self-Hosted Runner on GitHub

  • Go to your GitHub repository β†’ Settings β†’ Actions β†’ Runners β†’ New self-hosted runner.

  • Select Linux as the platform.

  • Follow the instructions to download and configure the runner.

Prepare Environment for CI/CD

  • Ensure Docker and Docker Compose are installed and running.

  • Your CI/CD workflow will generate a .env file on the self-hosted runner during deployment.

  • The runner will execute docker compose up -d to start the Flask API and database.

Testing the Deployment

πŸ™Œ Credits

This project was built for learning and demonstration purposes. Inspired by real-world backend system design and frontend integration practices.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published