#3.0 Wishlist

1 messages ยท Page 1 of 1 (latest)

nova barn
high cliff
#

Better Logging Interface

In my experience, the interface does not seem accessible (read: not easy) compared to other features of litestar. It seems to be a wrapper around https://docs.python.org/3/library/logging.config.html#logging-config-dictschema. While stdlib logging is not the greatest, I would be happy with a higher abstraction that makes this easier to use, even if it means losing out on some fine-grained config (in which case I would be ok to fallback to the current interface)

nova barn
#

commit club

pale thorn
#

you passed me, btw

nova barn
high cliff
nova barn
pale thorn
nova barn
cloud nexus
#

For me it would be better error messages. After using it for some times now, I get what the errors are stating but when I started, it was sometimes hard to find the root cause of the issue raised by the framework. I don't have any particular example in mind but I do not know if you had any feedback from other users on that

nova barn
cloud nexus
nova barn
# cloud nexus Yes can be a good idea

I think so too. I understand there is quite a maintenance burden but it would be nice.
We never had anyone else chime in from <@&1084813023044173866>. What do you all think?

umbral jacinth
#

Well, I can just repeat myself and say that I think it's a great idea generally, but we'd have to be aware of the maintenance burden, which is quite relevant here since having this feature not work well would actually be worse than not having it at all IMO

nova barn
#

I almost think if we do it we need tests that verify the wording stays as we expect it (and links are up to date ofc) or something ๐Ÿ˜…

thin inlet
#

Along those lines, perhaps more extensive reporting of where something failed or was improperly configured. I don't have an example at hand, either, but in the past, upgrading package versions would cause a function/route to bomb out based on the return type that was set, for example, but it wouldn't say which function/route was failing. Better reporting would help a lot with debugging.

#

This might be a huge ask, but I'm someone who still uses Pydantic over msgspec/DTOs, and am used to the flexibility it provides, like an example I posted a while back where list[int] | list[str] | list[UUID] fails due to how msgspec handles that scenario. It works as expected (i.e. reading the type logically) when using Pydantic, so it seemed like something that should work, at least in my head. I wonder if there's a way to fall back to Pydantic or something like that? Or, probably better, is there perhaps a way that preferred backend could be brought back through the use of plugins?

scarlet tangle
#

This was pretty much the idea of having a DTO protocol instead of being tightly bound to DTO factory, there could be alternate implementations of DTOs that could be developed by the community. Was removed in my all time favourite PR: https://github.com/litestar-org/litestar/pull/2088

green hullBOT
scarlet tangle
#

Iโ€™ve toyed with the idea of reintroducing the protocol because that same PR shoved a whole bunch of shit onto the public interface of DTOFactory that doesnโ€™t need to be there and makes it harder to iterate on its design. The protocol would let us develop an alternate implementation without breaking support for the current.. Iโ€™m just not sure itโ€™s worth it.

#

Iโ€™m really not in favor of us maintaining a pydantic dto backend again though.

shut fractal
#

was thinkin about allowing dependency injection in middlewares and exception handlers

scarlet tangle
hollow vessel
#

Regarding middlewares, was there a reason the current API was written the way it was? I think it'd be really nice to have something like Sanic where you get a Request and/or Response objects based on how it's attached. I know we have something similar with the lifecycle hooks, but the after_requestand after_response feels a bit unergonomic to me since they don't include the Request and Response respectively.

hollow vessel
nova barn
#

after_requestand after_response feels a bit unergonomic to me since they don't include the Request and Response respectively.

Oh i didnt know this, ive not had to tap into these... that feels weird

hollow vessel
umbral jacinth
#

It's basically a remnant from the Starlette days

#

We removed Starlette's middleware abstraction, and just kept ours as simple as possible

#

There's definitely upsides to this IMO

#

The current way they are implemented is the most "powerful", as they are simply ASGI apps, meaning you can basically do anything you want, albeit sometimes a bit convoluted

#

I think for 3.0, cleaning up the lifecycle hooks would be a good thing

#

In theory I think they should be able to do what you're looking to do if we implement them correctly

#

i.e. just having a "request -> request" > "response > response" pipeline

#

There's definitely value in having plain ASGI middlewares though, as it allows for great compatibility

#

If you have a plain ASGI middleware, it will work on any ASGI framework, since it's all just ASGI apps

hollow vessel
pale thorn
#

I donโ€™t know if this makes sense or if thereโ€™s anything that can be done about this, but some sort of protobuf integration would be great. It could be in the form of a msgpec to proto generator or anything in between.

umbral jacinth
#

Uuh

pale thorn
#

LOL

umbral jacinth
#

Didn't you share a library like this to me a while ago?! ๐Ÿ˜„

pale thorn
#

In thought I might get that.

#

Yes I did

umbral jacinth
#

Ha!

pale thorn
#

Dataclass to proto, I think it was. Or something similar.

umbral jacinth
#

Haven't really used it but it looked interesting. Can't that just be used with Litestar already?

pale thorn
umbral jacinth
#

Thinking what a protobuf integration would look like. Since it's necessarily aot compiled, we can't really do stuff like with the DTOs

pale thorn
#

It probably could. I honestly havenโ€™t spent too much time thinking through it

#

I just see lots of protos on a daily basis.

#

And it seems like there has to be a way to bridge the gap

umbral jacinth
#

Well if you have any concrete ideas let me know?

#

Would definitely be interested in that

pale thorn
#

Yeah, Iโ€™m going to keep thinking. I just wanted to mention it here in case there were better ideas

#

Another one that's on my wish list. Zod schema generation. It would be nice to extend our typescript generator to also create Zod schemas for form validations. It should be relatively straightforward to convert jsonschema to zod

nova barn
#

Zod sounds like a bad guy.

pale thorn
#

isn't it the bad guy in power rangers?

nova barn
#

Thought that was Zordon

#

not that i know or anything..

pale thorn
#

yeah, i think that's right

#

sure...

#

that's what they all say

nova barn
#

Zod is a villain in DC comics i think

blazing mulch
nova barn
blazing mulch
#

I asked a question in "General" recently about dynamic selection of DTO so that e.g. one can filter fields from models dynamically. A full solution to that, including proper integration with the automatic OpenAPI schema generation, would still be top of my wishlist.

umbral jacinth
blazing mulch
# umbral jacinth I don't think that's possible? To generate an OpenAPI schema, the fields need to...

Well, that's the challenge... The OpenAPI spec certainly allows one to say that an endpoint returns a number of alternative types which might eg just be alternative subsets of fields from a base model. What I'm looking for is dynamic selection of those types. For myself, I consider this an essential feature - without it, nothing really qualifies as a real API server, since the need to vary return type is essential for security (the only alternative would be to have separate endpoints for e.g. PublicProfile and PrivateProfile which is an anti-pattern). I don't wish my comments to come across negatively -Django REST framework doesn't have this built-in either but I've been able to write a plugin for it that can at least filter the fields dynamically. (I haven't yet started on integrating this with DRF OpenAPI schema generation but I'm hopeful it can be made to work).

I'm only just learning Litestar so perhaps DTO is the the wrong place to achieve what I want l. But fundamentally, it should be possible to specify that an endpoint returns one of a number of pre-defined types and then dynamically choose the return type. The OpenAPI spec can then be produced from the pre-defined list of alternative types. Please do consider it further - I'm happy to answer any more questions.

umbral jacinth
#

Okay so what you're asking is not to dynamically alter the DTOs, but to specify multiple DTOs that can be returned?

blazing mulch
#

Well, I prefer to specify my requirement in an implementation-agnostic way. For my use case (which I think would be pretty common), I need a way of saying that an endpoint can return one of a number of pre-defined alternative types ie When I code an endpoint, I'll state which alternative types are possible, so they are part of the definition of the endpoint. And then I need to be able to dynamically determine which type will be returned. For example, I might have a UserProfile model which contains name and email address. For some users/requests, I want to return just the name. For others I want to return name and email address. We can think of this as returning either a PublicProfile (just name field) or FullProfile (name and email). The alternative return types are pre-defined, so the OpenAPI schema generation can in principle know what return types are possible.

Now, how you implement this in Litestar is a question I'll leave to you. Might be DTOs. Might be something different in the way that one defines endpoints. Heck, maybe this is already possible...!

umbral jacinth
#

Hm

#

I'm not sure if I fully understand your ask then, or if this is just an issue of lacking documentation, but it seems to me what you're describing is already supported?

#

You can easily define a union of types that you return, which will be correctly represented in the OpenAPI schema

#
from dataclasses import dataclass

from litestar import get


@dataclass
class Foo:
    id: str


