Flask

Flask Api & Mysql

Kita akan membuat API untuk mengelola data Mahasiswa dan Program Studi

Author : Celvine Adi Putra | Date : November, 10 2025 (Updated November, 11 2025)

Kita akan menggunakan UV sebagai package manager di python pengganti pip, Flask sebagai framework, dan MySQL sebagai database.


Membangun REST API

Apa yang Akan Kita Pelajari?

Kita akan membuat API untuk mengelola data Mahasiswa dan Program Studi. Hasil akhirnya adalah endpoint URL yang bisa kamu gunakan untuk:

  • Melihat daftar mahasiswa beserta prodinya.
  • Menambah, mengedit, dan menghapus data mahasiswa.
  • Data tersimpan permanen di database MySQL.

Prasyarat

Sebelum memulai, pastikan kamu memiliki:

  1. Python (versi 3.14.* ke atas disarankan).
  2. UV (Tools modern pengganti pip/virtualenv).
  3. MySQL Database (Bisa menggunakan XAMPP, Laragon, Docker, atau WSL).

Langkah 1: Setup Project dengan UV

Mengapa UV? UV jauh lebih cepat daripada pip standar dalam menginstall paket dan membuat virtual environment.

  1. Buat folder baru dan masuk ke dalamnya:

    bash
    mkdir flask-api-mysql
    cd flask-api-mysql
  2. Inisialisasi project dengan UV:

    bash
    uv init -p 3.14.0
  3. Aktifkan virutalenv :

    bash
    .venv\Scripts\activate
  4. Install library yang dibutuhkan:

    bash
    uv add flask flask-sqlalchemy pymysql

    Informasi

    • flask: Framework.
    • flask-sqlalchemy: ORM (Object Relational Mapper) agar kita bisa mengelola database menggunakan kode Python, bukan SQL manual.
    • pymysql: Driver agar Python bisa "terkoneksi" dengan MySQL.

Langkah 2: Struktur Folder

Agar kode rapi, kita akan memisahkan logika database (Model) dengan logika aplikasi (Controller/Routes). Buat struktur seperti ini:

app.py
models.py
pyproject.toml

Informasi

  • app.py: Entry point & Routes (Controller)
  • models.py: Definisi Table Database (Model)

Langkah 3: Membuat Model Database (models/models.py)

Langkah ini mendefinisikan "schema" data kita. Kita menggunakan SQLAlchemy.

Mengapa ada BaseModel? Perhatikan kelas BaseModel. Kita membuatnya agar setiap tabel otomatis punya kolom created_at dan updated_at tanpa perlu kita tulis ulang di setiap tabel. Ini prinsip DRY (Don't Repeat Yourself).

Buat file models/models.py:

models.py
from datetime import datetime
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

# 1. Base Model (Template untuk semua table)
class BaseModel(db.Model):
    __abstract__ = True  # Ini tidak akan dibuat jadi tabel, hanya template
    created_at = db.Column(db.DateTime, nullable=False, default=datetime.now)
    updated_at = db.Column(db.DateTime, nullable=False, default=datetime.now, onupdate=datetime.now)

# 2. Model Mahasiswa
class Mahasiswa(BaseModel):
    __tablename__ = 'mahasiswa'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)
    npm = db.Column(db.String(50), nullable=False, unique=True)
    profile_picture = db.Column(db.String(50), nullable=False)

    # Foreign Key ke Prodi
    prodi_id = db.Column(db.Integer, db.ForeignKey('prodi.id'))
    
    # Relationship (Relasi agar bisa memanggil m.prodi)
    prodi = db.relationship('Prodi', uselist=False, back_populates='mahasiswa')

    # Helper untuk convert object ke JSON
    def to_dict(self):
        return {
            'id': self.id,
            'name': self.name,
            'npm': self.npm,
            'profile_picture': self.profile_picture,
            'prodi_id': self.prodi_id,
            'created_at': self.created_at,
            'updated_at': self.updated_at
        }

# 3. Model Prodi
class Prodi(BaseModel):
    __tablename__ = 'prodi'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)

    # Relasi balik ke Mahasiswa
    mahasiswa = db.relationship("Mahasiswa", back_populates="prodi", lazy=True)

    def to_dict(self):
        return {
            'id': self.id,
            'name': self.name
        }

