Skip to main content

Fusion

A modern, async-first ASGI web framework for Python with type-safe dependency injection.

PyPI Python CI Coverage License


[!WARNING] This project is under active development and is not production-ready. APIs may change without notice between versions.


Overview

Fusion is a lightweight ASGI web framework built on two pillars:

  • msgspec — high-performance JSON serialization and validation
  • Type-hint-driven DI — declare dependencies as annotated fields; Fusion resolves them automatically at request time

It is designed for Python 3.14+ and is async-first throughout.


Installation

# From PyPI (once published)
pip install fusion

# From source
git clone https://github.com/okanakbulut/fusion.git
cd fusion
pip install -e .

Quick Start

# app.py
from fusion import Fusion, Get, Handler, Object, Request, Response


class Greeting(Object):
    message: str


class HelloHandler(Handler):
    async def handle(self, request: Request) -> Response[Greeting]:
        return Response(Greeting(message="Hello, World!"))


app = Fusion(routes=[Get("/hello", handler=HelloHandler)])

Run it:

pip install uvicorn
uvicorn app:app
GET /hello  →  {"message": "Hello, World!"}

Core Concepts

Objects (Data Models)

Object is a msgspec.Struct-backed base class for all serializable data. Define fields with standard type annotations:

from fusion import Object

class User(Object):
    id: int
    name: str
    email: str

Handlers

Subclass Handler and implement async def handle(self, request: SomeRequest) -> SomeResponse.

  • The request parameter must be type-annotated with Request or a subclass of it.
  • The return type determines how the response is serialized.
from fusion import Handler, Object, Request, Response

class Output(Object):
    message: str

class MyHandler(Handler):
    async def handle(self, request: Request) -> Response[Output]:
        return Response(Output(message="ok"))

Routing

Use Route for explicit method lists, or the shorthand helpers Get, Post, Put, Delete, Patch:

from fusion import Fusion, Get, Post, Route

app = Fusion(routes=[
    Get("/items", handler=ListItemsHandler),
    Post("/items", handler=CreateItemHandler),
    Route("/items/{id:int}", methods=["GET", "DELETE"], handler=ItemHandler),
])

Path parameter syntax:

Pattern Matches
{name} any string segment
{id:int} integer
{id:uuid} UUID

Request Parameters

Request-scoped parameters (QueryParam, PathParam, Header, Cookie, RequestBody) must be declared on a Request subclass — not directly on a Handler. The handler's handle method receives that subclass as its request argument.

Query Parameters

from fusion import Fusion, Get, Handler, Object, QueryParam, Request, Response

class SearchRequest(Request):
    query: QueryParam[str]
    page: QueryParam[int]
    tags: QueryParam[list[str]]   # ?tags:list=a,b,c

class SearchResult(Object):
    query: str
    page: int

class SearchHandler(Handler):
    async def handle(self, request: SearchRequest) -> Response[SearchResult]:
        return Response(SearchResult(query=request.query, page=request.page))

Headers

from fusion import Handler, Header, Object, Request, Response

class AuthRequest(Request):
    authorization: Header[str]
    user_id: Header[int]          # header value is coerced to the declared type

class AuthHandler(Handler):
    async def handle(self, request: AuthRequest) -> Response[Object]:
        token = request.authorization  # "Bearer ..."
        ...

Request Body

from fusion import Body, Handler, Object, Request, Response

class User(Object):
    name: str
    email: str

class CreateUserRequest(Request):
    body: Body[User]

class CreateUserHandler(Handler):
    async def handle(self, request: CreateUserRequest) -> Response[User]:
        return Response(request.body)

Dependency Injection

Use @factory to register a factory function for any type (e.g. a database connection). Declare the type as a field on an Injectable; Fusion calls the factory automatically.

from fusion import Fusion, Get, Handler, Injectable, Object, Request, Response, factory

class Database:
    def __init__(self, dsn: str) -> None:
        self.dsn = dsn

@factory
async def database_factory() -> Database:
    return Database("postgresql://localhost/mydb")

class StatusHandler(Handler):
    db: Database          # resolved from the factory above

    async def handle(self, request: Request) -> Response[Object]:
        # self.db is a Database instance
        ...

Lifecycle Management (async context managers)

Wrap a factory with @asynccontextmanager for per-request setup and teardown:

from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from fusion import factory

class Session:
    pass

@factory
@asynccontextmanager
async def session_factory() -> AsyncIterator[Session]:
    session = Session()
    try:
        yield session
    finally:
        await session.close()      # runs after the handler returns

Middleware

Subclass BaseMiddleware, implement async def handle(self, request), and attach it to a route:

