[0:00] knowing how to architect your [0:01] application is a big deal when it comes [0:04] to building a fast API product we don't [0:06] just want to build a backend application [0:09] we want to build something that is clean [0:11] and easy to scale so in this video we'll [0:14] be creating a fast API application that [0:16] is following all of the best practices [0:19] which includes the domain layer like [0:21] where the enti development lives the [0:23] application layer where all the business [0:25] logic of the application lives the [0:27] infrastructure layer which is where the [0:29] database and rate limiting lives and [0:31] then we're also going to include all of [0:33] the unit testing and end to end testing [0:36] this video will cover everything you [0:37] need to get started with a clean [0:39] architecture for your next product if [0:41] you're new to the channel I'm Eric Roby [0:43] a software engineer with over a decade [0:45] of experience and I've helped over [0:47] 100,000 developers learn and grow within [0:50] their craft all right so with that let's [0:52] dive into some code all right so I [0:54] already have a environment created so I [0:57] have a directory that's already created [1:00] called clean architecture which is [1:01] hosting our application inside our [1:03] directory I just have a source directory [1:06] with a empty main.py file and then I [1:09] have a requirements. text file which has [1:12] all of our dependencies that we need to [1:15] run our application in a production [1:18] environment so not necessarily what we [1:19] need to run on our local machine but [1:22] what we need for a production [1:24] environment now in my requirements dd. [1:27] text this is what we need for for our [1:30] development environment which is like [1:32] you right now on your personal machine [1:33] if you want to code so what we can do [1:35] here is PIP install - R requirements [1:38] ddev [1:43] dotex this will install all the [1:45] dependencies that we need for our entire [1:48] project so now what we can do is let's [1:50] go ahead and jump into our main.py file [1:53] the first thing that we want to do is [1:54] just add all of the Imports that we're [1:56] going to be creating in the future and [1:58] that we need for this main.py file which [2:01] is just our database our entities our [2:03] API and our fast API and then what we [2:05] want to add here is simply our app which [2:08] is equal to fast API and then we're [2:11] going to have this base. metadata. [2:13] create all bind equals engine this we [2:16] only want to uncomment when we're [2:19] creating new tables when we run our [2:21] application for the first time this will [2:23] break our tests later on so I leave it [2:25] uncommented but it is a good thing to [2:27] have just in case you want to rapidly be [2:29] able to create new entities perfect now [2:32] we're going to get a bunch of errors and [2:33] that's okay cuz we're going to be [2:34] building everything step by step now the [2:37] next thing I'm going to just Implement [2:38] just so we have all of our [2:40] configurations down pth is something [2:41] called logging so I'm going to create [2:43] logging piy and inside logging it's [2:46] going to be a way for us to be able to [2:47] document kind of all of our endpoints so [2:49] if something happens in production or [2:51] happens on our environment where we [2:52] don't have to necessarily jump in and [2:54] start debugging everything with code we [2:55] can go ahead and just kind of look at [2:57] the logs that our application is [2:59] creating and and just be able to [3:00] identify hey that's where the error is [3:02] just by looking at the terminal so in [3:05] here I'm going to add this logging which [3:07] we're going to import logging we're [3:09] going to import an En from our string [3:12] and then we're going to just have this [3:13] log format debug which is showing us how [3:16] our log is going to format um debugs [3:19] then we're going to have our log levels [3:20] which is going to be info worn air and [3:23] debug and what this does is when we add [3:26] these into our code it'll tell us like [3:28] hey this is just information like maybe [3:30] this is just something that you might [3:31] want to know we might be like hey this [3:33] is a warning in our code if this happens [3:36] this is specifically going to cause um [3:39] Downstream effects then there's like air [3:41] like our application's broken and then [3:43] we can have debug errors for like in the [3:45] future so if you have like an object [3:46] that's getting weird we can just throw [3:48] in a debug so then when we're running [3:49] our application it'll be like hey this [3:50] is um the object debugged now what our [3:53] configure logging does is it takes in [3:55] one of these log levels and then we kind [3:57] of just do different things so it takes [3:59] it it capitalized which it already is [4:01] capitalized we find what level it is in [4:04] our log levels and then we're just kind [4:05] of setting up different permissions [4:07] based on the log level and then we just [4:09] enable our basic configuration to be [4:11] logging and if you've never logged [4:12] before we're going to be implementing [4:13] this in our application so you'll be [4:15] able to see exactly what's happening but [4:17] overall this is what it's going to look [4:18] like when you want to set it up from [4:20] scratch the next thing we're going to be [4:21] setting up is our rate limiting so [4:23] inside our source directory let's go [4:25] ahead and create a new file called rate [4:27] limiting and inside here we want to [4:30] implement our slow API which was what we [4:32] installed now this will allow us to be [4:34] able to do rate limiting on an endpoint [4:37] so what that means is we are saying hey [4:39] maybe our register we only allow a [4:42] certain user to be able to hit our [4:43] register three times before maybe we're [4:46] saying you need to take a break and this [4:48] allows us to protect from like DDS [4:50] attacks or some other kind of attacks [4:52] where people are trying to spam or use [4:54] Bots to call our endpoints over and over [4:55] and over again just to kind of collect [4:57] data we're putting rate limiting on in [4:59] this git remote address means it's going [5:00] to collect the IP address and we're [5:02] going to say hey based on your IP [5:03] address we are not going to allow you to [5:05] call certain endpoints you know X number [5:08] of times because it could be security [5:10] maybe the the endpoints cost money it's [5:12] something just to be like whoa slow down [5:14] you're calling way too many in points [5:16] way too um too fast so that's what we're [5:18] implementing right here and then we're [5:19] going to be adding it in our endpoints [5:21] here in a little bit and now what we [5:22] want to do is make sure we make this a [5:24] python package by saying underscore [5:27] uncore and knit underscore uncore .p [5:30] that means we can now call these very [5:32] easily within our project and now let's [5:34] go ahead and create our first package [5:37] now our first package is going to be [5:38] called entities now let's go ahead and [5:40] just say entities just like we did [5:43] before inside entities we need to say [5:45] new file [5:46] uncore and [5:49] nitpy that makes entities a python [5:53] package and now in this project we're [5:54] going to have two entities one for our [5:57] to-dos and one for our users so we can [6:00] go ahead and create um two more files in [6:02] here to-do and [6:06] users now first let's go into our to.py [6:09] and let's add some code right here so [6:12] what we're saying is we are simply going [6:14] to be adding our SQL Alchemy we're going [6:16] to be using uu ID for our um primary key [6:21] and then we're going to be using date [6:22] time and some other things in here so [6:23] here we're just saying what the priority [6:26] of this to-do is which is going to be 0 [6:28] to 4 we're going to create this now [6:30] to-do class and we're going to name this [6:32] table todos plural we have an ID of [6:35] column uid as uu ID is true it's going [6:39] to be our primary key and the default is [6:41] to is to use The UU ID for we're going [6:44] to have a user ID which is going to be a [6:46] foreign key back to the users which we [6:49] haven't created yet and that's also [6:50] going to be a uu ID then we're going to [6:52] have description due date is completed [6:54] created at and completed at and then um [6:57] our priority which is going to be part [6:58] of our numerations up here and then [7:01] simply we're just going to have this [7:02] string which returns all the data inside [7:05] we now want to do something very similar [7:07] for our users where we're going to have [7:10] a new table of user which is going to be [7:12] called and this table is going to be [7:13] called users our ID is also going to be [7:16] a uu ID and we're going to have an email [7:19] first name last name and password now [7:22] the only difference is we can see that [7:23] it's the to-dos have a foreign key back [7:26] to our users but our users don't have a [7:28] foreign key to our to-dos and that's [7:30] because it's a one to manyi relationship [7:32] there's only one user who can have [7:34] multiple to-dos but these todos can't [7:36] have multiple users right so we're going [7:38] to have one user that can hold multiple [7:40] todos now the next thing we're going to [7:42] add is this database and I kind of want [7:44] to put this in its own folder which is [7:46] going to be called database now inside [7:49] our database we're going to create a new [7:51] file of uncore uncore and [7:53] nitor [7:55] dopy and inside here we're going to [7:58] create a new python class called core [8:01] dopy now this is going to have quite a [8:04] bit of information inside so we're going [8:06] to have everything we need we're going [8:07] to load from ourv if you want to stick [8:11] this URL in a EnV file or we could use [8:15] sqlite or we could just hardcode [8:17] postgress right here we're going to have [8:19] our create engine session local base and [8:22] our git DB which creates a database [8:25] session and then we're going to have [8:26] this DB session which equals our [8:28] dependency injection DB session that we [8:30] can pass into functions so just for um [8:34] for practice let's go ahead and create a [8:36] new file called EnV now EnV files are [8:41] often a way for you not to have to pass [8:44] in your private secret information into [8:48] a source code repository so what we [8:50] could do is instead of passing this [8:53] directly into code we can create a new [8:56] thing called database [8:58] URL which is going to be equal to [9:01] something and we can just grab this [9:05] information paste it right [9:08] here and now I'm going to comment out [9:11] this as [9:13] well and I'm going to un comment here so [9:17] what's happening right now is we are [9:19] grabbing the database URL from a EnV [9:23] file and we're making sure that's set as [9:25] our database URL so when we create our [9:28] engine now let's go ahead and create a [9:30] new package right here called [9:34] off and inside our off we're going to [9:37] create a new file of uncore [9:40] anitore [9:42] dopy and now is when we're really going [9:45] to start seeing this clean architecture [9:47] design where we're going to have a [9:49] controller which consumes an endpoint a [9:50] service which does the um business logic [9:53] and then the models which is the pantic [9:54] data validation schemas so inside here [9:57] let's go ahead and create a new file of [10:00] controller. [10:02] Pi a new file of service. [10:08] piy and a new file of [10:13] model.py we're going to go backwards [10:15] from the model to the service to the [10:17] controller just so we can see how it's [10:19] created so what are the models that [10:21] we're going to need well we're going to [10:22] need a uuid and pantic as Imports but [10:25] we're going to really just have a couple [10:27] different ones for the authentication [10:28] which is to to register your user and to [10:30] be able to sign in a user so to register [10:32] you a new user we need a a email a first [10:35] name a last name and a password and then [10:37] we're going to do whatever we need to do [10:38] with that to save it to a database we [10:40] need a class of token which just Returns [10:43] the token back to the user for um [10:45] authentication which returns an access [10:47] token and a token type which is going to [10:49] be like JWT bear token and then we want [10:51] a class token data which is what are we [10:54] going to give back to the user once we [10:55] validate a token and this standpoint [10:56] we're going to get their uu ID so then [10:58] we can fetch whatever information we [10:59] need for that user based on their [11:01] primary key let's then jump into our [11:03] service doy which this is going to start [11:06] having quite a bit of data here where we [11:08] are going to have a secret key and a [11:10] secret algorithm again we can move that [11:13] into a EnV file just like we did with [11:16] our database core and I kind of showed [11:17] you how to do that so feel free to go [11:19] ahead and do that this is definitely [11:21] something that you're going to want to [11:22] keep secret and probably not push around [11:23] in you know different um repositories [11:26] we're going to say the algorithm is [11:27] hs256 and then the access token expires [11:30] is 30 minutes so our uh token is going [11:33] to expire in 30 minutes oaf 2 Bearer [11:36] token is going to be inside our off. [11:38] token path so this is our um which we [11:40] haven't created yet in our controller [11:41] but it's going to be slof SL token and [11:44] then our bcrypt contacts we're going to [11:45] be using bcrypt as our hashing um [11:48] contact so we're going to be saving all [11:49] of our database at database passwords as [11:51] a bcrypt hash um we have a couple [11:54] functions here like verify a password [11:55] where we're checking the plain password [11:57] a user passes in with the hashed [11:59] password to make sure that they're [12:01] validated we're going to be able to [12:02] fetch hash passwords and we need to be [12:05] able to authenticate users so if a user [12:08] you know passes in their email and [12:10] password we want to be able to query by [12:12] that user fetch them verify that user if [12:15] it fails well then we're going to log a [12:17] warning for anybody to see in our actual [12:21] like logging of our application which [12:23] says failed authentication attempt for [12:25] the specific user that tried to log in [12:27] if not we're just going to return a user [12:28] and you'll see that we have this [12:30] authenticate user getting called below [12:33] we have our create access token which is [12:34] our JWT code which takes in a sub email [12:37] ID and expiration date and then we [12:39] encode that JWT with our secret key and [12:42] algorithm so to make it like valid we [12:44] can then verify a token which returns a [12:46] models of token data so if we look up [12:49] here we can see that we're importing our [12:52] models and then we have our payload our [12:54] user ID we return the token data if [12:56] everything works out or we throw a [12:58] warning and we throw an authentication [13:00] error of token verification failed same [13:02] thing for our register user we're [13:04] passing in a new database session we [13:06] request a users's register request we [13:09] validate it and do everything that we [13:10] need to do for that and if not we throw [13:13] a registered user and we log it we can [13:16] get our user this is what we're going to [13:18] be using for um our dependency injection [13:21] for validating current users when we're [13:23] trying to deal with to-dos we have this [13:25] as an annotation now to fetch the [13:27] current user which is going to get our [13:28] current current user and then we have [13:30] our login for Access token which just uh [13:32] takes in a username and password and we [13:34] just hey does it check out we call [13:36] different functions or we throw an error [13:38] and then what we want to do here is add [13:41] in our controller our router for our API [13:45] routing which we're saying you can only [13:47] call register user five times an hour so [13:51] we're like hey you just can't sit here [13:52] and just keep creating new accounts over [13:54] and over again we're going to you know [13:56] slow it down now this request looks like [13:58] it's not being used but it's required [14:00] for our rate limiter to fetch the IP [14:03] address and then we also have a login [14:05] function which Returns the token now [14:07] this is from rate limiting limiter we [14:09] need to make sure this is from rate [14:13] limiting and perfect that's how we're [14:14] going to implement our off we're then [14:16] going to create a new folder called [14:19] users inside users we want the same [14:22] exact stuff so we're going to saycore [14:24] uncore [14:25] init.py [14:27] we also want to just say um controller. [14:32] py [14:37] service. and [14:39] model.py let's start with our model.py [14:42] where we're going to have a user [14:44] response which is going to have an ID [14:45] email first name and last name and a [14:47] password change of our current password [14:49] new password and new password [14:50] confirmation so a user once they log in [14:53] so once we hit the off endpoint we log [14:56] in a user we're going to return a jwbt [14:58] token and then when a user tries to log [15:00] in we'll grab the user token we so when [15:02] a user logs in they are able to fetch [15:04] information abouts well they can't fetch [15:06] all the information we save in the [15:08] database like their password cuz why [15:09] would we just give that out to people [15:11] but what we will give them is their ID [15:13] their email their first name and last [15:14] name and then we're also going to allow [15:16] users to be able to change their [15:17] password so you can pass in your current [15:20] password what you want your new password [15:21] to be and then kind of the confirmation [15:23] for the new password to make sure the [15:24] passwords match and these are going to [15:26] be the base models for the pantic data [15:28] validation that we're going to be using [15:29] here inside our service um we're going [15:32] to say get user by ID where we pass in [15:35] our database session and then here we [15:37] can just kind of fetch our user and [15:39] we're going to say get user by ID we're [15:41] going to fetch that user and then if [15:43] everything works out we're going to [15:44] return our user as a user response so a [15:46] user can say hey I want to fetch my [15:48] information by my ID after I validate [15:50] that I'm a the actual user and then [15:52] we'll have change password which takes [15:54] in the database session the user ID and [15:57] our password change mod model that we [15:59] just created in our model where we can [16:01] fetch the user by their ID we can verify [16:04] um their passwords or the current [16:06] password so the password that they typed [16:07] in is that really their password do the [16:09] new passwords match so the new password [16:11] and the new password confirm and then we [16:13] can finally update the password at the [16:16] end and then lastly in our controllers [16:19] we're going to have it prefixed as/ [16:21] users where we're going to have our [16:23] current user as our current user and [16:26] we're going to get our user by the ID in [16:28] the the same thing right here perfect [16:32] and now let's go ahead and check out our [16:34] to-dos so we can create a new folder [16:38] called [16:39] to-dos where again we're going to do the [16:41] exact same thing of uncore uncore in nit [16:44] uncore uncore dopy then we want to [16:46] create a new file called controller. py [16:50] a new file called service dopy and a new [16:55] file called [16:57] model.py now inside here we can just [16:59] paste some information we can say we [17:01] want our to-do base which is going to be [17:02] like if you want to create a new to-do [17:04] description due date priority create is [17:06] going to use exact same thing and then [17:08] our to-do response is just going to have [17:09] our ID is complete completed at and then [17:11] we're going to say hey we want this [17:13] model configuration to be um config [17:15] dictionary so we can consume and use the [17:17] data a little bit easier let's then jump [17:19] into our service where we can say hey we [17:23] want this to um be create Todo with our [17:27] current user token data d session to-do [17:29] model. create where we've done this a [17:32] ton of different times where we're just [17:33] passing in data and we're creating a new [17:35] to-do log it if necessary same thing for [17:39] our to-do get to-dos so based on an [17:41] authenticated user fetch all the to-dos [17:43] based on The UU [17:45] ID get to-do by ID so we can just pass [17:48] in The UU ID after we verify the user [17:51] fetch the specific to-do by its ID [17:54] return it back to the user update a new [17:56] to-do which allows us to be able to just [17:58] up update to-dos and then we have [18:00] complete to-do which makes the is [18:02] completed value the opposite so true or [18:05] false and then we save it to our [18:06] database and then we have our delete [18:08] to-do which allows us to delete a to-do [18:11] and then lastly we have our controller [18:13] which allows us to be able to call each [18:17] of those endpoints so like create a new [18:19] to-do get to-dos get to-do update to-do [18:22] complete to-do and delete to-do all [18:24] based on validating a specific user now [18:27] one thing that we are and this is all [18:31] model let me just go through this real [18:36] quick and now one more thing that we [18:39] want to implement is exception handling [18:41] so we can see like in our service we're [18:43] throwing some exception [18:45] handlers and let's just go into here [18:47] create a new file called [18:50] exceptions. piy and here we just want to [18:53] add some exceptions so our to-do error [18:56] um to-do not found to-do creation error [18:59] userbase error and we're just doing a [19:02] super call back up to the initialization [19:04] of the error that we're calling [19:06] beforehand and this just allow us to be [19:08] able to see a little things a little bit [19:09] cleaner be able to throw different [19:10] status codes and be able to kind of just [19:12] be able to scale our application a [19:14] little easier now the last thing I'm [19:16] going to add here inside our source I'm [19:20] going to say new file I'm going to name [19:22] this api. py where we're going to add [19:25] now all of our routers right here and [19:29] then in our main.py we already have [19:31] Register App register routes we can go [19:33] over here and just say hey we want to [19:35] register our routes of our app and then [19:38] we also want to make sure that we add [19:41] our configuration for us to be able to [19:43] throw some logs and then add them right [19:46] here so with everything that we just [19:49] added if we jump into our terminal and [19:51] we say uicorn [19:53] source. main colon app-- reload [20:00] we're getting an error inside our source [20:02] do off oops cannot import models oops [20:06] where am I saying models that should be [20:10] model that is my [20:13] bad and we also doing it in here yeah [20:16] that should be [20:20] model see even when you think you have [20:24] everything planned coding can throw some [20:26] errors at you oh goodness I did it in [20:28] all of them all right let's go back into [20:30] users uh should be [20:35] model model [20:38] model model all [20:40] right now everything should be good all [20:42] right so now if we open up our [20:44] application we go to our Port there it [20:47] is here's our whole application [20:49] everything looks great and if you try [20:50] and get something it's going to throw a [20:52] not authenticated because you have to [20:54] register an account and then grab the [20:56] token stick the token up here to be able [20:58] to to handle all the endpoints but now [21:00] let's go over how we can test this [21:02] application so there's two ways we can [21:03] really test it we can test it with unit [21:05] testing and we can test it with in to [21:07] end testing and how we can do that is [21:10] let's jump into our source let's go [21:13] ahead and say new folder oops let's jump [21:16] outside of our Force um our source and [21:18] let's create a new folder called tests [21:22] and now inside our tests we want to [21:24] create a newcore uncore and [21:27] nitor do Pi file and the first thing [21:30] we're going to create in here is [21:31] something called a com test file so com [21:34] test.py this is more of like all the [21:37] stuff to get our application ready to be [21:39] tested so here we know this is actually [21:42] that and this is limiting but what we're [21:45] doing here is we're just setting up a [21:47] fake SQL light test or SQL light test [21:50] database for our application we're um [21:53] doing everything we need so we're [21:54] setting up all of our entities for our [21:56] test database and then we're just [21:57] setting up our High test fixtures which [21:59] are more or less ways to set up data for [22:02] our tests to be called and we're doing [22:05] that all over in here first thing we're [22:07] going to do is our off test service so [22:09] we can come in here and we can say test [22:12] off [22:14] service. we just add in all of this [22:18] information where we're going to be able [22:19] to test against our verify password [22:22] authenticate user test login for Access [22:25] token be able to create a user or [22:27] testing a against everything inside our [22:30] service we can come over here and create [22:32] another file called [22:34] testore [22:36] users service. py where we're just [22:40] adding everything inside of here where [22:42] we're now going to be testing our um G [22:45] user information and then be able to [22:47] change a password and we're testing [22:49] really every way someone could try and [22:51] change their password and making sure [22:53] that all of our configurations are [22:55] correct and then the last thing is our [22:57] test to so our testore todos um service. [23:03] py we want to make sure all these are [23:05] good so here there's a whole bunch of [23:07] different tests about creating a to-do [23:09] getting to-dos get todos by ID [23:12] completing to-dos deleting to-dos [23:14] everything we need there and really what [23:16] we need to do here is we can just say [23:18] pie [23:19] test if you run pie test we can see our [23:22] um tests start running and everything [23:25] looks good we have all of our things [23:27] passed we can ignore this right now it's [23:29] saying something is going to be [23:30] potentially removed in the future but it [23:32] doesn't matter because a lot of times [23:34] these deprecation warnings last way past [23:36] the version date or sometimes they don't [23:38] even ever get fixed so that's okay we [23:40] can see that all of our all of our tests [23:42] pass and we have our test DB but now we [23:45] want to do in to end testing so what [23:47] that means is we are starting at the end [23:49] point and we're testing everything down [23:51] and everything back from the endpoint so [23:53] inside our test let's create a new file [23:55] called e2e which stands for end to end [23:58] and I'm going to say new file of [24:02] testore off uncore npoints [24:08] dop this is now going to test all of our [24:11] endpoints literally calling the [24:12] endpoints we can say we're calling the [24:14] off SL token we're calling the off SL [24:16] token again we are doing our endpoint [24:19] testing all the way down to our service [24:21] and back instead of just our [24:23] services we're going to do the same [24:25] thing here for testore [24:28] users uncore endpoints dopy and this is [24:33] going to test everything about a [24:35] specific user based on the endpoint all [24:37] the way down we're testing hey are we [24:39] getting the the right status code are we [24:40] getting the right responses we're just [24:42] making sure that everything looks really [24:44] good and now lastly we want to say [24:47] testore todos uncore um [24:51] endpoints we're again we are just [24:53] testing everything from a to-do [24:55] perspective based on the endpoint so [24:58] awesome stuff let's go ahead and just do [25:00] pie test again and now we should be [25:02] testing all of our endpoints as well and [25:04] this percent over here is just like it's [25:06] incrementing up to 100% where 100% is [25:09] done that's not like code coverage so [25:11] now we have all of our tests passing [25:13] cool so now for remember we added our [25:16] postgress right here and we want to make [25:19] sure that that is like the new [25:21] information so I'm just going to say uh [25:22] source. EnV from my standpoint that sets [25:26] all the environmental variables for um [25:28] my my local environment but what we want [25:29] to do now is let's deploy all of this on [25:32] Docker so if you don't already have it [25:35] um go ahead and download Docker from [25:38] your application and here you can see [25:40] that I don't have very much installed on [25:42] my Docker at the moment and what we're [25:44] going to do here is make sure that we [25:46] can run our application from Docker and [25:49] then also postgress will be created in [25:52] Docker so we're going to deploy our [25:53] application on Docker and we're going to [25:54] deploy our postgress on Docker so the [25:57] very first thing we need to do is is [25:58] create a new file inside our main thing [26:00] called a Docker file and inside here [26:03] we're saying use Python 3.11 from our [26:05] work directory of app we're going to [26:08] install our requirements. text we want [26:10] to copy the project files of our source [26:12] source and then expose on Port 8000 [26:15] using our Command that we use to run our [26:16] application and now we need a [26:20] Docker a Docker [26:25] compose yaml file [26:28] which is going to do everything else for [26:30] us so right here we can see that it's [26:31] going to spin up a postgress environment [26:34] which is going to be called clean fast [26:37] API on our port and we already set this [26:39] information up in our Docker in our um [26:41] database core file and then we're going [26:43] to deploy this postgress environment so [26:46] we're going to deploy our postgress [26:48] environment and we're going to run our [26:49] fast API [26:51] application and we can do that by saying [26:53] Docker compose up-- build when you do [26:57] this it's going to pull the image from [26:59] the latest postgress if you don't [27:01] already have it it's going to spin up [27:04] that postgress environment and then it's [27:05] going to deploy our application our [27:07] application is going to be communicating [27:09] with that postgress environment so it [27:10] says application startup was complete if [27:12] we come back over here and we refresh [27:16] this application is literally running [27:18] from a postgress database now in our [27:20] Docker environment if we open up Docker [27:22] we can see right here we have our [27:24] postgress running and our uh product [27:26] running if we like [27:29] but it's all in the same container so if [27:30] we just wanted to stop it we can just [27:31] stop that [27:32] container and our application crashes [27:34] just like expected because we turned it [27:36] off in Docker so awesome awesome stuff [27:38] guys this is I know this was a lot um [27:41] the the code is you can download the [27:43] code directly into descriptions I just [27:45] kind of wanted to walk through what [27:47] exactly is happening in case you wanted [27:49] to you know dive more into it so you can [27:51] scale and build from here this is [27:54] already a very professional application [27:56] which is using Docker tests in to end [27:58] tests unit tests structured in a way to [28:01] really scale our application so use this [28:03] how you like I hope you enjoyed it and I [28:05] will see you in the next video