Langkah 4: Konfigurasi & Routes (app.py)

Sekarang kita mulai membuat API.

Warning

Sebelum menjalankan kode ini, buka MySQL client kamu (phpMyAdmin/DBeaver) dan buat database baru bernama flask-api.

Salin kode berikut ke app.py:

app.py
from flask import Flask, jsonify

# Format: mysql+pymysql://username:password@host/nama_database
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+pymysql://root:123@localhost/flask-api"

db.init_app(app)

# Route Cek Server
@app.route("/")
def index():
    return jsonify({
        "meta": {
            "code": 200,
            "message": "Server is up"
        },
        "data": None
    }), 200

if __name__ == "__main__":
    with app.app_context():
        # Membuat tabel otomatis jika belum ada
        db.create_all()
    app.run(debug=True)

Jalankan project dengan :

bash
python app.py

Coba akses di browser dengan url http://localhost:5000, seharusnya di browser kamu tampil seperti berikut ini

{
  "meta": {
    "code": 200,
    "message": "Server is up"
  },
  "data": null
}

Selanjutnya kita akan melengkapi CRUD untuk mahasiswa nya pada file app.py

app.py
from flask import Flask, jsonify, request
from models.models import db, Mahasiswa, Prodi

app = Flask(__name__)

# Konfigurasi Koneksi Database
# Format: mysql+pymysql://username:password@host/nama_database
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+pymysql://root:123@localhost/flask-api"

db.init_app(app)

# Route Cek Server
@app.route("/")
def index():
    return jsonify({
        "meta": {
            "code": 200,
            "message": "Server is up"
        },
        "data": None
    }), 200

# =========================================
# CRUD MAHASISWA
# =========================================
# 1. READ ALL (Mengambil semua data)
@app.route("/mahasiswa", methods=["GET"])
def mahasiswa():
    list_mahasiswa = Mahasiswa.query.all()
    
    # Kita menggabungkan data mahasiswa dengan data prodi-nya
    data_json = []
    for m in list_mahasiswa:
        m_dict = m.to_dict()
        if m.prodi:
            m_dict["prodi"] = m.prodi.to_dict()
        else:
            m_dict["prodi"] = None
        data_json.append(m_dict)

    return jsonify({
        "meta": {
            "code": 200,
            "message": "List Mahasiswa"
        },
        "data": data_json
    }), 200

# 2. READ ONE (Detail Mahasiswa)
@app.route("/mahasiswa/<int:id>", methods=["GET"])
def detail_mahasiswa(id):
    # get_or_404 otomatis return error 404 jika data tidak ada
    selected_mahasiswa = Mahasiswa.query.get_or_404(id)
    return jsonify({
        "meta": {
            "code": 200,
            "message": "Detail Mahasiswa"
        },
        "data": selected_mahasiswa.to_dict()
    }), 200

# 3. CREATE (Menambah Mahasiswa)
@app.route("/mahasiswa", methods=["POST"])
def tambah_mahasiswa():
    try:
        data = request.get_json()

        # Validasi sederhana (Opsional tapi disarankan)
        if not data.get("npm") or not data.get("name"):
            return jsonify({"meta": {"code": 400, "message": "Nama dan NPM wajib diisi"}}), 400

        new_mahasiswa = Mahasiswa(
            name=data["name"],
            npm=data["npm"],
            profile_picture=data.get("profile_picture", "default.jpg"), # Kasih default jika kosong
            prodi_id=data.get("prodi_id")
        )

        db.session.add(new_mahasiswa)
        db.session.commit() # Simpan permanen ke DB

        return jsonify({
            "meta": {
                "code": 201,
                "message": "Berhasil menambahkan mahasiswa"
            },
            "data": new_mahasiswa.to_dict()
        }), 201
    except Exception as e:
        # Selalu handle error agar server tidak crash
        return jsonify({
            "meta": {
                "code": 500,
                "message": f"Internal Server Error: {str(e)}"
            },
            "data": None
        }), 500

