34 comments

[ 2.9 ms ] story [ 55.6 ms ] thread
This is well written, thanks!
Litestar is awesome. It’s great it’s got more than a single maintainer too.
Excellent post that actually gets into important details for real-world applications. I'm a huge fan of the design of Litestar.

> I also still think there are a lot of bad use cases for repositories and service layers that people should avoid, but that’s a digression which should probably become its own post

As a huge proponent of the repository pattern, I'll be looking forward to this post.

Good to see it using port 8000 as default, and not Flasks 5000 (does not work on Mac anymore)
It's a python web framework, for those curious to know more before clicking through.
Pretty cool post! I'm not sure how I feel about SQLAlchemy (not the star of the post but mentioned quite a bit); it's such a big ball of state that has so many surprises, I wonder if some people build entirely without it.
I think the way to go with SQLAlchemy is to use the models and alembic for migrations and schema definition but to write the sql and do transaction management by hand. Losing time to figure out how a query you know how to write can be constructed within the ORM is just too much imo.
Thanks for writing this. I have similar gripes about FastAPI having developed an application over the past few years; I'm also continually surprised at how prevalent the attitude is that FastAPI has excellent docs, given how divorced the tutorial / toy examples in the docs are from real-world development and measurement of an API.
I've been using Litestar for over a year now, serving both JSON and templated HTML. Great all-around Python async framework that manages to be fast (faster than FastAPI), lightweight, and still has enough batteries included to host a website with auth, sessions, etc. I'm a fan of first-class msgspec support and the Controller class for nested routing.

Highly recommend.

love litestar.. working on migrating a couple of internal consoles to it from fastAPI.

the docs could use some love though.

i feel most of it is references [1], the "how to"s could be better.

inb4, "where pull request", i don't grok asgi or the framework nuances to be able to say how to improve on it.

[1] https://diataxis.fr/

Thank you for writing this, I've been building a large backend with FastAPI for the last year or so and I've gone through all the levels of the purgatory.

