WEBVTT

00:00:00.001 --> 00:00:04.960
We all know about Flask and Django, and of course, FastAPI made a huge splash when it

00:00:04.960 --> 00:00:06.360
came on the scene a few years ago.

00:00:06.360 --> 00:00:11.220
But new web frameworks are being created all the time, and they have these earlier frameworks

00:00:11.220 --> 00:00:12.320
to borrow from as well.

00:00:12.320 --> 00:00:16.920
On this episode, we dive into a new framework gaining a lot of traction called Litestar.

00:00:16.920 --> 00:00:19.340
Will it be the foundation of your next project?

00:00:19.340 --> 00:00:24.160
Join me as I get to know Litestar with its maintainers, Jacob Coffey, Janek Noviante,

00:00:24.160 --> 00:00:25.640
and Jacob Finchner.

00:00:26.160 --> 00:00:31.680
This is episode 433, recorded August 30th, 2023.

00:00:31.680 --> 00:00:49.500
Welcome to Talk Python To Me, a weekly podcast on Python.

00:00:49.500 --> 00:00:51.240
This is your host, Michael Kennedy.

00:00:51.240 --> 00:00:55.360
Follow me on Mastodon, where I'm @mkennedy, and follow the podcast using

00:00:55.360 --> 00:00:58.740
at Talk Python, both on fosstodon.org.

00:00:58.740 --> 00:01:03.820
Keep up with the show and listen to over seven years of past episodes at talkpython.fm.

00:01:03.820 --> 00:01:07.560
We've started streaming most of our episodes live on YouTube.

00:01:07.560 --> 00:01:12.260
Subscribe to our YouTube channel over at talkpython.fm/youtube to get notified

00:01:12.260 --> 00:01:15.120
about upcoming shows and be part of that episode.

00:01:15.120 --> 00:01:20.060
This episode is brought to you by Sentry and us over at Talk Python Training.

00:01:20.060 --> 00:01:22.800
Please check out what we're both offering during our segments.

00:01:22.800 --> 00:01:24.680
It really helps support the show.

00:01:24.680 --> 00:01:28.100
Hey everyone, Janek, Jacob, Cody.

00:01:28.100 --> 00:01:29.480
Welcome to Talk Python To Me.

00:01:29.480 --> 00:01:29.960
Hey.

00:01:29.960 --> 00:01:31.480
Thanks for having us.

00:01:31.480 --> 00:01:32.420
Good to be here.

00:01:32.420 --> 00:01:34.340
Yeah, it's great to have you all here.

00:01:34.340 --> 00:01:41.440
Really excited to talk about one of my favorite topics, web frameworks, APIs, async, performance,

00:01:41.440 --> 00:01:42.180
design patterns.

00:01:42.180 --> 00:01:43.320
Like, let's do this.

00:01:43.320 --> 00:01:43.840
Let's do it.

00:01:43.840 --> 00:01:44.820
Cool.

00:01:45.140 --> 00:01:49.800
So we're going to talk about Litestar, which is somewhat new to me.

00:01:49.800 --> 00:01:51.240
I haven't known about it that long.

00:01:51.240 --> 00:01:57.060
But looking at the GitHub stars being several thousand GitHub stars and release two, it's

00:01:57.060 --> 00:01:58.460
definitely been going for a while.

00:01:58.600 --> 00:02:03.280
So it's a really cool framework that I think people will definitely be excited to learn about.

00:02:03.280 --> 00:02:05.680
But before we get to that, let's start with you all.

00:02:06.240 --> 00:02:08.180
Just a quick introduction for each of you.

00:02:08.180 --> 00:02:12.260
Go around the pretty bunch squares of our video here.

00:02:12.260 --> 00:02:13.700
Janek, you want to go first?

00:02:13.700 --> 00:02:14.300
Yeah.

00:02:14.300 --> 00:02:14.920
Yeah, sure.

00:02:14.920 --> 00:02:15.800
My name is Janek.

00:02:15.800 --> 00:02:20.340
Obviously, I'm a Python developer from Germany, currently living in Cologne.

00:02:20.920 --> 00:02:26.380
So I'm a bit behind the rest of the other guys when it comes to the time zones.

00:02:26.380 --> 00:02:28.060
I would put it a different way, Janek.

00:02:28.060 --> 00:02:29.460
I'd say you're living in the future.

00:02:29.460 --> 00:02:31.220
You're living hours ahead of time.

00:02:31.220 --> 00:02:32.240
You know what's already happened.

00:02:32.240 --> 00:02:32.600
Oh, yeah.

00:02:32.600 --> 00:02:33.980
That sounds much nicer.

00:02:33.980 --> 00:02:35.780
Yeah.

00:02:35.780 --> 00:02:38.820
Well, I currently work as a Python developer.

00:02:38.820 --> 00:02:42.960
Before I got into Python, I worked as a carpenter.

00:02:42.960 --> 00:02:47.000
So I built Furniture and other cool stuff.

00:02:47.000 --> 00:02:49.860
I think that's all we need to know right now.

00:02:50.120 --> 00:02:52.660
So Cody, why don't you continue?

00:02:52.660 --> 00:02:53.640
Yeah, I'll be happy to.

00:02:53.640 --> 00:02:54.900
So hey, Michael.

00:02:54.900 --> 00:02:55.420
Hey, guys.

00:02:55.420 --> 00:02:55.900
I'm Cody.

00:02:55.900 --> 00:02:59.520
Really, I've kind of had an interesting journey into the Python space.

00:02:59.520 --> 00:03:03.160
And so I'm kind of probably atypical from the rest of the team here.

00:03:03.160 --> 00:03:07.660
And so I've actually been a long time database guy, specifically in Oracle.

00:03:07.660 --> 00:03:14.020
And so lots of big, nasty data warehouses, large transaction systems,

00:03:14.020 --> 00:03:17.280
and building all the glue that you've got to do to make that stuff run.

00:03:17.460 --> 00:03:21.680
And so I guess really my intro to Python was really around DevOps,

00:03:21.680 --> 00:03:25.600
doing how do you make the whole environment stay running?

00:03:25.600 --> 00:03:27.160
How do you keep it efficient?

00:03:27.160 --> 00:03:29.560
And it's really focused on the database side of things.

00:03:29.560 --> 00:03:32.780
And so about 10 years ago, moved to Dallas from Alabama,

00:03:32.780 --> 00:03:36.740
originally where I'm from, and joined a small team of Oracle developers.

00:03:36.900 --> 00:03:39.480
And so we got acquired by one of the big four consulting firms.

00:03:39.480 --> 00:03:45.000
And so at that point, I shifted from development and into cloud migrations.

00:03:45.000 --> 00:03:49.620
So I did quite a bit of just Oracle database migrations into the various cloud providers.

00:03:50.020 --> 00:03:56.860
And now, just long, about six years later, I've now wound up at Google as part of the database black belt team there.

00:03:56.860 --> 00:04:00.560
And I still try to figure out exactly what a database black belt is.

00:04:00.560 --> 00:04:04.780
But a year in, really what I can tell you is that what I do is talk to all of our biggest customers

00:04:04.780 --> 00:04:10.020
and figure out what are the features and things that they need to make their enterprises run on Google Cloud.

00:04:10.120 --> 00:04:12.660
And we work with the engineers to make that happen.

00:04:12.660 --> 00:04:13.860
What an interesting background.

00:04:13.860 --> 00:04:17.140
I would say having a really good background in databases,

00:04:17.140 --> 00:04:19.280
and especially the DevOps side of databases,

00:04:19.280 --> 00:04:22.420
is a pretty unique view for building a web framework.

00:04:22.420 --> 00:04:23.780
A lot of people are all about,

00:04:23.780 --> 00:04:27.700
oh, I got to have something from my front-end code, my JavaScript I'm writing, right?

00:04:27.700 --> 00:04:29.360
And that's really not the same.

00:04:29.360 --> 00:04:29.680
Yes.

00:04:29.680 --> 00:04:32.200
And my career actually originally started using,

00:04:32.200 --> 00:04:34.980
well, I guess now they're called low-code tools,

00:04:34.980 --> 00:04:37.280
but we used to call them rapid application development.

00:04:37.280 --> 00:04:42.220
And so there's just lots of database builders where there really wasn't any actual Python

00:04:42.220 --> 00:04:44.400
or any Java or those types of code involved.

00:04:44.400 --> 00:04:45.860
It was all PLSQL.

00:04:45.860 --> 00:04:50.140
So I was actually happy to get involved in the Python world and move out of that space.

00:04:50.140 --> 00:04:50.640
Yeah.

00:04:50.640 --> 00:04:51.180
Excellent.

00:04:51.180 --> 00:04:51.720
Awesome.

00:04:51.720 --> 00:04:52.120
Jacob.

00:04:52.120 --> 00:04:54.800
Hey, I'm pretty new to being a developer.

00:04:54.800 --> 00:04:58.280
I spent the last four or five years in the system space,

00:04:58.280 --> 00:05:01.380
more on like the IT side of things,

00:05:01.380 --> 00:05:04.300
building systems and helping users.

00:05:04.300 --> 00:05:09.180
The last year, though, I've gotten to DevOps on my team at O'Reilly.

00:05:09.180 --> 00:05:10.820
Auto Parts, not the book people.

00:05:10.820 --> 00:05:12.420
Been really interesting.

00:05:12.420 --> 00:05:15.940
But recently I got to join this team and I'm learning a lot.

00:05:15.940 --> 00:05:18.480
So this is really exciting for me to be here.

00:05:18.480 --> 00:05:18.760
Yeah.

00:05:18.760 --> 00:05:20.420
Well, it's awesome to have you here.

00:05:20.420 --> 00:05:24.800
And like I said, a really exciting web project that I think people will appreciate.

00:05:24.800 --> 00:05:28.380
So let's go ahead and jump into it.

00:05:28.380 --> 00:05:33.940
So Litestar at Litestar.dev, effortlessly build performant APIs.

00:05:33.940 --> 00:05:38.040
So who wants to give us the elevator pitch here?

00:05:38.040 --> 00:05:40.880
The 30-second view of Litestar.

00:05:40.880 --> 00:05:42.940
Yannick, I think Cody should do that.

00:05:42.940 --> 00:05:43.280
Oh.

00:05:43.280 --> 00:05:45.760
Yannick, I'd like to hear what you think.

00:05:45.760 --> 00:05:49.440
And then I can give you about my perspective of how I kind of joined the team

00:05:49.440 --> 00:05:51.080
and what it means to me.

00:05:51.160 --> 00:05:54.260
But I think it would be helpful to hear how you think about it.

00:05:54.260 --> 00:05:54.500
All right.

00:05:54.500 --> 00:05:56.300
So what is Litestar?

00:05:56.300 --> 00:06:00.540
Well, I think our tagline puts it pretty well.

00:06:00.540 --> 00:06:04.020
We definitely have a focus on building APIs.

00:06:04.020 --> 00:06:09.120
So not, well, not the typical HTML applications monolith.

00:06:09.120 --> 00:06:12.000
It's often compared to FastAPI.

00:06:12.000 --> 00:06:17.200
And it has similarities, definitely, which FastAPI is already in the name.

00:06:17.200 --> 00:06:19.000
It's also focused on building APIs.

00:06:19.000 --> 00:06:23.280
But for us, really important is the effortless part.

00:06:23.280 --> 00:06:28.960
So what we strive to do is to take away all the, well, not all the,

00:06:28.960 --> 00:06:33.460
but as much as we can take away the boilerplate for developers,

00:06:33.460 --> 00:06:39.240
you usually have to do anyway when you're building any project of size,

00:06:39.580 --> 00:06:45.660
which includes lots of stuff like authorization, caching, ORM integration,

00:06:45.660 --> 00:06:49.620
and all these kind of things that you usually have to do.

00:06:49.620 --> 00:06:57.120
And with micro frameworks like Flask or FastAPI or Starlette or any of the other ones out there,

00:06:57.120 --> 00:07:01.640
because there are a lot of them, and they are all really great at what they're doing.

