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.


Talk Python's Mastodon Michael Kennedy's Mastodon