- Declare routes
- Send Html
- ...
- Profit!
Red Dwarf is a very minimal and very opinionated Python ASGI server.
Red Dwarf:
- only use the Python default library
- is fast?
- has sensible helper functions / looks familiar
- is decently secure?
- gets you running in seconds
First, Red Dwarf highly recommends Datastar.
Datastar's philosophy (and Red Dwarf's) is "keeping state in the backend".
But the "backend" is not equal to the "server", it's more than that. It's your database, your cronjobs, your pubsub system, your other Python scripts...
Red Dwarf gets out of your way by doing what a server should do (receiving and responding to Web requests) and nothing more, so you can write your Python however you see fit.
Simply run uv add red-dwarf to your project to get going.
Note: You need Python minimum version ????????????????????? hmm yes if you have to import zlib/compress which are optional modules but even there with asyncio, let's say 3.11
Then, head over to Quickstart
Red Dwarf is intended as a first "entry point" into the world of Datastar.
Don't use Red Dwarf if:
- you need multiple workers
- you need middlewares
- you need telemetry
- you need advanced server functions
- you're doing anything serious
We instead recommend the following tools:
After installing uv add red-dwarf,
create a new file named app.py and write:
from red_dwarf import App, Input, Output
app = App()
@app.get("/")
def index(i,o):
return o.html("<h1>hello world!</h1>")
if __name__ == "__main__":
app.run()Open a web browser and go to http://localhost:8080/: you should see the hello world message.
What we did here:
- Created our server with
app = App() - Registered a handler
indexfor GET request on the home page@app.get("/") - Told the handler to output HTML
- Started the server with
app.run()
Let's modify app.py:
from red_dwarf import App, Input, Output
app = App()
@app.get("/")
def index(i,o):
return o.html(
'''
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link rel="stylesheet" href="/static/css/index.css"/>
<script type="module" src="/static/js/datastar.js">
</head>
<body class="gc">
...
</body>
</html>
'''
)
if __name__ == "__main__":
app.run()We now have a proper HTML "file" (well, a string) which now includes Datastar and gold.css.
You'll notice immediately that the page is reactive. Go to localhost and fill the input: the text is updated on the frontend by Datastar.
Next, we'll add a route to react from the backend. Start by modifying the index HTML to:
...
then add route "dwarf_name" like this:
...
Tada!
Finally, let's see how alive and patch work by opening a SSE stream.
We start from our previous code:
...
And we modify the body to open a SSE stream:
...
Now we just need to add the SSE route:
...
Go to localhost and watch the dwarf count time!
NB: we only allow patching html with datastar default merge strategy that should work 100% of the time patch signals by piggybacking them on whatever (put that shit in details)
we like caddy
NB: for nginx you have to disable X-Accel-Buffering in the headers for sse streams
We like SQLITE and TINYDB
We like REDIS and NATS
We like what Stario and are waiting for t-strings
We like LOGGER
We like ASYNCIO but are waiting for free threading
Question?
Answer.
Can I use sync functions?
No. Everything in RD is async by default.
Can I respond with json?
No.
Is there type matching on regex routes?
No, they're strings.
How can I debug?
We recommend using print().
How can I test?
We recommend using assert.
What?! You didn't write any tests
Not yet, no.
Can I add routes at runtime?
Well you can change your code and the server will restart, but... why do that?
Why is there no compression?
We let the reverse proxy do it. because caddy will compress it faster, after its own middlewares and can also use precompressed files!! to save some cpu (rarely the bottleneck tbh but hey) yeah you lose some on the compression window
Why is there no static files?
We let the reverse proxy do it, and the browser cache them with etags.
But why?
Just like everybody needs a frontend framework (like Datastar), everybody needs a reverse proxy. So more people work on them, which means they're likely better.
We do the absolute minimum at the Python level and let Caddy's Go code and Chrome's C++ do the heavy lifting.
What status code can I send?
200 for html. and the rest is classic.
How can I set cookies?
Very easy, request.cookies.get html(cookie={'key': value})
How do I add headers
headers=["Wait: What"] there's no reason to do it, so if you do it please be prepared
How to serve static templates
Just load them into memory!
Use a real framework
I prefer starlette for actually reading the code.
