Learn Fast API With This ONE Project
By Tech With Tim
Summary
## Key takeaways - **FastAPI auto-generates interactive docs**: FastAPI automatically creates fully interactive API documentation at /docs and /redoc where you can test endpoints directly by pressing 'try it out' and 'execute'. This is the coolest feature of FastAPI. [28:11], [29:15] - **ImageKit handles all media complexity**: ImageKit manages image/video upload, optimization, cropping, and transformations via simple URL parameters like tr=w-500,h-300 or e-contrast, eliminating the pain of manual media handling. Backend uploads via ImageKit API return URLs with full metadata. [01:10:27], [01:24:20] - **JWT tokens via FastAPI-Users**: FastAPI-Users automatically generates complete JWT authentication system including login/register/reset endpoints at /auth/jwt - just include the router and add current_active_user dependency to protect routes. Users get tokens that identify them for all subsequent requests. [01:30:54], [01:45:14] - **SQLAlchemy async ORM setup**: SQLAlchemy async ORM replaces in-memory dictionaries with persistent SQLite database using declarative_base models, async_sessionmaker dependency injection, and simple session.add/commit/refresh pattern for CRUD operations. Models auto-create tables on startup. [46:30], [01:04:01] - **Pydantic schemas validate everything**: Pydantic BaseModel schemas automatically validate request body data, generate perfect docs, and enforce output types - FastAPI rejects invalid data before functions execute and documents exact response schemas in /docs. [43:28], [45:56] - **Dependency injection simplifies DB access**: FastAPI's depends() injects database sessions and current_user automatically into every protected endpoint - just add session: AsyncSession = Depends(get_async_session) and user: User = Depends(current_active_user) to access database and authenticated user. [01:01:39], [01:52:24]
Topics Covered
- APIs Secure Data Access Layer
- FastAPI Auto-Docs Transform Development
- Dependency Injection Automates Sessions
- ImageKit Backend Uploads Secure Control
- JWT Tokens Stateless Authentication Wins
Full Transcript
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. Now,
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 we'll be
some code. So, the project we'll be building here is a simple photo and video sharing application. Think of it like the really early days of Instagram except it won't have nearly as many
features. Now, the way this works is you
features. Now, the way this works is you can sign in. So, I've set up a really simple user interface here in something called Streamllet. We'll talk a little
called Streamllet. 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
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
well. Now, you have the ability to upload something. So, for example, if
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
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.
it. It's just going to take a second.
And then this will be uploaded to our feed. So right now it's just uploading
feed. So right now it's just uploading the video. And if we go back to the feed
the video. And if we go back to the feed here, we should see the photo app. And
you can see the photo shows up by us and that it's on today's date when I'm recording this video. Okay, so that's the application. I know it seems pretty
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
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 myro board that I put together because I want to start going through some theory that'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 are free to use and play with. you don't need to pay for
play with. you don't need to pay for them and they just make this process significantly easier. So big shout out
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 programming 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 an API really does for us is
data. All an API really does for us is it facilitates the access and control of data. In our case, it's going to be
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 browsed to a website. And I want to just go over the
website. And I want to just go over the components of the URL so that 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.devlaunch
us. This is our domain. techwithim.net.
This is the domain. The domain will typically end in like us or.com or.net or.ca or something along those lines.
or.ca or something along those lines.
Now after the domain, you have what's referred to as the path or sometimes called the endpoint. 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.devaunch.us/tim.
training.devaunch.us/tim.
So, I'm going to the /tim page right here. /courses/python.
here. /courses/python.
So, I'm going to the python courses page right. And for a typical website, these
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
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 Aapi.com/photo,
Aapi.com/photo, right? And then we can access a
right? And then we can access a particular photo. You'll see what I mean
particular photo. You'll see what I mean in a 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. You'll
see some kind of path or endpoint, a question mark and then one or multiple query parameters. In this case, we have
query parameters. In this case, we have a parameter video equal to 1 2 3. Okay.
And then if we come here, we have UTM source is 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
you like. You just have to have these amperands that separates them. Okay? So
just understand that these are the core components of a URL and an endpoint. 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.
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
post, right? We can make a post, we can sign in, we can sign out. 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 programming interface. The API you can essentially
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
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 backend, this kind of secure location. It will essentially
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
thing. For example, deleting a post. We
would send a request to the backend. The
backend would say, "Okay, yep, you know, I'm going to be able to delete this post." And then it would return the
post." And then it would return the response saying, "Hey, this post was deleted. I want to upload a photo. I
deleted. I want to upload a photo. I
send a request to my backend. 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 backend 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
video. Now, when we send a request, so we have this front end, right? This
client, it sends some data to the back end. And the main parts of a request are
end. And the main parts of a request are the following. Okay, we have a type of
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
here, right? So this path or this endpoint and oops, I didn't mean to save that. And we have a body. This is
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
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
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
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
user. Okay, we are signed in as, you know, Tim attechwithtim.net. The header
would kind of indicate that when 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 uh clear, sorry, when we actually get into the API example. We then have the response. Okay, so the request is 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 backend is the thing that we as a developer typically write 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 backend will send a status code to the front end indicating what happened with the request. Common status codes are 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 might need. And then we have headers. Same
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
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 /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
endpoint. What this means is that we are retrieving some data from this resource essentially from /books. We then have delete. This is the type of method you
delete. This is the type of method you would use when you want to delete something. We have post. This is the
something. We have post. This is the type of method you use when you want to create something. And you have put. This
create something. And you have put. This
is the type of uh method you would use when you want to update something. So
remember how 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
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 send. Then we go over to HTTP status codes. Now, you don't need to
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,
may see. So, you send this request, right? You say, "Hey, I want to get a
right? You say, "Hey, I want to get a book, for example." All right? Then,
what the uh 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 2011 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
server error. Don't need to memorize them. I'm just showing you that there's
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 all crystallize, but this is just going to really help you understand how we design the APIs. Okay,
so user wants to update a post that they made. So, the type is patch. This is
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. 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
update. So I'm using patch. Okay, so the type or the method of my request is patch. The path is / API/post.
patch. The path is / API/post.
And then this is the ID of the post. So
I'm saying, okay, I want to go to my API. I want to modify a post. This is
API. I want to modify a post. This is
the ID of the post that I want to modify. And then the body would be this.
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 backend and then this is the request, okay, that we create. Now from the backend we get a
create. Now from the backend we get a response back. So we send a request, we
response back. So we send a request, we get a response back and the response looks like this. We have status code 204 which stands for updated and then we have some body and this body says hey this is the title, this is the
description, this is the post ID, this is when it was updated, this is when 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 you know 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, um it's a little bit more complex than simply sending requests and getting responses. Essentially, what you need to
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
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
want to 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,
with data. Creating, reading, updating, deleting data. That's effectively what
deleting data. That's effectively what an API almost always deals with. And
it's doing that so it 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
using PyCharm. Now you can use any code editor or IDE 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
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
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
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
desktop. Okay? And you can again use any editor that you want. Um, 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
Python. The first is pip. The second is UV. Now, I'm going to suggest that you
UV. Now, I'm going to suggest that you use UV because this is significantly more modern and it just works a lot better. Uh, but if you don't want to use
better. Uh, but if you don't want to use UV, you can replace the commands I'm going to show you with pip. Okay, so UV 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 UV installed on your computer, what you can do from this open folder is you can type
UV innit 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 uvanit 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
your folder. You're going to see a main.py file, a piprotoml, and a few
main.py file, a piprotoml, and a few other files that you don't really need to worry about too much. Now, this
piprotoml 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
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 uvad and then we're going to start by just adding
fast API. Okay? So, we're going to type
fast API. Okay? So, we're going to type uvad fast api. When we do that, if we go back into pi project autotoml, you'll see this dependency has 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
code. Now, as well as fast API, what we're going to do is we're going to type uvad and we're going to install python-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. Now,
there's a few other things that we need to install as well, so just bear with me. We're going to type uvad and we're
me. We're going to type uvad 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 add
fast API- 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
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 add and then we're going to add the image kit. So, we're going to say image kit like this. Uh, and sorry, it's going to be image kit io. 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 uv and we're going to add uvicorn
and then standard. Unicorn 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 Unicorn. 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 io sq 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. 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 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
now. Okay, so inside of this file, there are three variables that we need to define. The first is going to be
define. The first is going to be imagekit_private_key.
All right. The next is going to be the imagekit_public_key.
And then the last is going to be the imagekit URL. Okay. So, we're going to
imagekit 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
three values. So, we don't need to come back to this until much later in the video. Now, you may be wondering, what
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
to go. So we can just focus on coding and 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 ImageKit. So I'm quickly just going to open up the ImageKit website. I'm going to leave a link to
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
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, uh, 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 am
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 need 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.
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
it. Again, don't share this with other people. I will delete it afterwards. And
people. I will delete it afterwards. And
then lastly, we're going to grab this URL endpoint. Okay, which should be
URL endpoint. Okay, which should be right up here. And we're going to paste this inside. And now we have the keys
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
our project. So I'm going to make a new folder. And this folder is going to be
folder. And this folder is going to be called src. This is typically best
called src. This is typically best practice when you're writing a fast API application. you create this source or
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 gotten to the point where everything is set up. what we're going to do from this uh 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 a set of
parenthesis. Now this is the fast API
parenthesis. 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
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
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.get.
Now when you do this what you're going to do is you're going to specify the endpoint. So we're going to say slash
endpoint. So we're going to say slash and then something like you know hello dashworld. Okay, so this is the path or
dashworld. Okay, so this is the path or the endpoint. Now, I also forgot I need
the endpoint. Now, I also forgot I need to put an at symbol here because this needs to be a decorator in fast API. A
decorator is something with the at before it. And what you do beneath this
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 parenthesis, and then inside of here, you can return some data. So, let's just
quickly return the autocompleted data where it says message hello world. Okay,
so we have atapp.get/hello
world. What we're saying is, hey, when you go to our API and you go to /hello world, this function is going to be called and then we're going to return this data. Now, the data that we always
this data. Now, the data that we always return from our endpoints is either going to be a paidantic 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
right? You have some key associated with some value. And the reason why we return
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
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 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
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 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 uicorn like this. We're then
going to say if_ame is equal to_main then we're going to say unicorn.run.
We're going to put app colon app. I know this seems weird. I'll
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 uh is equal to 8,000 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 Uicorn, which we've already installed, right, with UV, 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
it. the API that I want to run is app.app. So that's inside of app. So the
app.app. So that's inside of 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 were 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
essentially. So we have the name of the folder is app, right? The name of the Python application is app and then this here is app and we have app.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
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
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 be 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 is. If we're on the same machine, it's going to be localhost.
We're going to go to port 8000, and then we can access this resource right here, which is /hello-orld.
So for this what we can do is run this main.py file. To do that we simply type
main.py file. To do that we simply type uv 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 um uh what is it? Editor. So let
me just shut the other app down and rerun it. And then we should be good to
rerun it. And then we should be good to go. You can see that it's running now.
go. You can see that it's running now.
Again that issue you wouldn't have run into. It's cuz I had a demo application
into. It's cuz I had a demo application running in a different um editor that I have open. You'll see what's happened
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 reloader
process and by me specifying reload equals true anytime I save or make a change to this file like if I do something I don't know hello here and then I save this you'll notice that the
file or sorry the server will shut down and restart with the changes that I made. So, it's really useful for when
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
URL. So, you can just click it and open it up. Now, it's saying this 0.0.0 isn't
it up. Now, it's saying this 0.0.0 isn't working. So, what we can do is change
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 uh 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 /doccks. Okay, when you do that, it's
/doccks. 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
have been. So what I can do is press try it out. I can press execute and then you
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 successful response.
success. Okay, 200 successful response.
So there you go. We just sent a request, right? We tested it out. It's all
right? We tested it 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
this. You can directly go here and test out all of your endpoints by simply going to the slashdocs endpoint. This
will become more useful later on, but always check this out. It's very, very useful. Now, there's also another
useful. Now, there's also another endpoint uh called /redoc.
This is kind of a newer version of that docs endpoint. It works the exact same
docs endpoint. It works the exact same way. We can test this out if we want um
way. We can test this out if we want um you know test the API etc etc uh and kind of see how this works. I am not going to uh dive into this too much right now but the point is there's this other endpoint called redoc which you
should be aware of but the one that I prefer to use is called /doccks. Now
just another quick thing if we wanted to we also could just directly go to slashhello-world.
If we do that you see it 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 and the browser is able to actually render it and show it for us. But generally for all of the
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 /docs page or another tool to test the API. So we've now written kind
of this dummy get endpoint. However, it
doesn't really help us accomplish our project goal, right, of creating, you know, posts. So what I want to do now is
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
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
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
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 endpoint and we're going to say at app.get get and this is going to be
app.get get and this is going to be slash posts. Okay. And for the function,
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
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
have. So, we're simply just going to return text post like that. Super
simple. If we go back here now to this and we refresh, you'll see that we have uh why is it still showing that? Okay,
let me just restart my server because for some reason sometimes this messes up. So, we'll just restart it. Okay. And
up. So, we'll just restart it. Okay. And
I don't know what was going on there. I
had some weird issue. But anyways, I got this now back to the docs page. And you
can see we now have slashposts. 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
posts. However, if we put a post in here, then we would be able to retrieve it. So, that's a good endpoint. But what
it. So, that's a good endpoint. But what
I'm going to do now is I'm going to start making some posts. So, I'm going to have some ID 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
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 endpoint that allows me to retrieve one individual post. So first of all, let's
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 at app.get and I'm going to type /post slash and then inside of parenthesis 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,
individual post. So we're doing ID, right? And then what I'm going to do
right? And then what I'm going to do here is say define get_ost 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 do get and then I can 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 um 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 then try out go with one and execute. You can see now that we get the
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 posts then I can raise an HTTP exception. I can specify a status
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.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 chatbt generate a
bunch of test posts because I'm going to use these um throughout the rest of this kind of section here to demo a few more things. All right, so what we've done is
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
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
functions. So up until this point it's been pretty straightforward, right? We
just can call /ost. We can call for a particular ID. Now I want to make it
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
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 10 or something right whatever.
Um so that we can actually filter kind of the number of posts maybe that we're receiving. So what I'm going to do is
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
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
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 parameter
to. So my idea with the limit parameter is that maybe I don't want to receive all 10 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. and we'll just use that and
that's fine. and we'll just use that and then otherwise we'll just return the text post. So now if I go back to my
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
parameter. So what I can do is go try it out for limit I can pass maybe three for example and then execute and what it gave us an error. Okay that's weird. So
the reason we got that error sorry is because uh 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 uh
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
them. So, let's go back here and let's go refresh. And then we'll go here. You
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
parameter. If you want to do that, you simply specify it in the uh parameters here. You can make it optional, which
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 um then you could 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 is 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 int 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 of fast API. And that's why you use what's
API. And that's why you use what's called a Python type hint inside of the parameters and you specify like what does 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.post
and I'm going to do slashpost.
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_ost.
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
hidden information. It's not directly inside of the URL. It's in the field called body. And the way that we accept
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 schemas. 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 import or we're going to say sorry from piantic import 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 uh 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
we have title and content for our 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 a little bit confusing. This is referred to as something called a 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
app.py, Pi we're going to say from app do schemas import and then we're going to import the schema that we just wrote which is the postcreate schema. Okay.
Now what we can do is for our 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 pidantic model by default fast API knows that we're receiving request body. Okay. So not
receiving the uh 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
post. So we can do something like let's do the following. Um okay we're going to say text post and then max of text
post.keys plus one because we need to
post.keys plus one because we need to find what the next ID essentially should be. And then we're going to say is equal
be. And then we're going to say is equal to and 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 it 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
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
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, uh 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
data. Now to delete data, it would be pretty straightforward. You go
pretty straightforward. You go app.delete
app.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. and we'll show it more when we actually get into kind of the finalized project. All right, so we're
finalized project. All right, so we're almost going to move on to databases, but I just want to cover one or two 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 /docs and have a look at it. You'll see that it doesn't show
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
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 um 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
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 postcreate type. Now that's going to look exactly
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 can 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 it's 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
us. You can see it gives us the value here. Example value is going to have
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
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 this 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 it out and I execute, you'll see it actually gives me an error 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 new
response. So, we need to go back to 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
post response. And then for getting all of the posts, uh 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 reinitialized with this new dictionary. Now that's not ideal. So
dictionary. Now that's not ideal. So
what we're going to start setting up is database connection. So, we're going to
database connection. So, we're going to go to our app folder and we're going to make a new file called db.py.
Now, inside of all of these Python API libraries, you typically have something called a OM. An OM stands for an object relational mapping and it's something that allows you to prevent writing SQL
code or NoSQL queries and instead to write Python like code to be able to define data, retrieve data, create data, etc. Okay. Now, the OM that we're going
etc. Okay. Now, the OM that we're going to use here is something called SQL Alchemy. Um, I believe we already
Alchemy. Um, I believe we already installed it. If we didn't, then we can
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, you know, 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.ABC
import the async generator. We're then
going to say import uyu ID which is something we can use to generate a unique identifier. We're then going to
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
column correctly. We're going to import string. We're going to import text,
string. We're going to import text, datetime, and foreign key, which we'll use later on. Uh, and did I spell foreign correctly? No, I did not. So,
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
alchemy. Okay, this is going to be dialects.
Let's spell that correctly. Postgres SQL
import UU ID. We're then going to say from SQL alchemy.exe ext.async
io import the async session and the async session maker as well as the create async uh engine. So let's bring in the async session maker. Again, I know this stuff
session maker. Again, I know this stuff seems confusing. It's just a little bit
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
it again. And then we're going to bring in from sql alchemy.org import the declarative base, but this is actually going to be a little bit different. It's
going to be declarative base 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
memorized. You don't need to memorize it. It's simply something that we need
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 this. So,
what we're going to do is we're going to define a uh variable and we're going to say the database URL is equal to SQLite.
Okay, so SQLite plus a io sq light.
Okay, colon three slashes dot slash test. DB. Now, what this is going to do
test. 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.db, which is in the current directory. And sorry, this needs
current directory. And sorry, this needs to be a plus here. So, let's fix that.
That we want to use SQLite plus AIOS SQLite, which is essentially an asynchronous version of SQLite, which is a really simple database that we can run locally. later. If you wanted to connect
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 would stay
exactly the same. You would 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
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,
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
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
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. Now this is important.
declarative base. Now this is important.
You need to inherit from the declarative base so that it knows uh 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
our database. Okay. Now we need to specify the table name. So we're going to say underscore table named 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 type UUID. We're going to say as_UID 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. UUID4.
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 uh entity
column. Now for every single uh 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 uni
be unique. So in this case, every uni that we're that 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 UU ID 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
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 name, so like caption ID, we say it's going to be a column. If
we're storing data, if we're storing a relationship, we would set it to a relationship or a foreign key or something different. And then you
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.
be text. Then I'm going to have a URL.
This is going to be the URL for the uh 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
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
column. Same thing, string nullable is false. We're then going to have the file
false. We're then going to have the file name. It'll be the exact same thing. So
name. It'll be the exact same thing. So
string nullable equals false. We'll then
have a created at and we'll say column date time. And rather than nullable,
date time. And rather than nullable, we're just going to say default is equal to datetime dot UTC now. And then we're going to import datetime.
So we're going to import uh datetime like that. Not now. Import date time
like that. Not now. Import date time like that. And now this should work.
like that. And now this should work.
Okay. So that's all that we need for the post. Later we will adjust this slightly
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 or actually create at 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
database URL. We're then going to say the async session_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
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 tables. What this is going to do is
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.be.
Okay. And then this is going to be as connection. And what we're going to do
connection. And what we're going to do is say await con. Okay. The connection.
So run_sync and then we are going to say the declarative base. Okay, dot
declarative base. Okay, dot metadata.createall.
metadata.createall.
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.
database. That's all that it's doing.
We're then going to have another function. We're going to say async
function. We're going to say async define get async session. Okay, this is going to return
session. Okay, this is going to return an async generator with an async session and none. And then we're going to say
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, again
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
tables, right? What this does is it starts the database engine and then it creates all the tables and creates the database. Then we have this get async
database. Then we have this get async session. What this is going to do is it
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
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 the the kind of important stuff later. So
that's all we need from this particular file. Later we will add a few other
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
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
database. So we're going to say from app db import the post the create dbn tables and the 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,
created. Now, in order to do that, again, this is going to look a little bit complex. Don't worry, just set up
bit complex. Don't worry, just set up once and then you're good. We're going
to say from SQL Alchemy.exd.async.io
and then we're going to import the async session. Then we also need to import
session. Then we also need to import something called the async context manager. So, we're going to say from
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 at async context manager. and we're going to say async
manager. and we're going to say async define lifespan. We're going to take in
define lifespan. 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
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
about this. It's kind of advanced Python syntax you don't really need to understand. And then all we're going 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 lifespan is equal to lifespan. 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
started. It's going to create the database and the tables for us and just make sure that this is essentially handled correctly and cleanly exit once the when sorry the application stops.
Now these other imports we'll use later on. Uh for now we'll just leave them in.
on. Uh 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
us. However, it's giving me an issue saying datetime.Uutc UTC isn't a
saying datetime.Uutc UTC isn't a function. Let me quickly fix that. So,
function. Let me quickly fix that. So,
we're have to actually change this to say from daytime import daytime. So,
just change that import here in the DB file. And now, if you come back here and
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.
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
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
base. And that should fix the problem for us. Essentially, we just cannot
for us. Essentially, we just cannot directly inherit from declarative base.
Uh we need to do this kind of other base class first. A little bit weird, but
class first. A little bit weird, but that will allow us to set up the database. I know the database setup
database. I know the database setup 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
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.
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,
created. It looks like it was for me, which means the database connection is set. And now we don't really need to
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 do 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
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.
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
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 uh what is it form and the depends.
Okay. Now we're going to make a new endpoint and we're going to say at app dot and this is going to be post because we're going to be uploading a file and this is going to be slashupload.
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 posted. So in order to do this, we're
be posted. 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
occur. Fast API is asynchronous by default. So you can always use the async
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 endpoint. Okay. We're then going to say
endpoint. 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 endpoint. For example, you can send a
endpoint. 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.
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
dependency injection. This is essentially a dependency injection. and
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
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
function. So we're saying we want to get the asynchronous se 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
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
the function. It will get the database object. It will pass it to us and then
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,
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
it. That's going to take us a second to be able to do because we need to use image kit. So before I do that, I'm
image kit. 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 type is equal to for
now we can just go photo then we can do a comma and we can say the file name is equal to dummy name okay so let's go dummy url 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 do add
okay and we simply add this post here that we created. Then we say await session.
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
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
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 wait session.refresh
wait session.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
database. So for example we have this created at and this ID when we specify the post we didn't specify an ID or a created at that gets automatically
created when we commit this in the session. So when we refresh the post it
session. So when we refresh the post it essentially gets hydrated with that extra data where we now get the ID and the created at 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 uh 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 at app.get
and we're going to call this the feed.
So we're going to say slashfeed. We're
then going to say async define get 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
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
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.execute.
Okay. And this is how you can execute a query. And we're going to say select and
query. And we're going to say select and we want to select on the post um what do you call it object or post kind of table. And we want to say order_by.
table. And we want to say order_by.
And we're going to say post dot. Okay.
Like this created 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
descending, right? So, I want to go in the order in which they were created.
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
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 doall.
Now what the resultall 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
at once. It's due to the way that Fast API returns the results here. It 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
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 uh comprehensive post object that I can return to my front end. that's going to
include some data that we need about our post. So, we're going to say post
post. So, we're going to say post data.append.
data.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 do ID. So, I'm going to convert it from a UU ID object to a
string. I'm then going to say the
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 dot URL. I'm going to say the file type is the post.file type. And
then I'm going to say the file name is the post.file name. And I'm going to say
the post.file name. And I'm going to say created at is the post.created atiso
format, which is going to give me a time stamp in a format I can actually read.
Then I'm simply going to go down here and I'm going to say return post and I'm just going to return all of my post data. Okay. Uh 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
posts. This should allow me to create a post when I upload a file. Again, for
now the file um 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 backend. 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 says multiart form data. So, I'm going to try it out.
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 files, some pay slips from a while ago. Let's go caption hello world. And let's execute. And
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 that 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
this database. Okay, so now we have the ability to upload or create a new post and to kind of view the post. Now we
need to start actually handling the image and video upload. And to do that, we're going to use image kit. 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 kit, images.
py. Okay, so from here we're going to go images. py and we're going to start
images. py and we're going to start importing a few things. We're going to say fromv import load.env.
import load.env.
We're going to say from imagekit.
Okay, IO import if we can spell this image kit. And remember, we imported
image kit. 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.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 ourv
file. So to do this, we're actually just
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.get envage kit private key public key
os.get envage kit private key public key os.get env public key URL endpoint.
os.get env public key URL endpoint.
OS.get envage kit URL endpoint. Okay, so
the same variables that we have here.
And actually, let's just make sure they're spelled correctly because this one is a little bit different. So let's
fix this to just be imagekit URL. Okay.
Now, what are we doing here? Well, this
load.env env function will look for the presence of this enenv 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
access them. Now to access those variables we use this os.got 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 was which is what we're about to do. So
let's go into app.py py 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 imagekit ioles.upload
ioles.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
account 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, deleting them, cropping them, modifying
them, and more importantly just being our storage. So we don't need to worry
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 to 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. Uh, but you can. In our
case, we're going to use image kit as our DAM. So, it's automatically going to
our DAM. So, it's automatically going to handle all of the asset management for us. Eventually, when we start uploading
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
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
images. Now, in terms of using image kit, uh what we can do is use a single API. Funny enough, we're building an API
API. Funny enough, we're building an 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, 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
the docs. So, I'm going to go docs like this. Let's go to the Python
this. Let's go to the Python documentation. You can see it supports a
documentation. You can see it supports a ton of different frameworks. 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 backend or server upload. So,
essentially, the user is going to send some image to our backend and then our backend is going to upload this to image kit. Now, it's important that we do it
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
server. Whereas, if you were to upload this directly from a front end or a client like a JavaScript application or something, um, 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.
upload. You have some URL to an image.
In our case, it's going to be some data which I'll show you. And we can do imagekit.upload file. Pass the different
imagekit.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 it to
API. Our API will then upload it to image kit. We'll grab the URL, save that
image kit. We'll 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 sh 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
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.named named temporary file. We're
file.named named temporary file. We're
going to say delete is equal to false.
And we're going to say the suffix is equal to os.pathsplit
text. And we're going to say this is going to be file.file
name. And then this is going to be at index one. Okay, this is a little bit
index one. Okay, this is a little bit weird. Again, just bear with me here.
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
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 sh util.copy
file object. And this is going to be filefile 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 or 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,
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
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
temporary file. And then that kind of completes this width statement. Then
we're going to start doing the upload.
So we're going to say our upload result is equal to imagekit.upload_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.file name. And we're going to
to file.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
options. And in here we're going to say use 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 backend upload.
So we know that we uploaded this from our API. Now this is all we need. Like
our API. Now this is all we need. Like
that's literally how easy it is to use image getet. We just write this one line
image getet. We just write this one line where we essentially open the file. We
upload it to image kit 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.http
status code is equal to 200. That means that 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,
to read. But we have this try block, right? So, I'm just going to put an
right? So, I'm just going to put an except block here. So, we're going to say except 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. So, we're going to say if temp file path okay or or sorry, not or and os.path.exist
os.path.exist
the temp file path. Then we're just going to say os.unlink.
And we're going to unlink the temporary file path. And then we're going to say
file path. And then we're going to say file.file.close.
file.file.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
handle this. We're just going to say raise HTTP exception status code 500.
And we'll just put whatever the string error was so that 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.content content 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 imagekit and we actually will have the URL for the image hopefully that makes sense but that's
pretty much what we need to do for the upload and I think that should actually be good. So, what we'll do now is let's
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
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 NT 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
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. So
we're going to go try it out. 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
upload. And then we got some issues saying nontime object has no attribute HTTP status code. So we probably just spelled something wrong. So let's go here and fix this. Yeah. So we have
upload result. This needs to be response
upload result. This needs to be response metadata.
Okay. So let's spell metadata correctly.
And now let's test it again. Apologies
guys. This is just a part of programming. Refresh.
programming. 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
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 cuz we uploaded it twice even though we got an error the last time.
And you can see this shows up. And 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
with. Anyways, that's kind of the random image that you guys are seeing. Okay, so
it's working. We can see it's uploaded here in ImageKit. It's working now from the back end. What 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
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.
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. I'm going to show you how we can modify modify it, 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,
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 and then I'm going to 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 uh what do you call it? Um
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
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 AI
here, right? Like we have an AI 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 we want to put on top of this image and directly embed it. We can do effects and enchantment or enhancements, sorry. So
we can have like econtrast. So So let's actually copy this and see if we can get it. So we'll do TR and then H-300
it. So we'll do TR and then H-300 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 eharpen, let's change that to e d-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
that. And then we have video transformations. These are cool because
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. So for
example, we can use this ik thumbnail.jpeg. If we just put that at
thumbnail.jpeg. If we just put that at the end of the path, it'll just give us the first frame as a thumbnail. We can
get a thumbnail from a specific time five. So we're doing like 5 seconds in.
five. So we're doing like 5 seconds in.
That's how we're getting the thumbnail.
We can do transformations on the thumbnails. We can trim the videos so we
thumbnails. We can trim the videos so we only get a certain amount of length from it. And then of course the most
it. And then of course the most important thing in my opinion is the optimization. So you can do image
optimization. So you can do image optimization. So you can automatically
optimization. So you can automatically compress the images without losing quality and load them significantly faster. You also can do video
faster. You also can do video optimization and there's like so many different things. So if you do care a
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. Um, and even if you set it to
something. Um, 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
significant. So anyways, that's the images. They are, you know, working. Now
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 uh, 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. And so we actually have
authentication. And 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 at
endpoint here. Let's call this at app.delete.
app.delete.
For deleting, we're going to take in /ost and this is going to be a path parameter of our post ID. What we're
then going to do is say async define delete_ost.
We're then going to say postc 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
async session. We're then going to have a try. For the try, we're going to say
a try. For the try, we're going to say the postc_uyu ID is equal to uyuid.uyuid
and then this is going to be the postc ID. We're then going to say result is
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 do ID is equal equal just two
equals to the post underscore uyu ID.
Now the reason why we need to convert the post ID to a UU ID is because this will be a string by default and we need it to be this UU ID object. 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 dotscalers okay with a set of parenthesis and then dot first. What this is going to do is
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 scalers 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.de
post and then we're going to say await session do commit like that. And when we commit this it
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
true. And then we can have some message like post deleted successfully. And then
we're just going to have an except in case there's an error. So we'll 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
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 404 post not found. Um, okay. So, that's
maybe an issue. Let's go feed. And let's
try to get the post. And okay, it looks like it did delete that post maybe because it said 404, but then it deleted it. So, let's copy this. Let's paste the
it. So, let's copy this. Let's paste the other one and let's do it. And there we go. Okay, now it says success. True.
go. Okay, now it says 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
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 uh for our endpoint. So, let's go ahead and start
endpoint. So, let's go ahead and start doing that. To do that, I'm going to go
doing that. To do that, I'm going to go over to app.py. I'm going to make a new file, call this users. py. Now, this is where we're quickly going to talk about
JWT tokens. So, let me hop over to the
JWT tokens. So, let me hop over to the kind of I don't know whiteboard and explain that to you. Okay, so we're going to talk about authentication, right? This is arguably the most
right? This is arguably the most complicated part of most web applications. And for this app, we're
applications. And for this app, we're going to use something called JWT tokens or JWT O. JWT stands for JSON web tokens. They are a very common format
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 you know she makes some account she has some credentials. Okay
now she signs into the uh 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 O 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
Sally. So, if I see this token, it means that Sally is the one who sent this request. That's effectively what that
request. That's effectively what that means. Sally signs in. She then gets
means. Sally signs in. She then gets some token back. And this token is Sally's token. Anyone that has this
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 um 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,
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
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 um kind of hit our API and start interacting with
it. And that identifies them to us as
it. And that identifies them to us as that user. And there you go. Right? That
that user. And there you go. Right? That
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 that just makes this process a lot easier. So in our users
file, we're going to say import uyuid.
We're then going to say from typing import optional. And we have a lot of
import optional. And we have a lot of other stuff to import as well. We're
going to say from fast API import depends and request. And then what we're going
and request. And then what we're going to do is say from fast API users import the base user manager
the fast API users and the UU ID ID mixin as well as
models. Okay, now this is giving us an
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 API or fast API users.authentication.
users.authentication.
Okay. And we're going to import the authentication backend 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 users.database
users.database import the SQL Alchemy user database.
And we're going to say from app db import user and get user DB, which are two functions that we're going to go and write now. Okay, so because we're now
write 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.py Pi and we're going to start writing a user model that we can essentially use to store uh users. Okay. So to do this
we're going to say from fast API users db import the SQL alchemy and this is user database. Also going to import the
user database. Also going to import the SQL alchemy user table uu ID. Okay this
is complicated that sounds crazy long but these are just what we need to import. And then what we're going to do
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 uyu ID 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
the user. Essentially what we're doing here is we're creating this um table, this user table, so that 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 post is equal to this. And
now on this user, we'll be able to find all of their posts. Okay, so that's why we're saying back populates user. So
what I'm going to do now on this post um 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
have. So on my post I'm going to do this. I'm going to say my user ID is
this. I'm going to say my user ID is equal to column and then this is going to be uh what we want here uyu ID. We're
going to say as uyu ID is true. We're
gonna say foreign key and this is going to be user do 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.
Now, as well as 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 and we're going to say back populates posts. Now,
these relationships automatically link these objects together and allow us to use the post 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 you know too in depth but the relationships to be aware of are one to many, many to one and one to one. Most
frequently you are using one to many which means you have one user with many potential posts. All right. Now, if we
potential posts. All 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. So, 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 write here.
So let's go down to the bottom and we're going to say async define get user DB. We're going to say session. And this
DB. We're going to say session. And this
is going to be the async session equals depends. Okay. Okay. And let's spell
depends. Okay. Okay. And let's spell depends correctly. And depends is a
depends correctly. And depends is a capital of course. We're going to say 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
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 we're just writing a function that's going to get us the user database table. Um, so
that's effectively what this is doing.
Again, don't worry too much about it.
The database stuff is a little bit confusing. The point is it will just
confusing. The point is it will 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 users py file, okay, so from here, we're going to create some variable called secret. This should be equal to some random string. Um, 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
random. So now what I'm going to do is I'm going to say class user manager and this is going to be UU ID 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
manager. And then we're going to have the user and the UU IDU ID. Okay. Again, I know some of this
ID. Okay. Again, I know some of this stuff seems confusing. This is directly out of the fast API users 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
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 tok token secret both being equal to the same thing which is a secret. Can make it different if you
secret. Can make it different if you want but that's all that 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
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 registered 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
something. I'm just showing you some examples if you can 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 get user 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
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
fast API. We then are going to have the bearer_transport which is equal to the bearer transport and then we're going to have our token
URL be equal to o/jwt/lo.
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
a user. We're then going to say define get_jwt strategy. And this is going to return
strategy. And this is going to return JWT strategy. We're going to pass our
JWT strategy. We're going to pass our secret and we're going to pass our lifetime seconds. Now, the lifetime
lifetime seconds. Now, the lifetime seconds is how long you want a JWT token to be valid for before the user needs to sign in again. I believe this is 3600
seconds, which is going to be um what is that 10 minutes or 1 hour or something?
Yeah, I think Yeah. So, sorry, this is 1 hour. So, 60 minutes is 3600 seconds.
hour. So, 60 minutes is 3600 seconds.
So, you can change how long you want the token to be alive 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 onto your computer for example and they would um you know be able to just start using
the application because the JWT token is still alive. Okay, now we have the JWT
still alive. Okay, now we have the JWT strategy. We're going to define the off
strategy. We're going to define the off 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
JWT strategy. Okay. We're then going to say fast API users is equal to fast API users user uuid.uid
get user manager and o backend. 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 backend that
user manager. This is the backend that we're using which is JWT tokens. Then we
say the current active user is equal to fast API users.curren user active equals true. What this is going to do is when
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 off 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. py. So what we're going to do is
users. py. So what we're going to do is go back to app.py and we're going to start actually using this because now we need to essentially connect different endpoints to this um what do you call
it? JWT kind of backend which you're
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 oback backend.
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 O
endpoints that we need to our fast API users endpoint. So you'll see what I
users endpoint. So you'll see what I mean in a second, but we're going to say the following. We're going to say at
the following. We're going to say at app.include include routouter and we're
app.include include routouter and we're going to include the fast API users.get
offers routouter with the offc_backend and we're going to say the prefix for
this is equal to slash off slashjwt 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
users. So, I'm just going to say app.include router, and I'm going to
app.include router, and I'm going to connect it to all of the endpoints here.
And I'm going to prefix this with /jwt, which means I go to /jwt plus all of the endpoints that are automatically included by my fast API
users um kind of module that I brought in here. Okay, so things like resetting
in here. Okay, so things like resetting your password, uh, 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're 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.include router, and this is going to
app.include router, and this is going to be fast API users.getregister 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 go 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
need. So for user read what we need to do is we need to say from fast API users import
schemas. I then need to import UU ID. So
schemas. I then need to import UU ID. So
I'm now going to come and I'm going to say class user read and this is going to be from schemas.base
user and this is going to be UU ID doU ID. 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.base userreate. And then
I'm going to say pass. And I'm going to say class user update is going to be schemas.base base user update and then
schemas.base base user update and then pass. Now, these are schemas that
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 um 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 the schemas that we need. So,
from apps schemas, we're going to bring in the user read the user create. Okay.
And the user update which we'll have in a second. All right. And now this should
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
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 at appincclude routouter and then we can say fast API users.get get resert reset password
users.get get resert reset password router. Right? And now if we do that, we
router. Right? And now if we do that, we now are have the ability to reset the password. Now let's keep going. Let's
password. Now let's keep going. Let's
say app.incclude router and we can do the get verify router. Now 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
user read. And then lastly we can have get users router. So I'm going to say app.incclude include router get users
app.incclude 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 uh so that we can test and make sure it's working. So now if I go here you
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 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 new account here. So I'll go tim at techwithtim.net.
here. So I'll go tim at techwithtim.net.
And then for the password, we'll go one, two three four five six seven eight. Okay, we'll just make is active
eight. Okay, we'll just make is active true. And then we won't set anything for
true. And then we won't set anything for the super user is verified. Uh 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
doing. So actually, let me just change this like timwithtim.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
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
this token. So now I can go to for example users me and I can just send the request here and you're going to see that it tells me hey I am tim one attewithim.net 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
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
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
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
dependency. So for example for the feed same thing we add the user we say okay you know it's just a get current active user and now it won't work unless we have the user. Now same thing for the post we're going to get the current
active user. So let's do that. And we're
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
this operation. So let's go back to the DB and for our post we're now associating with users. Okay, so that's all good. But for our app when we are
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 ID and let's make this equal to the user do ID. So now we're storing the
do 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
user. So once we check for the post down here, we're going to say if the post dot user ID does not equal the user do ID,
then we need to raise 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
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 for
get 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
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 ID is equal to the string of post dot user 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
that. So we're going to say is owner and we're going to say post dot user ID is equal to user ID. So now we're checking all right is the user ID of this post
equal to our ID. If it is, that means we made it. If it's not, well, we didn't
made it. If it's not, well, we didn't make it. Okay. And then I think that
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
say email. And then I'm going to say this is post do user do email. And I
think that will work. Uh, but I'm not 100% sure. Let's see. Okay. Okay, so now
100% sure. Let's see. Okay. Okay, so now we kind of have protected our endpoint 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 going to say tim1
that account. So we're going to say tim1 techwithtim.net.
techwithtim.net.
Okay, if we can spell this correctly.
All right, then we're going to have 1 2 3 4 5 6 7 8 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 do user.
Um that is not working properly just due to how we're loading the user. So I'm
going to do another approach here. This
is a little bit more complicated, but will allow us to get the emails. So I'm
just going to say result is equal to a weight. This is going to be
weight. This is going to be session.execute
session.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 in result doall.
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 u do ID and this is going to be you do 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
like this. So to get the email we can simply say user_dict and then this is going to be do get and it will be post do 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
correct user. So let's go here and try to delete. Execute. And you see it says
to delete. Execute. And you see it says you don't have permission to delete the post. Perfect. Okay. So that wraps up
post. Perfect. Okay. So that wraps up the API. The API is working. It's fully
the API. The API is 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
here. What I'm going to do is just make a new file. And I'm just going to call this frontend. py. I'm just going to
this frontend. 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 uvad streamllet.
Now, that's going to add Streamlit to our dependencies, which is going to allow us to now run this front end. So,
again, the all of this code that you see will be available from the link in the description. You can literally just copy
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 uv run streamllet run main dot or not main.
This is front end. py. When I do that, it will start running streamllet. 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 attechwithim.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. Um, failed to get user info. Okay,
feed. Um, failed to get user info. Okay,
interesting. So, we'll fix that in a second. Uh, but it should bring you to
second. Uh, 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 feed. We have my username, the date, the image, and then we can delete this.
And we can also expand it directly from here. Now, I want to talk about kind of
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
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
Streamlit. Now, first things first, the JWT token is something that we store in our session. That just means that we're
our session. That just means that we're storing it. So, we're constantly using
storing it. So, we're constantly using it anytime we send a request to the back end. Okay. The way that you send JWT
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 authenticated request. You saw this in
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 a password and I'm passing it to this URL.
So O JWT login, right? Passing my data and then I'm able to log in. Now when I do that, I get some token data and I store that access token in my state. So
I'm able to use it as my JWT token to sign in. I then get my user info by
sign in. I then get my user info by passing a request to user/me so I can make sure that I'm signed in successfully. And I store that again in
successfully. And I store that again in my session state. Same thing for registering. I send a request to
registering. I send a request to allregister and then after I register I send another request to get my JWT token um 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
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.
to upload. Okay, so let's go to upload.
Let's change this here and let's upload here this one with maniv and me which is a testimony and 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
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
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 kit 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
the image. So if we go down here, let's do this. You can see that I have these
do this. You can see that I have these file types. So I check if it's an image,
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
this. So this is essentially the text that you 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
very quickly. Okay, so that's how I'm kind of um transforming this. Now, I
want to show you uploading a video, which we'll do in one second, um, and then displaying the caption for that video. But that's kind of how um, I'm
video. But that's kind of how um, I'm doing that transformation with ImageKit and displaying the images. So, let's go back. And now, if I refresh this, uh,
back. And now, if I refresh this, uh, let's sign in again. So, 1 2 3 4 5 6 7 8 and go log in. Then, let's wait a second
here. Okay. And we'll go to the feed
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 cuz I made the font size 100. If I made it like 1,000, obviously it would be even much bigger. And you can see the text
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
well. So, let's go files and let me find a video. Okay, so I just found this
a video. Okay, so I just found this screen recording that's not that big.
So, I'm just going to upload this screen recording. So, you can see right there.
recording. So, you can see right there.
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. You see it's going to take
image kit. You see it's going to take one second to load here and then there you go. We get the video and we can
you go. We get the video and we can watch it, right? And it just shows up directly here. Now, same thing with when
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
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,
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
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 uh 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 example 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 resized to be quite small. And you can see now that we
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. Um the quality is quite low obviously because of how I adjusted it and the size that I made it
but you can see that it is working and sorry if I just scared you there with that audio. Um that scared me as well
that audio. Um that scared me as well but the point is it is loading. Um 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 o 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
tutorials. And while I know this was a really long video and a lot of code, I wanted to be super detailed and covered everything in as much depth as I possibly can. If you guys enjoyed this
possibly can. If you guys enjoyed this video, make sure you leave a like, subscribe to the channel, and I will see you in the next one.
[Music]
Loading video analysis...