@dataclass
class Bar:
    name: str


@get()
async def handler(m: str) -> Foo | Bar:
    return Foo(id="1") if m == "foo" else Bar(name="something")
#

I was thinking about DTOs here because with DTOs, you can't currently define multiple DTOs and dynamically select one of them at runtime

#

I haven't given it a lot of thought but I think that should also be possible. @scarlet tangle wdyt?

north lark
#
  1. Extensions with various hooks for routes, controllers, etc. Like it's done in Strawberry GraphQL https://strawberry.rocks/docs/guides/custom-extensions#custom-extensions
  2. Better DI mechanism with more lifecycle management. Or even better make it framework aware like it's done in BlackSheep https://github.com/Neoteroi/rodi
GitHub

Implementation of dependency injection for Python 3 - GitHub - Neoteroi/rodi: Implementation of dependency injection for Python 3

Strawberry is Python library to build GraphQL APIs, built on top of dataclasses

umbral jacinth
#

The DI is actually something we'd like to do. We just talked about this in yesterday's office hours ๐Ÿ™‚

blazing mulch
blazing mulch
umbral jacinth
#

They can also handle stuff like renaming fields

#

Marking fields for various things in a base model

#

Or handling cases like defining a schema from some other mapping like a SQLAlchemy model

north lark
umbral jacinth
#

Basically, there's nothing DTOs do you couldn't do without them, they just make a lot of things way more convenient

blazing mulch
umbral jacinth
#

This is a good example IMO of such a convenience

pale thorn
umbral jacinth
#

Yeah, I also think being able to do a union return with DTOs would be a great feature to have ๐Ÿ‘

#

Would require some substantial changes though

#

As the whole concept of DTOs is that it separates the data returned from the handler from the data being rendered and sent in the response

pale thorn
#

Would a partial flag on the DTO work?

umbral jacinth
#

You'd still have to somehow tell the response which DTO to use

#

Because currently the idea of DTOs is that you can just return the data unchanged

#

Now it would need some wrapper

#

Hm

#

Now that I think of it, this sounds quite useless if we implement it like that ๐Ÿ‘€

#

If you need to return different things.. You might as well return the things you want directly ๐Ÿ˜„

#

No need to get the DTO involved

blazing mulch
# pale thorn Well, I can think of at least a use case for something like what you've suggeste...

Let me try to explain my use case a little differently. Suppose I have a datebase model which has been defined with a set of fields. In different contexts, I want the endpoint associated with that model to be able to return different subsets of those fields. We can think of each alternative subset of fields as being defined by an alternative type (like my FullProfile/ PublicProfile example).

I would think this sort of use case would be absolutely standard and very common. The main reason for wanting it (in my view at least) is security - with different fields requiring different levels of permission. (Indeed, a completely different approach to this, but which could achieve the same thing, would be to implement field-level authorisation).

I would think this use case is absolutely standard. My reason for thinking so is that the only workarounds involve some seriously bad anti-patterns like splitting a single endpoint into multiple similar endpoints.

umbral jacinth
#

Or am I missing something? ๐Ÿ‘€

#

I think my issue of understanding isn't what you want the end result to be, but how you'd like to get there.
Wanting to return different things from your handlers and having those options be accurately represented in the OpenAPI schema is something I would also absolutely classify as a standard use case, which is why that's already supported

blazing mulch
# umbral jacinth I think my issue of understanding isn't what you want the end result to be, but ...

I was mainly replying to @pale thorn in case he was thinking of something else (the partial update feature from InertialJS seems to be a different sort of use case).

For @umbral jacinth, yes, your previous code was on the right track. I'd need to work out how to "cast" from one type (the full model returned from the database) to a partial model, but I'm sure there's some way of doing that, I just haven't played with pydantic/sqlalchemy enough yet.

umbral jacinth
#

I'd need to work out how to "cast" from one type (the full model returned from the database) to a partial model, but I'm sure there's some way of doing that
This is exactly what DTOs are meant to do

#

And we have one for SQLAlchemy ๐Ÿ™‚

blazing mulch
# umbral jacinth And we have one for SQLAlchemy ๐Ÿ™‚

Sorry to drag this conversation on, but now I'm less sure what you're recommending. Your previous code suggestion didn't involve DTOs and (if I understood correctly) couldn't work with DTOs because one can't (yet) dynamically choose which fields the DTO would exclude. Apologies again if I'm just misunderstanding sth - happy to continue this conversation elsewhere if you'd prefer.

Just to add, I've given a simple example so far that involved choosing between just two return types - a FullProfile and a PublicProfile. But it could easily occur that one needs to choose between more than two return types. A practical example might be where data within an organisation is categorised as either public, internal or confidential.

digital rune
nova barn
#

Wishlist Item

Do not tie the run commands to just uvicorn

pale thorn
nova barn
#

do you know where? word doc? I can add it to the project thingy

pale thorn
#

I think we just talked about doing this. Not sure if it made it to a trackable item yet

#

you can get to a similar outcome now by doing what I did in the Granian plugin

scarlet tangle
#

Hey @blazing mulch - I'm pretty sure I am on the same page as you. We have already discussed this: #general message and the issue I pointed to in GH is probably the best place for extended discussion so that we don't crowd out this channel.

sharp drum
#

I would love it if we could reduce code duplication in class controller method signatures. So, instead of having to repeat service: FooService in all of the handler methods, you could declare service: FooService in the class body or a constructor or something and then access it through self.service.

scarlet tangle
#

that's interesting @terranova!

tardy karma
#

I'd like my handlers to automaticcally have the name of the function they decorate so that I can refer to them in my url_for without having to explicitely name them

scarlet tangle
tardy karma
# scarlet tangle can you show me an example of this <@406022421636841472> ?

let's say in a template I have <a href="{{ url_for("login_get_name") }}"></a> , Currently with litestar I have to explicitely use the name kwarg in the decorator like:

@get("/login", name="login_get_name")
async def login_get_func(
    request: Request
) -> Template:

I'd like it to be similar to other frameworks ie if I do:

@get("/login")
async def login_get_func(
    request: Request
) -> Template:

then I can use url_for("login_get_func")

#

in essence the get decorator could by default get a name equal to the handler func name

scarlet tangle
#

do you know if its been discussed before in an issue or anything?

tardy karma
#

I know I mentionned it here when I started migrating some apps, but I'm not sure about a GH issue

scarlet tangle
#

on the surface it feels like it should be pretty easy

tardy karma
#

it was probably to me one of the most annoying thing to have to add on every handler when I switched

scarlet tangle
#

I bet

#

in BaseRouteHandler.__call__() we get the function and can set name if it is not None, otherwise leave it alone

tardy karma
#

not sure if it has to do with controllers and what in this case the name would look like, Controller.function ?

scarlet tangle
#

we could do that I think - what about for routes registered on a router also - assume the name needs to be unique, but no such constraint for function names so we'd probably have to wait until the app startup and assign the names once handlers are registered and their ownership chain is known

tardy karma
#

also while I think about it, I'd like url_for and url_for_static_asset to be just url_for if that's possible

scarlet tangle
#

no idea - we could definitely explore this - so at the moment, if a handler is not named, url_for et al just don't work?

tardy karma
#

or the template crashes cant remember

scarlet tangle
#

then maybe this wouldn't have to wait for 3.0 - we could do it as a feature on a minor bump?

#

want to open a feature request for this as there's probably some details that we need to thrash out without hijacking this thread?

pale thorn
umbral jacinth
#

I guess that depends on the feature

scarlet tangle
#

anyone mentioned simplifying logging in here yet?

nova barn
#

not here (afaicr) but its in some doc somewhere

scarlet tangle
#

OK, I'd like to add "simplified logging" ๐Ÿ™‚

scarlet tangle
#

thanks:) I didn't know that project existed!

umbral jacinth
#

I've been thinking about the simplified logging

#

iirc, the only reason we do the complex logging setup is so we can ensure we have an async-friendly logging handler

#

Because other than that, it's basically just a copy of the stdlib logging config

#

How about we don't do that, and instead just give the user the option to pass in a logger?
We can recommend some that are async-friendly (structlog for example has async support built in), but ultimately just let the user handle this

#

If we don't get a logger passed in but we need one (e.g. for exception logging), we just use the standard getLogger("litestar"), and that's it

#

Do we need anything more?

#

We could then split out the "logging config" to just be "what does litestar log" and not also simultaneously configure "how does litestar log"

#

I feel like that would make things already much easier for the standard use case

scarlet tangle
#

