MongoDB with Async Python Transcripts
Chapter: FastAPI Example
Lecture: Package Details Implementation
Login or
purchase this course
to watch this video and the rest of the course contents.
0:00
So let's go ahead and implement one of these. As I said at the beginning, when we started this code, writing this code, we already did all of the work.
0:09
Recall these services over here. We have a package service, and it has really cool things like package by name, which is already async,
0:19
goes and does the query asynchronously with Beanie. Excellent. And moreover, the fact that this package itself
0:28
is a Pydantic model, FastAPI has deep integration with those. So we can just return it instead of
0:34
returning this dictionary. So that's pretty excellent. Let's try this. So we'll come over
0:39
here, we'll say package equals package service. Package by name, passing name here. And if we go
0:49
look again in here, we might want to do some normalization, all of our names are stored as
0:55
is lowercase, it will say something like if not name or not name dot strip return none,
1:04
right? So if they pass nothing, then we don't want, we don't have anything going on there.
1:10
But we can also say name equals name dot lower dot strip. Actually, we don't need the second
1:16
one. Maybe for some reason, we'd want it for performance reasons. But if it turns out to
1:23
be nothing when you strip it, it's still going to come up empty in the database. We have nothing called nothing. There's no primary key.
1:30
That's just empty string, which is good. Okay, so we're doing a little bit more validation whenever you're accepting arbitrary input
1:37
from users on the internet. This is a concern. Also, you want to be careful whenever you're talking to a database that you're not falling
1:45
into the little Bobby tables trap. Just Google or search xkcd little Bobby tables.
1:52
It's amazing, it's a cartoon about SQL injection, but because we're using an ORM and because
1:59
we're not using a SQL database, we're pretty much fine here. So no worries about that, but we do want to make sure we canonicalize the name and also
2:07
test for it being missing. Excellent. So now we have our package and we should be able to just return package. Let's start with that.
2:19
This looks like it should work. And again, remember, this is asynchronous, so we have to await it.
2:24
But that's totally doable because we're in an async function. Thank you, FastAPI.
2:29
However, I don't know how far back you remember exactly the order in which what happened.
2:34
But this is not going to work out well this API slash package slash details, it's going to crash, it looks okay so far.
2:40
And I got that internal server error response 500. That is not great. What's going on here?
2:48
weird errors about, in fact, how the package ID has no attribute. Okay, that's a little
2:58
bit weird. The thing that we're missing, the thing that is actually missing here, because
3:03
we know that that code was running, right? We ran that code over and over again, previously
3:08
in the Beanie world. What's missing here is actually connecting these models over to the
3:15
database. And recall, we wrote this Mongo setup thing. So this should be easy. So let's do it
3:21
right here. We're going to say Mongo setup, little control space couple times, we'll import that at
3:28
the top right there. And we just in it, and it's pi pi. And of course, this is asyncio. So we got
3:34
to wait it, but we're not in an async function. And there's no real other place to do this. So So we can just let's try this.
3:40
This is not the way asyncio that run things like that might work, right? Why would not work? Run it again. I see it. It worked. It connected a pipe.
3:53
Yeah, ain't no problem. Internal server error. Event loop is closed. What? I love this closed. Yeah. Okay, so here's what happens.
4:03
When we set up that connection, the event loop that runs to actually do the setup, the
4:12
event loop that runs to do the setup, that the motor client that persists for the life
4:18
of the application that is running and that's basically grabbed onto and used for all the other async work done by motor and beanie.
4:27
But after line 15 is closed, and then FastAPI manages its own async event loop. So this is a no, don't do that. It looks like a reasonable thing.
4:40
What we need to do is kind of come along here and have another function called configure DB. And take this code down and we await it. Seems normal.
4:55
How in the world is this supposed to be any different? So what we can do is hook into the FastAPI apps on event and it is startup, I believe.
5:07
Let's run it again and see, make sure it's all cleaned up. Run it again and see, oh yeah, look at that. As the application is starting up,
5:15
the on app startup ran, but the important thing here is this is running on FastAPI's internally managed
5:23
AsyncIO event loop, which will be consistent with the same event loop that runs this function asynchronously.
5:31
So when we do this query back into the client, everything's going to hang together.
5:36
It's really important that the startup and the server execution or whatever other work
5:41
you're doing talking to the database happens all on the same consistent event loop.
5:47
One of the annoyances of Python's AsyncIO, I feel like that should be handled way more
5:53
seamlessly for us, but it is not, which is okay. Let's go run this again and see what happens.
5:58
Oh, there it is. FastAPI, high performance, easy to learn, fast to code, ready for production API
6:07
framework. We can go just check it out. Like you remember, this is the readme on the PyPI page. This
6:13
is long, and then you can see all the releases. But check this out. We went straight from MongoDB
6:19
using beanie, turn that as a model, which is a Pydantic model, right, something more
6:25
specialized, but it, the beanie documents are Pydantic models, as we know. And so it
6:31
just plugs straight into FastAPI. How awesome is that? Let's duplicate this over here. And
6:38
let's look at one more thing. Okay, so we go back here. With FastAPI, you can say slash
6:44
unless you turn this off in that huge constructor for the FastAPI object. And it says, look,
6:52
these are all the things that you can do. And check out how awesome this is. This is
6:56
detail. This is reason. Well, this is the one we wrote. We give it a name and what comes
7:01
back is a string. Wouldn't it be nice if we could tell people? No, it has a whole lot
7:09
of structure. What is the structure exactly what is in MongoDB for this particular example.
7:15
So let's add that real quick over here as well. So in the decorator, not in the function,
7:22
we can say response model equals package. Import that from our models. Run it again. Look at this. Look at this schema that we're telling everyone.
7:39
This is what it looks like to receive a response from my details. It has a dox URL, which is a string.
7:47
It's the last updated, which looks like a date, but they can't say date because it's JSON.
7:52
It has a list of releases and each release embedded looks like this.
7:55
There's maintainers, which looks like a string, you know, converted from an object ID, right? So here we go.
8:02
Look how awesome this is our first glance at why it's so cool to use Beanie along with FastAPI. Let us count the ways. All right.
8:12
So it's amazing to use Beanie with FastAPI because FastAPI is awesome with async. And Beanie is 100% async native.
8:22
So they work together really, really well there. Another one is when we model our database, we do that with these Beanie documents, which
8:32
are themselves Pydantic models, FastAPI is all about returning that data, parsing that
8:40
data correctly, and even using it for its help in schema generation, right?
8:45
There's tools that'll take that open API definition and generate strongly typed consumers of our awesome PyPI data if they wanted to. Cool, right?
8:56
One final thing before I'm willing to call this done. could come over here again and say FastAPI to internal server again, not again. What
9:05
is it? It is that none is not allowed an allowed type. Okay, for package. But if we just do
9:14
a print name, guarantee you, that's still coming through. Somewhere. It's still coming
9:22
through, right? It's deep down in here. So what we want to do is, like the errors, we're
9:30
trying to return none when we said the response model was a package. So what we want to do
9:34
is say, you know, if not package, we could say like this, I guess if package is none,
9:40
if you prefer to kind of be explicit like that, what we want to return instead of trying
9:44
to return none, which we saw as a crash, we can return error out of our API. So we can
9:51
go to FastAPI responses and return a JSON response where the error is, let's say, be
9:57
more specific name, package name not found. And very importantly, the status code also
10:10
communicates that this resource doesn't really live here. Let's try again. Try again. Look Look at that, not found.
10:20
If we go inspect, go to network, try again, you can see the response somewhere here, a way on the left, or the wrong way, is a 404.
10:30
And if we go to the raw data and pretty print it, the error is package FastAPI two is not found. But of course, if we do this one,
10:38
we get a 200 A-OK and we get all the details. Similarly, if we do Beanie and others, right? Really, really cool.
10:46
So that's what it that was the final step needed to kind of round this out. But look at this code. Look at how amazing it is.
10:53
That's the entire implementation of package slash details slash give us a name. Really really cool.
11:02
This is an incredibly small amount of work to add on to go from we have these core services
11:08
that talk to the database and these models and pedantic and beanie models to just turn
11:13
them into async user facing HTTP services with proper error handling and proper documentation and schema communication there with open API.
11:27
Hopefully you are impressed but do not forget, do not forget that you must initialize the connection to the database on FastAPI's startup event.
11:39
If you try to do it directly, you just get some weird, hard to understand message that the event loop is closed. You're like, ""Well, this is broken.
11:48
I don't know what to do. All right, this is what's happening. Hopefully that will stick. Just come back to this example if you forget.
11:53
All right, I consider this a huge step forward. We've already connected to the database.
12:00
We've already created a package and returned all of the details. And the integration is really, really sweet.