00:07:01.640 --> 00:07:07.160
But they do require to build a lot of boilerplates, which can be good,

00:07:07.160 --> 00:07:10.560
because it gives you a lot of control over what you're doing,

00:07:10.560 --> 00:07:13.420
and you can build it exactly how you want to.

00:07:13.420 --> 00:07:17.680
But it's also not, well, it's not completely effortless,

00:07:17.680 --> 00:07:20.580
which is what we are trying to achieve.

00:07:20.580 --> 00:07:22.500
Yeah, when I think about the web frameworks,

00:07:22.500 --> 00:07:27.100
I have this sort of bimodal distribution at like two ends.

00:07:27.100 --> 00:07:31.840
On one end, you have Django, where it comes with all of these helpers and all of these things.

00:07:31.840 --> 00:07:37.800
Like you just say, yes, I want to hold back in to manage my database with a UI, for example, right?

00:07:37.800 --> 00:07:42.960
But there's a bunch of those kinds of things, form creation and the ORM migrations.

00:07:42.960 --> 00:07:45.100
All that stuff is kind of just, you get it all.

00:07:45.100 --> 00:07:49.580
And the other end, you have Flask and you have FastAPI and a whole bunch of others,

00:07:49.580 --> 00:07:50.880
Sanic, you name it.

00:07:50.880 --> 00:07:53.320
And there's a lot, and they're all about,

00:07:53.320 --> 00:07:56.320
we're going to go and handle the request.

00:07:56.820 --> 00:07:57.860
And then it's up to you.

00:07:57.860 --> 00:07:58.900
Do you want a database?

00:07:58.900 --> 00:07:59.720
You can have a database.

00:07:59.720 --> 00:08:01.340
If you don't want one, don't have one.

00:08:01.340 --> 00:08:05.840
You know, in that regard, FastAPI itself is kind of almost prescriptive

00:08:05.840 --> 00:08:09.740
in that it talks about having like model exchange

00:08:09.740 --> 00:08:13.760
and defining models that create your, how the website works.

00:08:13.760 --> 00:08:15.500
Whereas Flask doesn't even have that.

00:08:15.500 --> 00:08:16.520
You just kind of nailed it.

00:08:16.520 --> 00:08:18.900
And this is actually how I kind of came into,

00:08:18.900 --> 00:08:20.420
it was actually Starlette at the time.

00:08:20.540 --> 00:08:22.540
But I guess about four years ago,

00:08:22.540 --> 00:08:26.820
I built a pretty large scale Django app as, as for some consulting work.

00:08:26.820 --> 00:08:30.460
And it was really around data quality and data migration.

00:08:30.460 --> 00:08:31.480
And it worked really well,

00:08:31.480 --> 00:08:34.200
but I was reading all this stuff about FastAPI.

00:08:34.200 --> 00:08:36.280
And I really liked what I was seeing.

00:08:36.500 --> 00:08:38.880
And the developer experience of that was really incredible.

00:08:38.880 --> 00:08:39.340
And, you know,

00:08:39.340 --> 00:08:42.660
the tools that those guys put together was just kind of second to none.

00:08:42.660 --> 00:08:47.520
It was really refreshing to see that kind of build in over what you see in Django.

00:08:48.040 --> 00:08:51.880
And so I started working with FastAPI and really liked it.

00:08:51.880 --> 00:08:57.380
But I got to where I felt like I had a lot of boilerplate that I added on top of that to get to that working app.

00:08:57.380 --> 00:09:01.940
And so when we joined up with Google, one of the things that I did was, was say,

00:09:01.940 --> 00:09:05.960
okay, I've got a lot of like boilerplate for things that I need to run this app.

00:09:05.960 --> 00:09:08.420
And, and maybe there's somewhere that I can contribute.

00:09:08.420 --> 00:09:12.000
And so at that point, I started looking around at all the web frameworks.

00:09:12.000 --> 00:09:15.000
And that's when I got introduced to Starlette at the time.

00:09:15.000 --> 00:09:17.660
And just to kind of, to give a little bit of a history,

00:09:17.660 --> 00:09:21.700
it was originally called Starlette because it was based on Starlette,

00:09:21.700 --> 00:09:24.160
just like all the other ASCII frameworks out there.

00:09:24.160 --> 00:09:27.060
And, you know, obviously Starlette is an awesome tool.

00:09:27.060 --> 00:09:31.320
And we were kind of, you know, paying respects to that by naming it Starlette.

00:09:31.320 --> 00:09:35.520
But obviously there's very few letters in between that and Starlette.

00:09:35.520 --> 00:09:39.380
And so what we found is that many of the posts that we made, people were confusing.

00:09:39.380 --> 00:09:40.820
Hey, did you mean Starlette?

00:09:40.820 --> 00:09:42.300
Because I don't know what a Starlette is.

00:09:42.300 --> 00:09:46.140
And so long story short, we said, okay, it's time for us to rename.

00:09:46.140 --> 00:09:50.440
And, you know, I guess we're all not too original because we just flipped the wording around.

00:09:50.440 --> 00:09:51.680
And that's how we came up with Litestar.

00:09:51.680 --> 00:09:53.260
It's a cool name.

00:09:53.260 --> 00:09:53.820
I like it.

00:09:53.820 --> 00:09:54.240
Yeah.

00:09:54.240 --> 00:09:56.360
And just for people who don't necessarily know,

00:09:56.360 --> 00:10:00.640
much of FastAPI's magic is that it's built on Starlette.

00:10:00.640 --> 00:10:05.820
And so in a sense, you're running on the same foundation as FastAPI in that regard, right?

00:10:05.820 --> 00:10:07.540
No, we're actually not anymore.

00:10:07.540 --> 00:10:08.320
So...

00:10:08.320 --> 00:10:08.380
Okay.

00:10:08.480 --> 00:10:09.520
That was the original.

00:10:09.520 --> 00:10:09.980
All right.

00:10:09.980 --> 00:10:18.360
I think we have dropped Starlette as a dependency about like six, seven months ago before version

00:10:18.360 --> 00:10:18.600
two.

00:10:18.600 --> 00:10:18.860
Yeah.

00:10:18.860 --> 00:10:21.080
So in the beginning, we were...

00:10:21.080 --> 00:10:23.280
Starlette is built very, very modular.

00:10:23.280 --> 00:10:30.140
You can use like FastAPI, use the whole thing, and you can just extend the router and don't care

00:10:30.140 --> 00:10:35.600
about anything, but it's also designed in such a way that you can just take certain things of it.

00:10:35.600 --> 00:10:40.340
So you can say, okay, I just like the routing and the rest I'll do myself.

00:10:40.340 --> 00:10:51.860
And what we originally did was we had our own router, our own routing system, and plugged that into Starlette and build our own application on top of that.

00:10:51.860 --> 00:10:59.460
But over time, we have diverged quite a lot from the way Starlette wanted to do things or...

00:10:59.460 --> 00:11:09.080
Well, not wanted to do things, but Starlette became a bit restrictive because we wanted to do things very differently at very deep parts of the Starlette stack.

00:11:09.320 --> 00:11:25.660
And so it kind of made sense to us that we just wrote our own basically and filled in the gaps that Starlette left behind, which wasn't an easy decision because Starlette is a very great piece of technology and it's very well done.

00:11:25.660 --> 00:11:27.680
And it's got a lot of credibility to it, right?

00:11:27.680 --> 00:11:28.000
Yeah.

00:11:28.000 --> 00:11:33.940
There's a lot of people that run it in production and that means something when you have a tool that is known to work well.

00:11:34.020 --> 00:11:36.040
It was a bit of a challenge to get that going.

00:11:36.040 --> 00:11:42.460
But yeah, at the moment we are from the ASGI side, our very own thing.

00:11:42.460 --> 00:11:46.420
We don't depend on anything else in that regard anymore.

00:11:46.420 --> 00:11:46.780
Okay.

00:11:46.780 --> 00:11:52.500
And is that all Python or has that got some other technology making it go in there?

00:11:52.500 --> 00:11:55.260
So that part, the ASGI part is all Python.

00:11:55.260 --> 00:12:03.120
We have some other non-Python parts, but they are not at the web serving site, let's say.

00:12:03.340 --> 00:12:07.480
So we've Rustified, I guess that's the term, if you will, at least one place.

00:12:07.480 --> 00:12:08.980
That's the URL parsers, right?

00:12:08.980 --> 00:12:09.960
The query parsers.

00:12:09.960 --> 00:12:10.380
Okay.

00:12:10.380 --> 00:12:12.400
That certainly is a strong trend these days.

00:12:12.400 --> 00:12:16.440
Although I'm surprised for a framework that hasn't been around that long that it's already got Rust.

00:12:16.440 --> 00:12:17.060
No, just kidding.

00:12:17.060 --> 00:12:20.560
We experimented about a year ago.

00:12:20.560 --> 00:12:25.580
We experimented actually with more Rust and that was to do the routing in Rust.

00:12:25.820 --> 00:12:29.180
So we use a Radix-based router.

00:12:29.180 --> 00:12:31.880
That's something Sanic does that as well.

00:12:31.880 --> 00:12:47.880
And we experimented with a Rust implementation, but it was decided that the speed up that we got from using Rust wasn't really worth the trade-off between it being harder to maintain and being less accessible for other contributors.

00:12:47.880 --> 00:12:55.640
Because most of the people who are using a Python web framework, they will know Python, but they're not necessarily that fluent in Rust.

00:12:56.140 --> 00:13:02.140
And, well, basically the router wasn't really that big of a bottleneck for Starlette at the time.

00:13:02.140 --> 00:13:06.460
So it didn't make a lot of sense to write that part in Rust.

00:13:06.460 --> 00:13:08.120
Yeah, it's a big trade-off, isn't it?

00:13:08.120 --> 00:13:16.600
I mean, even things like shipping wheels and just pushing out a version once you start to go into a, well, there's a per platform compilation step.

00:13:16.600 --> 00:13:18.180
That has a lot of friction, right?

00:13:18.180 --> 00:13:26.600
And I'm sure you could do a whole podcast just on packaging, but Python packaging in and of itself is not always the easiest or most intuitive process.

00:13:26.600 --> 00:13:30.140
And so, yeah, it definitely gets complicated when you add in another language.

00:13:30.140 --> 00:13:31.020
Yeah, I can imagine.

00:13:31.020 --> 00:13:40.740
And in a lot of cases, there are more low-hanging fruits that you can grab and just optimize things there before you say, okay, now we have optimized everything so well.

00:13:40.740 --> 00:13:44.480
The only way we can get faster if we now use a language like Rust.

00:13:44.480 --> 00:13:48.000
And I don't think we're at that part yet.

00:13:48.000 --> 00:13:50.460
So we have yet still a lot of things to do.

00:13:50.460 --> 00:13:51.140
That's excellent.

00:13:51.140 --> 00:13:52.640
It's a really good philosophy too, I think.

00:13:52.640 --> 00:14:00.620
There's an interesting new way to make your Python code faster that used to be the case when Moore's law was really in effect.

00:14:00.620 --> 00:14:07.180
You went from a 486 to a Pentium to a, you know, whatever, gigahertz and from megahertz to gigahertz and all those things.

00:14:07.180 --> 00:14:08.900
You just wait and the hardware got faster.

00:14:08.900 --> 00:14:09.940
So your code went faster.

00:14:09.940 --> 00:14:19.600
But with the faster CPython initiative and Quito and Mark Shannon and team over there, they're making Python quite a bit faster constantly with every release.

00:14:19.600 --> 00:14:20.480
And that's...

00:14:20.480 --> 00:14:21.680
It's really impressive what they've done.

00:14:21.680 --> 00:14:29.080
It was a noticeable for my, the projects that I'm currently using LightSore on, it was a noticeable increase in performance when I went to 3.11.

00:14:29.400 --> 00:14:32.640
And yeah, looking forward to seeing what all they do over the next couple of releases.

00:14:32.640 --> 00:14:33.080
Yeah.