Yeh, I pretty much agree. My opinion is that if we need to log something, we emit a standard lib log message and every logging library in the ecosystem has a way to ingest that. We can document patterns to configure logging using different libraries.

#

what would we do with the provided logger?

umbral jacinth
#

Idk, just the same things we do with the configured loggers now?

#

So pretty much just provide an interface to it via the request/app objects for convenience and use it for request/exception logging if configured

scarlet tangle
#

but can't we just emit std lib log messages and let the log libs scoop them up?

umbral jacinth
#

Ah

#

Now I get it

#

Yeah, that would work as well I think

#

The only thing I could think of as a potential downside to this is that we couldn't make use of e.g. structlogs async logging (because that actually provides async methods)

scarlet tangle
#

yeh - there's that - however even in the structlog docs they say this:

#

Be conscious about whether and how you use structlogโ€™s asyncio support. While itโ€™s true that moving log processing into separate threads prevents your application from hanging, it also comes with a performance cost.

Decide judiciously whether or not youโ€™re willing to pay that price. If your processor chain has a good and predictable performance without external dependencies (as it should), it might not be worth it.

umbral jacinth
#

Oh

#

Didn't realise that useses threading under the hood ๐Ÿ‘€

#

But yeah, makes sense

scarlet tangle
#

in terms of running in production, we should be emitting very few logs anyway

#

if we are in debug mode, then I could imagine we might want to emit useful logs to help debugging, but that shouldn't be run in production

#

so the vast majority of logs (i think) would be coming from the server access logs, and they'd be logging via stdlib (I think!... lots of thinking going on :D)

umbral jacinth
#

Yeah. Maybe having a section in the docs about potential pitfalls is good enough and we just use stdlib logging and that's it ๐Ÿคท

scarlet tangle
#

it would be nice to start again with a simple foundation - and then let that part of the product evolve according to need/wanted features etc.

umbral jacinth
#

Yup. I think a lot of the bells and whistles we have were built in anticipation for needs that have never manifested

high cliff
drowsy urchin
#

I'd like to see support for multiple authentication backends on the same app instance out of the box
Basically if we can use session cookies for some route handlers, JWT tokens for others, and even both on the same route handler.

cloud nexus
steep helm
#

From my experience, (1) a default encoder/decoder middleware implementation (drop in a RawBytes -> object you want to read function and go); (2) a simple way to generate multi-part ASGI messages for testing purposes

umbral jacinth
#

Could you elaborate the first point?

drowsy urchin
#

I'll shoot for the stars and ask for the ability to pass arguments to the retrieve_user_handler function at the route handler level and upwards
This would allow us to modify the query to load/defer relationships on the User object where required, etc

#

Maybe we can already do this with opt args? I'll check

#

Uh, wish granted, thanks everyone!
connection.route_handler.opt is accessible within the user handler

nimble cipher
#

A better documentation, I started a new project about 2 months ago and wanted to try litestar instead of FastAPI.
I found the documentation messy and frustrating to read so i gave up and went back to FastAPI.

#

This what made flask, FastAPI etc... so appealing, the examples and documentation is amazing.

hollow vessel
nimble cipher
#

Currently there is tutorials on basic todo app (endpoints), DTO (only dataclass) and sqlalchemy integration.

The usage section is overloaded with information, I would suggest to split it into two sections (basic tutorials and advance tutorials).

hollow vessel
pale thorn
#

it would be great to have an integrated request correlation ID. This would generate a unique UUID for each request. This could be used for structured logging, passing to backend workers, use for telemetry, etc...

quartz elbow
#

Just to back up the docs thing: I also often get the sense that there is just enough info available to do what I want, but it's not in a form I quite can translate to my particular need. I like the four forms of documentation (https://documentation.divio.com/) thing for this.

#

E.g. I think I wanted to know how to do something slightly complicated with mapping a SQLAlchemy model to a response format, and my problem lay somewhere in between Litestar, SQLAlchemy, and Pydantic docs, when I think a couple more examples, and/or a slightly more in-depth explanation of the moving parts, would've meant I solved it entirely within Litestar docs

#

Having said that, I don't think this is particularly a v3 thing unless you want to make it one; it's just something I've also grappled with a bit.

umbral jacinth
#

I'm trying to tackle this whenever I find the time, but writing good documentation is unfortunately not a trivial thing as it turns out and, for me personally, it requires more focus time and dedication than chipping away at some bugfixes whenever I find the time

#

We also have a lot of open issues regarding the docs, so if you're looking for anything in particular, please feel free to just open more. Any feedback there helps

quiet drum
#

didnt read through the rest of the posts :/ the wall of text frightened me..

my wishlist would be:

  • turn the main framework into class abstractions (i dont think thats the right word) like
app.set_router_handler = MyCustomRouteHandler
app.set_route_handler = MyCustomRouteHandler 
...

and use protocol for the handlers. try move pretty much "everything" to this approach. have some "supplied" handlers that a dev can extend on if they wish but the set_xx_handler uses protocol to define it. benifit here is that you can extend the framework amazingly this way and future framework core stuff can be added easily. "do 1 thing and do it well"

  • add extra functionality / not "core" related things to plugins. like the openapi.json rendering / displaying. can add some default plugins for like plugin_swagger, plugin_redoc
  • have an object for each route. and have a seperate hand over routers to it for it to generate the openapi spec stuff. app.set_openapi_generator = MyCustomOpenApiHandler - my use case here (and for the set_handler stuff) is to potentialy have a custom route handler that can accept something like "permissions=['a','b','c']" and since this doesnt impact the normal use of the route stuff for the framework (protocol) i can then use those for my custom openapi handler.
  • dependencies as objects and not dicts. dependencies useable "everywhere" if at all possible.