from fusion import Fusion, Get, Handler, Middleware, Object, Request, Response, Unauthorized
from fusion.middleware import BaseMiddleware

class AuthMiddleware(BaseMiddleware):
    async def handle(self, request: Request) -> Unauthorized | Response:
        token = request.headers.get("authorization", "")
        if not token.startswith("Bearer "):
            return Unauthorized(detail="Missing or invalid token")
        return await self.app.handle(request)

class ProtectedHandler(Handler):
    async def handle(self, request: Request) -> Response[Object]:
        ...

app = Fusion(routes=[
    Get("/protected", handler=ProtectedHandler, middlewares=[Middleware(AuthMiddleware)])
])

Responses & Problem Details

Fusion follows RFC 9457 for error responses. All error types serialize to application/problem+json.

Class Status Use case
Response[T] 200 Success with body
Created[T] 201 Resource created
NoContent 204 Success, no body
BadRequest 400 Invalid input
Unauthorized 401 Authentication required
Forbidden 403 Permission denied
NotFound 404 Resource not found
MethodNotAllowed 405 Wrong HTTP method
InternalServerError 500 Unhandled error
ValidationError 400 Field-level validation errors

Returning errors from a handler

from fusion.responses import BadRequest, NotFound

class ItemHandler(Handler):
    async def handle(self, request: Request) -> NotFound | Response[Item]:
        item = db.get(item_id)
        if item is None:
            return NotFound(detail="Item not found")
        return Response(item)

Field-level validation errors

from fusion.responses import FieldError, ValidationError

return ValidationError(
    detail="Validation failed",
    errors=[
        FieldError(field="email", message="invalid format"),
        FieldError(field="name", message="required"),
    ],
)

Custom problem types

import typing
from fusion.responses import Problem

class OutOfStockProblem(Problem):
    type: typing.ClassVar[str] = "https://example.com/problems/out-of-stock"
    status: typing.ClassVar[int] = 409
    title: str = "Out of Stock"

# In a handler:
return OutOfStockProblem(detail="Item #42 is out of stock")

CLI

Fusion ships a fusion command for serving your application.

Running the server

# Start the app with uvicorn (requires: pip install uvicorn)
fusion serve myapp:app

# Custom host / port / auto-reload
fusion serve myapp:app --host 127.0.0.1 --port 9000 --reload

Requirements

  • Python 3.14+
  • msgspec >= 0.21.1
  • typedprotocol >= 0.1.0

License

MIT — see LICENSE.md.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fusion-0.11.1.tar.gz (66.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fusion-0.11.1-py3-none-any.whl (25.6 kB view details)

Uploaded Python 3

File details

Details for the file fusion-0.11.1.tar.gz.

File metadata

  • Download URL: fusion-0.11.1.tar.gz
  • Upload date:
  • Size: 66.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for fusion-0.11.1.tar.gz
Algorithm Hash digest
SHA256 d03b30aa1b2f969ab271560597d1bd946034b184f7f9498a3cca1bbeec46e9c8
MD5 7ef65fbe4be1e0202e26ff7a5e4bfb89
BLAKE2b-256 d12a5843809a888989796dc344594d5205520bf970022f19b1ff3130b33493ff

See more details on using hashes here.

Provenance

The following attestation bundles were made for fusion-0.11.1.tar.gz:

Publisher: release.yml on okanakbulut/fusion

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fusion-0.11.1-py3-none-any.whl.

File metadata

  • Download URL: fusion-0.11.1-py3-none-any.whl
  • Upload date:
  • Size: 25.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for fusion-0.11.1-py3-none-any.whl
Algorithm Hash digest
SHA256 94ecc4a630556f7ed0e0465a8fc40f552f4a507f0bb3fc3e3e60ecb32f2062f0
MD5 dc607e51404eb0adff85c72da49651e4
BLAKE2b-256 79556387b0a2433b0c9a0930ad08f04bc059ea895fc4694780f14c090cf819f0

See more details on using hashes here.

Provenance

The following attestation bundles were made for fusion-0.11.1-py3-none-any.whl:

Publisher: release.yml on okanakbulut/fusion

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.13.1

2 files

0.13.0

2 files

0.12.0

2 files

This release

0.11.1 This release

2 files

0.11.0

2 files

0.10.0

2 files

0.9.0

2 files

0.8.4

2 files

0.8.3

2 files

0.8.2

2 files

0.8.1

2 files

0.8.0

2 files

0.7.0

2 files

0.6.1

2 files

0.6.0

2 files

0.5.0

2 files

0.4.1

2 files

0.4.0

2 files

0.3.2

2 files

0.3.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page