00:14:33.080 --> 00:14:39.260
We just last week, I think, no, this week perhaps, had the release candidate for 3.12.

00:14:39.260 --> 00:14:46.080
So kind of final beside the bugs, which is, you know, people can start testing it and see what's to come out of there as well.

00:14:46.080 --> 00:14:54.380
Yeah, we haven't actually tested yet with 3.12 because we're still waiting on some of our dependencies to be compatible with that.

00:14:54.820 --> 00:14:56.540
Jacob, do you want to say something?

00:14:56.540 --> 00:14:58.060
We just have two more.

00:14:58.060 --> 00:15:01.240
I think it's GRPC and Greenlit.

00:15:01.240 --> 00:15:05.840
Greenlit actually, I think, is ready, but they need to do a release to PyPI.

00:15:05.840 --> 00:15:10.420
But the GRPC is, I think, one of our stragglers.

00:15:10.420 --> 00:15:10.860
Okay.

00:15:10.860 --> 00:15:13.960
I've been eager to test that and see what kind of performance we can get.

00:15:14.040 --> 00:15:19.380
I guess we could do the RC one now, but we should probably test it out at some point.

00:15:19.380 --> 00:15:19.960
Okay.

00:15:19.960 --> 00:15:20.640
Interesting.

00:15:20.640 --> 00:15:21.060
Yeah.

00:15:21.060 --> 00:15:25.300
I mean, that's always the constant struggle, right, is you've got a lot of dependencies here.

00:15:25.300 --> 00:15:25.920
I don't know.

00:15:26.200 --> 00:15:30.020
A huge number of them are optional, but yes, it can get a little crazy.

00:15:30.020 --> 00:15:30.400
Yeah.

00:15:30.400 --> 00:15:32.040
That's actually a good point to make.

00:15:32.040 --> 00:15:38.440
You know, one of the things you'll see is that there are quite a lot of dependencies, but you'll see that a lot of them are tied to optional groups.

00:15:38.440 --> 00:15:46.600
And so one of the things that we wanted to do was make it quick for a user to kind of pip install one thing and have all the pieces they need to get started.

00:15:46.600 --> 00:15:58.800
And so you can say pip install Lifestar, and you can add the CLI or the standard group, and it'll automatically install the JS beautifier and the command line utilities and rich and a couple of other libraries.

00:15:58.800 --> 00:16:03.460
And so there's a lot of helpers to kind of make that a little bit more easy to just jump right in.

00:16:03.460 --> 00:16:03.720
Right.

00:16:03.720 --> 00:16:07.620
This portion of Talk Python To Me is brought to you by Sentry.

00:16:07.980 --> 00:16:14.320
Is your Python application fast, or does it sometimes suffer from slowdowns and unexpected latency?

00:16:14.320 --> 00:16:16.940
Does this usually only happen in production?

00:16:16.940 --> 00:16:20.120
It's really tough to track down the problems at that point, isn't it?

00:16:20.120 --> 00:16:26.980
If you've looked at APM application performance monitoring products before, they may have felt out of place for software teams.

00:16:26.980 --> 00:16:34.860
Many of them are more focused on legacy problems made for ops and infrastructure teams to keep their infrastructure and services up and running.

00:16:34.860 --> 00:16:37.520
Sentry has just launched their new APM.

00:16:37.520 --> 00:16:39.020
New APM service.

00:16:39.020 --> 00:16:46.060
And Sentry's approach to application monitoring is focused on being actionable, affordable, and actually built for developers.

00:16:46.680 --> 00:16:59.800
Whether it's a slow running query or latent payment endpoint that's at risk of timing out and causing sales to tank, Sentry removes the complexity and does the analysis for you, surfacing the most critical performance issues so you can address them immediately.

00:16:59.920 --> 00:17:12.180
Most legacy APM tools focus on an ingest everything approach, resulting in high storage costs, noisy environments, and an enormous amount of telemetry data most developers will never need to analyze.

00:17:12.180 --> 00:17:17.380
Sentry has taken a different approach, building the most affordable APM solution in the market.

00:17:17.420 --> 00:17:27.920
They've removed the noise and extract the maximum value out of your performance data while passing the savings directly onto you, especially for Talk Python listeners who use the code Talk Python.

00:17:27.920 --> 00:17:38.160
So get started at talkpython.fm/sentry and be sure to use their code Talk Python all lowercase so you let them know that you heard about them from us.

00:17:38.720 --> 00:17:42.580
My thanks to Sentry for keeping this podcast going strong.

00:17:42.580 --> 00:17:54.440
And it looks like you've got, say, like Oracle or DuckDB or other things that maybe not everyone, or, you know, Async, PG, all those types of things that you probably only need one of those, right?

00:17:54.440 --> 00:17:57.680
You're probably not doing MySQL, Postgres, and Oracle.

00:17:57.680 --> 00:17:59.420
Maybe, but probably not.

00:17:59.420 --> 00:18:00.740
We actually do all of them.

00:18:00.740 --> 00:18:18.000
And so with the same, and this is one of the things that I think is probably good to point out, is that with the repository contrib module that we've created, you actually can use the same repository, the same models, the same JSON type, and it will automatically select the best data type for whatever engine you're running.

00:18:18.000 --> 00:18:28.440
So, for instance, if you're on, let's just say that today you're running on Async, PG with Postgres, and you've got a JSONB data type using the built-in custom Litestar JSON type.

00:18:28.720 --> 00:18:30.940
And tomorrow you convert to Oracle.

00:18:30.940 --> 00:18:36.580
All you need to do is change your connect string, and it'll automatically deploy that to Oracle with the correct JSON type.

00:18:36.580 --> 00:18:41.600
And so you really don't have to do anything additional to make your code work between that.

00:18:41.600 --> 00:18:48.620
So, honestly, a lot of that came from my time with Django, where you got the, you know, one set of utilities worked with quite a few databases.

00:18:48.620 --> 00:18:52.540
And so I spent quite a bit of time kind of making sure that that worked.

00:18:52.940 --> 00:18:59.060
And so you'll see that including with the Alembic migrations that are coming out in 2.1 in a couple of weeks.

00:18:59.060 --> 00:19:10.720
And so through the CLI, you'll be able to actually manage your entire database, call and configurations and generate them, as well as, you know, launch and use your app through the same, you know, single CLI.

00:19:10.720 --> 00:19:12.940
I don't think I'd ever want to migrate to Oracle.

00:19:14.480 --> 00:19:16.520
Well, when you need Oracle, you do need Oracle.

00:19:16.520 --> 00:19:18.680
I'm aware of the stigma.

00:19:18.680 --> 00:19:21.660
But, yeah, there's some times and places for all of them.

00:19:21.660 --> 00:19:29.640
I think for those who are unaware what you're talking about, you're talking about the SQLAlchemy repository patterns that we offer.

00:19:29.980 --> 00:19:38.140
So the things you mentioned, they are built on top of SQLAlchemy, which in itself is already really flexible and makes it easy to change databases.

00:19:38.140 --> 00:19:43.260
But there are still a few gaps that you need to bridge yourself, like the ones you've mentioned.

00:19:43.260 --> 00:19:48.640
And this is an example of the things that we try to take care of.

00:19:48.640 --> 00:19:49.680
One last note on that.

00:19:49.780 --> 00:19:59.340
So a lot of the repositories that you might see for cookie cutter apps or the other existing templates out there usually stop at the basic CRUD operations.

00:19:59.340 --> 00:20:07.300
But what we've done here is actually implemented all the basic CRUD operations and efficient bulk operations based on whatever database you're using.

00:20:07.300 --> 00:20:09.480
And we choose the most optimal method for that.

00:20:09.480 --> 00:20:16.520
So that includes bulk add, bulk update, a merge statement, bulk delete, as well as all of the standard CRUD operations.

00:20:16.680 --> 00:20:27.280
And so one of the things that we really have focused on is making sure that this is an incredibly feature complete repository that has all of the functionality that you might want to use just right out of the box.

00:20:27.280 --> 00:20:28.060
That's really excellent.

00:20:28.060 --> 00:20:29.280
They all are handling that for people.

00:20:29.280 --> 00:20:38.100
And it gives me a sense of what you mean by the helping people do this stuff effortlessly, bringing a little bit of those batteries included feel of Django without.

00:20:38.100 --> 00:20:39.260
Putting in the batteries.

00:20:39.260 --> 00:20:43.160
Without the very prescriptive way that, say, Django does.

00:20:43.340 --> 00:20:52.080
I guess keeping the micro framework feel, but bringing along a lot of the stuff that people would otherwise have to choose and configure like, oh, OK, we're going to use I guess we'll use SQLAlchemy.

00:20:52.080 --> 00:20:53.880
Oh, did you call an async function?

00:20:53.880 --> 00:20:57.960
Oh, well, then you're going to also need the async SQLite library installed.

00:20:57.960 --> 00:20:58.920
How do I find that?

00:20:58.920 --> 00:21:02.400
And, you know, like those series of steps you've kind of got to go through.

00:21:02.400 --> 00:21:05.280
And it sounds like you've taken care of some of that for people.

00:21:05.540 --> 00:21:08.840
We try to, you know, obviously there are probably will continue to evolve it over time.

00:21:08.840 --> 00:21:13.720
But, you know, I've now used it a year at work at Google and it's it's really kind of satisfied.

00:21:13.720 --> 00:21:16.500
I'd say 95 percent of the use cases I need.

00:21:16.500 --> 00:21:21.060
And so I typically don't have to drop back into raw SQL anymore, which I think is a huge thing.

00:21:21.060 --> 00:21:24.800
And that's what I would like to propose and get everybody else to.

00:21:24.800 --> 00:21:26.700
And so that's where the focus there.

00:21:27.060 --> 00:21:35.480
And the one thing I'll add about it is that we still kind of maintain that micro framework philosophy because all this work is actually packaged up in something called a plug in.

00:21:35.480 --> 00:21:44.320
And so you can configure this one class and it automatically registers the route handlers, the own startup, own shutdown handlers that need to happen.

00:21:44.320 --> 00:21:48.300
It'll register the ASCII lifespan things that you need.

00:21:48.400 --> 00:21:57.420
And so basically you get this one piece where you can, you know, just set up your entire app and you don't have to, you know, do do or add the piece in several parts of your application.

00:21:57.420 --> 00:21:58.100
Yeah, excellent.

00:21:58.100 --> 00:22:04.320
So before we dive into the features, which we have been doing a little bit already, at least some of the philosophy, I want to talk about benchmarks.

00:22:04.320 --> 00:22:11.220
And I know benchmarks are a little controversial in the sense that, well, the way I'm using the framework is different than the way you use it.

00:22:11.220 --> 00:22:12.520
The way you use it is really fast.

00:22:12.520 --> 00:22:13.920
The way I, you know, whatever, right?

00:22:13.980 --> 00:22:19.040
Like putting, putting that out there and just giving a sense that this is a really fast framework.

00:22:19.040 --> 00:22:25.760
You know, how does it compare to things like FastAPI or court, which is the async version of Flask ish, right?

00:22:25.760 --> 00:22:29.860
They're working on unifying those more, but basically the async version of Flask for now.

00:22:29.860 --> 00:22:35.520
Sienek and then Starla, who wants to give us a quick summary of this graph you got here in the benchmarks page?

00:22:35.520 --> 00:22:40.880
Before we get into the benchmarks, you said it already, but I want to add another disclaimer.

00:22:40.880 --> 00:22:47.520
Like, as you said, benchmarks are really, really controversial topic and they're insanely hard to get right.

00:22:47.520 --> 00:22:53.480
And it's even harder to get actually benchmarks that are useful for your use case.

00:22:53.480 --> 00:23:00.140
And they show you what you actually want to measure because most of the time they don't.

00:23:00.140 --> 00:23:09.300
They measure something, but they often don't translate one to one or even somewhere close to that to real world performance.

00:23:09.300 --> 00:23:12.700
And I have spent a lot of time on these benchmarks.

