thecodex.expert · The Codex Family of Knowledge
Tier 4 · Advanced · Python Project

Mini Web Framework

Build your own tiny Flask: a framework where routes are registered with decorators and dispatched by URL. Understand the magic by building it.

🧠 Teaches how to think spoonfed, every age Last verified:

1 The Problem

We want to build a web framework — the thing Flask and Express are. You register routes with a decorator (@app.route("/hello")), and the framework matches incoming URLs to the right function. It teaches how frameworks actually work by building one: decorators that register, and a dispatcher that routes.

Where this shows up: understanding Flask, Django, Express, FastAPI — every web framework you will ever use. Building a tiny one removes the magic: you will see routes are just a dictionary and decorators are just registration.

2 How to Think About It

Think about what @app.route really does, before any code:

The plan — in plain English
1. The framework keeps a map of URL → handler function. → 2. The @route decorator simply adds an entry to that map: this URL runs this function. → 3. When a request comes in, look up its URL in the map and call the matching function. → That dictionary lookup is routing. The decorator is just a nice way to fill the dictionary.

Yes

No

Define handler

Decorate with route path

Decorator stores path to handler

Request arrives with a URL

Look up URL in routes map

Found?

Call handler, return result

404 Not Found

3 The Build — explained part by part

Here is a complete mini web framework. Each part is explained below.

Pythonframework.py
class App:
    def __init__(self):
        self.routes = {}      # path -> handler function

    def route(self, path):
        # A decorator that registers a handler for a path.
        def decorator(handler):
            self.routes[path] = handler
            return handler
        return decorator

    def handle(self, path):
        # Dispatch a request to its handler.
        handler = self.routes.get(path)
        if handler is None:
            return (404, "Not Found")
        return (200, handler())

app = App()

@app.route("/")
def home():
    return "Welcome home!"

@app.route("/about")
def about():
    return "About us"

if __name__ == "__main__":
    print(app.handle("/"))         # (200, 'Welcome home!')
    print(app.handle("/about"))    # (200, 'About us')
    print(app.handle("/missing"))  # (404, 'Not Found')
What each part does — in plain words
self.routes = {} — the entire framework’s memory: a dictionary mapping each URL path to the function that handles it.

def route(self, path): — this returns a decorator. When you write @app.route("/about"), Python calls route("/about"), gets back decorator, and applies it to your function.

def decorator(handler): self.routes[path] = handler — the actual registration: store this function under this path, then return it unchanged. That one line is what @app.route does — no magic.

handle(self, path) — the dispatcher: look up the path; if found, call the handler and return 200 with its result; if not, return 404. This is routing in its purest form.

@app.route("/") — using our framework now looks exactly like using Flask — because this is how Flask works underneath.
Common mistakes — and how to avoid them
✗ Thinking decorators are magic — they are just functions that take and return functions.
✓ See that @app.route(p) is exactly func = app.route(p)(func).
✗ Forgetting to return the handler from the decorator — then the name becomes None.
✓ Always return handler from the inner decorator.
✗ Not handling unknown routes — a missing path crashes.
✓ Use .get(path) and return 404 when None.

4 Test & Prove Each Part

We test that routes register and dispatch correctly, including the not-found case.

A registered route returns 200 and its result
An unknown route returns 404
Multiple routes each map to the right handler
Pythontest_framework.py
class App:
    def __init__(self):
        self.routes = {}

    def route(self, path):
        def decorator(handler):
            self.routes[path] = handler
            return handler
        return decorator

    def handle(self, path):
        handler = self.routes.get(path)
        if handler is None:
            return (404, "Not Found")
        return (200, handler())


def test_registered_route():
    """A registered route returns 200 and its body."""
    app = App()

    @app.route("/hi")
    def hi():
        return "hello"

    assert app.handle("/hi") == (200, "hello")


def test_unknown_route():
    """An unknown route returns 404."""
    app = App()
    assert app.handle("/nope") == (404, "Not Found")


def test_multiple_routes():
    """Each route maps to its own handler."""
    app = App()

    @app.route("/a")
    def a():
        return "A"

    @app.route("/b")
    def b():
        return "B"

    assert app.handle("/a") == (200, "A")
    assert app.handle("/b") == (200, "B")

Run with pytest -v. These tests use the framework exactly as a developer would — registering routes with the decorator and checking dispatch. That the decorator and dispatcher are simple enough to test in a few lines is the whole lesson.

5 API Documentation

The framework’s API — how a developer uses it.

@routeapp.route(path)register a handler
Usage
@app.route("/hello")
def hello():
    return "Hi!"
handleapp.handle(path)dispatch a request
Returns
(200, "Hi!")  or  (404, "Not Found")

6 Run It & Automate It

Run it locally
python3 framework.py
See routes dispatch. Then connect it to the Simple Web Server project to handle real HTTP requests!

Jenkins runs the tests automatically — each line explained below.

What you should see when it works
Terminala real run
(200, 'Welcome home!')
(200, 'About us')
(404, 'Not Found')
If it breaks — how to fix it
🚨 Your decorated function becomes None.
The decorator must return handler at the end.
🚨 KeyError on dispatch.
Use self.routes.get(path) not self.routes[path], so missing routes return None.
GroovyJenkinsfile
// Jenkinsfile — automated tests on every change.
pipeline {
    agent any
    stages {
        stage('Get the code') { steps { checkout scm } }       // download the code
        stage('Install') { steps { sh 'python3 -m venv venv && . venv/bin/activate && pip install pytest' } }  // test tool
        stage('Test') { steps { sh '. venv/bin/activate && pytest -v' } }   // run every test
    }
    post {
        success { echo 'All tests passed.' }
        failure { echo 'A test failed — look above.' }
    }
}
🎯 Try this next — make it yours
  1. URL parameters. Support /user/<id> dynamic routes. (Teaches: pattern matching.)
  2. HTTP methods. Register separate GET and POST handlers. (Teaches: method routing.)
  3. Wire to a real server. Connect to http.server for actual requests. (Teaches: integration.)
What you learned
You built a web framework and saw the magic dissolve: routes are a dictionary, and @app.route is a decorator that fills it. Understanding this makes Flask, Django, and Express feel obvious rather than mysterious. Related: Decorators, http.server.