This is the error this post builds up to:
src/chat.ts(8,60): error TS2339: Property 'text' does not exist on type 'ChatNotificationPayload'.
Enter fullscreen mode
Exit fullscreen mode
The Python server renamed a WebSocket field from text to body , and the TypeScript frontend found out at compile time, through a client generated from the server's code. Without that client the field reads as undefined , nothing throws, and the user sees an empty chat bubble. The same kind of bug ( content on one side, body on the other) once cost me most of a day, and it's how I opened my talk at AsyncAPI Conf 2026 , which covers the approach behind this post.
FastAPI already gives HTTP routes this kind of safety: type hints become an OpenAPI schema, a /docs page and generated clients. @app.websocket gets none of it. You receive a dict, check a string and send a dict back, and nothing describes those messages.
So in this post we build a small FastAPI room chat where every WebSocket message is a Pydantic model.…