Litestar Tutorial: Build a Typed ASGI API in Python
Litestar is a typed ASGI framework that turns your Python type hints into validation, serialization, and OpenAPI docs. This guide takes you from install to a working API.
What is Litestar?
Litestar is an open-source ASGI web framework for building APIs in Python. It was first released as Starlite and was renamed to Litestar with its 2.0 release, at which point it became a fully independent, community-governed project. Like other modern Python frameworks, it reads your type annotations and uses them to do real work: validating and parsing incoming requests, serializing responses, and generating an OpenAPI 3.1 schema with zero extra boilerplate.
What sets Litestar apart is its opinionated core with many features included. Dependency injection, DTOs (data transfer objects), authentication primitives, sessions, response caching, a command-line interface, and OpenAPI documentation all ship in the base package rather than as add-ons. It also defaults to msgspec for serialization, which is one of the fastest serialization libraries available for Python, while still letting you use standard-library dataclasses, typing.TypedDict, or Pydantic models when you prefer them.
How Litestar works
A Litestar application is an ASGI app: an object that an ASGI server such as Uvicorn or Granian can call. You build it from a few core concepts.
Route handlers are functions or methods decorated with one of the HTTP method decorators - @get, @post, @put, @patch, or @delete. The decorator captures the path, and the handler's signature describes everything else. A parameter named after a path variable becomes a path parameter, the return annotation drives serialization, and a typed body parameter becomes the parsed request body.
Controllers are classes that group related handlers under a shared base path. They are a core part of Litestar - the framework actively promotes class-based controllers as a way to organize larger APIs without repeating path prefixes or shared dependencies.
Dependency injection is layered. You register named dependencies with Provide(...) at the application, router, controller, or handler level, and the most specific layer wins. Any handler can then request a dependency simply by adding a parameter with the matching name. This is how you keep database sessions, configuration, and the authenticated user out of your business logic.
Finally, the type system ties it all together. Because Litestar inspects annotations at startup, a mismatched payload is rejected with a 400 before your code runs, and the OpenAPI schema stays in sync with the actual handlers automatically.
Installing Litestar
Install the standard bundle, which pulls in the CLI, the Uvicorn server, and Jinja2 for templating:
pip install 'litestar[standard]'
If you only need the framework itself with no server or CLI, install the bare package:
pip install litestar
Verify the install and inspect the CLI:
litestar version
litestar --help
The CLI auto-discovers an app named app in app.py (or an app package). Once you have a file, start the development server with live reload:
litestar run --reload
By default it serves on http://127.0.0.1:8000, with interactive OpenAPI docs at http://127.0.0.1:8000/schema.
Building a typed API with controllers and DI
Here is a small but complete API that uses an msgspec.Struct model, a class-based controller, and an injected in-memory repository. Save it as app.py.
from __future__ import annotations
import msgspec
from litestar import Litestar, Controller, get, post
from litestar.di import Provide
from litestar.exceptions import NotFoundException
class Book(msgspec.Struct):
id: int
title: str
author: str
class BookRepository:
def __init__(self) -> None:
self._books: dict[int, Book] = {}
self._next_id = 1
def add(self, title: str, author: str) -> Book:
book = Book(id=self._next_id, title=title, author=author)
self._books[book.id] = book
self._next_id += 1
return book
def get(self, book_id: int) -> Book:
try:
return self._books[book_id]
except KeyError as exc:
raise NotFoundException(f"No book with id {book_id}") from exc
def list(self) -> list[Book]:
return list(self._books.values())
# A single shared repository instance for the app's lifetime.
_repo = BookRepository()
def provide_books() -> BookRepository:
return _repo
class NewBook(msgspec.Struct):
title: str
author: str
class BookController(Controller):
path = "/books"
dependencies = {"repo": Provide(provide_books, sync_to_thread=False)}
@get()
async def list_books(self, repo: BookRepository) -> list[Book]:
return repo.list()
@get("/{book_id:int}")
async def get_book(self, repo: BookRepository, book_id: int) -> Book:
return repo.get(book_id)
@post()
async def create_book(self, repo: BookRepository, data: NewBook) -> Book:
return repo.add(title=data.title, author=data.author)
app = Litestar(route_handlers=[BookController])
A few things are happening here. The data: NewBook parameter tells Litestar to parse and validate the JSON body into a NewBook struct - send a payload missing title and you get a 400 automatically. The repo parameter resolves through the controller-level dependencies mapping; because provide_books is a plain synchronous function, we pass sync_to_thread=False so Litestar does not warn about blocking the event loop. The {book_id:int} path fragment is coerced to an integer, and a missing book raises NotFoundException, which Litestar renders as a clean 404.
Run it and exercise the endpoints:
litestar run --reload
curl -X POST http://127.0.0.1:8000/books \
-H "Content-Type: application/json" \
-d '{"title": "Dune", "author": "Frank Herbert"}'
curl http://127.0.0.1:8000/books/1
Open http://127.0.0.1:8000/schema and you will see the full OpenAPI document, generated from those handler signatures with no separate schema file to maintain.
When to use Litestar
Reach for Litestar when you are building a real API rather than a one-off script and you value an integrated toolkit over assembling your own. Its layered dependency injection, controllers, and DTO system become more useful as a service grows and you need consistent structure across many endpoints. The msgspec-based serialization makes it a strong pick for high-throughput JSON workloads, and the built-in OpenAPI output keeps your docs accurate by generating them from your code.
It is also a natural choice if you already think in terms of type hints and want the framework to enforce them at the edges. Teams migrating from Starlette or evaluating against FastAPI will find a familiar typed-handler model with more features included. If you only need a couple of routes or want the absolute smallest dependency footprint, a micro-framework may be a better fit - but for production services that will evolve, Litestar gives you room to grow without adding a dozen extra libraries.
Frequently asked questions
What is Litestar?
Litestar is an open-source, high-performance ASGI web framework for building APIs in Python. It uses your type annotations to validate and parse request data, serialize responses, and generate an OpenAPI schema automatically. It supports dataclasses, TypedDicts, msgspec.Struct, and Pydantic models.
Is Litestar the same as Starlite?
Yes. Litestar was originally released as Starlite and renamed to Litestar with the 2.0 release. The project is now an independent, community-governed framework and is no longer tied to the Starlette codebase it once built on.
How is Litestar different from FastAPI?
Both are typed ASGI frameworks, but Litestar leans on msgspec for serialization (rather than Pydantic by default), promotes class-based controllers, ships a layered dependency-injection system, and bundles a CLI, sessions, and DTOs in the core package. FastAPI remains lighter and Pydantic-centric.
What Python version does Litestar require?
Litestar needs a currently supported Python 3 release - check the Litestar docs for the exact minimum. It is an async framework built on the ASGI standard, and you run it with any ASGI server such as Uvicorn or Granian. The litestar[standard] install includes Uvicorn.
Do real production work, free. Live cloud workspaces in your browser - every project you complete lands on your portfolio.
Get started - free