Server web dan framework Python bukan satu program yang sama. Server menerima koneksi dari internet, sementara framework berisi kode aplikasi kita. Agar keduanya bisa saling bertukar, komunitas Python mendefinisikan dua standar antarmuka: WSGI untuk dunia sinkron dan ASGI untuk dunia asinkron. Memahami keduanya menjelaskan mengapa aplikasi Flask bisa dijalankan gunicorn, dan mengapa FastAPI membutuhkan uvicorn.

WSGI: Kontrak untuk Aplikasi Sinkron #

WSGI (Web Server Gateway Interface) didefinisikan dalam PEP 3333 dengan kontrak yang mengejutkan sederhananya: aplikasi hanyalah sebuah callable yang menerima dua argument — environ (dictionary berisi data request) dan start_response (fungsi untuk mengirim status dan header) — lalu mengembalikan iterable berisi body response. Kode berikut adalah aplikasi WSGI lengkap, dijalankan dengan server wsgiref bawaan standard library, lalu diminta data oleh client sungguhan:

python
import threading
import urllib.request
from wsgiref.simple_server import WSGIRequestHandler, make_server


class HandlerSenyap(WSGIRequestHandler):
    def log_message(self, format, *args):
        pass  # matikan log akses agar contoh tetap deterministik


def application(environ, start_response):
    status = "200 OK"
    headers = [("Content-Type", "text/plain; charset=utf-8")]
    start_response(status, headers)
    isi = (
        f"metode={environ['REQUEST_METHOD']} "
        f"path={environ['PATH_INFO']} "
        f"query={environ['QUERY_STRING']}"
    ).encode("utf-8")
    return [isi]


server = make_server("127.0.0.1", 8001, application, handler_class=HandlerSenyap)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()

response = urllib.request.urlopen("http://127.0.0.1:8001/produk?urutan=nama")
print("Status:", response.status)
print("Isi:", response.read().decode("utf-8"))

server.shutdown()

Output yang Dihasilkan:

text
Status: 200
Isi: metode=GET path=/produk query=urutan=nama

Mari kita telaah bagian intinya:

  1. Baris 11 (def application(environ, start_response)): seluruh kontrak WSGI ada di signature ini — server memanggil fungsi application dengan dua argument tersebut.
  2. Baris 14 (start_response(status, headers)): aplikasi memberi tahu server bahwa response berstatus 200 OK dengan header Content-Type khusus.
  3. Baris 15-19 (isi = ...): data request dibaca dari dictionary environ — metode, path, dan query string sudah dipisahkan server, tinggal dipakai.
  4. Baris 20 (return [isi]): aplikasi mengembalikan list berisi bytes, bukan string.

Dictionary environ adalah jendela aplikasi ke seluruh request. Pada eksekusi nyata di atas, nilainya antara lain:

Kunci environNilaiArti
REQUEST_METHOD'GET'metode HTTP yang diminta
PATH_INFO'/produk'path setelah domain
QUERY_STRING'urutan=nama'bagian setelah tanda ?
HTTP_USER_AGENT'Python-urllib/3.12'header User-Agent

wsgiref hanya untuk belajar dan pengembangan. Di produksi, aplikasi WSGI dijalankan oleh server seperti gunicorn atau uWSGI dengan perintah semacam gunicorn app:application.

ASGI: Kontrak untuk Dunia Asinkron #

WSGI lahir tahun 2003 dan hanya mengenal model satu request satu thread. Padahal kita sudah mempelajari di Section Asynchronous Programming bahwa async/await memungkinkan satu event loop menangani ribuan koneksi. ASGI (Asynchronous Server Gateway Interface) memperluas WSGI dengan tiga perubahan utama: aplikasi menjadi async def, argument environ diganti dictionary scope, dan pengiriman response dilakukan lewat await send(...) bertahap. Berikut aplikasi ASGI minimal yang dijalankan server uvicorn sungguhan:

python
import threading
import time

import httpx
import uvicorn


async def application(scope, receive, send):
    if scope["type"] != "http":
        return
    await receive()  # tunggu event body request (kosong untuk GET)
    isi = f"metode={scope['method']} path={scope['path']}".encode("utf-8")
    await send(
        {
            "type": "http.response.start",
            "status": 200,
            "headers": [(b"content-type", b"text/plain; charset=utf-8")],
        }
    )
    await send({"type": "http.response.body", "body": isi})


config = uvicorn.Config(application, host="127.0.0.1", port=8002, log_level="warning")
server = uvicorn.Server(config)
thread = threading.Thread(target=server.run, daemon=True)
thread.start()

while not server.started:
    time.sleep(0.01)

response = httpx.get("http://127.0.0.1:8002/produk")
print("Status:", response.status_code)
print("Isi:", response.text)

server.should_exit = True
thread.join()
print("Server uvicorn berhenti.")

Output yang Dihasilkan:

text
Status: 200
Isi: metode=GET path=/produk
Server uvicorn berhenti.

Perhatikan pola pengiriman dua tahap: http.response.start mengirim status dan header, lalu http.response.body mengirim isinya. Bentuk bertahap inilah yang membuat ASGI mampu menangani WebSocket dan long-polling — protokol yang mengirim banyak pesan pada satu koneksi yang sama, sesuatu yang mustahil bagi WSGI.

Sinkron vs Asinkron #

AspekWSGIASGI
Model eksekusisinkron, satu request sekaligussinkron dan asinkron
Bentuk aplikasidef app(environ, start_response)async def app(scope, receive, send)
Server populergunicorn, uWSGIuvicorn, hypercorn
Framework penggunaFlaskFastAPI (Django mendukung keduanya)
Kebutuhan khusushalaman web klasikWebSocket, realtime, trafik padat

Hubungannya dengan Framework #

Flask dan FastAPI pada dasarnya adalah pembungkus yang rapi di atas kedua kontrak ini. Flask menyembunyikan environ menjadi object request, sedangkan FastAPI menyembunyikan scope menjadi parameter bertipe. Bukti paling langsung: object aplikasi dari keduanya adalah callable, persis seperti kontraknya:

python
from fastapi import FastAPI
from flask import Flask

flask_app = Flask("cek-wsgi")
print("Flask app callable:", callable(flask_app))

fastapi_app = FastAPI()
print("FastAPI app callable:", callable(fastapi_app))

Output yang Dihasilkan:

text
Flask app callable: True
FastAPI app callable: True

Ketika gunicorn memanggil flask_app(...), gunicorn sedang menjalankan kontrak WSGI. Ketika uvicorn memanggil fastapi_app(scope, receive, send), uvicorn sedang menjalankan kontrak ASGI. Framework hanyalah lapisan kenyamanan — kontrak tetap dipegang server.