[![Logo](https://patx.github.io/micropie/logo.png)](https://patx.github.io/micropie)

[![License](https://img.shields.io/github/license/patx/micropie)](LICENSE)
[![PyPI](https://img.shields.io/pypi/v/micropie)](https://pypi.org/project/micropie/)
[![PyPI Downloads](https://static.pepy.tech/badge/micropie)](https://pepy.tech/projects/micropie)

## **Introduction**
**MicroPie** is a fast, lightweight, modern Python web framework built on ASGI for asynchronous web applications. Designed for flexibility and simplicity, it enables high-concurrency web apps with built-in WebSockets, session management, middleware, lifecycle event handling, and optional template rendering. Extensible for integration with ASGI-compatible tools like [python-socketio](https://python-socketio.readthedocs.io/en/stable/server.html#running-as-an-asgi-application) and [ServeStatic](https://archmonger.github.io/ServeStatic/latest/asgi/), it’s inspired by CherryPy and licensed under the BSD 3-Clause License.

### **Key Features**
- 📬 **Routing:** Automatic mapping of URLs to functions with support for dynamic and query parameters.
- 🔑 **Sessions:** Simple, pluggable session management using cookies.
- 🎨 **Templates:** Jinja2, if installed, for rendering dynamic HTML pages.
- 🚧 **Middleware:** Support for custom request middleware enabling functions like rate limiting, authentication, logging, and more.
- 💨 **Real-Time Communication:** Built-in WebSocket support for real-time, bidirectional communication.
- ☀️ **ASGI-Powered:** Built with asynchronous support for modern web servers like Uvicorn, Hypercorn, and Daphne, enabling high concurrency.
- ☁️ **Lightweight Design:** Only optional dependencies for flexibility and faster development/deployment.
- 👶 **Lifecycle Events:** ASGI lifespan event handling for startup and shutdown tasks (e.g., database initialization).
- 🏁 **Competitive Performance:** Check out how MicroPie compares to other popular ASGI frameworks below!

### **Useful Links**
- **Homepage**: [patx.github.io/micropie](https://patx.github.io/micropie)
- **Official Documentation**: [micropie.readthedocs.io](https://micropie.readthedocs.io/)
- **PyPI Page**: [pypi.org/project/MicroPie](https://pypi.org/project/MicroPie/)
- **GitMan Project**: [gitman.io/patx/micropie](https://gitman.io/patx/micropie)
- **GitHub Project**: [github.com/patx/micropie](https://github.com/patx/micropie)
- **File Issue/Request**: [github.com/patx/micropie/issues](https://github.com/patx/micropie/issues)
- **Example Applications**: [github.com/patx/micropie/tree/main/examples](https://github.com/patx/micropie/tree/main/examples)
- **Introduction Lightning Talk**: [Introduction to MicroPie on YouTube](https://www.youtube.com/watch?v=BzkscTLy1So)

### Latest Release Notes
View the latest release notes [here](https://github.com/patx/micropie/blob/main/docs/release_notes.md). It is useful to check release notes each time a new version of MicroPie is published. Any breaking changes (rare, but do happen) also appear here.

### **Table of Contents**
- [Installing MicroPie](#installing-micropie)
- [Getting Started](#getting-started)
- [Core Features](#core-features)
- [Learn by Examples](#learn-by-examples)
- [Comparisons](#comparisons)
- [Benchmark Results](#benchmark-results)
- [Suggestions or Feedback?](#suggestions-or-feedback)

### **Documentation Roadmap**
- **Tutorials**: [quick start](https://micropie.readthedocs.io/en/latest/tutorial/quickstart.html), [routing](https://micropie.readthedocs.io/en/latest/tutorial/routing.html), and [websockets](https://micropie.readthedocs.io/en/latest/tutorial/websockets.html).
- **How-to Guides**: Practical recipes for [sessions](https://micropie.readthedocs.io/en/latest/howto/sessions.html), [middleware](https://micropie.readthedocs.io/en/latest/howto/middleware.html), [streaming](https://micropie.readthedocs.io/en/latest/howto/streaming.html), and more.
- **Reference**: Full API docs for [App](https://micropie.readthedocs.io/en/latest/reference/app.html), [Request](https://micropie.readthedocs.io/en/latest/reference/request.html), [WebSocket](https://micropie.readthedocs.io/en/latest/reference/websocket.html), and related classes.
- **What’s New**: Version-by-version highlights at [What's new in MicroPie](https://micropie.readthedocs.io/en/latest/whats_new.html).

## **Installing MicroPie**

### **Installation Profiles**
Install whichever profile fits your use case:

| Profile | Command | Includes |
|---------|---------|----------|
| Minimal | `pip install micropie` | Core framework only |
| Standard | `pip install micropie[standard]` | Core + `jinja2` + `multipart` |
| All | `pip install micropie[all]` | Standard + `orjson` + `uvicorn` |

If you are just getting started, use **standard**:
```bash
pip install micropie[standard]
```

For an ultra-minimal setup, you can also use the standalone script (development version):

[micropie.py](https://raw.githubusercontent.com/patx/micropie/refs/heads/main/micropie.py)

Place it in your project directory and you are ready to go. Install optional packages only if needed:
```bash
pip install jinja2 multipart
```
Use `jinja2` for `_render_template` and `multipart` for multipart file/form parsing.

By default MicroPie will use the `json` library from Python's standard library. If you need faster performance you can use `orjson`. MicroPie *will* use `orjson` *if installed* by default. If it is not installed, MicroPie will fallback to `json`. This means with or without `orjson` installed MicroPie will still handle JSON requests/responses the same. To install `orjson` and take advantage of its performance, use:
```bash
pip install orjson
```

### **Install an ASGI Web Server**
In order to test and deploy your apps you will need an ASGI web server like Uvicorn, Hypercorn, or Daphne.

If you installed `micropie[all]` Uvicorn *should* be ready to use. If you didn't install all of MicroPie's optional dependencies, use:
```bash
pip install uvicorn
```

## **Getting Started**

### **Create Your First ASGI App**

Save the following as `app.py`:
```python
from micropie import App

class MyApp(App):
    async def index(self):
        return "Welcome to MicroPie ASGI."

app = MyApp()
```
Run the server with:
```bash
uvicorn app:app
```
Access your app at [http://127.0.0.1:8000](http://127.0.0.1:8000).

## **Core Features**

### **Route Handlers**

MicroPie's route handlers map URLs to methods in your `App` subclass. Handler input can come from automatic argument binding or request helper methods.

#### **Key Points**
- **Automatic Mapping**: URLs map to method names (e.g., `/greet` → `greet`, `/` → `index`).
- **Private Methods**: Methods starting with `_` (e.g., `_private_method`) are private and inaccessible via URLs, returning 404. **Security Note**: Use `_` for sensitive methods to prevent external access.
- **Automatic Argument Binding**: Handler args are populated from path/query/body data by parameter name.
- **Python Signatures**: Keyword-only parameters bind correctly, `*args` collects remaining path segments, and `**kwargs` collects remaining named request values.
- **Session Isolation**: Session values never bind to handler arguments; read trusted state explicitly from `self.request.session`.
- **Bounded Bodies**: Requests default to a 16 MiB total limit and multipart text fields to 1 MiB; configure `max_body_size` and `max_form_field_size` on `App`.
- **Request Helpers**:
  - `self.request.query(name, default)` for query-string values.
  - `self.request.form(name, default)` for URL-encoded or multipart form values.
  - `self.request.json()` for full JSON payloads, or `self.request.json(name, default)` for a key lookup.
- **HTTP Methods**: Handlers support all methods (GET, POST, etc.). Check `self.request.method` to handle specific methods.
- **Responses**:
  - String, bytes, or JSON-serializable object.
  - Tuple: `(status_code, body)` or `(status_code, body, headers)`.
  - Sync/async generator for streaming.

#### **Advanced Usage**
- **Custom Routing**: Use middleware for explicit routing (see [examples/middleware](https://github.com/patx/micropie/tree/main/examples/middleware) and [examples/explicit_routing](https://github.com/patx/micropie/tree/main/examples/explicit_routing)).
- **Errors**: Auto-handled 404/400; customize via middleware.
- **Dynamic Params**: Use `*args` for multiple path parameters.

#### **Automatic Argument Binding**
MicroPie can bind handler parameters directly from incoming request data:
```python
class MyApp(App):
    async def greet(self, name="Guest"):
        return f"Hello, {name}!"

    async def submit(self, username="Anonymous"):
        return f"Submitted by: {username}"
```
**Access:**
- [http://127.0.0.1:8000/greet/Alice](http://127.0.0.1:8000/greet/Alice) returns `Hello, Alice!`.
- [http://127.0.0.1:8000/greet?name=Alice](http://127.0.0.1:8000/greet?name=Alice) also returns `Hello, Alice!`.
- POST `application/x-www-form-urlencoded` to `/submit` with `username=bob` returns `Submitted by: bob`.
- POST `application/json` to `/submit` with `{"username": "bob"}` also returns `Submitted by: bob`.

Top-level JSON values retain their native types during binding. JSON objects
are not mirrored into `body_params`, so `request.form()` only reads form data.
Query strings and URL-encoded form bodies share MicroPie's permissive parsing
behavior: blank or bare fields are ignored and malformed encoding is tolerated.

#### **Helper-Based Request Access**
Use helper methods for query, form, and JSON payload access:
```python
class MyApp(App):
    async def greet(self):
        name = self.request.query("name", "Guest")
        return f"Hello, {name}!"

    async def submit(self):
        username = self.request.form("username", "Anonymous")
        return f"Submitted by: {username}"

    async def submit_json(self):
        data = self.request.json()
        username = self.request.json("username", "Anonymous")
        return {"submitted_by": username, "raw": data}
```
**Access:**
- [http://127.0.0.1:8000/greet?name=Alice](http://127.0.0.1:8000/greet?name=Alice) returns `Hello, Alice!`.
- POST `application/x-www-form-urlencoded` to `/submit` with `username=bob` returns `Submitted by: bob`.
- POST `application/json` to `/submit_json` with `{"username": "bob"}` returns JSON including `submitted_by: "bob"`.

Choose the simplest input style that fits the route:
- Use handler arguments when you only need named path/query/body values.
- Use helpers when you want to explicitly read one query value, one form value, or a JSON payload.
- Use raw attributes like `query_params`, `body_params`, `files`, `headers`, and `session` when you need repeated values, uploaded file streams, middleware-level inspection, or exact parsed structures.

Do not `await` `self.request.query(...)`, `self.request.form(...)`, or `self.request.json(...)`. They are synchronous accessors over request data MicroPie has already parsed. Uploaded file content is the exception: file bodies are streamed through async queues in `self.request.files`.

An uploaded file queue yields `None` only after a complete file. If multipart parsing, timeout enforcement, or a size limit aborts an open file, `get()` raises `MultipartFileError`; treat that as a failed upload rather than persisting partial content.

By default, MicroPie's route handlers can accept any request method. Check `self.request.method` in a handler when route behavior differs by method. For lower-level request internals such as `query_params` and `body_params`, see `docs/apidocs/reference/request.rst`.

### **Lifecycle Event Handling**
MicroPie supports ASGI lifespan events, allowing you to register asynchronous handlers for application startup and shutdown. This is useful for tasks like initializing database connections or cleaning up resources.

#### **Key Points**
- **Startup Handlers**: Register async handlers to run during `lifespan.startup` using `app.startup_handlers.append(handler)`.
- **Shutdown Handlers**: Register async handlers to run during `lifespan.shutdown` using `app.shutdown_handlers.append(handler)`.
- **Error Handling**: Errors during startup or shutdown are caught and reported via `lifespan.startup.failed` or `lifespan.shutdown.failed` events.
- **Use Cases**: Ideal for setting up database pools, external service connections, or logging systems on startup, and closing them on shutdown.

#### **Example**
```python
from micropie import App

async def setup_db():
    print("Setting up database...")
    # DB init code here
    print("Database setup complete!")

async def close_db():
    print("Closing database...")
    # DB close code here
    print("Database closed!")

class MyApp(App):
    async def index(self):
        return "Welcome to MicroPie ASGI."

app = MyApp()
app.startup_handlers.append(setup_db)
app.shutdown_handlers.append(close_db)
```
On startup, the `setup_db` method initializes the database connection. On shutdown (e.g., Ctrl+C), the `close_db` method closes it.

### Real-Time Communication with WebSockets and Socket.IO
MicroPie includes built-in support for WebSocket connections. WebSocket routes are defined in your App subclass using methods prefixed with `ws_`, mirroring the simplicity of MicroPie's HTTP routing. For example, a method named `ws_chat` handles WebSocket connections at `ws://<host>/chat`.

#### MicroPie’s WebSocket support allows you to:
- Define WebSocket handlers with the same intuitive automatic routing as HTTP (e.g., `/chat` maps to `ws_chat` method).
- Access query and path parameters through handler arguments, and session data explicitly through `self.request.session`.
- Manage WebSocket connections using the WebSocket class, which provides methods like `accept`, `receive_text`, `send_text`, and `close`.

Check out a basic example:
```python
from micropie import App

class Root(App):
    async def ws_echo(self, ws):
        await ws.accept()
        while True:
            msg = await ws.receive_text()
            await ws.send_text(f"Echo: {msg}")

app = Root()
```

#### Use Socket.IO for Advanced Real-Time Features
If you want more advanced real-time features like automatic reconnection, broadcasting, or fallbacks (e.g., polling), you can integrate Socket.IO with your MicroPie app using Uvicorn as the server. See [examples/socketio](https://github.com/patx/micropie/tree/main/examples/socketio) for integration instructions and examples.

### **Jinja2 Template Rendering**
Dynamic HTML generation is supported via Jinja2. This happens asynchronously using Python's `asyncio` library, so make sure to use `async` and `await` with this method.

#### **`app.py`**
```python
class MyApp(App):
    async def index(self):
        return await self._render_template("index.html", title="Welcome", message="Hello from MicroPie!")
```

#### **`templates/index.html`**
```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
</head>
<body>
    <h1>{{ message }}</h1>
</body>
</html>
```

### **Static File Serving**
MicroPie does not natively support static files, if you need them, you can easily implement it in your application code or integrate dedicated libraries like **ServeStatic** or **Starlette’s StaticFiles** alongside Uvicorn to handle async static file serving. Check out [examples/static_content](https://github.com/patx/micropie/tree/main/examples/static_content) to see this in action.

### **Streaming Responses**
Support for streaming responses makes it easy to send data in chunks.

```python
class MyApp(App):
    async def stream(self):
        async def generator():
            for i in range(1, 6):
                yield f"Chunk {i}\n"
        return generator()
```

### **Sessions and Cookies**
Built-in session handling simplifies state management:

```python
class MyApp(App):
    async def index(self):
        if "visits" not in self.request.session:
            self.request.session["visits"] = 1
        else:
            self.request.session["visits"] += 1
        return f"You have visited {self.request.session['visits']} times."
```

You also can use the `SessionBackend` class to create your own session backend. Session IDs default to validated UUIDv4 values; backends using another ID format can configure `session_id_factory` and `session_id_validator`. You can see an example of a custom backend in [examples/sessions](https://github.com/patx/micropie/tree/main/examples/sessions).

### **Middleware**
MicroPie allows you to create pluggable middleware to hook into the request lifecycle. Take a look at a trivial example using `HttpMiddleware` to send console messages before and after the request is processed. Check out [examples/middleware](https://github.com/patx/micropie/tree/main/examples/middleware) to see more.
```python
from micropie import App, HttpMiddleware

class MiddlewareExample(HttpMiddleware):
    async def before_request(self, request):
        print("Hook before request")

    async def after_request(self, request, status_code, response_body, extra_headers):
        print("Hook after request")

class Root(App):
    async def index(self):
        print("Hello, World!")
        return "Hello, World!"

app = Root()
app.middlewares.append(MiddlewareExample())
```

Middleware provides an easy and **reusable** way to extend the MicroPie framework. We can do things such as rate limiting, metadata validation, **explicit routing**, CSRF protection, and more. Request-body byte limits belong on `App` (or an outer ASGI/proxy layer), because `HttpMiddleware` runs after body parsing has begun.

MicroPie apps can be deployed using any ASGI server. For example, using Uvicorn if our application is saved as `app.py` and our `App` subclass is assigned to the `app` variable we can run it with:
```bash
uvicorn app:app --workers 4 --port 8000
```

## **Learn by Examples**
The best way to get an idea of how MicroPie works is to see it in action! Check out the [examples folder](https://github.com/patx/micropie/tree/main/examples) for more advanced usage, including:
- Template rendering
- Custom HTTP request handling
- File uploads
- Serving static content
- Session usage
- JSON Requests and Responses
- Socket.io Integration
- Async Streaming
- Middleware including explicit routing and more
- Form handling and POST requests
- WebSockets
- Lifecycle event handling
- URL shortener app ([live demo @ erd.sh](https://erd.sh/))
- And more

## **Comparisons**

### **Features vs Other Popular Frameworks**
| Feature             | MicroPie      | Flask        | CherryPy   | Bottle       | Django       | FastAPI         |
|---------------------|---------------|--------------|------------|--------------|--------------|-----------------|
| **Routing**         | Automatic     | Manual       | Automatic  | Manual       | Views        | Manual          |
| **Template Engine** | Jinja2 (Opt.) | Jinja2       | Plugin     | SimpleTpl    | Django       | Jinja2          |
| **Middleware**      | Yes           | Yes          | Yes        | Yes          | Yes          | Yes             |
| **Session Handling**| Plugin        | Plugin       | Built-in   | Plugin       | Built-in     | Plugin          |
| **Async Support**   | Yes           | No           | No         | No           | Yes          | Yes             |
| **Built-in Server** | No            | No           | Yes        | Yes          | Yes          | No              |
| **Lifecycle Events**| Yes           | No           | Yes        | No           | Yes          | Yes             |

## Benchmark Results

The table below summarizes the performance of various ASGI frameworks based on a 15-second `wrk` test with 4 threads and 1000 connections, measuring a simple "hello world" JSON response. Each framework was served with Granian using `granian --interface asgi --workers 8 app:app` on a Dell XPS 13 9350. [Learn More](https://gist.github.com/patx/39e846ed66bead3e42270ff193db35f8).

| Framework  | Requests/sec | Avg Latency (ms) | Max Latency (ms) | Transfer/sec (MB) | Total Requests | Data Read (MB) |
|------------|--------------|------------------|------------------|-------------------|----------------|----------------|
| MicroPie   | 234244.46    | 4.04             | 53.62            | 30.62             | 3536707        | 462.36         |
| BlackSheep | 231688.13    | 4.04             | 82.57            | 30.21             | 3495372        | 455.76         |
| Starlette  | 223883.45    | 4.24             | 76.26            | 29.22             | 3381117        | 441.35         |
| FastAPI    | 150351.61    | 6.57             | 131.65           | 19.52             | 2270601        | 294.85         |
| Quart      | 104469.09    | 11.75            | 197.47           | 13.41             | 1575417        | 202.18         |

## **Suggestions or Feedback?**
We welcome suggestions, bug reports, and pull requests!
- File issues or feature requests [here](https://github.com/patx/micropie/issues).
- Security issues that should not be public, email `harrisonerd [at] gmail.com`.

## **Rock Your Powered by MicroPie Badge**
[![Powered by MicroPie](https://img.shields.io/badge/MicroPie-CC0000?style=for-the-badge&logo=python&logoColor=white)](https://patx.github.io/micropie)

You can add a Powered by MicroPie badge to your projects README using the following markdown:
```
[![Powered by MicroPie](https://img.shields.io/badge/MicroPie-CC0000?style=for-the-badge&logo=python&logoColor=white)](https://patx.github.io/micropie)
```

© 2025 Harrison Erd