00:23:12.700 --> 00:23:19.780
And I want to say that the benchmarks didn't came about as us trying to compare to other frameworks,

00:23:19.780 --> 00:23:26.420
but they were experiencing some performance regression internally after a major change somewhere.

00:23:26.420 --> 00:23:28.420
And we were trying to track that down.

00:23:28.420 --> 00:23:39.420
And for that, I developed a quite comprehensive benchmark suit that tried to get us close to a real world usage of how we expected the framework to be used.

00:23:39.420 --> 00:23:43.180
And then that grew to compare other frameworks as well.

00:23:43.180 --> 00:23:53.480
And when I added the other frameworks, I tried to follow a very, very simple philosophy, which is not necessarily where some might say it's unfair.

00:23:53.700 --> 00:23:57.660
I think it's one way to get a comparable result.

00:23:57.660 --> 00:24:01.340
What I tried to do is to not optimize anything.

00:24:01.340 --> 00:24:08.120
I just used every, I built the same app in every framework with the framework as it comes out of the box,

00:24:08.120 --> 00:24:12.620
just took the straight up approach that's shown in the documentation.

00:24:12.620 --> 00:24:17.640
And I did that because from almost all of the frameworks, there is for every case,

00:24:17.640 --> 00:24:23.260
some way to make it a little bit more performant in this special case and in that special case.

00:24:23.260 --> 00:24:26.100
And I'm not an expert in all of these frameworks.

00:24:26.100 --> 00:24:33.620
And I'm sure if you start optimizing, there's no point where you can say, okay, now it's completely optimized.

00:24:33.620 --> 00:24:38.580
So I just took the completely opposite approach and didn't optimize anything at all.

00:24:38.580 --> 00:24:39.900
And that includes Litestar.

00:24:39.900 --> 00:24:45.300
We also do things that could be made more performant in Litestar, but we don't do them in the benchmarks.

00:24:45.300 --> 00:24:49.300
Well, that's just our bench line of what we are comparing to.

00:24:49.300 --> 00:24:52.260
And I just think it's an important context to have.

00:24:52.260 --> 00:24:53.100
Yeah, that seems fair.

00:24:53.100 --> 00:24:54.740
So we know what we are comparing.

00:24:54.740 --> 00:24:55.140
Right.

00:24:55.140 --> 00:24:55.500
Okay.

00:24:55.500 --> 00:24:59.740
So if we look at the benchmarks, one thing we can see there.

00:24:59.740 --> 00:25:02.980
So we have the synchronous and asynchronous performance.

00:25:02.980 --> 00:25:10.300
And one thing that we can see there is that for Litestar, it's almost identical compared to, for example, Starlet, where it's not.

00:25:10.300 --> 00:25:16.740
The reason for that is our model of execution for synchronous operations.

00:25:16.940 --> 00:25:34.580
What Starlette does is that for the synchronous function and asynchronous framework and it's blocking, you might potentially block your main thread and all other requests that are coming in at the same time.

00:25:34.580 --> 00:25:36.220
You don't want that.

00:25:36.220 --> 00:25:42.220
So definitely the safest option is to just put that in a thread pool, let it run and you're good.

00:25:42.220 --> 00:25:42.820
You're good.

00:25:42.820 --> 00:25:46.620
The thing is, threads are slower than asyncIO.

00:25:46.620 --> 00:25:54.420
And so what we do is we force our users to make a deliberate choice when they have a synchronous function.

00:25:54.420 --> 00:26:00.180
So we say, do you want to run that synchronous function in a thread pool or not?

00:26:00.180 --> 00:26:02.460
And if not, we just don't do that.

00:26:02.460 --> 00:26:09.660
Is that done by a parameter to the, you know, like at get or something and then you say thread pool, yes or no or something like that?

00:26:09.660 --> 00:26:10.980
You can set that as a parameter.

00:26:10.980 --> 00:26:17.380
And if you don't and you use a synchronous function, you get a very nasty warning, warning you about that.

00:26:17.380 --> 00:26:21.860
You can shut that off globally because some, yeah, that.

00:26:21.860 --> 00:26:24.020
Sync to thread equals false.

00:26:24.020 --> 00:26:24.700
Okay, cool.

00:26:24.700 --> 00:26:25.020
Yeah.

00:26:25.020 --> 00:26:28.660
You can shut that off globally if you don't want to be warned about it.

00:26:28.660 --> 00:26:40.580
But so we made the decision that it should be a deliberate choice if you want that behavior or not, because in many cases you don't actually need that behavior because you're not doing any blocking IO

00:26:40.580 --> 00:26:44.580
operations or any other blocking or CPU bound operations or whatever.

00:26:44.580 --> 00:26:50.740
So the, in fact, the synchronous functions are as blocking as the other async functions.

00:26:50.740 --> 00:26:54.960
So there's no benefit to be had from running in a thread.

00:26:54.960 --> 00:26:55.240
Yeah.

00:26:55.240 --> 00:27:05.940
And also a lot of times in production, the production server like G Unicorn or whatever is already using multiple threads or things to deal with that.

00:27:05.940 --> 00:27:08.340
And when, or at least multiple processes.

00:27:08.340 --> 00:27:16.480
And then when you're talking to things like a database or something, you're doing a network call, which deep down is probably releasing the GIL while it's waiting anyway.

00:27:16.480 --> 00:27:17.100
Right.

00:27:17.100 --> 00:27:22.140
There's a lot of subtles, subtleties that are, that are happening down there that maybe you don't want to juggle.

00:27:22.140 --> 00:27:22.420
Right.

00:27:22.420 --> 00:27:27.520
One point to add to that is that the sync to thread option applies to the dependency injection as well.

00:27:27.660 --> 00:27:30.920
And so it's not just the routes that you can add that flag to.

00:27:30.920 --> 00:27:36.380
And so to your point about databases, all those pieces can, can have that same kind of behavior.

00:27:36.380 --> 00:27:41.960
There's a benchmark for that as well somewhere that shows the same difference for the dependency stuff.

00:27:41.960 --> 00:27:42.240
Okay.

00:27:42.240 --> 00:27:42.700
I think.

00:27:42.700 --> 00:27:43.180
Yeah.

00:27:43.180 --> 00:27:46.340
So that's one difference and another difference.

00:27:46.820 --> 00:27:56.280
And so just to add, this is one choice that started makes for you and by extension, FastAPI as well makes for you that you can't easily turn off.

00:27:56.280 --> 00:28:02.620
But if you look, for example, at the sonic example, you see that it doesn't suffer from the same problem.

00:28:02.620 --> 00:28:06.640
So it's, you can attribute that to that, to this decision.

00:28:06.640 --> 00:28:15.600
The other big difference is because what we're looking at here is serializing a dictionary into a list of dictionaries into JSON.

00:28:15.600 --> 00:28:30.700
And one of the reasons why Litestar is so much faster in this than FastAPI, for example, is because we use msgspec, which is a JSON validation and parsing library.

00:28:30.700 --> 00:28:37.680
Well, not just JSON, it's also for message pack, which is an insane thing.

00:28:37.680 --> 00:28:46.940
It's an insanely great piece of technology, which we have been using, I think, for almost a year now when we started to introduce it.

00:28:46.940 --> 00:28:48.200
Yeah, that one.

00:28:48.200 --> 00:28:50.140
And it's super fast.

00:28:50.140 --> 00:28:51.500
It's written in C.

00:28:51.500 --> 00:28:57.180
The code can be a bit hard to get into because it's like one massive 12,000 line C file.

00:28:57.480 --> 00:29:03.820
So if you're not very familiar with C and the Python C API, it's not going to be an easy read.

00:29:03.820 --> 00:29:08.820
Yeah, but it's insanely fast and it supports a lot of things out of the box.

00:29:08.820 --> 00:29:17.980
So, for example, well, JSON, so all built in Python data types, but it also supports data classes and type dicks, which helps us a lot.

00:29:18.580 --> 00:29:34.840
And FastAPI, on the other hand, by default, well, it uses, for one, uses a standard library JSON module, which isn't as fast as any of the other external, well, not external, third-party JSON libraries that you can have.

00:29:34.840 --> 00:29:43.000
And it also uses Pydantic to validate the data, which I have to point out is something that we do not by default.

00:29:43.000 --> 00:29:46.820
So that's the reason why there's such a big difference.

00:29:46.820 --> 00:29:58.040
And even after Pydantic 2 has been released, which has been rewritten in Rust and has had a significant gain in performance.

00:29:58.360 --> 00:30:03.160
Yes, Samuel Colvin says something like 22 times faster, which is remarkable.

00:30:03.160 --> 00:30:09.260
Yeah, but still, if you just don't do that step at all, it's obviously going to be faster.

00:30:09.260 --> 00:30:10.660
Yeah, that's true.

00:30:10.660 --> 00:30:16.860
Can you, do you remember this graph here, whether this is FastAPI based on Pydantic 1 or 2?

00:30:16.860 --> 00:30:17.960
This is Pydantic 2.

00:30:18.120 --> 00:30:18.320
Okay.

00:30:18.320 --> 00:30:23.060
You could see that it's noticeably faster now with Pydantic 2.

00:30:23.060 --> 00:30:24.880
So there has been a huge gain.

00:30:24.880 --> 00:30:37.820
And to be fair to Pydantic and FastAPI, mostly FastAPI, you could also use FastAPI's ORJSON response, which uses ORJSON to serialize that.

00:30:37.820 --> 00:30:39.720
And it would be a lot faster.

00:30:39.720 --> 00:30:44.640
But as I said earlier, that would, to me, fall into the category of optimization.

00:30:45.280 --> 00:30:47.860
You could do similar things for Litestar.

00:30:47.860 --> 00:30:50.960
And what we wanted to compare is performance out of the box.

00:30:50.960 --> 00:30:53.180
And this is what you, what you get.

00:30:53.180 --> 00:30:56.980
Talk Python To Me is partially supported by our training courses.

00:30:56.980 --> 00:31:01.720
Python's async and parallel programming support is highly underrated.

00:31:01.720 --> 00:31:09.140
Have you shied away from the amazing new async and await keywords because you've heard it's way too complicated or that it's just not worth the effort?

00:31:09.140 --> 00:31:14.740
With the right workloads, a hundred times speed up is totally possible with minor changes to your code.

00:31:15.260 --> 00:31:17.160
But you do need to understand the internals.

00:31:17.160 --> 00:31:25.360
And that's why our course, Async Techniques and Examples in Python, show you how to write async code successfully as well as how it works.

00:31:25.360 --> 00:31:31.260
Get started with async and await today with our course at talkpython.fm/async.

00:31:32.060 --> 00:31:33.960
That's some of the stuff you're talking about, right?

00:31:33.960 --> 00:31:35.860
You could include new JSON parsers.

00:31:35.860 --> 00:31:39.660
You could include uvloop, for example, and there's a lot of optimizations, right?

00:31:39.660 --> 00:31:41.560
The benchmarks are on uvloop.

00:31:41.560 --> 00:31:44.960
I think that's one optimization we did across the board for everybody.

00:31:44.960 --> 00:31:47.840
Everyone uses a single uvicorn worker.

00:31:47.960 --> 00:31:48.180
Yes.

00:31:48.180 --> 00:31:52.180
So the environment is the same for all frameworks that we test.

00:31:52.180 --> 00:32:01.140
It's uvicorn with uvloop, the siphon dependencies, and one worker pinned to one CPU core that's shielded.

00:32:01.140 --> 00:32:04.280
So it just sort of gets something comparable.

00:32:04.280 --> 00:32:05.480
And that's awesome, actually.

00:32:05.480 --> 00:32:06.060
That's really cool.

00:32:06.060 --> 00:32:06.540
I like it.

00:32:06.580 --> 00:32:12.380
And I guess I'd just like to point out, though, that often there's other things that are in your bottleneck in your application, right?

00:32:12.380 --> 00:32:15.540
And so obviously benchmarks, take them with a grain of salt.