ive had to go back to fastapi. developing on it just goes much quicker for me ๐Ÿ˜ฆ but i still โค๏ธ litestar.

  • steal some ideas from fastapi. i hit a few pain points that fastapi just sorted out for me in the background. like being able to define form = dataclass and it populates that dataclass with form values. AFAIK litestar you need to do a few things for something like that to work (anotated form type to form-encode). (i honestly cant remember what the other issues were now :()
umbral jacinth
# quiet drum didnt read through the rest of the posts :/ the wall of text frightened me.. my...

turn the main framework into class abstractions (i dont think thats the right word) like
app.set_router_handler = MyCustomRouteHandler
app.set_route_handler = MyCustomRouteHandler
...

and use protocol for the handlers. try move pretty much "everything" to this approach. have some "supplied" handlers that a dev can extend on if they wish but the set_xx_handler uses protocol to define it. benifit here is that you can extend the framework amazingly this way and future framework core stuff can be added easily. "do 1 thing and do it well"

Maybe I'm misunderstanding something, but because of how Litestar is designed, you don't need this

#

You can use your own route handler classes if you like without the need to register them anyhwere, since they're not tied to anything to begin with

#

Since you just pass the handlers to your app/router/controller, you're free to use any subclass you like

#

This holds true for almost everything

#

Since you register routers and controllers in the same way, you can use custom classes there as well

quiet drum
#

afaik you cant define somehting that takes extra params for the get() and then sue them elsewhere. the backend just adds those as openapi_extra dict?

#

try do something like hide endpoints based on permissions the user has. after much hacking i got it right in the end but fudge. not something the framework makes easy

#

(this is just 1 of the things i had issues with purely as an eg)

umbral jacinth
#

You'd have to implement logic to handle this in both cases

#

But of course you can. If you subclass e.g. get to take out the kwargs you want, and then only pass the rest onto the base class

quiet drum
#

true :/

umbral jacinth
#
class my_get(get):
  def __init__(*args, my_arg, **kwargs):
    self.my_arg = my_arg
    super().__init__(*args, **kwargs)

@my_get("/", my_arg=True):
def handler() -> None:
  ...
#

Is all you need

#

I'd like to think that this is actually way more elegant than having to register a class on a god object somewhere ๐Ÿ˜‰

quiet drum
umbral jacinth
#

add extra functionality / not "core" related things to plugins. like the openapi.json rendering / displaying. can add some default plugins for like plugin_swagger, plugin_redoc
100% agree with you and we're actually going that route already. Lots of stuff has been moved to plugins and more will come ๐Ÿ™‚

#

have an object for each route. and have a seperate hand over routers to it for it to generate the openapi spec stuff
Can you elaborate on this?

#

i hit a few pain points that fastapi just sorted out for me in the background. like being able to define form = dataclass and it populates that dataclass with form values. AFAIK litestar you need to do a few things for something like that to work (anotated form type to form-encode).
True, I think we can do better there

#

(i honestly cant remember what the other issues were now :
Well if you do remember them, that'd be really valuable feedback and much appreciated!

quiet drum
#

Can you elaborate on this? <-- the openapi stuff is "built" (as far as i know anyways) when the routes are added to the routers - as in its baked into the core of it. not a case of handing routes to a "renderer" if this makes sense? try add in x-examples to a routes openapi (with something defined per route without making a ton of operation classes) ๐Ÿ˜› see the pain

#

i kinda think it would be super hard to not do it as a baked in tho

#

the api json spec is quite a bit larger on litestar than say fastapi as well

tardy karma
#

i hit a few pain points that fastapi just sorted out for me in the background. like being able to define form = dataclass and it populates that dataclass with form values. AFAIK litestar you need to do a few things for something like that to work (anotated form type to form-encode).
I hit the same, from having a handler with async def login(username: Annotated[str, Form()], password: Annotated[str, Form()]): to having in litestar something like data: Annotated[User, Body(media_type=RequestEncodingType.URL_ENCODED)], and having to define User, but retrospectively I prefer litestar approach, the fastapi one leads to sublte bugs like having the wonrg form encoding in templates if not very careful, at least with this I get a config error from litestar if it's not setup properly, in Fastapi the defaults are obsucre and let you do dumb things

green hullBOT
quiet drum
#

โค๏ธ

umbral jacinth
rich knot
#

What about being able to completely bypass msgspec for parsing and serialization and passing it to some other tool?
For context during the discussions that lead to this PR: https://github.com/litestar-org/litestar/pull/2491
There were some issues with exchanging types between msgspec and Pydantic.
Pydantic itself can handle parsing and serialization so maybe using either DTOs or plugins or something else, it would be possible to supply own parser/serializer (that complies with a Prototype maybe) and leave out the msgspec validation?

green hullBOT
#

Pull Request Checklist

  • โœ… New code has 100% test coverage
  • โœ… (If applicable) The prose documentation has been updated to reflect the changes introduced by this PR
  • โœ… (If applicable) The reference documentation has been updated to reflect the changes introduced by this PR
  • โœ… Pre-Commit Checks were ran and passed
  • โœ… Tests were ran and passed

Description

  • downtype Pydantic types which cannot be instantiated (e.g. EmailStr)

Close Issue(s)

umbral jacinth
#

I'm not going to say this will never happen, but I think it's very unlikely

#

Simply because msgspec is engrained into the core of Litestar, and this is by design

#

Supporting generic validation backends would be an extremely complex undertaking if we still want to make use of all the features, and the added benefit is, honestly, quite small

#

Yes, msgspec doesn't cover all use cases. But it covers most of them good enough

#

In general, while we do try to support a lot of different use cases and tooling, Litestar's goal is explicitly not to support everything there is

#

And just speaking from a very pragmatic point of view, once you start supporting one additional validation backend, there's no good reason to not support a third and a fourth etc.

#

The switch from Pydantic to msgspec was a careful and deliberate decision, and we believed that msgspec could support everything we needed, and I still do

#

The core issue here is simply something that already presents itself right now, but we mostly just hand off to the user: To handle error cases and conversion results meaningfully, Litestar needs to know how these tools behave

#

Of course you can build an abstraction, but those are either leaky, or support only a common subset among the tools they wrap, so you wouldn't really benefit from them

#

Personally, I think the DTOs + some further optimisation and extension of their capabilities are the way forward here

#

That being said, the "bypassing validation/parsing" is something we could explore a bit more, although the uses are limited without deep integration into Litestar. You can already just get the raw data and do with that what you want, and everything else is only really useful if Litestar knows about the structure that's expected, so again, I think DTOs are the best solution there

#

Regarding the "compiled to protobuf" (I think that's what you meant?): This is actually something very interesting I already experimented again and definitely want to revisit in the future. Although I have to say that, at least from a performance point of view, it's going to be extremely tough to beat msgspec in Python, because it's just that fast

pale thorn
#

for instance, you can provide a protobuf to spanner and bigquery as your record response

#

there's betterproto that lets you do something like this:

from dataclasses import dataclass

import betterproto


@dataclass
class Greeting(betterproto.Message):
    """Greeting represents a message you can tell a user."""

    message: str = betterproto.string_field(1)
umbral jacinth
#

It's not even that far fetched

#

We already have a DTO compiler

#

It just compiles to Python ๐Ÿ˜„

#

Oh yeah, that's something I forgot to mention: Since we already have the DTO codegen thing, a next logical step for that would be to compile that to native code, which should be more performant. Sort of a.. DTO jit.

#

I did some very basic experiments with this a while ago (@pale thorn you'll remember) and while it did give a performance improvement, it was still nowhere near the speed of msgspec

#

I guess the truth is that it's simply very hard to outperform an expertly crafted C library as long as you're still targeting CPython in the end

cloud nexus
#

Hello,

I thought a bit about some things but I do not know if they are relevant:

  1. Have an endpoint to extract current configuration of the litestar app (also can expose configuration of plugins in a standard way). The point in that is to be able to query the app in a secure way to perform checks about the current configuration.
  2. I do not know if we have to use the stores for that but have a simple way to integrate s3 storage or google cloud storage or azure (or if we need plugins for that)
umbral jacinth
#

I do not know if we have to use the stores for that but have a simple way to integrate s3 storage or google cloud storage or azure (or if we need plugins for that)
Depending on what you want to do with files from there, this is already built in

#

E.g. you can serve files from s3/azure blob/gcs with the File response

cloud nexus
#

yeah just regular storage of files

blissful fern
#

When it comes to security could you add in 'include' to JWTAuth?
Currently you have
jwt_auth = JWTAuth[User]( retrieve_user_handler=retrieve_user_handler, token_secret=environ.get("JWT_SECRET", "abcd123"), # we are specifying which endpoints should be excluded from authentication. In this case the login endpoint # and our openAPI docs. exclude=["/login", "/schema"], )
I would like to have something like:
jwt_auth = JWTAuth[User]( retrieve_user_handler=retrieve_user_handler, token_secret=environ.get("JWT_SECRET", "abcd123"), # we are specifying which endpoints should be authentication. include=["/secret", "/admin"], )

high cliff
high cliff
pale thorn
blissful fern
# pale thorn And it excludes everything not under those paths by default?

Yes, for example:
include = '/admin'
would include:

  • /admin
  • /admin/members
  • /admin/section1
  • /admin/section2
    but would NOT include:
  • /public/
  • /events/
  • /people/
    I think that this would help those projects which use Litestar but think of security after the fact.
    Not every project is planned before work begins, or a project where down the road some new routes are added and need to secured.
    I was thinking that this could act like the exclude, but instead of excluding the route it includes the route.
    Just a thought
umbral jacinth
#

You can already do this. exclude takes a regex, so all you need is a regex that matches everything but admin

#

(not the best solution semantically, but it should get the job done ๐Ÿ™‚)

blissful fern
#

That is/was not apparent to me, and for future maintainers of the code this could be easy to overlook

pale thorn
#

We can add the include now, I would think. That is a non-breaking addition.

umbral jacinth
#

It just adds a bit more complexity. Since we're allowing regexes we'll have to somehow check those don't overlap

umbral jacinth
blissful fern
#

Adding the information to the docs would be helpful, and maybe that's add that is needed

high cliff
#

Add support for kwargs in Provide so I can avoid writing closures.
eg: Provide(get_database_engine, conn_string="sqlite://dbone.sqlite") should translate to get_database_engine(conn_string='sqlite://dbone.sqlite')

refer: #1196314005485142036 message for a real world ish usecase and some disucssion with @hollow vessel

blissful fern
#

Maybe this is just wrong for Litestar,
A background daemon which can used to execute scheduled API calls.
In theory the web server is running.

pale thorn
#

when you have the USE_SERVER_LIFESPAN flag set to true, it will automatically start the background process when you execute litestar run

#

if you want to roll your own, you want to hook into the server_lifespan context manager to execute your extra code

green hullBOT
#

litestar_vite/plugin.py line 83

def server_lifespan(self, app: Litestar) -> Iterator[None]:
pale thorn
#

If you use the CLIPlugin, it will automatically call the server_lifespan on start

blissful fern
#

Well okay then, I'll be sure to check this out.
Thank you

open stone
umbral jacinth
#

making it as a framework aware like in BlackSheep
Can you elaborate?

#

Will this be made as an actionable item?
I'd say yes. We'll have to sort through all of these items and bake them into some sort of project/milestone/issues

cloud nexus
pale thorn
cloud nexus
umbral jacinth
#

I think a list of all the requests would be good. We could then sort through that, maybe in a meeting? <@&919261960921546815>

shut fractal
#

I would love to see more robust documentation. With many examples and descriptions.

nova barn
ashen garnet
#

#general message

nova barn
#

agree

unkempt briar
unkempt briar
unkempt briar
# umbral jacinth That being said, the "bypassing validation/parsing" is something we could explor...

quick comment on this: the bytes -> msgpec object performs validation since (i think) it uses msgspec.convert (or something similar, don't remember the name of the method since I usually abstract this). the regular class instantiation of msgspec objects doesn't perform any validation (there's an open issue to allow something like validate_on_init in msgspec. So this shouldn't be too difficult, in the sense that, instead of using msgspec.convert, it would be possible to just conver the bytes to a built-in (eg. dict), and instantiate the msgspec object directly (MyStruct(**params))

unkempt briar
#

sharing my wishlist here

  • would love to see injected requests to the handlers. right now i need to manually specificy request: Request on every handler, would like to just be able to use self.request (just like self.my_injected_dependency) on the class level.
  • having the ability to use msgspec to define multiple query params would also be super helpful. for routes that have plenty of filters fields,offset,limit,filter_a,filter_b... the handler is huge an the next step would almost always be to add those parameters into an object to that I can pass it to the service/repo for filtering/manipulation. i imagine this could be implemented with a specicial query_params or params kwarg, which would inspect the annotation, convert the params at the route level, and convert into the msgspec struct when injecting it into the handler.
  • alternative simpler interface for middleware/hooks. i understand the power and flexibility the ASGI logic brings, but it's not the simplest interface to work with - even a simple process-time header takes quite a bit of code to get working. something a bit more high level would be super helpful, since most of the times I imagine endusers will be manipulating the request before it's handled, and the response before it's actually sent.
  • openapi schema respecting the order defined on the controller - i find it makes a huge different in DX to have this. you'd usually handle a specific api resource with some logic like GET / then POST / then GET /:id then PATCH /:id then DELETE /:id or something like that, having it all shuffled in the documentation it's very hard to have a high level view of the resources, specially with heavy routes (2-3 levels of nesting). I think there was a PR to make it deterministic not long ago - but I haven't looked into the current implementation to understand why it doesn't respect the definition ordering.
#

Last but not least, understand this is a bit more "philosophical", but I'd love to see a direction where the "core" is fairly lightweight, and most of the batteries that are currently included, would move to an extensions/plugins logic. few example that comes to mind: stores, channels, databases, metrics. thinking of django vs flask as benchmarks, something like flask + officially supported extensions would be great, and provide great flexibility both for those that want a ligthweight and "simple" framework to work with and those who want more stuff included. anyways, just thinking out loud. i've been having a really nice time once I got to a good setup, so appreciate the work on this either way.

pale thorn
umbral jacinth
#

having the ability to use msgspec to define multiple query params would also be super helpful
You can already do this though. You can define a msgspec.Struct with the query params you want and then use it as a dependency

unkempt briar
hollow vessel
unkempt briar
#

I would personally keep this in the wishlist though, feel that just being able to do def myhandler(self, query_params: MyFilters) makes more sense, since it this not actually a dependency, but rather a parsing feature (following a similar logic as the data: MyStruct or some_param: str.

hollow vessel
#

It should be working with msgspec, pydantic and attrs as well. There was a bug that prevented this, but that was fixed.

unkempt briar
#

got it, it might be my local version then - currently running 2.5.4, will try on 2.6.x

hollow vessel
green hullBOT
#

Summary

Litestar supports query parameters that are validated as Pydantic models. As seen in the code under "Basic Example", validation of query parameters do indeed work as per the constraints set in the Pydantic model. While the route works as intended, the OpenAPI schema generated has no query parameters.

  "paths": {
    "/": {
      "get": {
        "summary": "Foo",
        "operationId": "Foo",
        "responses": {
          "200": {
            "description": "Request fulfilled, document follows",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvenOdd"
                }
              }
            }
          }
        },
        "deprecated": false
      }
    }
  }

Basic Example

from typing import Annotated
from litestar import Litestar, get
from pydantic import BaseModel, Field


class EvenOdd(BaseModel):
    even: Annotated[str, Field(regex=r"^\d*[02468]$")]
    odd: Annotated[str, Field(regex=r"^\d*[13579]$")]


@get()
async def foo(query: EvenOdd) -> EvenOdd:
    return query


app = Litestar([foo])


from litestar.testing import TestClient

with TestClient(app) as test:
    data = {'even': 0, 'odd': 1}
    response = test.get('', params=data)
    # query parameter validation works as expected
    assert response.status_code == 200

    data = {'even': 1, 'odd': 0}
    response = test.get('', params=data)
    # query parameter validation fails as expected
    assert response.status_code == 400
    assert 'Validation failed for GET' in response.json()['detail']

Drawbacks and Impact

Users who are migrating from other frameworks could expect the same behavior.

Unresolved questions

There is no "parameters" present in the schema. Is this intended behavior, or can the schema generation be changed so that "pydantic models as query parameters" work?

Funding

  • If you would like to see an issue prioritized, make a pledge towards it!
  • We receive the pledge once the issue is completed & verified
Labels

Enhancement, Good First Issue, Help Wanted :sos:, OpenAPI

unkempt briar
high cliff
# green hull

this is probably my most popular issue ๐Ÿ™‚ I see this every other month now

unkempt briar
#

ohhhh, so the router does accept a query: MyStruct, the issue is "just" on the doc generation then

#

just read through the issue, is there any guidance for this so far? implementation wise, whether this is actually desired, etc

hollow vessel
umbral jacinth
#

That's the same as

@get()
async def handler(param_1: str, param_2: int | None = None) -> None:
 ...
#

This works because DI works with every callable, not just functions ๐Ÿ™‚

umbral jacinth
# green hull

I'm thinking if we actually need this? Functionally, the same can be achieved via DI now, so it would just be a difference with how it's declared.
Thinking a bit ahead, with the new DI for 3.0, the above example could look something like

@get()
async def handler(params: Inject[Params]) -> None:
 ...
#

IMO that's just as convenient as something like

@get()
async def handler(params: Query[Params]) -> None:
 ...

or the proposed

@get()
async def handler(query: Params) -> None:
 ...
gloomy pond
#

I still like the last one the most. No additional import and it's shorter by an "inch".

umbral jacinth
#

True, but this would also clash with the idea of not having these magic keyword arguments anymore, which is a requirement for being able to freely define the parameter you want to receive automatically injected request data on

hollow vessel
umbral jacinth
#

The DI approach doesn't result in the correct creation of the OpenAPI spec right?
It should! If it doesn't, that's a bug

quartz elbow
#

It would be great to have a way to specify endpoints that can return multiple representations of a resource based on an Accept header. E.g. I think at the moment we set a single media_type on the endpoint, which could instead be a union

#

Also be great to return a File based on BytesIO (or similar) representation, rather than always having to read a file from disk. E.g. to get a nice file representation in OpenAPI, for an API that streams on a file served from an internal API - currently have to buffer it locally, save it to disk, then load it again, instead of just forwarding on a file stream.

pale thorn
#

I also don't think we'd have to wait for 3.0 on this

nova barn
#

Has anyone been updating the project tracking this list? I have sortve lagged behind and i see we've had quite a for more requests ๐Ÿ˜„

#

its okay if they are "able to be done in v2" i think, we can mark them as such in that project, and then we can have good visibility into what was ack'd in this thread

umbral jacinth
#

Nope. Definitely a chore that needs some doing

pale thorn
#

!byte summarize this stuff โ€”NOW

rancid sirenBOT
#
Command Error

Command "byte" is not found

Command

None

Message

!byte summarize this stuff โ€”NOW

Channel

#1182745702942646313

Author

@pale thorn

Guild

Litestar

Location

[Jump](#1182745702942646313 message)

nova barn
#

I'm really interested in how a smaller core library would look. I know that's quite a big ask, though.

umbral jacinth
#

@pale thorn have you been leaking our private chats? ๐Ÿ˜›

#

@nova barn yeah, I've been thinking about this as well. My thought was that we might create something similar to what Starlette is to FastAPI

#

That would also make it easier to e.g. mypyc / selectively rustify that part

#

I don't think it's actually going to be that difficult

#

We already have a relatively clear split between the low-level ASGI stuff and the fluff above it

shut fractal
#

Maybe it doesn't have to wait until version 3... It would be nice with a full html 404 page with links to the various openAPI pages (e.g. /schema, /schema/swagger) so I new users don't have to remember / know what the paths are for them.

#

Then I could just leave "/" without any handlers and as long as the user has the IP they can figure it out.

pale thorn
#

As an example. This is what you see when you browse to the root of the Vite server when youa re using the litestar-vite plugin

honest rampart
green hullBOT
high cliff
honest rampart
#

Another thing I'm missing is a wildcard / catch all route handler.

ex. /api/example/(.*) matching all subroutes.

currently, to my knowledge, It's only possible to define multiple fragments to get the subpath on the same handler:

@get(
        [
            "/",
            "/{path:str}",
            "/{path:str}/{path2:str}"
            ]
    )
ashen garnet
#

Can't try it rn but wouldn't "/api/example/{path:path}" work here?
edit: yes it works if i understand your request correctly

umbral jacinth
honest rampart
high cliff
tardy karma
green hullBOT
tardy karma
#

streams are going to be more and more used imho with all those LLM apps. The nice thing about these is UX-wise you can start displaying content as soon as it is available. Now if you just use a ServerSentEventResponse or Stream and have nothing to do with the data it's working fine as it is, but as soon as you have to "use" the data, to "stream" it in a db for instance, it's becoming tedious (3rd time I research my own issue to solve this....)

#

alternatively I can do a docs PR that emphasizes a little more the mention that already exists or add a note on streams / sse that DI wont be accessible in the generator function they take as argument

pale thorn
honest rampart
#

I think, besides an integration into sqlalchemy, an integration into mongodb would be nice.

cloud nexus
pale thorn
high cliff
#

To anyone who has replied / following this thread, I have applied very minimal due diligence and created items on the 3.0 wishlist

I have responded with emojis acorss the entire thread. For a quick overview use these emojis.

3๏ธโƒฃ - added as an item / left a comment in a related item
โŒ : - not added (reasons usually are responded as replies to message)
โœ… : - already present in some minor version of litestar 2.x or in main. IOW, this is already available / "maintainer / member suggested approach"

If you see more than one of these emojis on the same message, it means it had more than one feature request or was deemed to have more than one and some were accepted and some were not. (reading the following responses should make it clear)

Just to be clear on this, adding something to wishlist does not mean it is a guaranteed 3.0 feature. As mentioned, I did my best to not remove anything from this list, so it must (mostly) be a direct copy paste of things you have mentioned. Maintainers / Members will have the final say on what can and can not be done. It was just becoming huge and this was done to make things manageable.

If you feel I have made an unfair call on your feature request, do feel free to tag me or anyone and we can discuss more. ๐Ÿ˜‡ The list is not immutable (its a wishlist not a wishtuple ๐Ÿ˜… )

high cliff
high cliff
scarlet tangle
#

I've found it quite easy to run apscheduler alongside litestar for running scheduled things

nova barn
#

I canโ€™t wait for v4 @shell prairie ๐Ÿ˜„

scarlet tangle
#

I'm using it ๐Ÿคทโ€โ™‚๏ธ (literally couldn't wait)

pale thorn
scarlet tangle
#

yeah, its 4.0.0a4 if I remember correctly - I'm not using any of the backed stores, just the in-memory stuff, and all working nicely so far

cloud nexus
scarlet tangle
#

However you would just need to start the worker apscheduler outside of the litestar application lifespan if using multiple workers, right? Server lifespan, or otherwise.

cloud nexus
high cliff
scarlet tangle
tardy karma
cloud nexus
tardy karma
# cloud nexus Well their official documentation says it best to use that in some cases and gra...

I cant say I've lot of experience scaling uvicorn so take this with some precaution ofc but 1. event loops and multiprocesss dont mix well, 2. this workers switch was originally a way to try to mimic what wsgi servers were doing with web_concurrency iirc 3. uvicorn's multiprocess as it needs to support many OS is quite rough and weak , I'd use only reload on dev mode and scale differently

pale thorn
#

Server_lifespan is really helpful for this. It lets you start processes independent of the worker lifespan

umbral jacinth
#

uvicorn's multiprocess as it needs to support many OS is quite rough and weak , I'd use only reload on dev mode and scale differently
Can you elaborate on this @tardy karma?
I remember reading in the docs (I think it has been removed since) that you should always use a proc manager (e.g. gunicorn) if you need multiple workers and don't use the built-in ones, but I never really understood why

#

What exactly is it that uvicorn doesn't do / does badly that's an issue here?

#

Fwiw, apps that actually need to scale, I usually run with 1 worker and scale with k8s ๐Ÿคท
Easier to manage resources that way if you have more fine-grained control over the requests/pod

tardy karma
#

it doesnt restart your worker should it die

umbral jacinth
#

Oh. Haha ๐Ÿ˜„

tardy karma
#

using workers is like praying for the process not to die

umbral jacinth
#

Well, that's a good reason

#

Should probably put that in the docs ๐Ÿ˜ฌ

tardy karma
#

should just drop windows support imho, this is iirc the reason why there is no proces management, I'm no multiprocessing pro but once I was looking at that

#

and this is super hard to get right and in fact it's not right,

#

I kind of remember gunicorn at some point had a windows support PR and abandonned it

pale thorn
#

I guess I should investigate how Granian handles this as well.

umbral jacinth
#

The issue with gunicorn + uvicorn worker is that it's a huge performance penalty

umbral jacinth
pale thorn
tardy karma
#

yes forgot about this as well, it's fastapi which "promoted" this uvicorn + gunicorn thing and I've always been very dubious about it, it just adds complexity for no benefits to me

quartz elbow
#

should just drop windows support imho
This seems a bit much - isn't it better to recommend the best setup for linux/windows/macos separately? People might deploy into Linux environments but still have to develop on Windows, so a simple way to run locally might be useful?

cold rain
#

as far as DI framework goes, why not team up with an existing project / team such as https://github.com/Lancetnik/FastDepends who also works on / with https://github.com/airtai/faststream which should likely be kept compatible with Litestar (get on their compatibility list for more exposure)

GitHub

FastDepends - FastAPI Dependency Injection system extracted from FastAPI and cleared of all HTTP logic. Async and sync modes are both supported. - Lancetnik/FastDepends

GitHub

FastStream is a powerful and easy-to-use Python framework for building asynchronous services interacting with event streams such as Apache Kafka, RabbitMQ, NATS and Redis. - airtai/faststream

umbral jacinth
#

I have evaluated a few popular DI libraries so far, and while it absolutely makes sense to use something that already exists, no library I've looked at offers the deep integration we'd need to provide the set of features Litestar currently has, as the DI is tied very closely to Litestar's data validation/parsing pipeline (which is crucial to provide optimal performance)

#

While it can be done with these libraries, the code to make the DI itself work in Litestar is actually fairly minimal, and the integrations would all be more complex than that, so it make sense to stick with our own for now

#

If there's a library out there I've missed that allows for an easier integration on our part, I'm all ears tho

cold rain
umbral jacinth
#

Yes, the plan is to support DI natively in a lot more places, and manually everywhere you have access to the DI provider

#

It's probably the most requested feature for 3.0

swift marsh
#

Does litestar have server lifespan? For starting some sort of things that should be executed once (like apscheduler, cron, etc)

pale thorn
#

you need to use the litestar CLI. And the server_lifespan wraps the Uvicorn/Granian launch process

swift marsh
#

Also, I have one question about litestar webworkers, is it suitable for production or gunicorn is better?

pale thorn
#

this is different from the asgi lifespn, btw

pale thorn
pale thorn
#

you can use the litestar-granian plugin for a drop in replacement over the litestar run command

swift marsh
pale thorn
#

It is much faster than using Gunicorn with any sort of asgi backend. Also, Uvicorn now has the pieces needed to restart the server process, so I don't think Gunicorn is needed at all in the ASGI stack any more.

#

So, in short, I think the litestar run command is suitable for production

swift marsh
#

is it possible to manage worker processes while using granian?

#

like gunicorn

pale thorn
#

yeah, i've been able to do customize it quite a bit. I'm not sure specifically what you are after, but I think it's probably got you covered.

swift marsh
#

btw, will litestar worker management be documented?

pale thorn
#

Yep, it will

swift marsh
#

ty a lot

pale thorn
#

I believe there's an open issue for it so that we don't forget

strong orbit
#

a method to easily extend JSON serialization provided by AA.
see #1280207918352044032 message
start of discussion: #learning message

swift marsh
#

Pydantic models, data classes or msgpack structs as query params

shell prairie
quartz elbow
#

I have a wishlist item: docs (or support) for channels to send messages to SSE clients. I.e. if a single change needs to be broadcasted to many clients, it would be great to use a channel for that. Maybe that's possible, but from what I read, channels are more for websockets than SSE.

quartz elbow
#

Another docs request: the migrate from Flask section - it'd be good to add something about migrating off using Flask's g, as that thing is a really nice pattern that I imagine a lot of people use

strong orbit
#

and/or flask_restx

quartz elbow
#

Update: I think I sorted it by using request.state, but I'm a bit worried it's wrong because it seems like too perfect a match for g to not already be documented ๐Ÿ™‚

thick hare
#

I am not sure if someone suggested but, i would like to be able to use DTO's for other kind of responses like CSV, and Stream and more. We have two hacked example usage for them. First one for we do generate CSV for nearly every entitiy. So i've wrapped another response type for this. Another one is sometimes we want to stream an object to response (via SSEData or Streaming Response). I do it by extremly hacky way.

Might be possible like decoupling it from response and generate before response. It may not work for Streaming tough

pale thorn
#

Interesting.

#

Maybe you can share some of the patterns you are using; it might trigger some additional ideas from others

thick hare
#

This is the "csv" response one. It works like JSON response. I think it wont work for single object return, can't remember will test again.

pale thorn
thick hare
# pale thorn I wasn't laughing at the code btw, it was your comment. The code is fine! You ...

I was jokingโ€”sometimes you just have to work around it. But adding support for dtobackend to handle other types of responses would be helpful.

For example, streaming is a great fit for many of our scenarios. One case is our integration with a food delivery platform: thereโ€™s a button that retrieves (some regional data) for each restaurant (20+ of them) and updates the corresponding entries. Since each request takes about a second, it takes over 20 seconds to finish. By streaming the data and handling the rest with React Query, we simplify things a lot.

languid solar
#

rename the project to litecat and make the logo a ๐Ÿˆ its the only way to get taken srsly by real deal devs

thick hare
#

It seems cats do love litestar, uwu

last crypt
#

Litecat

fervent marten
#

Ability to support multiple content-types by the same endpoint for request (accept) / response

  • Instead of @get("/resources", media_type=MediaType.JSON) one could @get("/resources", media_types=[MediaType.JSON,MediaType.MessagePack])

This is already supported by openapi 3+, but is impossible to use. To cover the functionality, it is currently possible only by duplicating endpoints, but its not so elegant. Routing is given by the Accept/Content-Type headers, instead of duplicating endpoints.

https://swagger.io/docs/specification/v3_0/media-types#multiple-media-types

thick hare
thick hare
quartz elbow
#

Wishlist item: a really clear way for naming of OpenAPI types to work. E.g. if I return Item as a SQLAlchemy object, it should have the same type name as a list of Items in a related ItemsGroup object that I return from another endpoint which drags Items with it.

quartz elbow
#

Wishlist item (if the wishlist is still worth using?): figuring out a way to support typed SSE endpoints.

keen trellis
#

I was surprised to see messagepack implemented before protobuf

#

At least on the iOS side, protobuf is almost the de facto format for anything that is large enough for performance to matter

pale thorn
#

Msgspec supports messagepack out of the box

keen trellis
#

That makes sense

pale thorn
#

Protobuf would be nice though.

keen trellis
#

A clean integration would be awesome. Apple and Google use protobuf for many of their first party APIs. I need to use it as the JSON I am returning can take several hundred ms to parse on a phone.

quaint schooner
#

Should we support all types of JOSE in our auth backend? Afaik we only have support for JWT

JOSE stands for JSON Object Signing and Encryption. It's a set of standards used to sign and encrypt data using JSON-based data structures. In other words, JWA, JWE, JWK, JWS, and JWT are all part of JOSE.

TL;DR:

JWA: Defines a set of crypto algorithms and identifiers used in JWE, JWK and JWS.
JWK: Defines a representation of crypto keys used as input for the algorithms defined in JWA using JSON-based data structures.
JWE: Defines encrypted content using JSON-based data structures. The encryption/decryption is performed with the algorithms defined in JWA.
JWS: Defines signed content using JSON-based data structures. The signing/verification is performed with the algorithms defined in JWA
JWT: Defines subject claims using JSON-based data structures. The claims can be optionally protected via JWE or JWS.
pale thorn
#

We already support all of this as far as I'm aware

#

At least authenticating against one of these services

#

I don't think we are going to be a provider of these (at least in the short term or in the main library)

pale thorn
#

I'll share an example that works with JWKS shortly

#

I use it with IAP auth in production daily

quaint schooner
#

O.o

orchid summit
#

So I just gave litestar a try and encountered some non-self-explainatory issues. Eg. Telmplate documentation talks about url_for_static_asset for static asset links, but no hint that this only works with Litestar(static_files_config=[...]) - which is deprecated (doesn't work with create_static_files_router).

So my wishes after an evening playing around with Litestar would be

  • Documentation improvements
  • Better error messages
  • Log warnings for usage of deprecated stuff when run in --debug mode
pale thorn
#

Log warnings for usage of deprecated stuff when run in --debug mode this should be there

#

Agree on the static files stuff. There's probably some inconsistencies in the docs on usage there

orchid summit
#

I have only discovered this in the stacktrace as there seems to be some deprecated decorator to be used. Just gave it a try again with static_files_config and --debug - there was no hint whatsoever in the terminal output.

pale thorn
#

that doesn't sound correct then. we should probably take a look at that before 3.0

umbral jacinth
#

This has to do with your warnings setup

#

You need to run with warnings enabled

#

They should also pop up in your test suite when you're running pytest

quaint schooner
#

https://discord.com/channels/919193495116337154/1163057045440827513

I am not sure if we have this already or not; so I was thinking what if we have a logger config list where user will provide list of status codes and litestar will only log one single line for that specific status code log instead of logging whole stack trace.

app = Litestar(
    logging_config={
      "disable_stack_trace": [404],
    },
)

Before:

INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     127.0.0.1:40280 - "GET / HTTP/1.1" 200 OK
INFO:     127.0.0.1:40280 - "GET /favicon.ico HTTP/1.1" 404 Not Found
ERROR - 2023-10-15 15:42:43,553 - litestar - config - exception raised on http connection to route /favicon.ico

Traceback (most recent call last):
  File "/home/advik/.cache/pypoetry/virtualenvs/todomvc-J4iM3Twa-py3.11/lib/python3.11/site-packages/litestar/_asgi/routing_trie/traversal.py", line 153, in parse_path_to_route
    node, path_parameters, path = traverse_route_map(
                                  ^^^^^^^^^^^^^^^^^^^
  File "/home/advik/.cache/pypoetry/virtualenvs/todomvc-J4iM3Twa-py3.11/lib/python3.11/site-packages/litestar/_asgi/routing_trie/traversal.py", line 54, in traverse_route_map
    raise NotFoundException()
litestar.exceptions.http_exceptions.NotFoundException: 404: Not Found

The above exception was the direct cause of the following exception:
...

After:

INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     127.0.0.1:40280 - "GET / HTTP/1.1" 200 OK
INFO:     127.0.0.1:40280 - "GET /favicon.ico HTTP/1.1" 404 Not Found
mental grove
pale thorn
mental grove
pale thorn
#

so, you can do this already

#

this is a uvicorn error, is ist not?

pale thorn
mental grove
pale thorn
#

ah, ok. in that case we are on the same page here

#

this can be implemented quite quickly and in a non-breaking manner in v2 @quaint schooner

#

LoggingConfig needs to get the "disable_stack_trace" key added into it

#

then we need to update the default excecption handler to look at this config

#

by default, we can't change which codes are "limited logged"

#

because that would be breaking

#

but we could in 3.0

mental grove
umbral jacinth
pale thorn
#

yeah, there are a number of tracebacks that are printed when they shouldn't be imo

umbral jacinth
#

What else shouldn't we print?

pale thorn
#

I'll have to go back through my logs, but I've noticed a few over the last year and half

#

404 is the biggest one

#

maybe it is all just 404

umbral jacinth
pale thorn
#

i'll take a look tomorrow

#

when i can grep the logs

#

we should do something special for a 418 though

#

If anyone can tell me what that's used for, that would be awesome

quaint schooner
#

418 I'm a teapot

#

This code was defined in 1998 as one of the traditional IETF April Fools' jokes, in RFC 2324, Hyper Text Coffee Pot Control Protocol

umbral jacinth
pale thorn
#

exactly!

quaint schooner
pale thorn
#

or print the nursery rhyme

umbral jacinth
pale thorn
#

they work

#

lol

quaint schooner
pale thorn
#

I have check marks in some of my logs

umbral jacinth
umbral jacinth
#

Yeah

rich knot
#

Hello my biggest wish is the ability to completely circumvent msgspec and define custom parsers and serializers. Just taking the raw body of the request and parse it using whatever I need - Pydantic, Dacite or completely custom logic.
And in a way specifically that it doesn't trigger Errors in mspgspec.
Maybe this is already possible but there's probably no formalized or idiomatic way to do this

umbral jacinth
#

You can just do

@post("/")
def handler(body: bytes):
``` if all you want is the raw request body
#

But I don't think that's what @rich knot is asking for, as they mention "custom parsers and serializers"

pale thorn
broken oak
#

Hello guys,

I'd like to add having a zstd compression in litestar to the wishlist!

I think @cloud nexus has already done something on this in the past, but now that chromium and firefox browsers support zstd as well, it'd be a great time to add it to Litestar?

umbral jacinth
#

This could be a regular feature request that can be done outside v3 as well

#

If you want, you can just open an enhancement request on GitHub

broken oak
#

sure!

jagged notch
pale thorn
#

Yep. And should be supported already. We could enhance it by making Litestar-datastar similar to htmx though.

eternal marlin
#

Datastar SDK has litestar support built in. Any other features you'd like?

nimble cipher
silent grove
#

Hello, please consider allowing non-litestar-but-main-app-created prom metrics to be register-able to litestar's prom handler.

#

That is, litestar can emit prom metrics for the http/websocket server that it manages.
It will be great if it can also register metrics from general python code in an application.
Ex: The ones from any HTTP client that's doing calls. https://github.com/karpetrosyan/httpx-metrics

GitHub

Contribute to karpetrosyan/httpx-metrics development by creating an account on GitHub.

#

For places where I used to have prometheus_client and hand-rolled metrics, I instead write them to data/*.prom files, and have the litestar-managed /metrics endpoint read the files, stitch em together and emit the content at /metrics (including litestar's own metrics).

#

However, for libraries like httpx-metrics that start their own WSGI prom server, or write to some existing registry, it'll be nice to get their metrics into litestar's care too

modest oar
#

I'm using dishka for DI, and it works quite elegantly. It too follows the pattern of declaring dependencies as fields in the class rather than arguments on their methods.

silent grove
# silent grove Hello, please consider allowing non-litestar-but-main-app-created prom metrics t...

Turns out I just needed to set this envvar before the start of my py app, and during the app (not litestar app) start, handle dir creation (on startup) & cleanup (on exit)
https://prometheus.github.io/client_python/multiprocess/

#

This way, prometheus-async, httpx-metrics, and litestar, all write to the dir, and litestar's PrometheusController picks them all up

quartz elbow
pale thorn
umbral jacinth
#

The new file-system implementations allow reading and streaming (with ranges), so you should be able to stream whatever your need

quartz elbow
#

Oh wow

#

I'm glad I asked - thanks!

#

I'll have a look

umbral jacinth
#

Can you be more specific, maybe give a concrete example? The page you linked covers multiple topics relating to streaming. I was thinking of file systems since you specifically asked about files. Maybe I misunderstood what you're trying to do?

quartz elbow
#

Oh - sure. Sorry, probably was a bit ambiguous.

#

Here's the original query:

Also be great to return a File based on BytesIO (or similar) representation, rather than always having to read a file from disk. E.g. to get a nice file representation in OpenAPI, for an API that streams on a file served from an internal API - currently have to buffer it locally, save it to disk, then load it again, instead of just forwarding on a file stream.

umbral jacinth
#

Yes. But I'm not entirely sure what you mean by that if it's not covered by the new capabilities of file responses ๐Ÿ‘€

quartz elbow
#

It's just in the chat directly above this - I asked if it ever got anywhere

umbral jacinth
#

Yes, I know. What I do not know is what exactly you meant by your original question. As I understood it originally, it should be covered by the new feature. But it appears that is not the case, so I'm assuming I did not understand correctly. So I'm asking you to clarifiy that original question ๐Ÿ™‚

quartz elbow
#

Oh cool - fair enough

#

So - back to this:

Also be great to return a File based on BytesIO (or similar) representation, rather than always having to read a file from disk. E.g. to get a nice file representation in OpenAPI, for an API that streams on a file served from an internal API - currently have to buffer it locally, save it to disk, then load it again, instead of just forwarding on a file stream.
Is the idea then that I wrap the remote HTTP API in a custom filesystem implementation, so I can then make a File object and return from it?

#

Just to clarify the scenario - I have a litestar app that fronts another application. The other application streams files to me over HTTP when someone requests one through my API.

#

With my current implementation, on request I stream the file to a temporary disk location, and then return a File object to my client with the path to that temporary file set.

umbral jacinth
#

Why do you need the File object in the first place if you have a stream at hand? Can't you just use a stream response then?

quartz elbow
#

I can, but the API documentation generation isn't great for that

#

Ideally the File object would be a BytesIO I can plug anything into, I suppose, so I can make the data how I like while generating the nicer docs.

#

Or maybe not a BytesIO because I think that buffers it all up. But a stream version of that, maybe? Like io.BufferedReader?

umbral jacinth
#

You could also return a Stream and declare the OpenAPI things manually.

@get(content_media_type="application/octet-stream", )
async def handler() -> Stream;
    return Stream(<pass in the generator>)
#

This should get you the same OpenAPI schema as declaring -> File

quartz elbow
#

I'll give it a try

#

Thanks!

quartz elbow
#

Quick vote for adding LiteStar v3 support to Pycharm, if that's possible

pale thorn
versed star
#

Would it be possible to type hint routes with litestar (not base Pydantic) DTOs directly instead of passing DTOs to the route handler decorator?
e.g. write something like:

from advanced_alchemy.extensions.litestar import SQLAlchemyDTO, SQLAlchemyDTOConfig
from litestar import Controller, post
from .models import User

class UserWriteDTO(SQLAlchemyDTO[User]):
    config = SQLAlchemyDTOConfig(exclude={"id"})

class UserController(Controller):
    path = "/user"

    @post("")
    async def create(self, data: UserWriteDTO) -> User:
        ...

This seems more intuitive to me (coming from Django serializers at least, n=1) and would more strongly enforce static type safety by preventing the following, which (if I'm understanding Litestar correctly) would pass type checking but fail in runtime:

from litestar import Controller, post, get
from advanced_alchemy.extensions.litestar import SQLAlchemyDTO, SQLAlchemyDTOConfig

class UserDTO(SQLAlchemyDTO[User]):
    config = SQLAlchemyDTOConfig(exclude={"id", "password_hash"})

class UsersController(Controller):
    path = "/users"
    dto = UserDTO

    @post("/")
    async def create(self, data: User) -> User:
        data.id # <--- id is a parameter of User, but excluded by. UserDTO
pale thorn
#

not sure if this helps you

umbral jacinth
#

I think you're misunderstanding DTOs a bit @versed star.
In your example async def create(self, data: UserWriteDTO) -> User:, data is not an instance of UserWriteDTO, but User. DTOs are really for transfer only. You'll never receive the actual DTO model directly.
They are strictly a mapping from encoded data -> DTO type -> target type, or target -> DTO -> encoded data

#

Being able to write async def create(self, data: User) -> User: is not only correct, but it's also the point of DTOs; It separates the data transformation step from the logic. The data transformation happens behind the scenes

versed star
#

We rolled our own boolean OR/any_of guard, but I'm sure this something that would be appreciated if supported by Litestar natively:

def any_of(*guards: Guard) -> Guard:
    """
    Passes if any given guard passes. Equivalent a boolean OR on a set of guards.

    Example:
        guards=[any_of(requires_superuser, requires_owner)]
    """
    async def combined_guard(connection: ASGIConnection, handler: BaseRouteHandler) -> None:
        last_exception: PermissionDeniedException | None = None

        for guard in guards:
            try:
                result = guard(connection, handler)
                # Await result to handle async guards
                if hasattr(result, "__await__"):
                    await result  # pyright: ignore[reportGeneralTypeIssues]
            except PermissionDeniedException as e:
                last_exception = e
                continue
            else:
                return

        raise last_exception or PermissionDeniedException("Access denied.")

    return combined_guard
umbral jacinth
#

One big upside IMO would be if they + security + auth backends would all be fully composable, with full OpenAPI support

#

As of now, I think unless we've got that, there's not enough of an upside for too much potential downside

#

It's also easy enough to do this on your own at this level, so not much is lost if Litestar doesn't support it out of the box

cloud nexus
heady shadow
#

Is there any ETA for the release? I would like to start a new project in 1-2 months.

mental grove
#

When will the release be announced?

umbral jacinth
#

When it's ready

#

We don't have a concrete timeline