An experimental FastAPI router that uses msgspec for JSON request bodies and responses.
This is a prototype. Its API may change, and it relies on private FastAPI internals, so it is pinned to FastAPI 0.142.2 to 0.143.x. It is a personal project, not part of msgspec; feedback is welcome in the issues.
pip install msgspec-fastapiimport msgspec
from fastapi import FastAPI
from msgspec_fastapi import MsgspecRouter, install_openapi
class City(msgspec.Struct):
name: str
population: int
router = MsgspecRouter()
@router.post('/cities')
async def create_city(city: City) -> City:
return city
app = FastAPI()
app.include_router(router)
install_openapi(app, router)- A msgspec body parameter (a Struct or another type msgspec can decode) is decoded from
the raw request bytes by a
msgspec.json.Decoderbuilt once at route registration. A non-JSON content type returns 415, malformed JSON returns 400, and a missing or invalid body returns 422. Bodies are decoded in msgspec strict mode: for example, the JSON string"1"is not accepted for anintfield, unlike FastAPI with Pydantic. - A msgspec response type, from the return annotation or
response_model, is encoded with msgspec. An instance of exactly the declared Struct class is encoded without revalidation, as FastAPI does for a Pydanticresponse_model; other values are validated against the declared type. Nested values and type parameters of a trusted instance are not checked. - Responses of routes without a return annotation or
response_modelare also encoded with msgspec, so the output can differ from FastAPI'sjsonable_encoder(for example,bytesare base64-encoded andDecimalis encoded as a string). - Path, query, header and cookie parameters keep FastAPI extraction, aliases and
defaults, but are converted with
msgspec.convert(strict=False)instead of Pydantic, so coercion differs for some inputs (for example," 1.5"is rejected forfloatand"nan"is accepted forDecimal). Parameter types must be supported by msgspec; Pydantic-specific types such asAnyUrlraiseTypeErrorat route registration. install_openapiadds the msgspec JSON schemas to the OpenAPI document. It is required for a valid document: call it once after including the routers, passing everyMsgspecRouterthat is not included directly in anotherMsgspecRouter.- A Pydantic body or response model is left to FastAPI; the route parameters are still validated with msgspec.
- Only one msgspec body parameter per route, and it cannot be combined with other
FastAPI body fields. Only JSON bodies are supported:
application/json,application/*+json, or a request without a content type. - Structs, alone or inside containers, are not supported as path, query, header or cookie parameters.
response_model_include,response_model_exclude,response_model_exclude_*andresponse_model_by_alias=Falseare rejected for typed msgspec responses.
uv run --group test pytest