00:32:15.540 --> 00:32:25.900
And the other thing is that msgspec is awesome, but it's not as feature complete as something like Pedantic, which is really great.

00:32:25.900 --> 00:32:28.220
So I think there's some differences there.

00:32:28.220 --> 00:32:30.940
And so we wanted to make sure that you have the ability to use both.

00:32:30.940 --> 00:32:38.480
But in the context of benchmarks, you know, sometimes I guess it's worth noting that Pedantic is probably doing more or can do more than msgspec.

00:32:38.480 --> 00:32:44.480
But I don't think it's necessarily always going to be what you see here, the serialization piece that's going to be your slowest part.

00:32:44.480 --> 00:32:44.860
I agree.

00:32:44.860 --> 00:32:50.260
As a database guy, you might have database indexes and the lack thereof coming to mind or something, right?

00:32:50.260 --> 00:32:51.780
Well, that's one of the things, right?

00:32:51.780 --> 00:32:53.760
You know, and you kind of touched on it.

00:32:53.760 --> 00:32:59.520
It's the network latency and those kind of things between that's really going to consume quite a bit of the time.

00:32:59.680 --> 00:33:18.040
And I think we do have a benchmark, which is serialization of complex objects like Pedantic models or data classes or something like that, which actually I think is very interesting because it shows that if you're using Pedantic with Litestar, it's actually not faster than FastAPI.

00:33:18.040 --> 00:33:23.960
Because then what you're measuring is the speed of Pedantic, which in both cases is the same.

00:33:23.960 --> 00:33:26.020
And you can, it sounds like, which is interesting.

00:33:26.020 --> 00:33:27.440
Okay, so quick takeaway.

00:33:27.440 --> 00:33:29.440
Litestar is quite fast.

00:33:29.640 --> 00:33:31.940
One of the reasons you might choose it is the speed.

00:33:31.940 --> 00:33:34.240
And it sounds like there's a lot of good options there.

00:33:34.240 --> 00:33:34.580
All right.

00:33:34.580 --> 00:33:35.760
But not the only one.

00:33:35.760 --> 00:33:38.680
On that note, if you allow me to point out one more thing.

00:33:38.680 --> 00:33:39.180
Of course.

00:33:39.180 --> 00:33:40.600
We are quite fast.

00:33:40.600 --> 00:33:50.860
And I think for the feature set that we have, we are probably among the fastest, but we are not by far the fastest ASGIF framework out there.

00:33:50.900 --> 00:33:52.900
That would be, to my knowledge, black sheep.

00:33:52.900 --> 00:33:53.280
Black sheep.

00:33:53.280 --> 00:33:55.200
Which is insanely fast.

00:33:55.200 --> 00:34:00.580
And we actually don't include that in the benchmarks because it makes the benchmarks absolutely useless.

00:34:00.800 --> 00:34:04.780
Because then you just have one gigantic bar that's black sheep.

00:34:04.780 --> 00:34:12.760
And then you have two very, very teeny tiny bars, which is everything else, which is another micro framework that's written in Cython.

00:34:12.980 --> 00:34:14.420
I have not heard of black sheep.

00:34:14.420 --> 00:34:15.600
That's something I have to look into.

00:34:15.600 --> 00:34:16.140
Okay.

00:34:16.140 --> 00:34:16.540
Cool.

00:34:16.840 --> 00:34:19.080
But obviously, speed is interesting.

00:34:19.080 --> 00:34:19.840
Speed is important.

00:34:19.840 --> 00:34:25.640
It certainly is something that if it was really poor, people might choose like, well, it's interesting, but it's not that fast.

00:34:25.640 --> 00:34:30.040
But given the speed, it's certainly an advantage, not a drawback.

00:34:30.040 --> 00:34:34.200
But I think a lot of the advantages come from a bunch of the features.

00:34:34.200 --> 00:34:39.180
So maybe we could talk through some of these and whoever wants to just jump in on them as we go, feel free to.

00:34:39.180 --> 00:34:42.080
So I think it probably came through already from the conversation.

00:34:42.280 --> 00:34:47.880
But the programming API is very micro framework, Flask, FastAPI like, right?

00:34:47.880 --> 00:34:57.000
You create a function, you do an at get, give it a URL, decorator on the front, and you've got an endpoint on the web that you can do things with.

00:34:57.000 --> 00:34:58.600
So that's pretty straightforward.

00:34:58.600 --> 00:34:59.760
At its core, exactly.

00:34:59.760 --> 00:35:01.140
And we take it one step further.

00:35:01.140 --> 00:35:04.220
So, you know, all of the patterns you know and love from Django.

00:35:04.220 --> 00:35:07.600
So some of the things that you see from Django REST framework.

00:35:07.600 --> 00:35:12.760
So we have controllers that are very similar to that, where you can define a class and have multiple methods in it.

00:35:12.760 --> 00:35:15.800
And so, you know, that's really kind of where things start to differentiate.

00:35:15.800 --> 00:35:21.260
But at its core, we definitely wanted to make sure you had that exact micro framework experience that you see everywhere.

00:35:21.260 --> 00:35:24.600
So the first one, let's just touch on some of the main features here.

00:35:24.600 --> 00:35:27.280
The first one is data validation and parsing.

00:35:27.280 --> 00:35:30.840
So leveraging the power of type-ins, which is very, very nice.

00:35:30.840 --> 00:35:32.640
Who wants to highlight that feature?

00:35:32.640 --> 00:35:33.520
I think you're on mute.

00:35:33.520 --> 00:35:34.720
Good that you pointed that out.

00:35:34.840 --> 00:35:40.720
So that's definitely one of the areas that was directly inspired by FastAPI.

00:35:40.720 --> 00:35:52.100
Because FastAPI, a few years ago, came up with this brilliant idea of the combination of just levering the type-ins and the emerging Pydantic stuff and whatever.

00:35:52.100 --> 00:35:57.160
And build your APIs on that, based around that as your core.

00:35:57.860 --> 00:36:03.160
And it's been very increasingly popular to build your APIs like that.

00:36:03.160 --> 00:36:06.860
So it's definitely directly inspired and influenced by this.

00:36:06.860 --> 00:36:09.560
We are approaching things a bit differently, though.

00:36:09.560 --> 00:36:12.740
So, for example, you are not tied to Pydantic.

00:36:12.740 --> 00:36:21.280
You can use any data modeling, not any, but a lot of data modeling libraries that you might want to choose are supported out of the box.

00:36:21.660 --> 00:36:22.660
Pydantic is supported.

00:36:22.660 --> 00:36:32.460
You can also use msgspec, which supports some data modeling like Pydantic, not as featureful, but very, very fast.

00:36:32.460 --> 00:36:34.180
You can use attrs.

00:36:34.180 --> 00:36:42.180
You can use plain data classes or type dicts to validate your data and to transform your data,

00:36:42.180 --> 00:36:53.420
which is what you are currently looking at, which are our DTOs, which have been written by the brilliant Peter Schutt, who isn't here with us today,

00:36:53.420 --> 00:36:56.100
which are, well, data transfer objects.

00:36:56.100 --> 00:37:03.520
So they are a way for you to define how your data should be transformed on the way in or on the way out.

00:37:03.720 --> 00:37:13.280
So you have incoming data that's unstructured, JSON data, and you have a target model, and you might want to apply certain transformations to that,

00:37:13.280 --> 00:37:16.220
say, rename fields from snake case to camel case.

00:37:16.220 --> 00:37:18.200
Very common thing to do.

00:37:18.200 --> 00:37:18.640
Right.

00:37:18.640 --> 00:37:27.660
While you are validating it on the flyer that it confirms to a certain schema, for example, a Pydantic model or a data class.

00:37:27.660 --> 00:37:35.400
And so DDOs are basically an abstraction layer between that, where you can say, okay, this is my source model.

00:37:35.400 --> 00:37:36.820
This is my Pydantic model.

00:37:36.820 --> 00:37:41.460
And it has a user ID that's an integer and it has a name that's a string.

00:37:41.460 --> 00:37:48.600
And by default, Litestar, if you give it that, will validate that the incoming data confirms to that schema.

00:37:48.600 --> 00:37:57.960
We'll have Pydantic run all the validation and parsing on it like you would normally, which is quite similar to how FastAPI does it,

00:37:57.960 --> 00:38:01.620
or how you might also want to do it by hand.

00:38:01.620 --> 00:38:07.140
The DTOs come in where you have one data model that has different representations.

00:38:07.140 --> 00:38:11.500
So, for example, you might have a database model that's a SQLAlchemy model.

00:38:11.500 --> 00:38:22.960
But on the way out, you don't want to include the password field because of course reasons, but you want it on the way in when you create the user to sign up.

00:38:22.960 --> 00:38:31.260
So one way to do that manually would be if you're using Pydantic to create two models, one for the way in, one for the way out,

00:38:31.260 --> 00:38:37.440
or to create one base model with all the properties that are the same and then two additional models, whatever.

00:38:37.880 --> 00:38:41.520
DTOs basically do that, but they do it for you.

00:38:41.520 --> 00:38:44.980
So you don't have to actually write out those two models.

00:38:44.980 --> 00:38:49.080
They can take in one of the models, one of the supported model types.

00:38:49.080 --> 00:38:56.640
I think at the moment we support Pydantic, SQLAlchemy, msgspec, Adders, and Data Classes.

00:38:56.640 --> 00:38:57.860
Correct me if I'm wrong.

00:38:57.860 --> 00:38:58.920
No, I think you got all of them.

00:38:58.920 --> 00:39:03.520
So if you have a class of that type, you can create DTO from it.

00:39:03.520 --> 00:39:11.560
And then you have a DTO config where you can say, exclude these fields or only include these fields and rename these fields.

00:39:11.560 --> 00:39:16.440
And all you have to do is create a type annotation with that DTO.

00:39:16.440 --> 00:39:25.420
And Litestar will take it, use it to transform your data, and then give your back your original model in the form you specified.

00:39:25.420 --> 00:39:26.040
I see.

00:39:26.040 --> 00:39:31.780
And you say in the Decorator, you said the DTO model that does that conversion for you.

00:39:31.780 --> 00:39:32.320
Got it.

00:39:32.320 --> 00:39:33.140
Yeah, that's a good point.

00:39:33.140 --> 00:39:37.340
You said it in the Decorator and not at the point where you receive or return the data.

00:39:37.340 --> 00:39:43.300
So the data you receive and return will always be the actual model that you're dealing with,

00:39:43.300 --> 00:39:47.900
which has the great benefit that your type annotations are always correct.

00:39:47.980 --> 00:39:55.580
And you don't have to worry about that, about, you know, casting something to something else or doing the civilization in your route handle directly,

00:39:55.580 --> 00:40:01.040
because otherwise the type annotations for the return type won't match because you have excluded the field or whatever.

00:40:01.040 --> 00:40:07.540
So you just set it completely separately from that, just as information for Litestar to say,

00:40:07.540 --> 00:40:10.300
okay, use this to do the transformations.

00:40:10.660 --> 00:40:15.840
But the end result is my original model, whatever you want it to be.

00:40:15.840 --> 00:40:21.260
Okay, so this is kind of the model equivalent of FastAPI, the DTO.

00:40:21.260 --> 00:40:22.300
That's really neat.

00:40:22.380 --> 00:40:29.940
There's a lot of, maybe a little bit of overlap in something like SQL model, right, where you can declare your SQLAlchemy model as a Pydantic model.

00:40:29.940 --> 00:40:34.100
And in this case, we, and you're welcome to use SQL model with Litestar.

00:40:34.100 --> 00:40:38.580
But in this case, you can now just use the normal SQLAlchemy model and declare a DTO,

00:40:38.580 --> 00:40:45.360
and it'll automatically convert that to, you know, a msgspec struct on the way out and serialize it that way.

00:40:45.360 --> 00:40:45.740
Very cool.

00:40:45.900 --> 00:40:46.600
That's a good point.

00:40:46.600 --> 00:40:48.340
You bring up the msgspec struct.

00:40:48.340 --> 00:40:54.460
So that's one other area where we use msgspec for to create these models,