# 4. UPDATE (Edit Mahasiswa)
@app.route("/mahasiswa/<int:id>", methods=["PUT", "PATCH"])
def update_mahasiswa(id):
    try:
        data = request.get_json()
        selected_mahasiswa = Mahasiswa.query.get_or_404(id)

        # Update field yang ada saja
        if "name" in data: selected_mahasiswa.name = data["name"]
        if "npm" in data: selected_mahasiswa.npm = data["npm"]
        if "profile_picture" in data: selected_mahasiswa.profile_picture = data["profile_picture"]
        if "prodi_id" in data: selected_mahasiswa.prodi_id = data["prodi_id"]

        db.session.commit()

        return jsonify({
            "meta": {
                "code": 200,
                "message": "Berhasil update mahasiswa"
            },
            "data": selected_mahasiswa.to_dict()
        })
    except Exception as e:
        return jsonify({
            "meta": {
                "code": 500,
                "message": str(e)
            }
        }), 500

# 5. DELETE (Hapus Mahasiswa)
@app.route("/mahasiswa/<int:id>", methods=["DELETE"])
def delete_mahasiswa(id):
    try:
        selected_mahasiswa = Mahasiswa.query.get_or_404(id)
        db.session.delete(selected_mahasiswa)
        db.session.commit()

        return jsonify({
            "meta": {
                "code": 200,
                "message": "Berhasil menghapus mahasiswa"
            },
            "data": selected_mahasiswa.to_dict()
        })
    except Exception as e:
        return jsonify({
            "meta": {
                "code": 500,
                "message": "Internal Server Error"
            }
        }), 500

# Endpoint Tambahan: List Prodi
@app.route("/prodi", methods=["GET"])
def prodi():
    list_prodi = Prodi.query.all()
    return jsonify({
        "meta": {
            "code": 200,
            "message": "List Prodi"
        },
        # Menampilkan prodi beserta list mahasiswanya (Nested)
        "data": [{**p.to_dict(), "mahasiswa": [m.to_dict() for m in p.mahasiswa]} for p in list_prodi]
    }), 200

if __name__ == "__main__":
    with app.app_context():
        # Membuat tabel otomatis jika belum ada
        db.create_all() 
    app.run(debug=True)

Penjelasan

  1. db.create_all(): Kita tidak perlu menulis CREATE TABLE di SQL manual. Flask-SQLAlchemy akan melihat class model kamu (Mahasiswa, Prodi) dan membuat tabelnya otomatis saat aplikasi pertama kali dijalankan.

  2. List Data & Unpacking dictionary (**): Pada baris [{**p.to_dict(), ...}].

    • p.to_dict() mengambil data dasar Prodi.
    • ** membongkar dictionary tersebut.
    • Lalu kita menimpa atau menambah key baru "mahasiswa".
  3. db.session

    • add(): Menaruh data sementara.
    • commit(): Menyimpan data kedalam database. Kalau lupa commit, data tidak akan tersimpan!

Langkah 5: Menjalankan Aplikasi

Jalankan aplikasi kamu dengan perintah:

bash
python app.py

kamu akan melihat output Running on http://127.0.0.1:5000.

Cara Tes (Menggunakan Postman):

  1. Buat Prodi Dulu (Opsional tapi penting untuk relasi): Karena kita belum buat endpoint POST untuk Prodi, kamu bisa insert manual via Database (phpMyAdmin) ke tabel prodi: INSERT INTO prodi (name) VALUES ('Teknik Informatika'); (Ingat ID nya, misal ID = 1).

  2. POST Mahasiswa:

    • URL: http://localhost:5000/mahasiswa
    • Method: POST
    • Body (JSON):
    request body
    {
      "name": "Mahasiswa 1",
      "npm": "2024250026",
      "profile_picture": "mahasiswa1.jpg",
      "prodi_id": 1
    }
  3. GET Mahasiswa:

    • Buka browser ke http://localhost:5000/mahasiswa.

Selesai

Kamu telah belajar:

  1. Menggunakan UV untuk manajemen project.
  2. Menghubungkan Python dengan MySQL menggunakan SQLAlchemy.
  3. Melakukan operasi CRUD lengkap.

Langkah Selanjutnya: Cobalah untuk menambahkan endpoint POST /prodi agar kita bisa menambah program studi langsung dari API.