📝 Zusammenfassung
openai-gpt-4o-mini
## HAUPTTHEMA
In diesem Video wird gezeigt, wie man mit FastAPI eine Foto- und Video-Sharing-Anwendung erstellt, die grundlegende und fortgeschrittene API-Funktionen wie Authentifizierung und Datenbankanbindung umfasst.
## KERNPUNKTE
• **Projektübersicht**: Aufbau einer einfachen Foto- und Video-Sharing-Anwendung, ähnlich den frühen Tagen von Instagram, mit einem Fokus auf den Back-End-Code und API-Entwicklung.
• **Benutzeranzahlung**: Die Anwendung bietet Funktionen für Benutzeranmeldung und -registrierung, die über JWT-Token gesichert sind.
• **Datenbankintegration**: Verwendung von SQLAlchemy für die Datenbankverwaltung und Speicherung von Benutzer- und Postdaten.
• **APIs und Endpoints**: Die Anwendung verwendet verschiedene HTTP-Methoden (GET, POST, DELETE) zur Verwaltung von Posts und Benutzerdaten.
• **Datei-Upload**: Implementierung des Uploads von Bildern und Videos mit der ImageKit-API, einschließlich der Handhabung von Transformationen und URL-Optimierungen.
• **Frontend-Integration**: Verwendung von Streamlit zur Schaffung einer benutzerfreundlichen Oberfläche für die Interaktion mit der API.
## FAZIT/POSITION
Das Video vermittelt umfassende Informationen zur API-Entwicklung mit FastAPI und demonstriert, wie wichtige Funktionen wie Authentifizierung, Datei-Uploads und Datenbankmanagement implementiert werden können. Es richtet sich an Entwickler mit grundlegenden Python-Kenntnissen, die lernen möchten, wie sie robuste Anwendungen erstellen können.
In this video, I'll teach you fast API
by working through a real project. I'll go over everything from the absolute basics
to some more advanced concepts like setting up authentication,
logging in various users, connecting to a database, and all of the components that you actually need
if you want to build a real production grade application. And that said,
this video is not designed for absolute beginners. While I will teach you everything from scratch
as it relates to APIs and kind of web app development, I'm going to assume
that you have some experience in Python. That said, let's quickly
have a look at the finished project, and then we'll get into some of the theory
and start writing some code. So the project will be building. Here is a simple photo and video sharing application. Think of it like the really early days of Instagram,
except you won't have nearly as many features. Now the way this works is you can sign in. So I've set up a really simple user interface here
and something called Streamlit. We'll talk a little bit
about that later in the video. But this isn't going to be focused
on building the interface. It's more going to be focused on the back end
and the logic and the well API with fast API. So you can see that I was able to sign in here,
and then immediately I'm brought to a feed where I can see some different photos. I can see the date they were posted
and the user that posted them. And then I also have some videos as well. Now you have the ability to upload something. So for example, if you were to come here and let's
just pick maybe a random photo that we have here, okay, let's just go like this and call this thumbnail
and then we share it. It's just going to take a second. And then this will be uploaded to our feed. So right now just uploading the video. And if we go back to the feed here
we should see the photo appear. And you can see the photo shows up by us. And then it's on today's date
when I'm recording this video. Okay. So that's the application. I know it seems pretty basic,
but I promise this is going to teach you a ton of concepts that you need to understand
as it relates to fast API. And I think the most important thing
is all of the authentication and authorization, which most people skip in these beginner
type tutorials. So with that said, let's hop over to this mirror board that I put together
because I want to start going through some theory. It's really important to understand
before we can even start building APIs. And by the way, as we get later into the video
and we're going to start setting up the images and videos,
that is notoriously pretty difficult to do. In order to do that effectively,
we're going to use today's sponsor image kit. Don't worry, they're free to use and play with. You don't need to pay for them
and they just make this process significantly easier. So big shout out to them. But more on that later. Okay, so let's get into the video. Now we're going to use fast API right. This is fast application programing interface. That's what API stands for. Now this is essentially a back end framework. What that means is that
this is going to be running on some type of server, and it is going to be essentially controlling data. All in API really does for us
is it facilitates the access and control of data. In our case
it's going to be image or video posts right. Or different user accounts. But before we can get into all of that, we need to start understanding
some kind of core concepts of web apps in general. So let's start by talking about URLs and endpoints
so that we can get the terminology out of the way. Now this whole thing right here
and this one as well is a URL. You've seen this, you know, millions of times before,
especially when you've browse to a website. And I want to just go over the components of the URLs
so we understand what they are. Now the first is the domain okay. The domain is essentially the website. You know, the space of the URL. So training.dev launched us. This is our domain tech with Tim on it. This is the domain. The domain will typically end in like.us or.com or dot net or TCA or something along those lines. Now after the domain you have what's referred to as the path or sometimes called the end point. Now this is the particular route or kind of the page or resource
that you're going to be accessing from this domain. So for example
we have training, our dev launched us Tim. So I'm going to the slash Tim page right here
slash courses slash Python. So I'm going to the Python courses page right. And for a typical website these make a lot of sense. But for our APIs
we're going to have to design these ourselves to kind of control the access and the route
or the endpoint to particular resources. So when we look at the project that we're going
to build here, where we're sharing, you know, videos or photos, we might have an endpoint
that is, you know, our api.com/photo, right. And then we can access a particular photo. You'll see what I mean. Second but let's keep going. Now the next point is the query parameter. Now the query parameter
is some extra bit of information that is typically used to filter the page,
or to get some more specific type of data. It always will come after a question mark. So you'll see some kind of path or endpoint,
a question mark, and then one or multiple query parameters. In this case
we have a parameter video equal to 123 okay. And then if we come here we have UTM sources
equal to YouTube. And page is equal to two. So we have two parameters. And you can have as many parameters as you like. You just have to have these ampersands
that separates them okay. So you just understand that these are
the core components of a URL at an end point. Now let's keep going here and talk about the request
and the response structure. So whenever we visit some type of website
we refer to that website as the client or the front end okay. So us as a user we go to our computer. We type, you know some website dot com okay. And then we are now on the client or the front end. Okay. And the front end is this kind of visual interface
that we're able to interact with that we're able to use in the case of our Post application or our photo application
we can see the different post, right. We can make a post. We can sign in, we can sign up. Now, the way that that actually
works behind the scenes is that this user interface is communicating
with some type of API. API stands for Application Programing Interface. The API
you can essentially see as kind of a secure layer. It's running on a different device,
some kind of server. So some computer essentially sitting in some location,
and it's facilitating all of the access to our data. So if we want to sign into our account,
we send a request from the front end or the client to this API. And this API returns some response. Okay. This is the flow that I'm trying
to get you to understand. You go to some website, you do something. If this thing involves some access to data, right? Especially if it's
maybe some confidential data or something, what needs to happen is a request will be sent
to some back end, this kind of secure location. It will essentially check can this user
do the thing that they want to do. And then if they can,
it will send this response back doing that thing. For example, deleting a post,
we would send a request to the back end. The back end would say, okay, yeah,
you know, I'm going to be able to delete this post. And then it would return the response saying, hey,
this post was deleted, I want to upload a photo. I send a request to my back end. I say I want to upload some photo, it returns some response and says yes,
your photo was uploaded successfully. So all of the heavy lifting,
all of the secure operations, everything related to data
essentially takes place on this back end or this API. And that's what I'm going to be showing you
how to build in this video. Now when we send a request
so we have this front end right. This client, it sends some data to the backend. And the main parts of a request
are the following okay. We have a type of the request
which we're going to talk about in a minute. Or the method we have a path. The path is what we looked at here. Right. So this path or this endpoint and oops
I didn't mean to say that. And we have a body. This is optional. But this includes additional data
that we want to send along with the request. So for example
like the image that we want to upload or the caption or the name of the post or something like that. And then we have headers. This is typically other additional information
that has to do with things like authentication. So in the header of our request we would include something
that indicates that we are, you know this user okay. We are signed in as you know
Tim Witek with Tim dot net. The header would kind of indicate
that we send that to the back end. I know this seems a little bit vague.
Just bear with me. We're going to make all of this
crystal clear. Sorry. When we actually get into the API example. We then have the response okay. So the request is the thing that we send from the front end to the back end,
essentially saying, hey, we want to do something. And the response is what comes back from the back
end to our front end or to our client. Remember, the front end is the thing
that you as the user actually see, and then the back end is the thing
that we, as the developer typically writes that facilitates
all of the communication and the data. So from the response we include a status code. You may have seen something like,
you know 404 before which means not found. That's an example of a status code. So our back end will send a status code
to the front end indicating what happened with the request common
status codes or something like 200. For example. Which means successful. I'm going to show you a few more in a second
and you get the idea. Then we have a body same as we have the body. In the request, we can send some additional data
back to the front end that the front end may need. And then we have headers. Same thing. Headers will be related to typically security authentication
the type of data, some weird things like that. If I scroll down here
I'm just going to show you a few status codes and a few of the request types
which are important to understand. Actually, let's start with the request types. So here is an example of a very simple API. This is a books API. And this is the slash books route or path or endpoint
or whatever you want to call it. Now you can see that
we have something called a Get endpoint. What this means is that we are retrieving some data from this resource, essentially from slash books. We then have delete. This is the type of method you would use
when you want to delete something. We have post. This is the type of method you use
when you want to create something and you have put. This is the type of method
you would use when you want to update something. So remember I mentioned when we send a request we specify some type or some method
and that indicates what the front end wants to do. So if the front end wants to get some data
about some books, it sends a Get request. If it wants to delete a book,
it sends a delete request. If it wants to create a new book,
it sends a Post request. And you can send all of these requests
to the same route or the same endpoint. Okay. And based on the type of the request,
you can do something different. So I hope that makes sense. But these are the common methods
or types of requests that you can set. Then we go over to Http status codes. Now you don't need to memorize all of this, but these are just some examples of status codes
that you may see. So you send this request right. You say, hey I want to get a book, for example. All right then what the back end or the API is going
to do is it's going to retrieve that data for you. It's going to send it to you. And along with that it's
going to give you a status code. So for example, it may say 200, which means okay,
may say 201 because it created something. You know, you get all these different ones
like these redirection. You have errors like bad request,
unauthorized payment required, a bunch of stuff that you can look at, you know,
internal server error, don't need to memorize them. I'm just showing you that there's a bunch of status
codes that are commonly used in web development. Okay. Now let's just have a look at an example
request and response. And I promise we'll get into the API. And again this will crystallize. But this is just going to really help
you understand how we design the APIs. Okay.
So a user wants to update a post that they made. So the type is patch. This is actually the exact same thing as put. So if you ever see patch it's the same as put
which just means you want to update something. If I wanted to create something
I would use post right. But I don't want to do that. I want to update. So I'm using patch. Okay.
So the type or the method of my request is patch. The path is slash API slash post. And then this is the ID of the post. So I'm saying okay I'm gonna go to my API. I want to modify post. This is the ID of the post that I want to modify. And then the body would be this. This is the data that I'm actually sending where
I'm saying, hey, this is the updated title and this is the new caption that I want to use
or the description or whatever for my post. Then the headers includes this. This is essentially my authorization token indicating, hey, I'm authorized to be able to perform this operation because of this thing
right here, which we'll talk about later on. So we take all of this, we send this to our back end, and then this is the request, okay, that we create. Now from the back end we get a response back. So we send a request, we get a response back. And the response it looks like this. We have status code 204 which stands for updated. And then we have somebody in this body says hey
this is the title. This is the description,
this is the post ID, this is when I was updated. This is what it was created. And it gives us that information back
to the front end so we can display it to the user. We then have some headers. And in this case we say, hey, the type of data
that we're returning back here is application Json, which is essentially just the format of this data,
which we can talk about later. Okay. I know that's a lot of information,
but that is the kind of flow of an API. And hopefully this is going to help us understand
how we create APIs in a minute when we start coding them out. Now, last thing
that we'll talk about a little bit later, I'll just quickly show it to you
is the authentication. So when it comes to using an authenticated API, it's a little bit more complex
than simply sending requests and getting responses. Essentially what you need to do is get something
called a JWT token. This JWT token looks something like this, where you send this along with every single request to indicate, hey,
I'm authorized to perform this type of operation. Essentially, you're identifying yourself
and what user you are so that the API knows you can do this thing
or you can't do this thing. You'll we'll look at that later. I don't wanna confuse you at this point, but that is
kind of the primer on web app development and APIs. Again, think of an API as essentially
this kind of back end server that sits there, that facilitates
all of the operations that have to deal with data creating, reading, updating, deleting data. That's effectively
what an API almost always deals with. And it's doing that so we can do it in a secure way
and then return data to some front end where the user can view it, display it,
you know, mess with it, etc.. So with that said, let's get onto the computer and
let's start actually writing some code in fast API. All right. So I'm inside of my code editor here. And for this video I'm going to be using PyCharm. Now you can use any code editor
or idea that you want. But I typically do recommend PyCharm
especially for Python projects because well it is PyCharm and it supports Python the best. In fact, I do actually have a long term
partnership with PyCharm. So if you want to use it for free,
you can click the link in the description. Try it out and see if you like it. It definitely is a great editor. And again what I recommend
for pretty much any heavy Python projects. Okay, so what I've done inside of PyCharm
is I've just opened up a new folder. You can see I've got a folder here
called fast API tutorial. The way I did that is I essentially just went to open
and I just opened a folder on my desktop. Okay. And you can again use any editor that you want. Just make sure you open a folder. Now from here what I'm going to do
is I'm going to open up my terminal and I'm going to start setting up my fast
API project. Now, in order to do that in Python,
you need to use something called a package manager. There's two notable package managers in Python. The first is Pip. The second is you've. Now I'm going to suggest that you use you've because this is it's significantly more modern
and it just works a lot better. But if you don't want to use you've you can replace
the commands I'm going to show you with Pip. Okay.
So you've is something that you need to install. If you don't have it installed I will leave a video
on screen that explains how to do so. But once you have you've installed on your computer. What you can do from this open
folder is you can type, you've init and then dot. What this is going to do is create a new UV project
for you, where you're able to isolate all of the dependencies for this particular project
in this kind of one folder. Okay. So we're going to type UV init dot. What that's going to do for you is it's
going to create a few files inside of your folder. You're going to see a main.py file,
a Pi project, AutoML, and a few other files
that you don't really need to worry about too much. Now, this py project AutoML file is going to include
all of the dependencies for your project and it's what we're going to start modifying now by installing some different dependencies
that we need for this project. So if you want to work with fast API
in order to do that, you need to install it. So what we're going to do
is we're going to type UV add. And then we're going to start by just adding
fast API. Okay. So we're going to type you've add fast API. When we do that if we go back into Pi project AutoML you'll see this
dependency is automatically been added for us. Okay. Now that we have it installed we'll be able
to actually use it inside of our Python code. Now as well as fast API. What we're going to do is we're going to type UV add
and we're going to install Python dash dot env. Now this is something that we need to use
to manage environment variables. Because in a minute we're going to have
some environment variables for handling our images and videos. So we're going to go ahead and install this. There's a few other things
that we need to install as well. So just bear with me. We're going to type UV ad. And we're going to install fast API users. And then inside of square brackets
we're going to type SQL alchemy like this. Okay. So make sure it's spelled exactly like this. UV ad fast API dash users SQL alchemy. This is what we're going to use when we start handling the authentication
and the authorization later on in our project. So we're going to go ahead and press enter. And then same thing.
It should get added to our dependencies. Okay. There's a few other ones
that we're going to need here. So we're going to type UV ad. And then we're going to add the image kit. So we're going to say image kit like this. And sorry it's going to be image kit I o. Now this is the package we're going to use to handle our images
and videos again, which we'll look at later on. We're also going to say you've add,
we're going to add UV corn and then standard UV. A corn is a web server in Python
that allows us to serve our fast API application. You'll see how that works in one minute. So we're going to add UVA corn. And I promise we are almost done.
Just a few more that we need to add. And then sorry we're going to add one more here,
which is going to be a I o s q light like that. We're going to use this
for interacting with our database. We may potentially need some more later,
but for now I think this should be fine. And that should handle
all the dependencies that we need for this project. Okay. So we're going to close that. And we're just going to quickly go here
and make a new environment variable file. Now to do that we're going to go new file. And we're going to call this file object env. This is the file that you create
when you want to store sensitive credentials tokens or keys that your application
is going to rely on. In our case we need to access a key for image kit, which is going to allow us to handle the image
and video uploads, which I want to do now. Okay, so inside of this file
there are three variables that we need to define. The first is going to be image kit
underscore private underscore key. All right. The next is going to be the image kit
underscore public underscore key. And then the last is going to be the image kit
underscore URL. Okay.
So we're going to put equal signs for all of these. And we're just going to quickly
grab these three values. So we don't need to come back to this
until much later in the video. Now you may be wondering what the heck are we doing? Tim, we haven't even started writing the API. I promise we're going to get there. I just want to get through everything,
do all of the setup, and make sure that it's all ready to go
so we can just focus on coding it. I'm not moving around too much right now. What we're doing is we're creating this
essentially kind of secret environment variable file. This is something that's going to hold some values
that we need for uploading the images and videos. Like I mentioned
we're going to use image kit to do this. So what I need to do is get some keys
and values from image kit. So I'm quickly
just going to open up the image Kit website. I'm going to leave a link to this in the description. What we're going to do
is we're just going to make a new account on here. Again it is free to use this.
You do not need to pay for it. And essentially what this does is
give you all kinds of amazing tools for handling your images and videos,
which is typically a huge pain. But they have all kinds of things like image
and video optimization, formatting, cropping. It's very interesting. So anyways,
what we're going to do is make a new account. I've already just made a new one,
so again, I'll leave that link in the description. And from here, what we're going to do
is go on to the developer options. From developer options we're going to look
for our public key our private key. And then we're going to get our URL
which is up here. So I'm going to copy my public key. And then I'm going to put my public key right here. I then I'm going to copy my private key,
which is something that you do not want to share with other people. And before it will allow you to do this, you
really do need to set a password for your account. So if you press this, I'm
just going to blur my email. But I'll press the profile page
and I'll just quickly set a password. Okay. Now the password is set. So we'll go back to developer options. We'll go private key. And I'm just going to copy this
after I use my password. So let's use that and copy this. Okay. So now that I've copied it
I'm going to go back to PyCharm. I'm going to paste it again. Don't share this with other people
I will delete it afterwards. And then lastly we're going to grab this URL endpoint
okay. Which should be right up here. And we're going to paste this inside. And now we have the keys that we need okay. So we're going to close the environment
variable file. And now what we're going to do is start setting up
kind of the scaffolding for our project. So I'm going to make a new folder. And this folder is going to be called src. This is typically best practice
when you're writing a fast API application. You create this source
or actually let's change it to be an app directory where you actually have all of the code
for your application. And then you have this main.py file, which is kind of
what triggers the application to run. So what we're going to do inside of this app folder is we're going to make a new file,
and we're going to call this app.py. And this is where we're going to start actually writing our fast API
app and start getting into some Python code. All right so let's start writing a API. We've gone to the point where everything is set up. What we're going to do from this file right here. So from here we're going to type from fast
API, import fast API with this capitalization. And we're going to say
app is equal to fast API with the set of parentheses. Now this is the fast
API application that we just created. And what we'll need to do
now is start setting up the different paths or endpoints
that we want to have accessible on our API. Now remember for our API we're setting this up
essentially to handle data, to be able to accept some type of request from our front end
or our client, and to return some type of data so we can create data, delete data, read data, update
data. Right. We need to decide because we're designing this API. So I'm going to start by just writing some simple
dummy endpoints just to test and see how this works. Then we'll get into endpoints
that actually make more sense. So the way that you make an endpoint in fast API is you type app, which is the name of this variable
right here that we defined dot. And then you specify the method
for this particular endpoint so it can be get post put delete depending on what you want this to do. Now the most basic type
which is common is to use app dot get. Now when you do this what you're going to do
is you're going to specify the endpoints. So we're going to say slash. And then something like you know
hello dash world okay. So this is the path or the endpoint. Now I also forgot I need to put an app symbol here
because this needs to be a decorator and fast API. A decorator is something with the app before it. And what you do beneath
this is you define a function. The function should typically be named something similar to this endpoint or path,
but it doesn't need to be. You put a set of parentheses and then inside of here
you can return some data. So let's just quickly return the autocomplete data
where it says message hello world. Okay so we have add app dot get slash hello world. What we're saying is hey when you go to our API
and you go to slash hello world, this function is going to be called. And then we're going to return this data. Now the data that we always return from our endpoints is either going to be a pedantic object,
which we'll talk about later. I know that might not make a lot of sense. Or it's going to be a Python dictionary. The Python dictionary looks like this, right? You have some key associated with some value. And the reason why we return Python
dictionaries is because when we create APIs, we work with something called Json. Now Json stands for JavaScript Object Notation. It is the format essentially
for dealing with data across the web. And you can essentially think of Json the exact
same as you would think of as a Python dictionary. Okay, it's not exactly the same. There's a few minor differences,
but in our case we can assume that anything that is a valid Python
dictionary will be a valid Json object. Again, keep in mind there's some caveats there, but generally
that is the case, especially with simple data. Okay. So now we've got this application right. We've defined this endpoint. But what we need to do is run it. Now there's many different ways to run the API. But the way that I'm going to suggest we do
it is by going into this main.py file here, deleting everything inside of here,
and then importing this app file and running it using something called unicorn. So what we're going to do
is we're going to say import unicorn. Like this. We're then going to say if underscore underscore name is equal to underscore underscore
main underscore underscore. Then we're going to say you've a corn dot run. We're going to put app dot app colon app I know this seems weird
I'll explain what it is in one second. We're going to say host is equal to 0.0.0.0. And we're going to say the port, is equal to 8000. And we're going to say reload is equal to true. Okay. Now what are we doing here? Well, first what we're saying is, all right, I want to use this web server called UVA corn,
which we've already installed, right. With UVA. And I want to run a web server. Now for the web server I want to run an API on it. The API that I want to run is apt on app. So that's inside the app. So the app folder here, the app file. And then I want to run the API
which is inside of the variable app. So let's say I want to change this. And I called this hi. Okay then I would change this to be hi. All right. So just keep that in mind. That's how I'm getting these variables essentially. So we have the name of the Folder's app right. The name of the Python application is app. And then this here is app. And we have app dot app call hint app probably should
have picked a better name for that, but it's okay. Hopefully you get the idea. Now when I say host this is specifying the domain essentially that I want to run this server on because we're running this locally
on our own computer. When I specify 0.0.0.0,
that just means run it on any available domain. So it's going to run on what's called local host,
which is just our own host, so only we can access it as well as our private IP address,
meaning anyone else on the network would be able to access this as well
if they knew the private IP address of this machine. Now, there is ways to run this publicly
so anyone can access it. Not going to get into that in this video,
but essentially the way that you're going to able to access this application is you're going to go
to whatever the IP address of this machine is. If we're on the same machine, it's going to be local
host, we're going to go to port 8000, and then we can access this resource right here,
which is slash. Hello world. So for this what we can do is run this main.py file. To do that we simply type you've run Main.py. Go ahead and press enter. And it says that there's some issue. This is because I'm currently
running this app on another what is it editor. So let me just shut the other app down and rerun it,
and then we should be good to go. You can see that it's running now again,
that issue you want to run into, it's because I had a demo application
running in a different, editor that I have open. You'll see what's happened here. Is it now says unicorn running on. And then it shows you the URL or the domain
where this is running. Right. And then it kind of goes through this thing
saying, hey, you know, started the reload process. And by me specifying reload equals true. Anytime I save or make a change, this file,
like if I do something I don't know. Hello here. And then I save this. You'll notice that the file sorry. The server will shut down and restart
with the changes that I made. So it's really useful
for when we're debugging and building something because it just shuts down and restarts
anytime you make a change. Now, if I want to actually be able to view
my application, what I can do is go to this URL
so you can just click it and open it up. Now it's saying this 0.0.0 isn't working. So what we can do is change this to be 127.0.0.1 or localhost port 8000. And when we do that, it should give us this,
thing here saying detail not found. That is totally fine. That's exactly, exactly what we're expecting. Okay. So our API is now running and it's time
to talk about the coolest feature of fast API, which is the docs endpoint. So here what you can do whenever you have a fast API
application is you can go to slash docs. Okay. When you do that, it's going to bring you to a page
that looks like this, which actually specifies all of the endpoints and the configuration
that you've set up for your API. So from here,
if I open this up, we'll be able to actually test out our API by pressing this try out button. This is going to send a sample request
to this endpoint. And then tell us what the response would have been. So what I can do is press try it out
I can press execute. And then you see what it does
is it sends a request to this URL. And then it tells me that I got this
as my response saying message hello world. And then it also told me the code of this was 200,
which means success. Okay, 200 and successful response. So there you go. We just sent a request, right? We tested out. It's all working. This is the thing I love about fast
API is that you can actually do this. You can directly go here and test out
all of your endpoints by simply going to this slash docs endpoint. This will become more useful later
on, but always check this out. It's very, very useful. Now there's also another endpoint called re doc. This is kind of a newer version of that docs
endpoint. It works the exact same way. We can test this out if we want. You know, test API etc., etc.. And kind of see how this works. I am not going
to, dive into this too much right now. But the point is this other endpoint called re doc,
which you should be aware of, but the one that I prefer to use is called docs. Now just another quick thing. If we wanted to,
we also could just directly go to slash Hello World. If we do that you see will give us message
hello world because we set up a Get endpoint. And by default
whenever you go to a URL in your browser, you send what's called a Get request, right? And because we sent the get request,
we got the response back in the browser is able to actually render it
and show it for us, but generally for all of the other endpoint requests
we're going to be looking at, we're going to have to use this slash docs page
or another tool to test the API. So we've now written kind of a dummy get endpoint. However doesn't really help us accomplish
our project goal right of creating, you know, posts. So what I want to do now is I want to start adjusting
the endpoint to actually make sense to our project. It will change over time,
but we're going to slowly kind of build towards what's called a Crud application where we have
create, read, update and delete functionality. So what I'm going to do is I'm going to delete this,
and I'm going to start setting up some stuff for handling posts. So what I want to do is
I want to set up my application so I can essentially retrieve
and create new user posts. For now, we're going to start with text posts,
but then later we'll get into the images. So I'm going to make a new dictionary
and I'm going to call this my text posts is equal to. And we're just going to have an empty dictionary
like this okay. Now what we're going to do
is we're going to make an end point and we're going to say at app don't get
and this is going to be slash posts okay. And for the function we're going to say
define get all posts like that. Now what this is going to do is just return
all of the posts that we have. So we're simply just going to return text posts
like that. Super simple. If we go back here now to this and we refresh, you'll see that we have
why is it still showing that? Okay, let me just restart my server
because for some reason sometimes this messes up. So just restart it, okay? And I don't know what was going on
there. Had some weird issue. But anyways, I got this. Now back to the docs page
and you can see we now have slash posts. And if I just try this out and execute,
you see it just gives me an empty response. Okay, that's what we're expecting
because currently we don't have any posts. However, if we put a post in here
then we would be able to retrieve it. So let's go to end point. But what I'm going to do now
is I'm going to start making some posts. So I'm going to have some idea like one okay. And I'm going to have this associated
if I can type properly with some post. So for my post I'm going to have another dictionary,
I'm going to have title, you know new post and content. You know, cool test post okay. And now I want to make an end point
that allows me to retrieve one individual post. So first of all, let's just go back
actually, and let's quickly test and go refresh okay. And let's try this out and execute. And you can see that we get the one post showing up. But maybe I want to be able to kind of filter
and get just an individual post. So I'm going to make a new endpoint. I'm going to say add app don't get. And I'm going to type slash post slash. And then inside of parentheses I'm going to type
what's known as a path parameter. So this is a dynamic value
that we can actually change and adjust in order to get an individual post. So we're doing ID right. And then what I'm going to do here is say
define get underscore post. And I'm going to say ID is of type int. Now what this is doing is it's going to directly map
this id parameter to the id value that I have inside of this path parameter
inside of curly braces, and give it as a parameter to my function
so I can use it inside of here. So now what I can do is I can say return text post okay dot get. And then I could get ID okay. So now what I'm able to do is if I go here and I refresh,
we should see that we have another endpoint. You can see it says get post with an ID
we can pass the ID. So let's pass ID of one and we execute this. It's not giving us anything. And I see the problem. That is because ID is a string
when it should be a number. So if we change this to a number now
and we go refresh and try out, go with one and execute. You can see now that we get the individual post
rather than a list of all of the posts. Okay, so just showing you this is how you create
what's called a path parameter. Now if we want to return an error here,
if the post doesn't exist, what I need to do is I need to import this Http exception. Now from this function
I can do something like if id not in text post, then I can raise an Http exception. I can specify a status code in this case
something like 404. And I can say the detail post not found. When I do that, that's going to indicate,
okay, we've had a 404 error, you know, not found and then post not found.
And that's how you can return an error. So now if we go back here and we refresh
and we try to access a post with ID like four or something and execute,
you'll see that it says detail 404. And then it gave us a 404 error code. Okay. So what I just did quickly is
I just had ChatGPT generate a bunch of test posts because I'm going to use these, throughout the rest of this kind of section here
to demo a few more things. All right. So what we've done is we've added a bunch of text
posts. We've added what's called a path parameter. We had a normal kind of query endpoint here. And the next thing that I want to do
is show you how we can use what's called query parameters inside of our functions. So up until this point
it's been pretty straightforward right. We just can call slash post. We can call for a particular ID. Now I want to make it work
so that we can do some kind of more advanced filtering using query parameters. And then we'll continue from there. So I'm actually going to go inside of this function
right here. And I'm going to start adding some optional
query parameters that we can pass to this. That will allow us to kind of filter
some of the content. So a query parameter remember
is the thing that comes after the question mark. So something like maybe you know length equals
ten or something right. Whatever. So we can actually filter kind
of the number of posts maybe that we're receiving. So what I'm going to do
is I'm going to add a query parameter called limit. I'm going to say limit colon int is equal to. And by default it's going to be none. Now what I've just done is
I've just specified that I now have the ability to pass a query parameter
called limit to this post endpoint. And I can then check if this query parameter exists. If it does, I can use it. If it doesn't, I don't have to. So my idea with the limit parameters
that maybe I don't want to receive all ten posts,
maybe I only want to receive the first three. Well, I can specify that with the limit. So let me show you how this works. I can do something like if limit, then what I'm going to do is say return text,
post up to the limit. Okay, now this is not bulletproof because if the limit is larger
than the number of posts, that will give us an error. But for now, that's fine. We'll just use that
and then otherwise we'll just return the text post. So now if I go back to my page here
and I refresh, you're going to see that if I look at post we have the ability
to add this limit query parameter. So what I can do is go try it out. For limit I can pass maybe three for example,
and then execute it. And what it gave us an error okay. That's weird. So the reason we got that error
sorry is because, we're trying to apply a list operation on a dictionary. There's multiple ways we can fix this. But for now, what I'm going to do just to make it
easy, is I'm going to say list of text posts. Dot. And this is going to be, values like that. So we'll just convert the values into a list
and then return them. So let's go back here and let's go refresh. And then we'll go here. You can see we have a limit again. Let's go maybe five and execute. And now you see we get a list with five items. If we change this to three we get only three items. And if we don't have any limit at all and we execute,
we get all of the items showing up. Okay. So just showing you
that's how you add a query parameter. If you want to do that,
you simply specify it in the parameters. Here you can make it optional which means
you can have something like is equal to none. Or you can make it mandatory by removing this. And now you have to pass the query parameter. If you don't then you could have potentially
get some errors inside of the function. You can pass multiple parameters like maybe,
you know, content length or something or whatever. And then you can specify
something like int can make it string. The reason why you need to specify the type here
so that it can be auto documented and validated by fast API. The way the fast API works
is that it automatically validates all of the data input that's coming into the function
and out of the function for you. So when I specify that this is an int,
if I try to send something other than an end to this function,
it's actually going to raise an error for me. Okay. Other than a number. So this documentation is actually very,
very good inside a fast API. And that's why you use what's called a Python type
hint inside of the parameters. And you specify like what is the function return,
what does it accept. So that it can be really well documented
and it can have what's called this data validation. Okay.
So at this point we've looked at query parameters. We've looked at path parameters. We've looked at the get endpoint extensively. Now I want to look at the post endpoint
and creating new data. So what I'm going to do is type at app dot post. And I'm going to do slash post okay. Now from here
what I want to do is be able to create a post. So I'm going to make a function
called create underscore post. What we're going to take for creating a post
is actually something different than for getting a post. So like I said in fast API
it has automatic data validation, which means it's going to check the data that's being
sent into the function to make sure it's accurate. Now up until this point, we looked
at the query parameters and the path parameters. But there's another way
that we can send data to our API. And it's by using something called the request body
okay. The body is kind of like more hidden information. It's not directly inside of the URL. It's in the field called body. And the way that we accept that type of data
is by creating something called a schema in fast API. So what I'm going to do is I'm going to make a
new file here, and I'm going to call this schema.py. Okay. Now inside of here what we're going to do
is we're going to define the type of data that we want to accept in our various endpoints. So in order to do this we're going to say imports
or we're going to say sorry from pi Dentek imports. The base model. And we're going to define a Python class. So we're going to say class post create okay. And this is going to inherit from the base model. And then we're going to specify in here the fields
that we want to accept essentially for a post. So for a post we're going to accept a title. And we're going to accept some content okay. So if title and content for a post. Now the way that this works
is that you inherit from this base model, which is kind of this special object in Python
that has some special features. And what we're able to do now is use this as a type to kind of receive body data inside of our functions. Again, I know it seems little bit confusing. This is referred to as something called schema,
very common inside of fast API to use this, and it's common
that you put it in a separate file called schemas. So from AP we're going to say from app
dot schemas import. And then we're going to import the schema that
we just wrote which is the post create schema okay. Now what we can do is for a create post
we can say post. So let's do this post colon post create. Now when we do this
because we're using a pedantic model by default, fast
API knows that we're receiving request body okay. So not receiving the query parameter
receiving the body. So now what we can do is we can use this post data
directly inside of the function to create a new post
so we can do something like let's do the following. Okay. We're going to say text posts
and then max of text post dot keys plus one. Because we need to find
what the next ID essentially should be. And then we're going to say is equal to one. We're not going to do post dict. What we're going to do
is we're going to make a dictionary, and we're going to say the title is post title and the content is post dot content. Now, because in my schema, I've defined that
my title is a string and my content is a string. Fast API will make sure that these are indeed strings
before to allows me to call this function. If they aren't strings, it's actually going to automatically raise an error for me
and tell me that I have a bad request, which means when I try to access the title or access the content
here, I know that they're going to be valid. So again, fast API automatically validates
the data that comes into the API based on the types that you set,
which is extremely useful. So now we've created this post endpoint. It's just going to create this new post. But what we really should do is
we should return the new post that was created. So to do that actually let's just do this. We can say post is equal to like this. Or maybe just new post is equal to this. And then we can say this is equal to new post
and we can return the new post. So now we have a post endpoint to create a new post. So what we can do is we can save
should automatically reload. So now if we come here and we go try it out
and we change this to like you know, cool post and new post or something. And we go execute. You can see it gives us the data back. And then if we go back to posts,
looks like the limit is required this time. So let's go like 12 and execute. And you can see we get the new post
and our cool test post is showing up. Awesome. So all of that is working. We now know how to accept request body right. So this different type of data and to create data now
to delete data it would be pretty straightforward. You go app dot delete and then same thing. And you would kind of continue along these lines. And there's more stuff that you could do
related to that. I'm not going to show
that this second would show it more. When we actually get into kind
of the finalized project. All right. So we're almost going to move on to databases,
but I just want to cover 1 or 2 small more things about fast API
that you should be aware of. Now the first is going to be the output type. So when we created the post here right,
we're just returning new post. And if we go back to our kind of documentation here, let's just go to slash docs
and have a look at it. You'll see that it doesn't show us
like what type of data is going to be returned. Okay. It gives us kind of an example. But this isn't exactly you know,
what we're looking for. It's not the best documentation in the world. So what we can do to enhance the documentation and
actually give us, some more validation in our code, which is better is we can specify the type of data
that's going to be returned from these functions. So for example, for my create post,
I can actually put this arrow here. And I can specify that I'm going
to be returning this post create type. Now that's going to look exactly like this right. So it's the same schema that we had here. Now if we wanted to return something
else we could do class post return or post response for example, can be the same thing,
but just so the name makes more sense. And then we can change this to post response. And we can import this from here post response. So now we're indicating
okay, whatever we're returning from this function is going to be of this type. Now what this is going to do
if I go back to my documentation here and I refresh, is is now going to indicate to us
if we look here, the type that's actually going to be returned to us,
you can see it gives us the value here. Example value is going to have title and content
being returned on the successful response. Because we've specified that type. Now what it also does is
it means we can only return data from this function. Now that is of this type,
if I try to return something else like just a normal dictionary,
if I were to actually execute this endpoint, it would give me an error and say,
hey, this doesn't match the schema, there must be some kind of problem. Okay? So it adds a layer of protection for us to make sure
that we're only returning what we want to return. And actually, in fact, let me show that to you. If I change this to like an empty dictionary,
and then I go to my front end and I refresh here
and I just go like try out and I execute, you'll see it actually gives me there
and it says, hey, you know, missing this data type, we're missing title, we're missing content,
which we should have in the response. So we need to go back to a new post
and then it'll work from now on. Okay. So if we want to type the rest of our data
we can do that as well. So for example, when we're getting a post,
we should also say that this will be the post response. And then for getting all of the posts,
we're not going to type this one right now because the response types are actually a little bit
different, even though it's not intended. Anyways, you get the idea. You can do this okay, which is another useful thing
to do inside of fast API. And if you wanted to have like a list of posts,
you do something like list okay and then post response like that. Awesome. So now that we have that, what I want to do
is start connecting us to databases. Because right now you'll notice if I refresh this application
all of a sudden my data is going to disappear. Only the data that I have written inside of my code
will stay persistent. Now, that's because right now, all of our data is
just inside of a dictionary that's stored in memory. So when our code refreshes, it all gets cleared
and kind of reinitialize with this new dictionary. Now that's not ideal. So we're going to start
setting up is a database connection. So we're going to go to our app folder. And we're going to make a new file called DBP. Now inside of all of these Python API libraries you typically have something called a ORM. And ORM stands for an object relational mapping. And it's something that allows you to prevent writing
SQL code or no SQL queries, and instead to write Python like code to be able to define data, retrieve data, create data, etc.. Okay, now the form that we're going to use here
is something called SQL alchemy. I believe we already installed it. If we didn't
then we can install it again in a second. But the point is, this will allow us
to really easily work with our data. So bear with me here, because this is a
little bit of code that we do need to set up. Not all of it's going to make 100% sense right now,
but I promise it will later on as we keep going. Okay, so let's start with some of our imports. So the first thing we're going to do
is we're going to say from collections dot ABC import the async generator. We're then going to say import Uuid, which is something we can use
to generate a unique identifier. We're then going to say from SQL alchemy import Create Engine. We're then going to say from SQL alchemy okay. And this is not what I want. We're going to say import the column. So let's spell column correctly. We're going to import string. We're going to import text datetime and foreign key which we will use later on. And I spell foreign correctly. No I did not. So let's spell foreign key correctly okay. We're then going to say from SQL alchemy okay. This is going to be dot dialects. Let's spell that correctly dot postgres SQL import Uuid. We're then going to say from SQL alchemy dot ext dot async IO. Import the async session and the async session maker
as well as the create async engine. So let's bring in the async underscore session maker. Again I know this stuff seems confusing. It's just a little bit of setup.
And then you never need to do it again. And then we're going to bring in from SQL
alchemy forum import the declarative base. But this is actually going to be a little bit
different. It's going to be declarative based like that. And the relationship okay. Now by the way all of this stuff I do not have
memorized, you don't need to memorize it. It's simply something that we need to be able
to set up the database. Once the database is set up,
then it's a lot easier for us to work with us. So what we're going to do
is we're going to define a, variable, and we're going to say the database
URL is equal to s q light. Okay. So SQL Lite plus a I o s q lights okay. Colon three slashes dot slash test dot db. Now what this
is going to do is it's going to allow us to connect to a local database file on our own computer
called test on DB, which is in the current directory. And sorry this needs to be a plus here.
So let's fix that. Then we want to use SQL Lite plus I o SQL Lite, which
essentially an asynchronous version of SQL Lite, which is a really simple database
that we can run locally later. If you wanted to connect to a production database,
you simply need to change this URL to a URL string for like a remote database
or a different type of database, and then everything in your code
will stay exactly the same. You just work in a different database. Now, what we're about to do here
is we're going to define what's called our data models. And then we're going to create the database
which will automatically create the data models for us. Now essentially the data models is the type of data
that we want to store. And if you've ever worked with databases before, you'll know that when you want to store data,
you need to specify the structure of that data, at least when you're working with a SQL database,
which is what we're doing right here. So we need to specify like, okay,
what do we want to store for a post for example? Well we need to know the user that posted it. We have to have an ID. We want to know
maybe the caption of the post, the URL of the file. That's the kind of stuff that I'm talking about here.
Okay. So what we're going to do is we're going
to create the data model for storing a post. We're going to make it a little bit more complex now,
and we're going to start working towards actually being able to store videos and photos for our API. So what we're going to do
is we're going to say class post, and this is going to inherit
from the declarative base. And this is important
you need to inherit from the declarative base so that it knows
that we are making this a data model. And this is something
that we're going to store in our database okay. Now we need to specify the table name. So we're going to say underscore underscore table
name underscore underscore is equal to post. For example. And then we're going to start specifying
all of the fields that we want to have or all of the columns
that we want to have in our data model. So we're going to say ID is equal to column. And then we're going to say Uuid. We're going to say as underscore
Uuid is equal to true. We're going to say primary key is equal to true. And we're going to say the default is uuid uuid four. And let me just fix the spelling here okay. Now let me quickly explain what we're doing here. Essentially what we're saying is
we want to have this ID column. Now for every single, entity that you have
in a database, you need to have some ID. Now, the ID is typically
what's referred to as the primary key. The primary key is something that must be unique. So in this case every you need the
sorry every ID that we have needs to be unique. That's why we've marked it as primary key. And because we've specified this Uuid thing. What this means is that we're automatically going
to generate a random unique ID for it every time we insert one into the database. So every time we create a new post, a new ID will
randomly be generated that is guaranteed to be unique, which is the primary key
or the way that we will look up this entity. Okay. Now the next thing that we're going to have
is we're going to have a caption. The caption is going to be some column and this is going to be text. Now you'll notice that the way that we specify
the data that we want to have is we say, okay, we're going to have some name
some field names like caption ID then we say it's going to be a column. If we're storing data for storing a relationship,
we would set it to a relationship or a foreign key or something different. And then you specify what you want in that column. In this case I want to store text. Right. So I say okay caption it's going to be text. Then I'm going to have a URL. This is going to be the URL for the,
what do you call it, photo or video. And this is going to be column of string. And we're going to say nullable is equal to false. Which means this can't be null.
It has to have a value. We're then going to say the file
type is equal to column same thing. String nullable is false. We're then going to have the file underscore name. It'll be the exact same thing. So string nullable equals false. Will then have a createdat. And we'll say column date time. And rather than nullable
we're just going to say default is equal to date time dot UTC. Now. And then we're going to import datetime. So we're going to import datetime like that. Not now import date time like that. And now this should work okay. So that's all that we need for the post later. We will adjust this slightly
to link it to an individual user. But for now, because we don't have users in our app,
we're just going to have kind of a simple post. So the way that this will work
now is that whenever we want to create a post, we'll need to pass a caption URL, file type, file
name and created at. We're actually created
that will be automatically created for us. And then we'll be able to make this new post
and store it in our database. If we had other data models, we would define them inside of here
or in other files and import them inside. But for now, this is how we're going to do it. So next we need to actually create the database. So we're going to say our engine is equal to this. Create async engine with a database URL. We're then going to say the async underscore session
underscore maker is equal to the async session maker. And we're going to pass our engine. And then we're going to say
expire on commit is equal to false. Don't worry too much about that. Essentially what we need to do right now is
we need to create this database engine, which can look at all of these models
and automatically create the database for us. We're then going to say async define create db underscore and underscore tables. What this is going to do is create the database
for us as well as create the tables. So we're going to say
async with engine dot begin okay. And then this is going to be as connection. And what we're going to do is say await Conn okay. The connection. So dot run underscore sync. And then we are going to say the declarative base
okay. Dot metadata dot create all. Now what this is going to do
is it's going to find all of the classes that inherit from the declarative base. And it's going to create them inside of the database. That's all that it's doing. We're then going to have another function. We're going to say async define get underscore
async underscore session okay. This is going to return an async generator
with an async session and none. And then we're going to say async with. And I know this is confusing. Bear with me. We're going to say
the async session maker as session. And we're going to yield the session okay. And bear with me. This is actually almost done at this point. What we're doing
is we're creating all the databases and the tables. Right. What this does is it starts a database engine and then it creates all the tables
and creates a database. Then we have this get async session. What this is going to do
is it is going to get a session which will allow us to actually access the database
and write and read from it asynchronously. Okay. So we're doing this using async. If you don't understand async in Python, don't worry
too much about it. I will explain kind of important stuff later. So that's all we need from this particular file. Later we will add a few other things
to store our users for the application, but for now this is fine. Okay,
so there we go. We've now written the database stuff. What we're going to do now
is we're going to go into our app.py file, and we're going to start importing
some of the things that we need. So we can actually use the database. So we're going to say from app dot db import the post the create db and tables. And we get async session. Okay. Now what we need to do is we need to set something up
so that as soon as the application runs, we create the database
automatically if it's not already created. Now in order to do that again
this is going to look a little bit complex. Don't worry. Just set up once and then you're good. We're going to say from SQL alchemy dot
nxt dot async IO. And then we're going to import the async session. Then we also need to import
something called the async context manager. So we're going to say from context lib. This is built into Python. Import the async context manager. We're then going to say add async context manager. And we're going to say async define life span. We're going to take in our app
which is an instance of fast API. And what we're going to do is we're going to say
await and we're going to say create DB and tables. And then we're going to yield. Don't worry too much about this. It's kind of advanced Python syntax. So you don't really need to understand. And then all we're going to do here is when we say
app is equal to fast API, we're going to say life span is equal to life span. What this is going to do
is it's going to automatically run this function as soon as the app is started. It's going to create the database
in the tables for us. And just make sure that this is essentially handled
correctly and cleanly. Exit once when sorry the application stops. Now these other imports we'll use later on. For now we'll just leave them in. Okay. So now if we just want to do kind of a sample test
here, we can shut down our server and rerun it, and it should automatically create the database
for us. However, it's giving me an issue saying
datetime dot UTC isn't a function. Let me quickly fix that. So we have to actually change this
to say from date time import date time. So just change that import here in the db file. And now if you come back here and we shut this down and we restart it and we got another error. Let me quickly fix that okay. So another silly error here. We're going to go inside of our db.py file. What we're going to do quickly is say class base. And this is going to inherit
from the declarative base. And then we're just going to say pass. And we're just going to change all instances
of declarative base down here to say base. Kind of a silly error again, a little bit complex. And then same thing when we go here
just change this to say base. And that should fix the problem for us. Essentially we just cannot directly inherit
from declarative base. We need to do this kind of other base class first. A little bit weird,
but that will allow us to set up the database. I know the database set up
seems a little bit confusing, but once it's set up again,
you don't need to change it. So let me just shut this down and then restart it. Okay, so the server is running.
We didn't get any errors. And the way we can test if this is working is
if this test DB file was created, it looks like it was for me
which means the database connection is set. And now we don't really need to touch much
in this database file. And we can just start using the database
to actually create new posts. So in fact let's start doing that. Let's actually scrap
pretty much everything that we have so far. I know it seems like why don't we get rid of it, but trust me,
we're just going to write this in a better way now. And what we're going to do is we're going to start
writing the endpoints for uploading or creating a new post
and doing the image and video upload. So as I mentioned,
we're going to start doing the file upload. Now to do that
we're going to have to import a few more things. And again just bear with me I'm going to explain it
step by step as we go through that. And then we'll start adding the users
a bunch of other stuff as well. So what we're going to do is from our fast
API import at the top, actually we're going to import file. We're going to import upload file. The what is it form. And the depends okay. Now we're going to make a new end point
and we're going to say add app Dot. And this is going to be post
because we're going to be uploading a file. And this is going to be slash upload. Now this is going to be
to essentially make a new post. So when we make a new post
we're going to upload either a video or a photo and then some caption for that video or photo. And then it will be post it. So in order to do this
we're going to say async define upload file. We're using async
because we're going to have some async operations in here
where we essentially wait for something to occur. Fast API is asynchronous by default, so you can
always use the async keyword for these functions. Now what we're going to do
is we're going to say file. And this is going to be upload
file is equal to file with three dots inside. What this means is that we're going to be able
to receive a file object to this end point. Okay. We're then going to say the caption is a string
which is equal to some form data. So rather than actually just accepting a request body
we're going to accept something called the request form. There's different types of data
you can send to the end point. For example you can send a file. You can send a form. You can send a request body.
You can send query parameters. This is just another type. So we're going to accept the caption
from some form data okay. We're then going to say
our session is an async session which is equal to depends. And this is going to be get async session. Now this is going to look a little bit weird. But in fast API
we have something called dependency injection. This is essentially a dependency injection. It'll light up in a second here
when we start using it, where what's going to happen is we're automatically going
to get the asynchronous session by calling this function and pass it
as the variable session inside of this function. This is how it works
when you want to essentially trigger another function
to run as a dependency for this function. So we're saying
we want to get the asynchronous session. Sorry for our database. So we can use the database inside of this function
in order to use it in this function. That's how you do it. You write this kind of dependency injection
where you say this function depends on this. Right here. It will run the function. It will get the database object,
it will pass it to us. And then we'll be able to use it
directly inside of here okay. So now inside of here what we're going to do
is we're going to take the file and we're going to upload it. That's going to take us a second to be able to do
because we need to use image, get. So before I do that I'm going to show you
how we can create a new post with some kind of fake post data,
and then how we can do actually the upload for that. So to make a new post, we can say
post is equal to post and we can specify things like the caption is equal to the caption. We can say the URL is equal to, you know, dummy URL. And later we'll fill that in. We can say the file
what is this file underscore type is equal to. For now we can just go photo. Then we can do a comma. And we can say the file underscore
name is equal to dummy name okay. So let's go to dummy URL. Don't dummy name. Again. There's a few other things we can add later. But for now this is fine. Now in order
for us to actually add this to the database, the way that we do that is we say session dot, add okay. And we simply add this post here that we created. Then we say await session dot commit. So the way that you add
something is you create a new post or create a new object
of whatever it is that you want to create. You add this to the database session
and then you commit the session. It's important that you commit this
because committing it will actually save it. Adding it to the session is like staging it, saying,
hey, this is ready to be added, but it won't actually fully be written into the database
unless you commit the session. Now another thing you can do after this is
you can say await session, dot refresh and you can refresh a particular object. And what this will do now is it
will go and look in the database and populate any entries here that were automatically created
when it was added to the database. So for example, we have this Createdat and this ID. When we specified the post, we didn't specify an ID or a createdat ID that gets automatically created
when we commit this in the session. So when we refresh the post
it essentially gets hydrated with that extra data where we now get the ID and the Createdat as a part of this post,
which we can now return to our user. So what we can do here is we can just say return, and then we can return the post. We don't need to do that,
but we can if we want. Right. So we can say return post,
and then we will get that data so we can test this. But before we're going to know
if it was actually added to the database, we need to write an endpoint
that's going to allow us to view the different posts. So we're going to say app dot get. And we're going to call this the feed. So we're going to say slash feed. We're then going to say async
define get underscore feed. And what we're going to do here
is we're going to say the session this is an async session
is equal to depends on get async session. Now the reason we're doing that
is because we need to access the database here in order to get all of our posts. So we need to bring this in as the dependency
injection. Now quickly I do need to import something. So I'm going to say from SQL alchemy import select because this is going to allow us
to select different posts. So now what we're going to do is we're going to say
result is equal to await session. This is our database dot execute okay. And this is how you can execute a query. And we're going to say select. And we want to select on the post. What do you call it objects or posts kind of table. And we want to say order underscore by and we're going to say post dot okay. Like this created underscore add dot descending okay. So querying here is a little bit weird. But what we're able to do is say okay
I want to select posts right. So I want to start looking through posts. And then you can add these various filters
like I want to order it by all of the posts that are created at
and the descending right. So I want to go in the order
in which they were created. And then I could also do something like,
you know, dot filter, right? Or like all these other things
and you can filter by specific criteria. I'm not going to go through all of it. But essentially
if you just look up like SQL alchemy Fast API online, you'll see all of the different ways
that you can query different data. If you want to just get all of the posts
and you don't care about doing any kind of filtering, you can just say select post
that will give all of them to you. Okay? And then if you want to check various fields
or relationships, you can do that. We'll look at that a little bit later on okay. So now we have our result. This is going to be all of our posts. Now what we're going to do is say post is equal to
and we're just going to go row okay zero for row in result dot. Now what the result on all is going to do is
just give us all of the results from this particular query. The reason why we're doing this
is because I want to convert this into a list. Essentially, we need to step through
all of the results and take them and pass them into this in order
for us to actually access them all at once. It's due to the way that fast API returns the results
here, returns in what's called a cursor object,
where you're stepping through the database. So what I'm doing is I'm
looping through all of the values and then just pulling them into individual values
that I store inside of here. Okay. Then what I'm going to do is I'm going to say
my posts underscore data is equal to a list. And I'm going to say for post in posts. And I'm going to start creating kind of a more,
comprehensive post object that I can return to my front end that's going to include some data
that we need about our posts. So we're going to say post data dot append okay. And then inside of here
we're going to have an object for the object. We're going to say id
is equal to a string of the post.id. So I'm going to convert it from a Uuid object
to a string. I'm then going to say the caption is the post caption. I'm going to say the URL if we can spell this correctly is the post URL. I'm going to say the file underscore
type is the post dot file type. And then I'm going to say the file underscore
name is the post up file name. And I'm going to say
createdat is the post dot createdat dot iso format, which is going to give me a timestamp in a format
I can actually read. Then I'm simply going to go down here
and I'm going to say return posts, and I'm just going to return all of my post data. Okay. And that should be post data like that. So that is going to complete this kind of get feed
function, which will give me all of my posts. This should allow me to create a post
when I upload a file again. For now, the file we haven't specified,
but we'll do that in a second. So now let's give it a test and see if this works. So let's rerun our back end. I'm just going to shut this down and restart it. Let's go here. Let's refresh. We see we have an upload and see it's multipart
form data. So I'm going to try it out. And now it allows me to choose a file. So let me pick some file here okay. So I just uploaded some file
some payslips from a while ago. Let's go caption hello world and let's execute and let's see what we get. And it gives us the response body okay photo, dummy URL date, hello world and the id. So now if we want to see
if it's actually in the feed, what we can do is go to get feed, try it out and execute. And you can see we get the post
I think I press this twice so we get two posts showing up here,
but it's reading those from the database. And the important thing is that if I were to shut
this application down and restart it, these would still be in the database
because, well, they're here persistently. Right. And they're stored in this file. If we want to delete them
we can just delete this database okay. So now we have the ability to upload
or create a new post and to kind of view the posts. Now we need to start actually handling the image
and video upload. And to do that we're going to use image k. So what we're going to do is make a new file here. And we're going to call this image kit or not image get images.py okay. So from here we're going to go images.py. And we're going to start importing a few things
we're going to say from env import local env we're going to say from image kit okay IO import. So we can spell this image get. And remember
we imported this or installed this at the beginning. We're going to import OS. And then we're just going to call this load
dot env function. We're then going to say
image kit is equal to image kit. And we are going to essentially specify
all of the variables that we defined here in our dot env file. So to do this
we're actually just going to use this autocomplete. We're going to say our private key is equal to OS dot
get env image get private key public key ostergaard get env public key url endpoint Oscar get env image
get url endpoint okay. So the same variables that we have here. And actually let's just make sure they're spelt
correctly because this one is a little bit different. So let's fix this to just be image kit. You are l okay. Now what are we doing here. Well this local env function
will look for the presence of this dot env variable
and essentially load it for us. Okay. So it will load these values in
so we can now access them. Now to access those variables we use this Oscar dot
get env function which will be able to find the presence of these environment variables
and load them into our code. Very important that we're doing this on the back end,
not on the front end. And I want to explain to you
how we now kind of upload content using image kit,
which which is what we're about to do. So let's go into app dot Pi and let's import
image kit so that we can start using it. So what we're going to do is say from app dot
images import and we're going to import image kit. And then we're going to say from
and this is going to be image kit IO dot models dot upload file request options. Import the upload file request options. Because we're going to use this in a second
to specify how we upload the file. Now before I start kind of diving into the code here,
I want to go to the image kit documentation
and start kind of explaining to you how this works. All right. So if you remember at the beginning of the video
we would have created an image kit account. The count looks something like this
where you have this dashboard. Now image kit can automatically host
all of the images for us. And it can handle uploading to leading them,
cropping them, modifying them, and more importantly,
just being our storage. So we don't need to worry about storing images
and videos, which can be a huge pain. Now, you do have the ability with image Kit
to connect this to external storage. So for example, if you go to external storage here,
you can add a new one and you can can connect. It's like an S3 bucket if you're familiar with what
that is or other locations where you can essentially have image kit managing this external bucket,
you don't need to do that, but you can. In our case, we're going to use image kit as our dam. So it's automatically
going to handle all of the asset management for us. Eventually when we start uploading stuff, we'll see
in our media library that it will show up here. We'll be able to click into the various files. We can view them here. We can view the information. We can edit the tags, get embeds URLs. All of this kind of stuff
just makes it very easy to manage the images. Now in terms of using image kit, what we can do is use a single API. Funny enough, we're building the API and we're going
to use an image kit API to upload the images. Once the images are uploaded,
we can then just change some query parameters in the URL for the image,
and we can specify the width, the height if we want to add text on top of it, if we want it
black or white, if we want to crop it or whatever. As you can kind of see, it's showing you right here,
which makes this extremely useful. It also has performance optimization,
a lot of other features. But let's go into the docs. So I'm going to go docs like this. Let's go to the Python documentation. You can see it supports
a ton of different frameworks. And I'm going to explain to you how we upload it. Now you can use this with a lot. As you can see JavaScript react angular,
all of this kind of stuff. In our case we're doing this from Python. And so because we're doing that we imported
or we installed the image kit IO library. And then we initialized it like this. Now we're going to be doing
what's called a back end or server upload. So essentially
the user is going to send some image to our backend. And then our back end
is going to upload this to image kit. Now it's important that we do it this way
so that we can securely control what images we upload or which ones we don't,
and have them stored on our server. Whereas if you were to upload this directly
from a front end or a client, like a JavaScript application or something,
that's not as secure, okay. And you don't have as much control
because you're essentially exposing different tokens for image kit on your front end,
which you may not want to do. So it shows us right here some kind of code
snippets of how you can do the upload. You have some URL to an image. Our case, it's going to be some data
which I'll show you and we can do image kit, dot upload file, pass the different piece of information
and then it just gets uploaded and it's going to return to us essentially an object
that looks like this where we have the URL for the image, the name tags, all of this kind of stuff that we can then use and store. Okay. And then if we want to generate a URL,
we can do something like this. Again, we're not going to talk too much about everything here,
but that's kind of how we do the upload. Hopefully this makes a little bit of sense,
but the point is user will send a file to our API. Our API will then upload its image
kit, will grab the URL, save that in our database,
and then serve that to our user on the front end. So we're going to go and we're going to import a few other things
that we're going to need to perform this upload. So what I'm going to do
is I'm going to say import S.H. util. I'm going to import OS, I'm going to import Uuid and I'm going to import temp file. Now the process is going to be
that when the user sends us this file we're going to create a temporary file
which is a copy of this file. We're then going to upload that copy. And then essentially remove or delete that copy
from the machine because we'll no longer need it. So what we're going to do is create a variable
and we're going to say our temp file path is equal to none. We're then going to say in a try block with okay temp file dot named temporary file. We're going to say delete is equal to false. And we're going to say the suffix is equal to Oscar path dot split text. And we're going to say
this is going to be file dot file name. And then this is going to be at index one. Okay. This is a little bit weird. Again
just bear with me here. We're going to say this is as temp file. And we're going to say this is as temp file. What this is going to do
is essentially make a named temporary file that ends in whatever the file name is
that was uploaded here. Then inside of here, since we have this temporary
file, we're going to say the temp file path is equal to the temp file name. And we're going to say S.H. util copy file object. And this is going to be file dot file. And then the temporary file. And let me just correct myself here. What we're doing is when we create this temporary
file object we're using a we're having the end of the temporary file have the same extension
as the file that was uploaded here. So if they upload a Jpeg, for example,
we create a temporary Jpeg. If they upload a mp4 we create a temporary mp4. Okay. So what we do is we create the temporary file. We then copy the contents of this file
into the temporary file. And then that kind of completes this with statement. Then we're going to start doing the upload. So we're going to say our upload result is equal to image gap dot upload underscore file. What we're going to do is we're going to say
file is equal to. And we're going to open the temporary file path in this RB mode which stands for read bytes. We're then going to say our file
name is equal to file dot file name. And we're going to say the options is equal to. And this is going to be the upload file
request options. And in here
we're going to say use underscore unique file name. This is going to be equal to true. And we're going to say the tags is equal to. And inside of a list
we're going to say back end upload. So we know that we uploaded this from our API. Now this is all we need. Like that's literally
how easy it is to use image get. We just write this one line where we essentially open
the file, we upload it to image get, and then it's going to give us a result that contains
all of the metadata that we need. So we're going to say if upload result dot
this is going to be response dot Http underscore status underscore code is equal to 200. That means if this was successful
then what we're going to do is all of this where we essentially create the post okay. Now we just need to structure this a little bit
better because it's a bit difficult to read. But we have this try block. Right. So I'm just going to put an accept block here. So we're going to say accept exception as E. And then we can put a pass right here for now. And then we're going to say finally
and in the finally block we're just going to make sure
we clean up the temporary file. So we're going to say if temp file path
okay or or sorry not or and OS dot path dot exists the temp file path. Then we're just going to say OS dot on link. And we're going to unlink the temporary file path. And then we're going to say file dot file dot close okay. So this is just going to clean up our file objects. So at the end of this function
we're all good and everything's cleaned up okay. And then for the exception
let's just quickly handle this. We're just going to say raise Http exception
status code 500. And we'll just put whatever the string error was
there. We're able to have a look at it okay. So now at least the try accept block is done. And we just need to handle this part. So what we're saying is all right
you know we've now created the temporary file. We've uploaded it. We're going to check the response. And we're going to make sure
that this was successful. Now if it was successful what we're going to do
is we're going to create this post. But for the URL
this time we're going to change it to be the upload result dot URL. For the file type, we're going to say this is video. If file dot content underscore type dot starts with video slash. Otherwise we're going to put the type as image. And for the file name this is simply going to be the upload result okay dot. And this is name. So now we're actually using the content
from image Kit. And we'll actually will have the URL for the image. Hopefully that makes sense. But that's pretty much
all we need to do for the upload. And I think that should actually be good. So what we'll do now
is let's bring open our terminal again. Let's just restart this and let's go back here and let's test this from our docs okay. So let's go upload. All right. We're going to go try it out. We're going to put a file. So we need to upload an image. So let me just upload one of these images here. Let's go hello world. And let's send this and it says module
name path has no attribute split text okay. So I need to remove one of the T's here. So it's split text in one word. So that was my problem there. Let's go back and refresh and just try this again. It's going to go try to upload again. Just upload an image or a video
needs to be some media. Say hello and then execute this. It's going to take a second
because it does need to do the upload. And then we got some issues saying
none time object has no attribute Http status code. So we probably just spelt something wrong. So let's go here and fix this. Yeah. So we have upload result. This needs to be dot response underscore meta data okay. So let's spell metadata correctly. And now let's test it again. Apologies guys. This is just a part of programing refresh. Try it out. Choose a file again some media okay. Hello world. And let's see. And here we go. We get the response body. And now what I want to check is the URL. So it has a URL here. So let's copy
this and let's paste this in our browser. And we should see that we get the image now
and it is uploaded. And importantly if we go to the media library
we should be able to refresh here. And we should see that
now the images appear happening twice because we uploaded twice,
even though we got an error the last time. And you can see this shows up. By the way,
this is Kenny, one of my co-founders from Dev Launch and someone that we were filming a testimonial video
with. Anyways, that's kind of the random image
that you guys are seeing. Okay, so it's working. We can see its upload here in image kit. It's working now from the back end. What I want to do next now is test out this feed
endpoint and see if this gives me all of the content. So let's execute. And you can see there we go. So we have this new post right. We have the image kit URL. And now I just want to talk
about kind of the advantage of using image kit and what we can do with these URLs. That is super cool,
which I'm going to show you in one second. So I'm going to grab one of these URLs,
which I would recommend that you do. And I'm going to show you how we can modify modified
sorry by literally just changing some parameters in this URL. Okay. So I have the URL in this one tab here. And then I also just pulled up the image kit
documentation and what you're able to see. Let me just make this a little bit bigger,
is that we can have these transformation parameters directly in the URL to modify the image. So notice I have, you know, image kit demo. And then we can do these transformations of
like the width and the height directly on this URL. Let me show you that I'm gonna show you
a bunch of other ones that we can do. So what we do is we go in between the file
name and the, what do you call it? Project ID for image kit, and we just put this in directly. And when we do that, you see that
I just modified the width and the height directly by cropping it, by literally
just putting those parameters. If I put like 500. See, now that it goes up,
if I put width of like I don't know what is this 700. See now we get an image
that looks a little bit more complete. Right. And we can just add it directly like that. Now we also can pass this as a query parameter
if that's easier for us to do. And you can see it
shows some examples of kind of resizing the image. Now if we go here to image transformations
you'll see there's just so many that we can do here. Right. Like we have an eye transformation
which is currently in beta which is kind of cool. We can add overlays directly on top of the image. So we can have like some local image you want to put
on top of this image and directly embed it. We can do effects and enchantment or enhancement. Sorry. So we can have like e contrast. So so let's actually copy this
and see if we can get it. So we'll do tr and then H-3 hundred e contrast okay. And make sure there's no space. And then if we run that you can see that
it gives us some more contrast. We can sharpen it. So if we want to pass E sharpen
let's change that to E dash sharpen. And you can see it now sharpens the image
a little bit. It's a little bit difficult
to see probably with my screen recording software. But it is doing that. And then we have video transformations. These are cool because we can have for example
like thumbnails for videos. So if we upload a video, we can try
to get a thumbnail from a specific portion of it. But for example, we can use this IK
thumbnail dot jpeg. If we just put that at the end of the path,
it will just give us the first frame as a thumbnail. We can get a thumbnail from a specific time five. So we're doing like five seconds in. That's how we're getting the thumbnail. We can do transformations on the thumbnails. We can trim the videos
so we only get a certain amount of length from it. And then of course, the most important thing in
my opinion, is the optimized version. So you can do image optimization. So you can automatically compress the images without
losing quality and load them significantly faster. You also can do video optimization. And there's like so many different things. So if you do care a lot about image and video,
definitely check out these docs that I will leave in the description. And one with video specifically
is like changing the quality of the video so you're not loading,
you know, massive 4K videos or something. And even if you set it to like 90% of the quality, it's,
you know, three times smaller, which is significant. So anyways, that's the images they are, you know, working
now I want to continue with the API. So we have the ability to kind of get a feed to
what do you call it. Upload a file. Now let's write the ability to delete a post. And then what I want to do is move on
and talk about authentication. So we actually have different users
kind of signing in. And only the user who made a post can delete a post. And like you need to be signed in
to be able to make a post. That's pretty important, right? So let's make another endpoint here. Let's call this app dot delete. And for deleting we're going to take in slash posts. And this is going to be a path parameter of our post
underscore ID. What we're then going to do
is say async define delete underscore post. We're then going to say post underscore id. And this is going to be a string. We're then going to say the session is async session. And that depends on the get async session. We're then going to have a try. For the try
we're going to say the post underscore uuid is equal to uuid dot uuid. And then this is going to be the post underscore id. We're then going to say result is equal. To await session dot execute. And we're going to say select post dot where okay. And we're going to say where the post.id is equal equal just to equals to the post underscore Uuid. Now the reason why we need to convert
the post id to uid is because this will be a string by default,
and we need it to be this Uuid objects. When we do the comparison, they match. Okay, so then after this we're going to say
the post is equal to result and we're going to say dot scalers
okay with a set of parentheses. And then dot first
what this is going to do is just return the exact result, rather than giving us
this kind of object that we need to loop through. So that's what scalars does,
even though I know it sounds a little bit confusing, we're going to say if we do not have any post,
then we're going to raise an Http exception and we're going to say
the post is not found with status code 404. Okay. And then otherwise what we're going to do
is just delete the post. So we're going to say await session dot delete post. And then we're going to say await session dot commit like that. And when we commit this it will delete the post. We're then going to return. And we'll just say success is true. And then we can have some message
like post deleted successfully. And then we're just going to have an accept
in case there's an error. So say accept exception as E. And then what we're going to do is just raise an Http
exception saying hey there's some error. This is the error. Okay. And that should be all that we need to do
for deleting a post so we can save that. And let's now give it a test. So let's go to here. Let's grab a post ID. So maybe one of these old ones. And let's refresh and let's go to posts. Let's pass in the post ID and execute this. And it said for or for post not found. Okay so that's maybe an issue. Let's go feed and let's try to get the posts and okay it looks like it did delete that post,
maybe because it said 4 or 4 but then it deleted it. So let's copy this. Let's paste the other one and let's do it. And there we go okay now so success
true post deleted successfully okay. So that looks like it's working. And then if we go back to the feed we can execute. And now we only have one post inside of here
which is a valid post. Cool. So deleting is working. Upload is is working getting is working. Now what I want to do is I want to start handling
the user authentication. This is all great,
but it only kind of matters if we can like sign in. And for example, if I make a post,
you know you shouldn't be able to delete it, right? We have to have kind of rules and authentication
and that kind of flow, for our endpoint. So let's go ahead and start doing that to do that
I'm going to go over to API. I'm going to make a new file call this users dot Pi. Now this is where
we're quickly going to talk about JWT token. So let me hop over to the kind of whiteboard
and explain that to you okay. So we're going to talk about authentication right. This is arguably the most complicated part
of most web applications. And for this app we're going to use something
called JWT tokens or JWT auth. JWT stands for Json Web Tokens. They are a very common format for web authentication. And the way that they work
is that they essentially validate or authenticate a user by the user, including this token
in all of the requests that they send to the server. So this little diagram that I have for
you explains it. Let's go through it. So essentially
the way this works is you have some user. Let's call her Sally. Right. And she logs into the application. Now before she can log in
she needs to make an account. But to make an account
you don't need to do anything fancy. So let's imagine. No, she makes some account. She has some credentials. Okay. Now she signed in to the, server. The way that she does that right
is she goes from her computer. So she's on some website
and she sends her login details to this off endpoint. So maybe that's her username and her password
right now this is our API or kind of our server. And what it does is it checks the user credentials
and it says okay, are these valid or are they invalid. They should be valid. So when they're valid what's going to happen
is it's going to generate a signed JWT token. Now this is a special token
that just looks like a random string of characters that essentially identifies Sally. It tells us, okay, this token belongs to Sally. So if I see this token,
it means that Sally is the one who sent this request. That's effectively what that means. Sally signs in, she then gets some token back,
and this token is Sally's token. Anyone that has
this token is Sally in the eyes of our API. Okay,
so what Sally does is she now stores this token, or really her computer stores it in the browser. She's not going to do it manually. And now for the rest of all of the requests
that she uses with our, API,
she sends this token along with the request. So she has the request plus this JWT token. We verify the token is correct
and then we say, okay, who is this? Oh it's Sally okay, great. So it's Sally. So we can now go do this thing
because Sally's allowed to do that thing. That's essentially how this works. This is an oversimplified explanation. The point is user signs in they get some token. They store this token. They then send that with every request as they, kind of hit our API and start interacting with it. And that identifies them to us as that user. And there you go. Right. That's how it works okay. So that's what we're going to implement. Now to do that
we're going to use something called fast API users, which is a module that we installed
and just makes this process a lot easier. So in our users file we're going to say import Uuid. We're then going to say from typing import optional. And we have a lot of other stuff to import as well. We're going to say from fast API imports. Depends and request. And then what we're going to do is say from fast
API underscore users import the base user manager, the fast API users, and the you uid id mixin as well as models. Okay, now this is giving us an error
because I need to make this fast API users plural. And now we're good. Then we're going to say from fast underscore API
or fast API underscore users dot authentication okay. And we're going to import
the authentication back end. If we can spell this correctly
we're also going to import the bearer transport as well as the JWT strategy. Now with this
you can use various types of token strategies. In this case we're going to use JWT. Don't worry there's some other ones as well. But we're not going to look at those right now. We're then going to say from fast API underscore users
dot database import the SQL alchemy user database. And we're going to say from app dot db import user and get user db, which are two functions
that we're going to go and right now okay. So because we're now going to have users in our app,
we are going to have to make some changes. So we're going to go to database dot pi. And we're going to start writing a user model
that we can essentially use to store users. Okay. So to do this
we're going to say from fast API underscore users dot db import the SQL alchemy. And this is user database. Also going to import the SQL alchemy user table Uuid. Okay this is complicated and sounds crazy long,
but these are just what we need to import. And then what we're going to do is beneath the base. We're going to say class user. And this is going to inherit from the SQL
alchemy base user table, Uuid and base. We're then going to say relationship okay. And this is going to be post. And then we're going to say this back populates
the user. Essentially what we're doing here
is we're creating this table this user table. So we can have a relationship between our posts
and between our users. We want to know what post was created by what user
and what posts exist for what user. So we're going to say posts is equal to this. And now on this user will be able
to find all of their posts okay. So that's what we're saying. Back populates user. So what I'm going to do now on this post database
model is I'm going to create the relationship content to the user so that from a post
we know what user posted it or what user created it. And from a user we know what post that they have. So on my post I'm going to do this. I'm going to say my user ID is equal to column. And then this is going to be what we want here Uuid we're going to say as Uuid is true,
we're going to say foreign key. And this is going to be user ID
and we're going to say nullable equal to false okay. Now a foreign key is essentially a reference
to another table. So in our case what we're saying is for this post we want to have a foreign key
which references the ID of a user. That's what we're doing. So we know the user ID that posted this post
that made this post. And it was well,
is that we're going to create a relationship and we're going to say user is equal to relationship. And then we're going to have user ID,
we're going to say back populates posts. Now these relationships
automatically link these objects together and allow us to use the dot posts
and the dot user attribute on both post and user. And the foreign key allows us to have this link
so that we know the user's ID. Now, what we've created here is what's known as a one to many relationship,
where one user can have many posts. There's different types of relationships
that we can define in our database models. This is not a SQL course,
so I'm not going to get into that too in-depth. But the relationships to be aware of
are one to many, many to one and 1 to 1. Most frequently you are using one to many, which
means you have one user with many potential posts. Right now, if we wanted to flip the relationship
around where one post had many users, for example, we would simply change the foreign key to exist
on the user table rather than on the post table. So typically the child. So in this case, like one user has many posts to
the post would be considered a kind of a child of the user in terms of the relationship
hierarchy is the one that contains the foreign key. Again, this is something you need to look at more
if you're designing databases. And again, this is not a SQL course. The point is we need to make this relationship. So now users are linked to posts. Now that we have that we've created the user it's
inheriting from base as well as from SQL alchemy. We can go back to users and we can start using this. And actually sorry there's one more function
I forgot I need to right here. So let's go down to the bottom
and we're going to say async define get underscore user underscore db. We're going to say session. And this is going to be the async session
equals depends okay. And let's build depends correctly
and depends is a capital of course. So we're going to say it
depends on get async session. And then what we're going to do here
is we're going to say yield SQL alchemy user database session and user. Now why is this giving me an error
I guess I didn't import it. So let's go from fast API. So from fast API import depends with a capital D. And I think that should be good. Now essentially what we're doing is just writing a function
that's going to get us the user database table. So that's effectively what this is doing. Again, don't worry too much better. The database stuff is a little bit confusing. The point is it'll just give us the database table
that's associated with the users, which we're going to have to use here. Now inside of this user's API y file okay. So from here
we're going to create some variable called secret. This should be equal to some random string. You can actually generate this
with a specific function on your computer. Point is you don't want to share this secret string
because this is actually what's used to sign your JWT tokens, which identifies them
as unique to this particular app. If someone were to have access to the secret key,
they would be able to decode your JWT tokens, which you don't want. So this is something that you want to keep secret,
hence the name secret. In my case, I'm just making it something random. So now what I'm going to do
is I'm going to say class User manager. And this is going to be uuid id mixin. So we're going to inherit from that. We're also going to inherit
from the base user manager. And then we're going to have the user and the Uuid. Don't you uid okay. Again I know some of this stuff seems confusing. This is directly out of the fast API user's documentation
you just set up one time and then you're good to go. You don't need to memorize it, just bear with me. So now what we're going to do is we're going to have
the reset password token secret and the verification token, the token secret, both
being equal to the same thing, which is a secret. Can make it different if you want. But that's all we need now. We actually don't need to do anything else
inside of this class. But I just want to show you
that we can write various functions here so that we can handle things that happen when a user
registers, when they forget their password, or when they are requesting to, for example,
verify their token. So what you can do is
you can hook into all of these common user operations that are going to automatically
be written for you by fast API users. So we can do something like async define. And then you can see there's a function
like on after register. And this is a function
that's automatically handled for us. And what we can do is we can just say you know print
you know user has register and there you go. Right. So after the user register boom
this print function will run. And if we wanted to do something specific
we could do that inside of here. We could have another one like async define
and then on after forgot password right. And then boom we can do something
on after request verify and then we can do something. I'm just showing you some examples.
If you could hook into these. If you look into the fast API users docs
and you can control what's happening based on certain operations,
it will be automatically handled for you. So now what we're going to do
is we're going to write a quick function. We're going to say async define gets underscore
user underscore manager. And we're going to say
the user DB is equal to SQL alchemy user database. And this is equal to depends get user db okay. We're then going to yield the user manager with the user db. So essentially we're taking our database user
kind of injecting that inside of here. And now we have this user manager class
which will allow us to manage the users in fast API. We then are going to have the bearer underscore transport which is equal to the bearer transport. And then we're going to have our token URL
be equal to auth slash jwt slash login. So when someone wants to log in
they go to this endpoint. And then they can pass their credentials
and they can log in as a user. We're then going to say define get underscore
JWT underscore strategy. And this is going to return JWT strategy. We're going to pass our secret. And we're going to pass our lifetime seconds. Now the lifetime seconds is how long you want a token to be valid before before the user needs to sign in. Again I believe this is 30/602, which is going to be,
what is that, 10 minutes or 1 hour or something? Yeah, I think up. Yeah. So sorry. This is one hour. So 60 minutes is 30/602. So you can change how long you want
the token to be a live for essentially. And if you want to invalidate the token
after a certain period of time, by default JWT tokens have some lifespan. The longer you make them, the more convenient
it is for your users, but also the less secure, because that means like, you know, someone could come
on to your computer, for example, and they would, you know, be able to just start using the application
because the JWT token is still live. Okay. Now we have the JWT strategy. We're going to define the auth backend, which
is going to be equal to the authentication backend. We're going to say the name is equal to JWT, the transport is the bearer transport. And we're going to say
get strategy is get JWT strategy okay. We're then going to say fast
API users is equal to fast API users. User uid you uid get user manager and off back end. So when we define fast API users, what we do is
we specify this is the user model that we're using. And this is how we get the user manager. This is the back end that we're using
which is JWT tokens. Then we say the current active user is equal to fast API users. Dot current user active equals true. What this is going to do is when we call this current
active user function, it's going to automatically give us the current active user by going
and checking the user's JWT token. Again, I know this stuff is confusing. You're not going to understand a lot of the stuff
that's being written. The point is this framework,
when you do the little bit of setup that we're doing here, will automatically handle
all of the authentication for you. We just need to kind of hook it up and do this setup. Once we do this setup,
all the JWT auth will be handled automatically and all you need to do is just use this,
which you're going to see. So now we've written everything that we need
for users API. So what we're going to do is go back to API. And we're going to start actually using this. Because now we need to essentially connect
different endpoints to this. What do you call it. JWT kind of backend which you're going to see. So now that we've done
that we're going to import this. So we're going to say from app dot users and we're going to import the off back end okay. We're going to import the current active user. And we're going to import fast API users. Now what we're going to do is for our app,
we're essentially going to connect the different off endpoints
that we need to our fast API users endpoint. So you'll see what I mean in a second.
But we're going to say the following. We're and say at app Dot include underscore router. And we're going to include the fast API. Underscore users dot get underscore off underscore router with the off underscore back end. And we're going to say the prefix for
this is equal to slash off slash JWT. And we can say the tags is equal to off. Now what we're doing here
is we're saying okay in my app I want to include all of the endpoints
that are automatically provided by fast API users. So I'm just going to say app dot include router. And I'm going to connect it
to all of the endpoints here. And I'm going to prefix this with slash off slash JWT which means I go to slash
slash JWT plus all of the endpoints that are automatically included by my fast
API users, kind of module that I brought in here. Okay. So things like resetting your password, you know,
after you forget the password, what happens? All of those endpoints are automatically written
and handled here for you. And we just include them into our app. You'll see what I mean in a second. Essentially we just including some routes
that are automatically written inside of fast API users in our app. Okay. Now we're going to keep going because there's
some more routes that we need to include. So we're going to say app Dot include router. And this is going to be fast
API users dot get register routes. This time we're going to prefix it with slash off
and the tags will be off. However for the register routes
we need to pass in some schemas which we're going to write in a second,
which is user read and user create. And then we're going to write those in schema. So let's do that. Really quickly and then come back
and I can explain what we need. So for user read
what we need to do is we need to say from fast API underscore users imports schemas I then need to import Uuid. So I'm now going to come in I'm going to say
class user read. And this is going to be from schemas dot base user. And this is going to be Uuid dot uuid okay. And then this is simply going to say pass. I'm then going to say class user create. This is going to be schemas dot base user create. And then I'm going to say pass. And I'm going to say class user update is going to be
schemas dot base user update and then pass. Now these are schemas
that automatically come from fast API users. But similarly to some of the schemas we had before,
we need to just create our own kind of dummy, schema that inherits from it
that we could then override if we want to. So that's exactly what we're doing now. We're going to go back to app
and we're going to import this game is that we need. So from app dot schemas
we're going to bring in the user read. We user create okay and be user update
which we'll have in a second. All right. And now this should be good to go okay. So that's that for the registration routes. So again same thing. We're now going to have the ability to register
automatically be created for us I'm going to show you this in the documentation
in a second from fast API users. Now if we wanted to bring in other routes
we can do that. So for example
we can say add app dot include underscore writer. And then we can say fast
API users dot get racer reset password router. Right. And now if we do that
we now have the ability to reset the password. Now let's keep going. Let's say app dot include router. And we can do the get verify router. And this one we actually need
because it will allow us to verify user. So we bring that in. And then notice automatically we bring in user read. And then lastly we can have get users router. So going to say app dot include router
get users router. And then we can include the user read
and the user update okay. So let's quickly just reload this. And I'm just going to shut this down and restart it. So that we can test and make sure it's working. So now if I go here you can see that
I have all these new routes that are popping up, right, like login,
login, register, forgot, password reset, password request, verify token, verify users, me
users, me users ID, user ID, user ID right, all of this kind of stuff that I'm able to now
utilize because I'm using fast API users. Again, I'm not going to go through literally every
single thing here, but I will show you the basics. So what I want to do now is
I want to register a new account. So what I can do to register is I can go try it out, and I'm just going to do a new
and a new account here. So I'll go Tim ad tech with Tim Dot net. And then for the password we'll go 12345678 okay. We'll just make is active. True. And then we won't set anything for
the super user is verified. You can change these
so you don't pass these when you register later on. But for now in kind of debug mode,
that's what we're doing. So actually let me just change just like Tim
one at tech with Tim Dot net. Let's execute this. And then it's going to now give me my ID my email
and then all of this information. And now what I can do is
I can sign in with this user. So what I'm going to do is I'm going to go
to authorize, I'm going to pass my email and my super secure password and press on authorize. And now it's going to essentially sign me in. Now that I'm signed in it's
going to give me this token. And what will happen is whenever
I send any requests, it's going to use this token. So now I can go to for example, user slash me. And I can just send the request here. And you're going to see that.
It tells me hey I am Tim. Want to talk with Tim like that? And notice that automatically this super long token was included in my request
because that's how this works. So if you sign in here with authorize,
then by default this token that identifies you is going to be sent in all of the future requests
as you're using this documentation, and you're going to be able to verify
if you're user or if you're signed in or if you're not signed in, etc.. So now all the user stuff is working. However, we need to associate users with the posts,
and we need to make it so that I can't actually call these endpoints
like upload feed or post unless I'm signed in. So let's do that. So I'm going to go to Main.py. And the way that I can make some of these routes,
what's called protected is I can add a dependency that forces the route
to get the current active user. And if the current active user doesn't exist,
it won't. Let me call this endpoint. So for example, to be able to actually
make an upload, well I need to have a user, right. Like I need to be signed in as a user. So what I can do is I can just change the function
to be like this. So I can just say user colon and then I can say user and I can say this is equal to depends and this is current active user like that. For user I can just import that from my database. So let's just import user like so. And now what will happen is
if I try to call this function and I'm not signed in and I cannot get the current active user,
this function will not work. So literally all you have to do now is if you want to
protect these routes, you just add this dependency. So for example for the feed same thing,
we add the user. We say okay
you know this is the get current active user. And now it won't work unless we have the user know. Same thing for the posts.
We're going to get the current active user. So let's do that. And we're going to have to change
some of the content of these functions now so that it actually saves
the user that is doing this operation. So let's go back to the DB and for our posts
we're now associating with users okay. So that's all good. But for our app when we are uploading images
we now need to associate them with that user. So for our upload result here
let's take user underscore ID and let's make this equal to the user id. So now we're storing the particular user
for every single post okay. So that's good. That's all we need to do there. Then for deleting the post. We need to only allow the user to do this
if they're signed in as the correct user. So once we check for the post down
here, we're going to say if the post dot user underscore ID does not equal the user ID, then we need to raise a Http exception. So let's do this okay. And we're going to say status code 403
which means unauthorized. You don't have permission to delete this post. So now if I try to create a post with an account
that didn't make this post or sorry, I try to delete a post with an account
that didn't make it. It's not going to allow me to do that. Okay, so now we've just added the authorization check and I think that is pretty much good. Now we're going to go to get feed and forget feed. We're going to make some enhancements here so that we actually know
which user made these posts in the feed. So first things first. What I'm going to do is I'm going to include
the user ID in my response here. So I'm going to say user underscore ID is equal
to the string of post dot user underscore id. And I'm also going to include, if we are the owner of this post
so that the front end knows that. So we're going to say is owner
and we're going to say post dot user underscore ID is equal to user.id. So now we're checking
all right is the user ID of this post equal to read. If it is that means we made it. If it's not well we didn't make it okay. And then I think that should pretty much be good. But there's one last thing
actually that we will do. Okay. So for every single one of our posts, I also want
to include some basic information about the user. And in this case that's going to be their email. And what I'm going to do
is I'm going to get the user's email. So I'm going to say email. And then I'm going to say this is post
dot user dot email. And I think that will work. But I'm not 100% sure. Let's see. Okay. So now we kind of have protected our endpoints
so that only authorized users are able to use this. And that should pretty much wrap up the API. But of course we want to test this. And then I'm just going to have a little simple
front end that I'll show you how to use so we can actually see it visually. All right. So let's authorize ourselves
by signing in with that account. So we're gonna say Tim one at tech
with Tim dot net okay. If we can spell this correctly
all right then we're going to have 12345678 and authorize okay. So now let's go okay. That's fine. From here let's go to the feed. And let's try to get the feed okay. And when we get the feed gives us internal server
error. Let's see what the problem is there okay. And of course the error came from this where
I'm getting the post user dot email. That is not working properly
just due to how we're loading the user. So I'm going to do another approach
here. This a little bit more complicated. It will allow us to get the emails. So I'm just going to say result is equal to await. This is going to be session dot execute. And I'm going to say select user. Then what I'm going to do is say
users is equal to row zero for row and result dot all I. Then I'm just going to create a list
of all of my users. This is not the most efficient way to do this,
but for this example it's fine. So we're going to say user dictionary is equal to
and this is going to be you.id and this is going to be
you dot email for you in users. Okay. Now what we're able to do is we're going to be able
to get the user's email like this. So to get the email
we can simply say user underscore dict. And then this is going to be dot get and it will be post dot user ID. Otherwise we'll just put unknown okay. So now this if we save it should reload okay. And now if we go back to the feed let's try it again. And you see now that we get the post
and it is working and saying we are not the owner of this post
because we were not signed in as that user. So now if I take this post ID right? And if I try to delete this post,
you're going to see that it doesn't work. So let's go here
because I'm not signed in as the correct user. So let's go here and try to delete execute. And you see
it says you don't have permission to delete the post. Perfect okay. So that wraps up the API. The API's working. It's fully functioning. The next thing that I'll quickly show
you is how we can get a nice little user interface here,
so we can actually play around with the UI. Now I'm not going to write
the user interface from scratch. If you want all of the code for this,
it will be linked in the description down below. You can simply copy it and paste it inside of here. What I'm going to do is just make a new file
and I'm just going to call this front end.py. I'm just going to copy a bunch of code
that I've written previously, which is the front end, and paste it in here. Okay. Now this code uses something called Streamlit,
which is something that we need to install. So what we're going to do
is we're going to keep our back end running. We're going to go into a new terminal
and we're going to type. You've add Streamlit. Now that's going to add Streamlit to our dependencies,
which is going to allow us to now run this front end. So again, all of this code that you see
will be available from the link in the description. You can literally just copy it
into a file called front end. And this is going to handle essentially interacting with the API
that we just spent all of that time writing. I will go over how it works in a second,
but let's just quickly test it. So to do this I'm going to type essentially you've run Streamlit run main dot here. Not mean this is front end.py. When I do that it will start running Streamlit. It should just open it in your browser
and then you have a user interface. What you can do is you can type in an account
like Tim at Tech with Tim Dot net. You can type in the password. When you do that, you can either sign up
and make a new account, or you can just log in. And if you press log in, it should bring you. Now let's wait a second to the feed. Failed to get user info. Okay. Interesting. So we'll fix that in a second, but it should
bring you to the feed once I fix this problem. Okay.
And you can see here that it now loads the feed. We have my username the date the image. And then we can delete this. We can also expand it directly from here. Now I want to talk about kind of how this works. So I'll walk through the code a little bit. But again
just copy the code from the link in description. It's not really valuable for me to write this out with you,
because this is not a tutorial on Streamlit. Now, first things first, the JWT token
is something that we store in our session. That just means that we're storing it,
so we're constantly using it. Anytime we send a request to the back end. Okay, the way that you send JWT token requests is you include bearer plus the JWT token, and then when you do that, you're able
to actually send an off authenticated request. You saw this in the examples when we were looking
at the documentation from fast API. But I'm just doing it directly,
kind of manually here in Python. Again, I'll show you
how the request kind of looks in a second. So here is the login page. Right. So when I want to log in
what I'm doing is I'm just passing a username and password and I'm passing it to this URL. So off JWT log in right passing my data. And then I'm able to log in. Now when I do that I get some token data. And I stored that access token in my state. So I'm able to use it as my JWT token to sign it. I then get my user info
by passing a request to user slash me so I can make sure that I'm signed in successfully,
and I store that again in my session state. Same thing for registering. I send a request to all slash register and then after I register
I send another request to get my JWT token. So that I'm good to go. Okay, now we have the upload page. On the upload page, what I do is I have files, right? So I have a file which is the uploaded file that
I select which I'm going to show you in a second. And I have a caption. I then include that in the request to upload. Right. As well as my headers which include my JWT token that's then able to upload the image
and then show it on the feed. Now the cool part here is when we start talking about actually displaying the images with image kit,
because it makes it super easy for us to view the images
and to actually perform transformations on them. So let me show you with the UI,
if I go here and I go to upload, okay. So let's go to upload. Let's change this here and let's upload here
this one with main even me, which is a testimonial
I'm going to say hello world dev launch worked okay because this helped him land a job. Let's go share. And then it's going to upload this file
for me to my backend API. And if I go back to the feed
now just takes a second to load. We'll see. The image appears here. And one thing you'll notice if I make these larger
is that we have this caption that I'm kind of putting directly on top of the images by using image Cat,
where I say hello world dev launch work. Now it's quite small. I'm going to show you how to make it larger, but the way that I do that is from my
UI before I load the image. So if we go down here, let's do this. You can see that I have these file types. So I check if it's an image,
which is all we've done right now. But I'll show you videos in a second. Then what we'll do is
we will, essentially transform the URL to use the transformation that puts the caption
directly on top of it that looks like this. So this is essentially the text, the use
if you want to add a caption. If I want to make it bigger, I can say like font size
100 and then it will make it much larger and then it directly transforms the image from me
from this one URL and passes it back very quickly. Okay, so that's how I'm kind of transforming this. Now I want to show you uploading a video,
which we'll do in one second. And then displaying the caption for that video. But that's kind of how I'm doing that transformation
with image kit and displaying the images. So let's go back. And now if I refresh this, let's sign in again. So 12345678 and go log in. Then let's wait a second here okay. And we'll go to the feed and okay get rid of that. And you can see now it's quite a bit larger
because I made the font size 100. If I made it like 1000
obviously it'd be even much bigger. And you can see the text shows up on the image. Now if I want to upload a video,
I can totally do that as well. So let's go files and let me find a video. Okay, so I just found the screen recording
that's not that big. So I'm just going to upload this screen recording. So you can see right there I'm going to go ahead
and press on share because it is a video. It will take a little bit longer to upload. So we'll just wait for that to complete. And then as soon as it's uploaded okay. So let's go back to the feed here. We should be able to see it directly from image Kit. So it's going to take one second to load here.
And then there you go. We get the video and we can watch it right. And it just shows up directly here. Now same thing with when we transform the images
we can transform the video. So let me show you a few examples
of how we actually do that. Let's go here. And what I'm going to do is I'm just going to put in
some transformation parameters. Right now. You can see that
I'm just putting it quality at max and width 300. But I can adjust this to, for example crop the video. So for example if I just take this transformation
parameter I'll just show you what this does. But essentially it crops the video and then adds a,
blurred background and puts it in vertical format. For me. In this case, it's already vertical,
but if it wasn't vertical, it would do that. So if I go back to my was it simple here
UI, let's just go back to the feed so it reloads okay. And actually you can see that this did work now. So it resize to be quite small. And you can see now that we have kind of this video
like in the middle and then we have this blurred background
which is exactly what I got the transformation to do. The quality is quite low obviously,
because of how I adjusted it in the size that I made it,
but you can see that it is working. And so if I just scared you there with that audio,
that scared me as well. But the point is it is loading. Cool. So I think that's pretty much it, guys. I mean, that covers
everything that I wanted to show you. We made a application that's able to post
photos and videos, is able to handle user art connected to a database, has all kinds of different, more advanced features that you typically don't see
in more beginner tutorials. And while I know this was a really long video
and a lot of code, I want it to be super detailed and covered everything in as much depth
as I possibly can. If you guys enjoyed this video,
make sure leave a like. Subscribe to the channel
and I will see you in the next one.