The "impossibly small" Microdot web framework
The Microdot web framework is quite small, as its name would imply; it supports both standard CPython and MicroPython, so it can be used on systems ranging from internet-of-things (IoT) devices all the way up to large, cloudy servers. It was developed by Miguel Grinberg, who gave a presentation about it at EuroPython 2025. His name may sound familiar from his well-known Flask Mega-Tutorial, which has introduced many to the Flask lightweight Python-based web framework. It should come as no surprise, then, that Microdot is inspired by its rather larger cousin, so Flask enthusiasts will find much to like in Microdot—and will come up to speed quickly should their needs turn toward smaller systems.
We have looked at various pieces of this software stack along the way: Microdot itself in January 2024, MicroPython in 2023, and Flask as part of a look at Python microframeworks in 2019.
Grinberg began his talk with an introduction. He has been living in
Ireland for a few years and "I make stuff
". That includes open-source projects, blog posts (on a
Flask-based blog platform that he wrote), and "a bunch of books
".
He works for Elastic and is one of the maintainers of the Elasticsearch
Python client, "so maybe you have used some of the things that I
made for money
".
Why?
With a chuckle, he asked: "Why do we need another web framework? We
have so many already.
" The story starts with a move that he made to
Ireland from the US in 2018; he rented a house with a "smart" heating
controller and was excited to use it. There were two thermostats, one for
each level of the house, and he was "really looking forward to the
winter
" to see the system in action.
As might be guessed, he could set target temperatures in each thermostat;
they would communicate with the controller that would turn the heating on
and off as needed. In addition, the system had a web server that could be
used to query various parameters or to start and stop the heaters. You
could even send commands via SMS text messages; "there's a SIM card
somewhere in that box [...] very exciting stuff
".
When winter rolled around, it did not work that well, however; sometimes
the house was too chilly or warm and he had to start and stop the heaters
himself. He did some debugging and found that the thermostats were
reporting temperatures that were off by ±3°C, "which is too much for
trying to keep the house at 20°
". The owner of the house thought that
he was too used to the US where things just work; "at least she thinks that in America everything is super-efficient,
everything works, and she thought 'this is the way things work in
Ireland'
". So he did not make any progress with the owner.
At that point, most people would probably just give up and live with the
problem; "I hacked my heating controller instead
". He set the
temperatures in both thermostats to zero, which effectively disabled their
ability to affect the heaters at all, and built two small boards running
MicroPython, each connected to a temperature and humidity sensor device.
He wrote code that would check the temperature every five minutes and send
the appropriate commands to start or stop the heaters based on what it
found.
So the second half of his first winter in Ireland went great. The sensors
are accurate to ±0.5°C, so "problem solved
". But, that led to a new
problem for him. "I wanted to know things: What's the temperature right
now? Is the heating running right now or not? How many hours did it run
today compared to yesterday?
" And so on.
He added a small LCD screen to display some information, but he had to
actually go to the device and look at it; what he really wanted was to be
able to talk to the device over WiFi and get information from the couch
while he was watching TV. "I wanted to host a web server [...] that
will show me a little dashboard
".
So he searched for web frameworks
for MicroPython; in the winter of 2018-2019, "there were none
".
Neither Flask nor Bottle,
which is a good bit smaller, would run on MicroPython; both are too large
for the devices,
but, in addition, the standard library for MicroPython is a subset of that of
CPython, so many things that they need are missing. A "normal
person
" would likely have just accepted that and moved on; "I
created a web framework instead.
"
Demo
He brought one of his thermostat devices to Prague for the conference and did a small demonstration of it operating during the talk. The device was connected to his laptop using USB, which provided power, but also a serial connection to the board. On the laptop, he used the rshell remote MicroPython shell to talk to the board, effectively using the laptop as a terminal.
He started the MicroPython read-eval-print loop (REPL) on the board in order to simulate the normal operation of the board. When it is plugged into the wall, rather than a laptop, it will boot to the web server, so he made that happen with a soft-reboot command. The device then connected to the conference WiFi and gave him the IP address (and port) where the server was running.
He switched over to Firefox on his laptop and visited the site, which showed a dashboard that had the current temperature (24.4°) and relative humidity (56.9%) of the room. He also used curl from the laptop to contact the api endpoint of the web application, which returned JSON with the two values and the time. There is no persistent clock on the board, so the application contacts an NTP server to pick up the time when it boots; that allows it to report the last time a measurement was taken.
Grinberg said that he wanted to set the expectations at the right level by
looking at the capabilities of the microcontrollers he often uses with
Microdot. For example, the ESP8266 in his thermostat device has 64KB of
RAM and up to 4MB of flash. The ESP8266 is the smallest and least expensive (around €5)
device with WiFi that
he has found; there are many even smaller devices, but they lack
the networking required for running a web server. The other devices
he uses are the Raspberry Pi Pico W with 2MB of flash and 256KB of RAM and
the ESP32 with up to 8MB of flash and 512KB of RAM. He contrasted those
with his laptop, which has 32GB of RAM, so "you need 500,000
ESP8266s
" to have the same amount of memory.
Features
The core framework of Microdot is in a single microdot.py file. It is fully asynchronous, using the MicroPython subset of the CPython asyncio module, so it can run on both interpreters. It uses asyncio because that is the only way to do concurrency on the microcontrollers; there is no support for processes or threads on those devices.
Microdot has Flask-style route decorators to define URLs for the application. It has Request and Response classes, as well as hooks to run before and after requests, he said. Handling query strings, form data, and JSON are all available in Microdot via normal Python dictionaries. Importantly, it can handle streaming requests and responses; because of the limited memory of these devices, it may be necessary to split up the handling of larger requests or responses.
It supports setting
cookies and sending static
files. Web applications can be constructed from a set of modules, using sub-applications,
which are similar to Flask
blueprints. It also has its own web
server with TLS support. "I'm very proud of all the stuff I was
able to fit in the core Microdot framework
", Grinberg said.
He hoped that attendees would have to think for a minute to come up with things that are missing from Microdot, but they definitely do exist. There are some officially maintained extensions, each in its own single .py file, to fill some of those holes. They encompass functionality that is important, but he did not want to add to the core because that would make it too large to fit on the low-end ESP8266 that he is using.
There is an extension for multipart
forms, which includes file uploads; "this is extremely complicated
to parse, it didn't make sense to add it into the core because most people
don't do this
". There is support for WebSocket
and server-sent
events (SSE). Templates
are supported using utemplate
for both Python implementations or Jinja, which only
works on CPython. There are extensions for basic
and token-based authentication and for secure
user logins with session data; the latter required a replacement for
the CPython-only PyJWT, which Grinberg
wrote and contributed to MicroPython as jwt.py.
There is a small handful of other extensions that he quickly mentioned as well.
"I consider the documentation as part of the framework
"; he is
"kind of fanatical
" about documenting everything. If there is
something missing or not explained well, "it's a bug that I need to
fix
". He writes books, so the documentation is organized similarly;
it comes in at 9,267 words, which equates to around 47 minutes of reading
time. There is 100% test coverage, he said, and there are around 30
examples, with more coming.
A design principle that he follows is "no dark magic
". An example
of dark magic to him is the Flask
application context, "which very few people understand
". In
Microdot, the request object is explicitly passed to each route function.
Another example is the dependency
injection that is used by the FastAPI framework to add
components; Microdot uses explicit decorators instead.
He used the cloc
utility to count lines of code, while ignoring comments and blank
lines. Using that, Django
comes in at 110,000 lines, Flask plus its essential Werkzeug library
is 15,500 lines, FastAPI with Starlette is 14,900 lines, Bottle is
around 3,000 lines, while the Microdot core has 765 lines ("believe it
or not
") and a full
install with all the extensions on MicroPython comes in at just shy of 1,700
lines of code.
He ended with an example of how Microdot can be so small by comparing the
URL matching in Flask with Microdot. The Flask version does lots more than
Microdot, with more supported types of arguments in a URL and multiple classes
in the werkzeug.routing
module; it has 1,362 lines of code. For Microdot, there is a more
limited set of URL arguments, though there is still the ability to define
custom types, and a single
URLPattern class; all of that is done in 63 lines of
code. "I don't intend to support everything that Flask supports, in
terms of routing, but I intend to support the 20% that covers 80% of the
use cases.
" That is the overall mechanism that he has used to get to
something that is so small.
An audience member asked about whether the Microdot code was minified in order to get it to fit. Grinberg said that doing so was not all that useful for MicroPython, but the code is smaller on the board because it is precompiled on another system; that results in a microdot.mpy file, which is bytecode for MicroPython. For example, on the low-end device he is using for his thermostats, Microdot would not be able to be compiled on the device itself. There are some other tricks that can also be used for reducing the RAM requirements, like putting the code into the firmware as part of the MicroPython binary.
The final question was about performance, and how many requests per second
could be handled. Grinberg said that he did not
have any real numbers, but that the device he demonstrated is "really
really slow
". That question led to a blog
post in late July where Grinberg tried to more fully answer it.
[I would like to thank the Linux Foundation, LWN's travel sponsor, for travel assistance to Prague for EuroPython.]
| Index entries for this article | |
|---|---|
| Conference | EuroPython/2025 |
| Python | Web |
Posted Aug 23, 2025 15:26 UTC (Sat)
by lyda (subscriber, #7429)
[Link] (2 responses)
When I wrote a lot of python, frameworks like this seemed great. But there's a better way. If you define the OpenAPI definition first, you can then generate the server, you can generate all the clients, you can generate tests for the server and client, as well as fuzz tests for the server. Less common, but you can do the same with gRPC. It also allows you to more easily move from one technology to another.
Posted Aug 24, 2025 3:10 UTC (Sun)
by ssmith32 (subscriber, #72404)
[Link] (1 responses)
Certainly not for the problem domain covered in the article.
Posted Nov 3, 2025 1:38 UTC (Mon)
by Rudd-O (guest, #61155)
[Link]
OpenAPI or gRPC is a better path forward
OpenAPI or gRPC is a better path forward
OpenAPI or gRPC is a better path forward