00:40:54.460 --> 00:40:58.820
because msgspec is extremely fast and has this struct type,

00:40:58.820 --> 00:41:02.600
which is sort of like an address class or a Pydantic model.

00:41:02.600 --> 00:41:06.240
But it has the benefit of being, as far as I know,

00:41:06.240 --> 00:41:12.180
the fastest library for that type of stuff for Python that exists at the moment.

00:41:12.740 --> 00:41:18.520
So what we are building there, the transformation layer is as performant as it can be.

00:41:18.520 --> 00:41:18.880
Excellent.

00:41:18.880 --> 00:41:21.960
In fact, I think, and I have to go look up the actual quote,

00:41:21.960 --> 00:41:27.000
but I think the struct is actually faster than the data class in a lot of scenarios.

00:41:27.000 --> 00:41:29.480
And so they've done an incredible job with that library.

00:41:29.480 --> 00:41:30.600
That is incredible, actually.

00:41:30.600 --> 00:41:32.580
And you're beating the building stuff, right?

00:41:32.580 --> 00:41:33.220
That's cool.

00:41:33.220 --> 00:41:33.820
All right.

00:41:33.820 --> 00:41:36.900
We talked a little bit about the open ecosystem, right?

00:41:36.900 --> 00:41:41.780
The ability is pedantic versus other, you know, custom DTOs, other libraries.

00:41:42.160 --> 00:41:47.080
OpenAPI, Swagger, the whole generate your documentation for you.

00:41:47.080 --> 00:41:48.240
That sounds pretty excellent.

00:41:48.240 --> 00:41:52.400
I'm guessing it's based a little bit on the DTOs as well to describe the schema.

00:41:52.400 --> 00:41:56.880
Every class has the ability to export what that output looks like.

00:41:56.880 --> 00:42:03.060
And so the DTO knows how to output its signature so that it can generate the correct open API schema.

00:42:03.460 --> 00:42:08.360
And I guess really the main thing to point out, and, you know, we obviously do the typical Swagger schemas,

00:42:08.360 --> 00:42:13.120
but one other thing that we add in is Redock and Stoplight elements as well.

00:42:13.120 --> 00:42:17.100
And so you've got a couple of options for your documentation host.

00:42:17.100 --> 00:42:18.060
Middleware.

00:42:18.300 --> 00:42:29.100
So middleware is things that can see the request before your view function runs or make changes after it runs for cores or logging or other things.

00:42:29.100 --> 00:42:30.240
Do you want to talk about that?

00:42:30.240 --> 00:42:33.820
Cody, do you want to talk about the compression stuff, for example?

00:42:33.820 --> 00:42:34.800
I'll happily do that.

00:42:34.900 --> 00:42:47.120
So, you know, you kind of nailed what the core of the middleware is, but really it's all those pieces that you need to add in to maybe add in security or add in some type of additional functionality, compression, for instance.

00:42:47.120 --> 00:42:53.140
And so a lot of, you know, outside of the plugin system, a lot of that functionality is included in the middleware.

00:42:53.140 --> 00:42:59.580
And so you'll see built-in stuff for most of the things that you're going to want to do out of a normal application.

00:42:59.760 --> 00:43:04.900
There's probably a few things that you may need to roll on your own, but we've got all the core things.

00:43:04.900 --> 00:43:07.980
And so you've got compression, both Brotley and GZIP.

00:43:07.980 --> 00:43:11.200
You've got open telemetry and Prometheus integration.

00:43:11.200 --> 00:43:25.280
You've got several different types of authentication backends that would get integrated here, including a session-based backend that, one, there's a cookie-based backend and a session-based backend where it stores, you know, on the actual server itself.

00:43:25.280 --> 00:43:29.440
And we also have a JWT auth configuration that you can use here.

00:43:29.520 --> 00:43:34.180
So I encourage all of the listeners to just check out what we have as part of the default middleware.

00:43:34.180 --> 00:43:40.000
But, you know, most of the things that they're going to want to do from a web app are going to be built right in.

00:43:40.000 --> 00:43:44.320
We also have the logging, the cores, all the basic stuff.

00:43:44.320 --> 00:43:47.080
See it like cross-site reference, rate limiting.

00:43:47.080 --> 00:43:50.660
Cross-site, yeah, requests forgery for forums, yeah.

00:43:50.660 --> 00:43:55.760
And then you can add your own, and they're all just little ASCII apps that you can plug in as you need them.

00:43:55.760 --> 00:43:58.220
On before and on after a request, something like that, right?

00:43:58.480 --> 00:44:08.460
This is really cool here where you could say in a particular view decorator, you can say, for example, exclude from CSRF for just this form, for example.

00:44:08.460 --> 00:44:11.000
And actually, this is something that you'll see as a feature.

00:44:11.000 --> 00:44:15.220
You're going to see this all throughout the code, and it's layered permissions.

00:44:15.220 --> 00:44:20.260
And so this excludes from CSRF and several other things.

00:44:20.260 --> 00:44:20.440
It's hard to say.

00:44:20.440 --> 00:44:20.980
You may see that.

00:44:20.980 --> 00:44:21.940
It is a mouthful.

00:44:21.940 --> 00:44:24.640
You're going to see that in several different places, right?

00:44:24.700 --> 00:44:30.500
And so you can apply that at the controller, or you can apply that at the route level that you see here.

00:44:30.500 --> 00:44:37.060
And so that's one of the helpful features that you'll see where you can put it in one spot, and it'll cascade down.

00:44:37.180 --> 00:44:38.440
Yeah, I saw that for the DTOs.

00:44:38.440 --> 00:44:38.700
Yeah.

00:44:38.700 --> 00:44:47.840
We have a lot of that, like, layered dependency stuff, like, well, dependency, but dependency injection, like you could also do at the app level or just at the controller level.

00:44:47.840 --> 00:44:49.160
There's so much other.

00:44:49.160 --> 00:44:54.960
On applications, controllers, routers, and the route handles, these are our basic layers.

00:44:54.960 --> 00:45:04.880
And most of these types of configurations, so middleware's dependencies, header configurations, middleware configurations, they're all layered.

00:45:04.880 --> 00:45:10.100
So you can apply them on every layer you want, and they will affect the layers below that.

00:45:10.100 --> 00:45:16.040
So it's quite flexible how you want to or where you want to configure your stuff.

00:45:16.040 --> 00:45:21.520
Well, the ORM integration, we talked a little bit about SQLAlchemy as well, so that's pretty cool.

00:45:21.520 --> 00:45:23.860
And I'll be happy to elaborate on that.

00:45:23.860 --> 00:45:27.000
But, you know, we've covered quite a bit of what you'll see here.

00:45:27.000 --> 00:45:34.800
I think the only thing that I haven't mentioned that we've integrated in and that will be coming in 2.1 is the use of Lambda statement.

00:45:34.800 --> 00:45:43.980
And I'm not sure if you're even have seen that or if your listeners have, but it's a relatively new function that's in SQLAlchemy to help with statement caching.

00:45:44.140 --> 00:45:46.620
So the repository's been converted over that.

00:45:46.620 --> 00:45:48.760
Actually, I see some great, great things in the chat.

00:45:48.760 --> 00:45:50.420
There's HTMX integration.

00:45:50.420 --> 00:45:52.100
Obviously, you want to make sure we touch on that.

00:45:52.100 --> 00:45:58.340
And I really want to let somebody talk about the WebSockets and the channels integration, too.

00:45:58.340 --> 00:46:01.420
So there's some really cool stuff that I'd love for your listeners to hear.

00:46:01.800 --> 00:46:16.420
Before we move on to the WebSockets, which I also want to talk about, I do want to give, since we're on the ORM integration, I can see some comments out there from, for example, Roman behind Beanie, which is a MongoDB ORM or ODM.

00:46:16.420 --> 00:46:18.320
It says, I like the DTO concept.

00:46:18.480 --> 00:46:20.100
Having such tools separately would be great.

00:46:20.100 --> 00:46:27.240
Having things such as SQLAlchemy objects when needed, Pandas data frames, PyDenic model, depending on the context, is really cool.

00:46:27.240 --> 00:46:29.520
We actually have talked about that.

00:46:29.520 --> 00:46:44.500
Well, not we, the people present here, but Peter Schutt, the person who created the DTO implementation at me, we have actually talked about that, making the DTOs a separate library, because it's a very useful concept.

00:46:45.080 --> 00:46:47.980
So it's not something we have planned.

00:46:47.980 --> 00:46:50.460
It's something that has crossed our minds as well.

00:46:50.460 --> 00:46:51.160
That's very cool.

00:46:51.160 --> 00:47:02.160
The question was, what about the MongoDB people or the other NoSQL folks for whom SQLAlchemy doesn't necessarily want to talk to because it's relational?

00:47:02.160 --> 00:47:03.380
What's the story there?

00:47:03.380 --> 00:47:05.400
Like, is it still pretty easy to use Litestar?

00:47:05.400 --> 00:47:06.080
It is.

00:47:06.080 --> 00:47:13.700
And I think that there's actually a native integration that's maybe not totally finished, but there is an open PR for a Mongo-based repository.

00:47:13.900 --> 00:47:19.020
So there's going to be that much tighter coupling coming soon for those that want to use it.

00:47:19.020 --> 00:47:22.440
But if they, there's nothing that would limit compatibility now.

00:47:22.440 --> 00:47:26.140
So if they want to go ahead and configure that with your application, you're certainly free to do so.

00:47:26.140 --> 00:47:30.740
But there will be a first-party kind of clean integration for those things coming soon.

00:47:30.740 --> 00:47:31.480
Oh, that's excellent.

00:47:31.480 --> 00:47:34.920
So for right now, you know, B&E is based on Pydantic.

00:47:34.920 --> 00:47:36.400
You all work with Pydantic.

00:47:36.400 --> 00:47:39.520
It sounds like, can you just use that as the go-between maybe?

00:47:39.520 --> 00:47:40.000
Absolutely.

00:47:40.320 --> 00:47:45.140
And you're free to use Pydantic with Litestar just as you, as you can with, with FastAPI.

00:47:45.140 --> 00:47:45.860
It'll just work.

00:47:45.860 --> 00:47:50.320
And so there's no, there's no reason to change everything to msgspec.

00:47:50.320 --> 00:47:54.420
If you want to, you can mix and match and leave everything in Pydantic if that's what you prefer as well.

00:47:54.420 --> 00:47:55.120
That's a good point.

00:47:55.240 --> 00:47:59.620
So the thing we do, all these integrations with Pydantic and Eris and whatever.

00:47:59.620 --> 00:48:04.800
So they are not, they are not baked in somewhere deep into the application.

00:48:04.800 --> 00:48:06.520
They are all plugins.

00:48:06.520 --> 00:48:13.140
Plugins that you could write yourself if you wanted to and write for every library that you desire.

00:48:13.360 --> 00:48:25.820
That's one of the larger things that we tackled with the 2.0 release, where we tried to decouple us from Pydantic because we were based on Pydantic before and we wanted to be more open.

00:48:25.820 --> 00:48:40.420
So we basically ripped out everything Pydantic in Litestar's core and put it into a plugin and at the same time made sure that the plugin API was so versatile that it could support all the features that we had supported before.

00:48:40.420 --> 00:48:59.440
And now we're at the point where it's very trivial actually to have support for a library like Pydantic with everything from DTOs to open API to serialization, validation, parsing, fully supported by a fairly trivial plugin that you have to add.

00:48:59.440 --> 00:49:06.460
So even if it's not provided out of the box, it's fairly easy to just do it yourself.

00:49:06.460 --> 00:49:07.060
It's really cool.

00:49:07.060 --> 00:49:07.460
All right.

00:49:07.460 --> 00:49:08.180
WebSockets.

00:49:08.180 --> 00:49:09.740
Let's talk about WebSockets.

00:49:09.740 --> 00:49:10.540
Here's a little bit.

00:49:10.540 --> 00:49:12.440
Yannick was the mastermind behind this.