I began using the standard "tutorial" style and started cringing when I saw the official template [1] place all CRUD operations in a single file (I've been doing Rails and Spring for a while before) and the way dependencies where managed... let's just say I wasn't feeling very comfortable.

Then came the SQLModel problems. The author pushes it very hard in the FastAPI docs (which imho are terrible because when I'm looking for docs I want that, documentation, not a fancy tutorial) but as an ORM (yes I know its a layer on top of SQLAlchemy) it doesn't even support polymorphic models and the community even has contributed PRs that have gone months without any review (is it even maintained anymore? I honestly can't tell).

I guess I'm the only one to blame for choosing FastAPI to build a big project but after having used it quite a lot (and also read its code because again, docs are extremely poor) I wouldn't recommend it for anything serious. Sure, if you want to build a quick CRUD then go ahead and use SQLModel and FastAPI, but keep in mind that its not built for complex applications (at least not without requiring you to write a framework on top, like I've unfortunately done).

So yeah, a big thank you to the author of this post because I will migrate to Litestar as soon as I wake up tomorrow.

I love Starlette but not a fan of FastAPI and do not use it.

I read this article but didn’t really get the sense there was anything sufficiently compelling to switch from Starlette.

I know fastapi gets the hype, but I have found plain starlette quite usable by itself. Sure, it doesn't have the whole kitchen sink, but if you just need something small and simple then it fits the bill. In comparison Litestar seems closer to fastapi/django in scope
How does this compare to Django? I see you have quite a bit of Django content. How would you decide to use Litestar vs Django on a new greenfield project?
Litestar looks promising because it has most of the features a Django developer would but in a more modern package. However, since it has so many features, I hope that the project doesn't get abandoned like so many frameworks these days because unlike tiny frameworks, it would be harder to migrate off of.
I think Litestar is superb for building API backends. Love it; use it; only good things to say. Their Advanced Alchemy is coming along nicely, too.

Litestar of course supports old-school server-template-rendered sites, too; it even has a plugin for HTMX requests and responses. In practice, I find that the patterns that serve API endpoints so well sometimes get in the way of old-school "validate form and redirect, or re-render with errors" endpoints. In particular, Litestar has no "true" form support of its own; validation is really intended to flag inbound schema mismatch on API calls, not flag multiple individual error fields. Using `@post("/route", exception_handlers={...})` is pretty awkward for these endpoints. I'd be excited to see some better tools/DX in-the-box here.

When I look at a litestar all it feels a lot more planned out and patterned. I wish I was better at async.

I’ve recently converted to Golang, but I’d love to come back and do a litestar app in the future.

Nice write-up! I've heard about Litestar now and then, haven't tried it, maybe I should give it a go. I've been using FastAPI quite heavily for the past several years.

I think OP's arguments about FastAPI being hard to work with in a bigger codebase are exaggerated. Splitting up the routes into multiple files, each with its own route object, and then importing and building up a big hierarchy of route objects, isn't that hard, it does the job for me. Agreed that it's probably not well documented enough, how to structure a larger FastAPI codebase - but follow a mix of best practices and your personal tastes, break it up into modules, split it into specific files for constants / errors / routes / schemas / crud / etc, and you can scale up sanely.

I haven't used SQLAlchemy with FastAPI - for my day job I mainly connect to data stores for which it doesn't make sense - so maybe I'm biased, because I've avoided that pain.

Litestar is really underrated but deserves much more usage! I’ve been meaning to try it for at least a year now, but always felt a bit scary to tell the team “hey let’s deviate from our stack”
Connexion is also worth a look IMO. It uses spec-first development (a major benefit in larger orgs and for public APIs), and can plug into different server frameworks.

(I used to be a maintainer, but it has been years since I worked on it).

> So if you’re going to be writing a database-backed web application in Python, and you’re not doing Django, you are almost certainly going to be using SQLAlchemy.

I've preferred the Django ORM over SQLAlchemy, but I'm curious what others feel. I've gone so far as to use Django ORM for non-web projects as well. It takes a bit of work to extract though. If Django ORM had a better stand-alone story, I think more people would use it.

SQLAlchemy is just a totally different beast to Django, it has a much higher learning curve but gives you so much more power and flexibility. It's a true data mapper ORM rather than the sad Active Record pattern which starts off well and quickly gets annoying.
Same here. Django's was my first ORM, and at the time I didn't get all the hate directed at ORMs (except for making inefficient queries). Having used a few more ORMs since then, I still consider Django's to have found the best balance.

I find the prevailing model of having DB rows mapped 1:1 to objects in memory and syncing changes automatically to be much more trouble than it's worth, sadly most ORMs seem to use it.

I'm not a fan of SQLAlchemy because of the Data Mapper pattern, and abstractions that force you to still think in terms of SQL tables and expressions.

For example, in Django I can have a User object. I want a update the user's first name:

my_user.first_name = "Joe"

my_user.save()

In SQLALchemy:

my_user.first_name = "Joe"

session.add(my_user)

session.commit()

Users can leave comments, so I want a query that aggregates comments for each user. In Django:

users = User.objects.all().prefetch_related("comments").annotate(comment_count=Count("comments"))

Each user will now have a `comment_count` property that contains the number of comments they left.

In SQLAlchemy:

session.query(User, func.count(Comment.id).label("comment_count")) .outerjoin(User.comments) .group_by(User.id) .all()

However, each User won't have the `comment_count` property. You have to manually associate them from the returned tuple.

I feel like SQLAlchemy wants to force you to do more work, whereas the Django ORM wants to provide you with the data you asked without forcing to you think about how to actually get the data from the database nor how to optimize the query. In Django, session management is done automatically, but it SQLAlchemy, you need to be aware of the session most times.

It's good to know SQL but you can use the Django ORM without knowing it. Not the same with SQLAlchemy. Could be a pro or a con depending on the situation. Definitely a pro for me because I don't like SQL.

> I've preferred the Django ORM over SQLAlchemy, but I'm curious what others feel.

Same here... better API IMO, just the right amount of abstraction (ActiveRecord-like has been fine for all the projects I was involved in) and plenty of escape hatches when needed.

Thank you for writing this - there is a very clear split you feel when using fastapi for single script web servers, vs trying to organize it. And I probably share all the mentioned annoyances around writing bigger projects with fastapi.
When will people understand that those very opinionated frameworks with enticing tutorials just ruin your life in the long term?

I see someone citing Spring(yikes) elsewhere, that falls in the same category as FastAPI. You don't need Spring most of the times, a simple dependency injection library and small frameworks to handle the web routing or specific features you need are often enough (recent contenders to the throne of default app framework have the same issues).

Am I the only one who prefers to just have separate models for API and database right from the start? I know it looks not DRY, but it is. Your API and your database schema are not the same thing. It's never that long before you need them to be separate so why not do it right from the start? I feel like this might actually be a win for LLMs because you won't feel the pain of adding a new field to both the db model and API model in trivial cases.

Litestar does look great and a true web framework like Flask and Starlette. Stuff like FastAPI and SQLModel is a joke imo. Developers should be able to compose these things themselves if they want to.

Agreed 100% FastAPI works but building complex applications in it is just not great. Taking a step back (and this will date me but..) I'm still astonished how the "Python microframework world" is slowly rediscovering everything JavaEE had 15 years ago. Anyhow, this looks nice. Now tell me how to handle error cases during streaming.. >.<
I agree that FastAPI is not the best but I would not jump to another framework just because of an article.

For FastAPI I know that I can go to [1] and see what a good FastAPI code base looks like. These days even Airflow is on FastAPI but haven't looked at the codebase.

For me to jump on Litestar, I would like to see a reference codebase to learn best practices. Otherwise its one more framework whose quirks I have to get comfortable with.

1 - https://github.com/polarsource/polar