00:49:12.440 --> 00:49:13.000
All right, Yannick.

00:49:13.000 --> 00:49:16.600
First, tell people what WebSockets are and why they care about them.

00:49:16.600 --> 00:49:18.680
Why is this not just another ACTP request?

00:49:18.680 --> 00:49:19.580
WebSockets.

00:49:19.580 --> 00:49:22.020
Explaining WebSockets in a few sentences.

00:49:22.020 --> 00:49:24.260
Yeah, you have three sentences.

00:49:24.260 --> 00:49:24.580
Go.

00:49:24.580 --> 00:49:33.460
So for people who are around, have been around longer in the web development space, they might remember long polling.

00:49:33.460 --> 00:49:35.020
So where you had.

00:49:35.020 --> 00:49:35.360
Yes.

00:49:35.360 --> 00:49:36.620
Where you faked.

00:49:36.620 --> 00:49:42.660
Well, back and forth communication between the server and the client by having a request that never terminates.

00:49:42.660 --> 00:49:48.600
And you can always send more data from the server because the request wasn't actually done yet.

00:49:48.600 --> 00:49:52.920
And I would say WebSockets is kind of like that concept but evolved.

00:49:52.920 --> 00:50:01.420
So you can easily send bidirectional data from the server to the client with a very, very minimal overhead.

00:50:01.420 --> 00:50:05.320
And WebSockets are a core functionality of ASGI.

00:50:05.320 --> 00:50:17.020
You could, there were several ways you could do WebSockets with WSGI, but they were all not very easy and straightforward because they are asynchronous by nature.

00:50:17.500 --> 00:50:20.680
This is what they are, they are asynchronous communication channels.

00:50:20.680 --> 00:50:25.300
So making that into a synchronous protocol is always a bit tricky.

00:50:25.300 --> 00:50:32.000
And I think there's no ASGI framework that I know of that does not support WebSockets in some way.

00:50:32.000 --> 00:50:37.740
So it is a core functionality of that type of Python framework, I would say.

00:50:38.120 --> 00:50:43.280
And so our WebSocket implementation has kind of like two layers.

00:50:43.280 --> 00:50:51.340
You have the low layer where it's basically you receive the socket object, which is just the connection.

00:50:51.340 --> 00:50:57.200
And then you can act on that connection and you have to accept it and you can terminate it and you can send data or whatever.

00:50:57.680 --> 00:51:04.120
And then you have what we call WebSocket listeners, which are an abstraction over WebSockets.

00:51:04.120 --> 00:51:16.640
And they basically work like you would normally define a route handle in Flask or FastAPI or Litestar, where you receive some data and then you return some data.

00:51:16.640 --> 00:51:20.800
And that is your function and the rest will be handled by the listener.

00:51:21.020 --> 00:51:25.340
So you define a handler function for an event that might occur.

00:51:25.340 --> 00:51:32.780
One of the cool aspects of this is that these support all the features that Litestar supports in other layers of the application.

00:51:32.780 --> 00:51:34.540
So you can use DTOs with them.

00:51:34.540 --> 00:51:36.380
You can use validation with them.

00:51:36.380 --> 00:51:45.680
So if you define a DTO and you say, OK, so this is my model and this is what I want to receive, the incoming data from the WebSocket will be parsed as this model.

00:51:45.680 --> 00:51:49.220
It will be validated and then it will be presented to your function.

00:51:49.480 --> 00:51:58.740
So functionally, these WebSocket listeners, they look and work exactly the same as a regular HTTP route.

00:51:58.740 --> 00:51:59.520
Yeah, that's really cool.

00:51:59.520 --> 00:52:09.380
Enables another thing that a lot of ACI frameworks don't have, which is handling of WebSockets synchronously, because we do the async stuff in the background.

00:52:09.840 --> 00:52:20.540
So you can use an asynchronous function, but you can also just use a synchronous function because all the dealing with the actual WebSocket itself is handled somewhere else.

00:52:21.100 --> 00:52:32.360
So my thought was probably this is what I want to write in a standard like I want to receive a message from the client back to the server or and process that there.

00:52:32.360 --> 00:52:42.080
But if you want to do like all the weird multicast stuff, different listeners and groups of listeners you can do with WebSockets, that's probably the lower level version you're talking about.

00:52:42.080 --> 00:52:42.360
Right.

00:52:42.360 --> 00:52:43.400
Did I get that right?

00:52:44.160 --> 00:52:45.180
Perhaps not.

00:52:45.180 --> 00:52:47.140
It depends how weird you want to get.

00:52:47.140 --> 00:53:03.400
So a fairly standard use case would be for something like, let's say, a chat room where you have some sort of predefined channels and then you have multiple clients that want to send data over the same channel and then fan it out to all the other clients.

00:53:03.940 --> 00:53:13.220
And for stuff like that, we actually have a full integration, which we call channels, which themselves aren't necessarily tied to WebSockets.

00:53:13.220 --> 00:53:18.200
They are basically distributed message bus sort of thing.

00:53:18.200 --> 00:53:24.100
I have yet to come up with a good short description of what channels actually are.

00:53:24.100 --> 00:53:26.940
It changes a bit from depending what I'm talking about.

00:53:26.940 --> 00:53:29.940
I think message bus is the way to think about it.

00:53:29.940 --> 00:53:30.120
Right.

00:53:30.120 --> 00:53:32.360
And it keeps a history of the events.

00:53:32.680 --> 00:53:34.340
So a message bus is perfectly fine.

00:53:34.340 --> 00:53:36.620
They are backed by at the moment by Redis.

00:53:36.620 --> 00:53:38.060
We have different methods.

00:53:38.060 --> 00:53:43.460
So you can use PubSub or other methods that are quite a bit more involved.

00:53:43.460 --> 00:53:43.760
Yeah.

00:53:43.760 --> 00:53:47.180
And they can also handle WebSockets for you.

00:53:47.180 --> 00:53:52.620
So you can say, OK, so I have I want to create a channel named chat.

00:53:52.620 --> 00:54:01.680
And every time someone signs up to that, please accept the WebSocket request and then add the client to this subscriber list.

00:54:01.680 --> 00:54:06.160
And then every time a message comes in, I want to do this.

00:54:06.160 --> 00:54:12.440
And then I also want to distribute the message they send to all these other clients in those channels.

00:54:12.440 --> 00:54:17.180
And I know if they send a special message, then I want to unsubscribe them.

00:54:17.540 --> 00:54:22.020
So for this kind of standard use case, we have that built in.

00:54:22.020 --> 00:54:25.740
You can, of course, build your own logic on top of that.

00:54:25.740 --> 00:54:36.500
And as you said, if you want to go into really weird stuff, you will have to use the low level interface, which is also there, which you can also access from the WebSocket listener.

00:54:36.500 --> 00:54:43.840
So you can, by a dependency injection, just receive the raw socket object if you want to deal with that for some reason.

00:54:43.840 --> 00:54:47.120
So it's available if you need it, but you can do the easy thing by default.

00:54:47.120 --> 00:54:47.880
Oh, awesome.

00:54:47.880 --> 00:54:48.700
That sounds really cool.

00:54:48.700 --> 00:54:49.600
And the channels sound great.

00:54:49.920 --> 00:54:53.960
Also, Chris out in the audience said, does that also mean server-side events are?

00:54:53.960 --> 00:54:54.560
Yes.

00:54:54.720 --> 00:54:56.940
Server-sent events, rather, excuse me, are available.

00:54:56.940 --> 00:55:04.540
And I saw that you have a dependency on HTTPX SSE, which is like a lightweight, lightweight WebSocket type of thing.

00:55:04.540 --> 00:55:05.080
It's very cool.

00:55:05.080 --> 00:55:07.120
That's a development dependency for testing.

00:55:07.120 --> 00:55:08.120
Ah, for testing.

00:55:08.120 --> 00:55:08.500
Okay.

00:55:08.500 --> 00:55:08.760
Yeah.

00:55:08.760 --> 00:55:13.020
So we do have server-sent events support built in.

00:55:13.020 --> 00:55:13.920
You can do that.

00:55:13.920 --> 00:55:17.780
And you can, for example, use that in combination with channels.

00:55:17.780 --> 00:55:24.440
So instead of fanning out the messages via WebSockets, you could do that with a server-sent event as well.

00:55:24.560 --> 00:55:24.800
Excellent.

00:55:24.800 --> 00:55:25.340
All right.

00:55:25.340 --> 00:55:27.280
We're getting quite short on time.

00:55:27.280 --> 00:55:34.240
I want to close this out with maybe what would be the last thing in a process of building out an app in Litestar.

00:55:34.240 --> 00:55:35.920
And that would be deployment.

00:55:35.920 --> 00:55:41.620
And I didn't see a lot of conversation about how I should be deploying this stuff on the website.

00:55:41.620 --> 00:55:47.000
But I saw in the benchmarks that you said, we use G Unicorn with the uvicorn worker and so on.

00:55:47.000 --> 00:55:51.000
And it sounds like maybe using G Unicorn is a good option.

00:55:51.000 --> 00:55:52.140
What's the deployment story?

00:55:52.220 --> 00:55:56.600
What do you tell people that want to put this in some real scaled-out production story?

00:55:56.600 --> 00:55:58.120
That's still a good option.

00:55:58.120 --> 00:56:00.780
But personally, I think Cody as well.

00:56:00.780 --> 00:56:03.540
We've just been using uvicorn.

00:56:03.540 --> 00:56:04.440
No, no.

00:56:04.680 --> 00:56:06.260
Not as a worker, just by itself.

00:56:06.260 --> 00:56:08.040
That has worked really well.

00:56:08.040 --> 00:56:09.880
It really just depends on how you're going to run it, right?

00:56:09.880 --> 00:56:21.980
So if you're using Docker or Kubernetes or something else that's managing that process, then it's possible that Gunicorn may not be something that really is needed in your environment, right?

00:56:22.060 --> 00:56:24.200
And in fact, it might actually just add overhead.

00:56:24.200 --> 00:56:32.940
And so if you've got something like Cloud Run or Docker or Kubernetes or anything like that, what we've realized is that sometimes it's quite a bit faster to just run it with uvicorn.

00:56:32.940 --> 00:56:41.600
And if the process dies, then your container management, whatever you're using to manage those things, will automatically start and scale those processes out.

00:56:41.700 --> 00:56:45.840
It notices that the container exited anyway, and so it's going to create a new one, right?

00:56:45.840 --> 00:56:46.120
Okay.

00:56:46.120 --> 00:56:46.560
Got it.

00:56:46.560 --> 00:56:46.840
Correct.

00:56:46.840 --> 00:56:47.040
Yeah.

00:56:47.040 --> 00:56:49.940
But if I'm going to a Linux machine directly?

00:56:49.940 --> 00:56:54.720
Then I think there's more of a decision to make on whether or not you want to host it through something like Gunicorn.

00:56:55.100 --> 00:57:02.340
And I guess the other thing I'd like to add is that really there's any of the ASCII servers can run Litestar.

00:57:02.340 --> 00:57:07.900
So you've got what Daphne, Hypercorn, there's work that we're doing right now with Succotify.

00:57:07.900 --> 00:57:11.220
It's a mouthful as well, but they've got some cool stuff going on there.

00:57:11.220 --> 00:57:13.700
And so hopefully we'll have compatibility with that soon.

00:57:13.700 --> 00:57:20.880
And so, yeah, the idea is that the same way that you would host any other ASCII app would apply to how you would manage a Litestar app.

00:57:20.880 --> 00:57:21.160
Excellent.

00:57:21.500 --> 00:57:26.280
Then maybe Nginx or something like that in front for SSL and Let's Encrypt and all those things.

00:57:26.280 --> 00:57:26.740
Absolutely.

00:57:26.740 --> 00:57:29.220
I think that's probably about it for time.

00:57:29.220 --> 00:57:30.560
Final thing here.

00:57:30.560 --> 00:57:35.980
So you all said you just released a week ago or so, last week, release version two.

00:57:35.980 --> 00:57:39.500
Want to give a quick shout out to the changes for people maybe already using it?

00:57:39.500 --> 00:57:41.000
I don't know if that's even possible.

00:57:41.000 --> 00:57:44.520
This has been in development for over seven months now.

00:57:44.520 --> 00:57:44.860
Okay.

00:57:44.980 --> 00:57:52.540
And there have been substantial changes to basically every part of the application that you could think of.

00:57:52.540 --> 00:57:57.400
A lot of the features that we have talked about today, they are new in version 2.0.

00:57:57.400 --> 00:58:00.100
The DTOs, they are new in 2.0.

00:58:00.100 --> 00:58:02.100
The SQLAlchemy integration is new.

00:58:02.100 --> 00:58:02.920
Channels are new.

00:58:02.920 --> 00:58:04.880
The WebSocket listeners are new.

00:58:04.880 --> 00:58:06.880
Message spec integration is new.

00:58:06.880 --> 00:58:08.860
Pedantic billing being optional is new.

00:58:08.980 --> 00:58:13.960
So I think it doesn't make a lot of sense to compare it to version 1.0 in this context.

00:58:13.960 --> 00:58:16.040
So just strongly encourage people to use 2.0.

00:58:16.040 --> 00:58:17.480
Yeah, definitely.

00:58:17.480 --> 00:58:19.420
Please use version 2.0.

00:58:19.420 --> 00:58:21.940
We also have the new stores interface.

00:58:21.940 --> 00:58:22.620
As well.

00:58:22.620 --> 00:58:28.500
And so many other features that I forgot about or don't have time to list.

00:58:28.500 --> 00:58:29.880
HTMX requests.

00:58:29.880 --> 00:58:30.940
All sorts of good stuff.

00:58:30.940 --> 00:58:31.160
Okay.

00:58:31.320 --> 00:58:36.900
Yeah, there have been so many people, amazing contributors over the last several months,

00:58:36.900 --> 00:58:42.000
spending time on this and delivering awesome features for us, for the community.

00:58:42.000 --> 00:58:44.480
There's been so much work going into this.

00:58:44.480 --> 00:58:54.100
And well, I was relieved when I finally was able to hit the publish button on GitHub and we could finally get it out.

00:58:54.100 --> 00:58:55.480
That's a big, big project.

00:58:55.480 --> 00:58:56.000
That's right.

00:58:56.000 --> 00:58:59.540
A lot of people have already been using version 2 in production.

00:58:59.840 --> 00:59:01.860
Cody, you have Jacob, you have as well.

00:59:01.860 --> 00:59:05.620
Basically, since we started development, I actually haven't.

00:59:05.620 --> 00:59:11.480
I've stuck on 1.5 for a long time.

00:59:11.480 --> 00:59:12.180
Now it's out.

00:59:12.180 --> 00:59:16.780
One of the things I'll add is that the velocity of the project is, it seems to be really high.

00:59:16.780 --> 00:59:22.000
And, you know, it's encouraging to see all the contributions and all the edits that everybody's making.

00:59:22.000 --> 00:59:26.580
And so I, for one, am really excited about what it's going to look like in a year.

00:59:26.720 --> 00:59:33.420
You know, I think we've got a lot of opportunity ahead of us and, you know, looking forward to seeing, you know, everybody jump in and try things out.

00:59:33.700 --> 00:59:38.660
And, you know, if something doesn't work the way that you want it to, feel free to open up an issue or hop on Discord.

00:59:38.660 --> 00:59:45.360
We're all very responsive and would love to kind of hear what our users are thinking about as they build their applications.

00:59:45.640 --> 00:59:46.680
Seems like a great framework.

00:59:46.680 --> 00:59:52.080
I really like the balance you're striking between the micro frameworks and some of the batteries included.

00:59:52.080 --> 00:59:53.980
So congrats to all of you.

00:59:53.980 --> 00:59:59.440
Now, before we get out of here, just I'll ask one quick question, you know, for I usually ask at the end.

00:59:59.440 --> 01:00:06.040
And that is a quick shout out to some package or library out there that you like and want to spread the word about.

01:00:06.120 --> 01:00:08.280
I guess I'll start DuckDB for me.

01:00:08.280 --> 01:00:15.340
So I have used a massive amount of DuckDB and you can kind of think about it as like an analytical SQLite.

01:00:15.340 --> 01:00:16.880
And so it's in process.

01:00:16.880 --> 01:00:22.280
And so you can start it up and just run SQL directly from your Python process.

01:00:22.500 --> 01:00:30.020
And so the project that I'm working on at Google actually has quite a bit of DuckDB as kind of this like middle ETL piece where data gets ingested.

01:00:30.020 --> 01:00:36.260
We do things in DuckDB and then that actually gets exported to BigQuery or other database engines.

01:00:36.260 --> 01:00:43.820
And so this really has kind of opened up the flexibility of us to be able to do quite a bit of transformations just in RAM without having to write to disk.

01:00:43.820 --> 01:00:44.260
That's cool.

01:00:44.260 --> 01:00:47.960
And in process SQL lab database management system.

01:00:47.960 --> 01:00:48.180
Cool.

01:00:48.180 --> 01:00:52.060
He has been recruiting us to use DuckDB for quite a while now.

01:00:52.440 --> 01:00:53.380
He succeeded.

01:00:53.380 --> 01:00:55.160
That's cool.

01:00:55.160 --> 01:01:01.420
I love what the standard library has, but Click is like one of my favorite CLI building tools.

01:01:01.420 --> 01:01:06.020
Rich is the thing that makes you make your terminal beautiful.

01:01:06.020 --> 01:01:11.360
But there's this really cool package that just, I mean, you can use them both separately, but rich-click.

01:01:11.360 --> 01:01:15.440
And it's what we use for our CLI.

01:01:15.440 --> 01:01:21.900
You have the great Click CLI building stuff, but then rich on top of it automatically makes everything pretty.

01:01:22.100 --> 01:01:22.500
That's cool.

01:01:22.500 --> 01:01:30.020
So it's like all the magic and niceness of rich, but available for a click, like colors in your help documents.

01:01:30.020 --> 01:01:34.860
I haven't gotten to check out Sebastian's typer yet, but I've seen some screenshots and it's sort of similar.

01:01:34.860 --> 01:01:37.160
I don't know a whole lot about that, but...

01:01:37.160 --> 01:01:38.840
I think it also uses rich, right?

01:01:38.840 --> 01:01:39.320
I think so.

01:01:39.320 --> 01:01:39.660
Nice.

01:01:39.660 --> 01:01:40.180
All right.

01:01:40.180 --> 01:01:40.500
Janek.

01:01:40.500 --> 01:01:42.960
Well, for me, it's got to be msgspec.

01:01:42.960 --> 01:01:44.700
Because it's just...

01:01:44.700 --> 01:02:04.600
So if you do any kind of JSON parsing or serialization or msgspec parsing or serialization or data modeling that you might usually want to do with a data class and want to add a bit of validation on top, or, you know, just if you're curious, you should absolutely check this library out.

01:02:04.600 --> 01:02:06.320
Because it's super amazing.

01:02:06.320 --> 01:02:09.280
The author is really, really great.

01:02:09.280 --> 01:02:16.580
I can just give him just a huge shout out because he's done such a great job at supporting our integration with it.

01:02:16.920 --> 01:02:30.980
It has been quite a tight collaboration at some points because when we started integrating it, there were a lot of things where we felt like, okay, so, well, we kind of can't use it right now because of this reason or that reason.

01:02:30.980 --> 01:02:39.980
And he's been so responsive and helpful in finding ways for us to work around that or just straight up implementing features that were missing for us.

01:02:39.980 --> 01:02:41.260
And it's really...

01:02:41.260 --> 01:02:41.760
That's pretty awesome.

01:02:41.760 --> 01:02:43.260
I can't thank him enough.

01:02:43.260 --> 01:02:44.820
It's really great.

01:02:44.820 --> 01:02:46.340
It's a pleasure to work with him.

01:02:46.340 --> 01:02:50.900
And it's an awesome library that everyone should check out, I think.

01:02:50.900 --> 01:02:51.220
Cool.

01:02:51.220 --> 01:02:53.400
That's news to me, so I will definitely check it out.

01:02:53.400 --> 01:02:53.900
All right.

01:02:53.920 --> 01:02:55.500
Well, thank you all for being here.

01:02:55.500 --> 01:02:56.660
Final call to action.

01:02:56.660 --> 01:02:58.440
People want to get started with Litestar.

01:02:58.440 --> 01:02:59.120
What do you tell them?

01:02:59.120 --> 01:03:00.600
Go to lightstar.dev.

01:03:00.600 --> 01:03:01.560
You can read the docs.

01:03:01.560 --> 01:03:03.740
Use it 15 minutes, 30 minutes.

01:03:03.740 --> 01:03:04.980
You'll know if you like it or not.

01:03:04.980 --> 01:03:06.840
Join us on Discord if you have questions.

01:03:06.840 --> 01:03:09.860
We're happy to help answer anything that may come up.

01:03:09.860 --> 01:03:12.600
I don't think I can add anything valuable to that anymore.

01:03:12.600 --> 01:03:14.020
All right.

01:03:14.020 --> 01:03:15.100
Well, yeah.

01:03:15.100 --> 01:03:16.020
Sounds good, guys.

01:03:16.020 --> 01:03:16.960
Thank you for being here.

01:03:16.960 --> 01:03:18.020
Congrats on the project.

01:03:18.200 --> 01:03:18.660
Thank you, Michael.

01:03:18.660 --> 01:03:19.080
Bye-bye.

01:03:19.080 --> 01:03:23.080
This has been another episode of Talk Python To Me.

01:03:23.080 --> 01:03:24.900
Thank you to our sponsors.

01:03:24.900 --> 01:03:26.500
Be sure to check out what they're offering.

01:03:26.500 --> 01:03:27.920
It really helps support the show.

01:03:27.920 --> 01:03:30.060
Take some stress out of your life.

01:03:30.060 --> 01:03:35.540
Get notified immediately about errors and performance issues in your web or mobile applications with

01:03:35.540 --> 01:03:35.840
Sentry.

01:03:35.840 --> 01:03:40.840
Just visit talkpython.fm/sentry and get started for free.

01:03:40.840 --> 01:03:44.440
And be sure to use the promo code TALKPYTHON, all one word.

01:03:44.440 --> 01:03:46.340
Want to level up your Python?

01:03:46.740 --> 01:03:50.380
We have one of the largest catalogs of Python video courses over at Talk Python.

01:03:50.380 --> 01:03:55.580
Our content ranges from true beginners to deeply advanced topics like memory and async.

01:03:55.580 --> 01:03:58.240
And best of all, there's not a subscription in sight.

01:03:58.240 --> 01:04:01.140
Check it out for yourself at training.talkpython.fm.

01:04:01.140 --> 01:04:03.220
Be sure to subscribe to the show.

01:04:03.220 --> 01:04:06.000
Open your favorite podcast app and search for Python.

01:04:06.000 --> 01:04:07.280
We should be right at the top.

01:04:07.280 --> 01:04:12.460
You can also find the iTunes feed at /itunes, the Google Play feed at /play,

01:04:12.460 --> 01:04:16.660
and the direct RSS feed at /rss on talkpython.fm.

01:04:17.080 --> 01:04:19.620
We're live streaming most of our recordings these days.

01:04:19.620 --> 01:04:23.040
If you want to be part of the show and have your comments featured on the air,

01:04:23.040 --> 01:04:27.460
be sure to subscribe to our YouTube channel at talkpython.fm/youtube.

01:04:27.460 --> 01:04:29.520
This is your host, Michael Kennedy.

01:04:29.520 --> 01:04:30.820
Thanks so much for listening.

01:04:30.820 --> 01:04:31.960
I really appreciate it.

01:04:32.220 --> 01:04:33.880
Now get out there and write some Python code.

01:04:33.880 --> 01:04:54.940
I'll see you next time.