Loading video player...
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
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
can sign in. So, I've set up a really
simple user interface here in something
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
posted and the user that posted them.
And then I also have some videos as
well. Now, you have the ability to
upload something. So, for example, if
you were to come here and let's just
pick maybe a random photo that we have
here. Okay, let's just go like this and
call this thumbnail. And then we share
it. It's just going to take a second.
And then this will be uploaded to our
feed. So right now it's just uploading
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
basic, but I promise this is going to
teach you a ton of concepts that you
need to understand as it relates to fast
API. And I think the most important
thing is all of the authentication and
authorization which most people skip in
these beginner type tutorials. So with
that said, let's hop over to this 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
them and they just make this process
significantly easier. So big shout out
to them, but more on that later. Okay,
so let's get into the video. Now, we're
going to use fast API, right? This is
fast application 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
it facilitates the access and control of
data. In our case, it's going to be
image or video posts, right? Or
different user accounts. But before we
can get into all of that, we need to
start understanding some kind of core
concepts of web apps in general. So,
let's start by talking about URLs and
endpoints so that we can get the
terminology out of the way. Now, this
whole thing right here and this one as
well is a URL. You've seen this, you
know, millions of times before,
especially when you've browsed to a
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.
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.
So, I'm going to the /tim page right
here. /courses/python.
So, I'm going to the python courses page
right. And for a typical website, these
make a lot of sense. But for our APIs,
we're going to have to design these
ourselves to kind of control the access
and the route or the endpoint to
particular resources. So when we look at
the project that we're going to build
here where we're sharing, you know,
videos or photos, we might have an
endpoint that is, you know, our
Aapi.com/photo,
right? And then we can access a
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
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
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
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
see as kind of a secure layer. it's
running on a different device, some kind
of server, so some computer essentially
sitting in some location and it's
facilitating all of the access to our
data. So if we want to sign into our
account, we send a request from the
front end or the client to this API and
this API returns some response. Okay,
this is the flow that I'm trying to get
you to understand. You go to some
website, you do something. If this thing
involves some access to data, right?
Especially if it's maybe some
confidential data or something, what
needs to happen is a request will be
sent to some backend, this kind of
secure location. It will essentially
check can this user do the thing that
they want to do and then if they can, it
will send this response back doing that
thing. For example, deleting a post. We
would send a request to the 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
response saying, "Hey, this post was
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
we have this front end, right? This
client, it sends some data to the back
end. And the main parts of a request are
the following. Okay, we have a type of
the request which we're going to talk
about in a minute or the method. We have
a path. The path is what we looked at
here, right? So this path or this
endpoint and oops, I didn't mean to save
that. And we have a body. This is
optional, but this includes additional
data that we want to send along with the
request. So for example, like the image
that we want to upload or the caption or
the name of the post or something like
that. And then we have headers. This is
typically other additional information
that has to do with things like
authentication. So in the header of our
request, we would include something that
indicates that we are, you know, this
user. Okay, we are signed in as, you
know, Tim 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
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
thing. headers will be related to
typically security, authentication, the
type of data, some weird things like
that. If I scroll down here, I'm just
going to show you a few status codes and
a few of the request types which are
important to understand. Actually, let's
start with the request types. So, here
is an example of a very simple API. This
is a books API and this is the /books
route or path or endpoint or whatever
you want to call it. Now, you can see
that we have something called a get
endpoint. What this means is that we are
retrieving some data from this resource
essentially from /books. We then have
delete. This is the type of method you
would use when you want to delete
something. We have post. This is the
type of method you use when you want to
create something. And you have put. This
is the type of 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
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
memorize all of this, but these are just
some examples of status codes that you
may see. So, you send this request,
right? You say, "Hey, I want to get a
book, for example." All right? Then,
what the 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
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
actually the exact same thing as put. So
if you ever see patch, it's the same as
put, which just means you want to update
something. If I wanted to create
something, I would use post, right? But
I don't want to do that. I want to
update. So I'm using patch. Okay, so the
type or the method of my request is
patch. The path is / 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
the ID of the post that I want to
modify. And then the body would be this.
This is the data that I'm actually
sending where I'm saying, hey, this is
the updated title. And this is the new
caption that I want to use or the
description or whatever for my post.
Then the headers includes this. This is
essentially my authorization token
indicating, hey, I'm authorized to be
able to perform this operation because
of this thing right here, which we'll
talk about later on. So we take all of
this, we send this to our backend and
then this is the request, okay, that we
create. Now from the backend we get a
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
do is get something called a JWT token.
This JWT token looks something like this
where you send this along with every
single request to indicate, hey, I'm
authorized to perform this type of
operation. Essentially, you're
identifying yourself and what user you
are so that the API knows you can do
this thing or you can't do this thing.
You'll we'll look at that later. I don't
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,
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
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
free, you can click the link in the
description, try it out, and see if you
like it. It definitely is a great
editor, and again, what I recommend for
pretty much any heavy Python projects.
Okay, so what I've done inside of
PyCharm is I've just opened up a new
folder. You can see I've got a folder
here called fast API tutorial. The way I
did that is I essentially just went to
open and I just opened a folder on my
desktop. Okay? And you can again use any
editor that you want. 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
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
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
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
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
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
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
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
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
are three variables that we need to
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
put equal signs for all of these. And
we're just going to quickly grab these
three values. So, we don't need to come
back to this until much later in the
video. Now, you may be wondering, what
the heck are we doing, Tim? We haven't
even started writing the API. I promise
we're going to get there. I just want to
get through everything, do all of the
setup, and make sure that it's all ready
to go. So we can just focus on coding
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
this in the description. What we're
going to do is we're just going to make
a new account on here. Again, it is free
to use this. You do not need to pay for
it. And essentially what this does is
give you all kinds of amazing tools for
handling your images and videos, which
is typically a huge pain, but they have
all kinds of things like image and video
optimization, formatting, cropping. It's
very interesting. So anyways, what we're
going to do is make a new account. I've
already just made a new one. So again,
I'll leave that link in the description.
And from here, what we're going to do is
go on to the developer options. From
developer options, we're going to look
for our public key, our private key, 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.
So, we'll go back to developer options.
We'll go private key and I'm just going
to copy this after I use my password.
So, let's use that and copy this. Okay,
so now that I've copied it, I'm going to
go back to PyCharm. I'm going to paste
it. Again, don't share this with other
people. I will delete it afterwards. And
then lastly, we're going to grab this
URL endpoint. Okay, which should be
right up here. And we're going to paste
this inside. And now we have the keys
that we need. Okay, so we're going to
close the environment variable file. And
now what we're going to do is start
setting up kind of the scaffolding for
our project. So I'm going to make a new
folder. And this folder is going to be
called src. This is typically best
practice when you're writing a fast API
application. you create this source or
actually let's change it to be an app
directory where you actually have all of
the code for your application and then
you have this main py file which is kind
of what triggers the application to run.
So what we're going to do inside of this
app folder is we're going to make a new
file and we're going to call this app.
py and this is where we're going to
start actually writing our fast API app
and start getting into some Python code.
All right, so let's start writing a API.
We've 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
application that we just created. And
what we'll need to do now is start
setting up the different paths or
endpoints that we want to have
accessible on our API. Now remember for
our API, we're setting this up
essentially to handle data, to be able
to accept some type of request from our
front end or our client and to return
some type of data. So we can create
data, delete data, read data, update
data, right? We need to decide because
we're designing this API. So I'm going
to start by just writing some simple
dummy endpoints just to test and see how
this works. Then we'll get into
endpoints that actually make more sense.
So the way that you make an endpoint in
fast API is you type app which is the
name of this variable right here that we
defined dot and then you specify the
method for this particular endpoint. So
it can be get, post, put, delete
depending on what you want this to do.
Now the most basic type which is common
is to use app.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
and then something like you know hello
dashworld. Okay, so this is the path or
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
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
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
some value. And the reason why we return
Python dictionaries is because when we
create APIs, we work with something
called JSON. Now, JSON stands for
JavaScript object notation. It is the
format essentially for dealing with data
across the web. And you can essentially
think of JSON the exact same as you
would think as a Python dictionary.
Okay, it's not exactly the same. There's
a few minor differences, but in our
case, we can assume that anything that
is a valid Python dictionary will be a
valid JSON object. Again, keep in mind
there's some caveats there, but
generally that is the case, especially
with simple data. Okay, so now we've got
this application, right? We've defined
this endpoint, but what we need to do is
run it. Now, there's many different ways
to run the API, but the way that I'm
going to suggest we do it is by going
into this main.py 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
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
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
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
locally on our own computer, when I
specify 0.0.0.0,
that just means run it on any available
domain. So, it's going to run on what's
called local host, which is just our own
host, so only we can access it. as well
as our private IP address, meaning
anyone else on the network would be able
to access this as well if they knew the
private IP address of this machine. Now,
there is ways to run this publicly, so
anyone can access it. Not going to get
into that in this video, but essentially
the way that you're going to 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, 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
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
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
running in a different um editor that I
have open. You'll see what's happened
here is it now says Unicorn running on
and then it shows you the URL or the
domain where this is running right and
then it kind of goes through this thing
saying hey you know started the 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
we're debugging and building something
because it just shuts down and restarts
anytime you make a change. Now, if I
want to actually be able to view my
application, what I can do is go to this
URL. So, you can just click it and open
it up. Now, it's saying this 0.0.0 isn't
working. So, what we can do is change
this to be 127.0.0.1
or localhost port 8000. And when we do
that, it should give us this 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
going to bring you to a page that looks
like this, which actually specifies all
of the endpoints and the configuration
that you've set up for your API. So from
here, if I open this up, we'll be able
to actually test out our API by pressing
this try out button. This is going to
send a sample request to this endpoint
and then tell us what the response would
have been. So what I can do is press try
it out. I can press execute and then you
see what it does is it sends a request
to this URL and then it tells me that I
got this as my response saying message
hello world and then it also told me the
code of this was 200 which means
success. Okay, 200 successful response.
So there you go. We just sent a request,
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
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
endpoint uh called /redoc.
This is kind of a newer version of that
docs endpoint. It works the exact same
way. We can test this out if we want 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
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
I want to start adjusting the endpoint
to actually make sense to our project.
It will change over time, but we're
going to slowly kind of build towards
what's called a CRUD application where
we have create, read, update, and delete
functionality. So, what I'm going to do
is I'm going to delete this and I'm
going to start setting up some stuff for
handling posts. So, what I want to do is
I want to set up my application so I can
essentially retrieve and create new user
posts. For now, we're going to start
with text posts, but then later we'll
get into the images. So, I'm going to
make a new dictionary and I'm going to
call this my text posts is equal to and
we're just going to have an empty
dictionary like this. Okay. Now, what
we're going to do is we're going to make
an endpoint and we're going to say at
app.get get and this is going to be
slash posts. Okay. And for the function,
we're going to say define get all posts
like that. Now, what this is going to do
is just return all of the posts that we
have. So, we're simply just going to
return text 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
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
here, then we would be able to retrieve
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
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
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,
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
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
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
we've added a bunch of text posts. We've
added what's called a path parameter. We
had a normal kind of query endpoint
here. And the next thing that I want to
do is show you how we can use what's
called query parameters inside of our
functions. So up until this point it's
been pretty straightforward, right? We
just can call /ost. We can call for a
particular ID. Now I want to make it
work so that we can do some kind of more
advanced filtering using query
parameters and then we'll continue from
there. So I'm actually going to go
inside of this function right here and
I'm going to start adding some optional
query parameters that we can pass to
this that will allow us to kind of
filter some of the content. So a query
parameter remember is the thing that
comes after the question mark. So
something like maybe you know length
equals 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
I'm going to add a query parameter
called limit. I'm going to say limit
colon int is equal to and by default
it's going to be none. Now what I've
just done is I've just specified that I
now have the ability to pass a query
parameter called limit to this post
endpoint. And I can then check if this
query parameter exists. If it does, I
can use it. If it doesn't, I don't have
to. So my idea with the limit 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
then otherwise we'll just return the
text post. So now if I go back to my
page here and I refresh you're going to
see that if I look at post we have the
ability to add this limit query
parameter. So what I can do is go try it
out for limit I can pass maybe three for
example and then execute 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
go refresh. And then we'll go here. You
can see we have a limit again. Let's go
maybe five and execute. And now you see
we get a list with five items. If we
change this to three, we get only three
items. And if we don't have any limit at
all and we execute, we get all of the
items showing up. Okay, so just showing
you that's how you add a query
parameter. If you want to do that, you
simply specify it in the uh parameters
here. You can make it optional, which
means you can have something like is
equal to none or you can make it
mandatory by removing this and now you
have to pass the query parameter. If you
don't 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
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
inside of the URL. It's in the field
called body. And the way that we accept
that type of data is by creating
something called a schema in fast API.
So what I'm going to do is I'm going to
make a new file here. And I'm going to
call this 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
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
find what the next ID essentially should
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
this post endpoint. It's just going to
create this new post. But what we really
should do is we should return the new
post that was created. So to do that,
actually, let's just do this. We can say
post is equal to like this or maybe just
new post is equal to this and then we
can say this is equal to new post and we
can return the new post. So now we have
a post endpoint to create a new post. So
what we can do is we can save should
automatically reload. So now if we come
here and we go try it out and we change
this to like you know cool post and new
post or something and we go execute you
can see it gives us the data back. And
then if we go back to posts, 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
pretty straightforward. You go
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
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
us like what type of data is going to be
returned. Okay, it gives us kind of an
example, but this isn't exactly, you
know, what we're looking for. It's not
the best documentation in the world. So,
what we can do to enhance the
documentation and actually give us 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
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
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
here. Example value is going to have
title and content being returned on the
successful response because we've
specified that type. Now what it also
does is it means we can only return data
from this function. Now that is of this
type. If I try to return something else
like just a normal dictionary, if I were
to actually execute this endpoint, it
would give me an error and say, "Hey,
this doesn't match 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
post and then it'll work from now on.
Okay. So, if we want to type the rest of
our data, we can do that as well. So,
for example, when we're getting a post,
we should also say that this will be the
post response. And then for getting all
of the posts, 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
what we're going to start setting up is
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
to use here is something called SQL
Alchemy. Um, I believe we already
installed it. If we didn't, then we can
install it again in a second. But the
point is this will allow us to really
easily work with our data. So bear with
me here because this is a little bit of
code that we do need to set up. Not all
of it's going to make, 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
say from SQL
alchemy import create engine. We're then
going to say from SQL alchemy. Okay. And
this is not what I want. We're going to
say import the column. So let's spell
column correctly. We're going to import
string. We're going to import text,
datetime, and foreign key, which we'll
use later on. Uh, and did I spell
foreign correctly? No, I did not. So,
let's spell foreign key correctly. Okay,
we're then going to say from SQL
alchemy. Okay, this is going to be
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
seems confusing. It's just a little bit
of setup and then you never need to do
it again. And then we're going to bring
in from sql alchemy.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
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
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
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
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
databases before, you'll know that when
you want to store data, you need to
specify the structure of that data. At
least when you're working with a SQL
database, which is what we're doing
right here. So, we need to specify like,
okay, what do we want to store for a
post, for example. Well, we need to know
the user that posted it. We have to have
an ID. We want to know maybe the caption
of the post, the URL of the file. That's
the kind of stuff that I'm talking about
here. Okay. So, what we're going to do
is we're going to create the data model
for storing a post. We're going to make
it a little bit more complex now and
we're going to start working towards
actually being able to store videos and
photos for our API. So what we're going
to do is we're going to say class post
and this is going to inherit from the
declarative base. 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
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
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
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
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
specify what you want in that column. In
this case, I want to store text, right?
So, I say, okay, caption, it's going to
be text. Then I'm going to have a URL.
This is going to be the URL for the 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
going to say the file type is equal to
column. Same thing, string nullable is
false. We're then going to have the file
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,
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. And now this should work.
Okay. So that's all that we need for the
post. Later we will adjust this slightly
to link it to an individual user, but
for now because we don't have users in
our app, we're just going to have kind
of a simple post. So the way that this
will work now is that whenever we want
to create a post, we'll need to pass a
caption, URL, file type, file name, and
created at 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
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
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
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
is say await con. Okay. The connection.
So run_sync
and then we are going to say the
declarative base. Okay, dot
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.
We're then going to have another
function. We're going to say async
define get async
session. Okay, this is going to return
an async generator with an async session
and none. And then we're going to say
async with and I know this is confusing.
Bear with me. We're going to say the
async session maker as session and we're
going to yield the session. Okay, 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
starts the database engine and then it
creates all the tables and creates the
database. Then we have this get async
session. What this is going to do is it
is going to get a session which will
allow us to actually access the database
and write and read from it
asynchronously. Okay, so we're doing
this using async. If you don't
understand async in Python, don't worry
too much about it. I will explain the
the kind of important stuff later. So
that's all we need from this particular
file. Later we will add a few other
things to store our users for the
application, but for now this is fine.
Okay, so there we go. We've now written
the database stuff. What we're going to
do now is we're going to go into our
app. py file and we're going to start
importing some of the things that we
need so we can actually use the
database. So we're going to say from app
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,
again, this is going to look a little
bit complex. Don't worry, just set up
once and then you're good. We're going
to say from SQL Alchemy.exd.async.io
and then we're going to import the async
session. Then we also need to import
something called the async context
manager. So, we're going to say from
context lib, this is built into Python,
import the async context manager. We're
then going to say at async context
manager. and we're going to say async
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
going to say await and we're going to
say create db and tables. And then we're
going to yield. Don't worry too much
about this. It's kind of advanced Python
syntax you don't really need to
understand. And then all we're going to
do here is when we say app is equal to
fast API, we're going to say 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
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.
Okay. So now if we just want to do kind
of a sample test here, we can shut down
our server and rerun it and it should
automatically create the database for
us. However, it's giving me an issue
saying datetime.Uutc UTC isn't a
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
we shut this down and we restart it and
we got another error. Let me quickly fix
that. Okay, so another silly error here.
We're going to go inside of our db.py
file. What we're going to do quickly is
say class base and this is going to
inherit from the declarative base. And
then we're just going to say pass. And
we're just going to change all instances
of declarative base down here to say
base. Kind of a silly error. Again, a
little bit complex. And then same thing
when we go here, just change this to say
base. And that should fix the problem
for us. Essentially, we just cannot
directly inherit from declarative base.
Uh we need to do this kind of other base
class first. A little bit weird, but
that will allow us to set up the
database. I know the database 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
and then restart it. Okay, so the server
is running. We didn't get any errors.
And the way we can test if this is
working is if this test db file was
created. It looks like it was for me,
which means the database connection is
set. And now we don't really need to
touch much in this database file. And we
can just start using the database to
actually create new posts. So in fact,
let's start doing that. Let's actually
scrap pretty much everything that we
have so far. I know it seems like why 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
going to start writing the endpoints for
uploading or creating a new post and
doing the image and video upload. So, as
I mentioned, we're going to start doing
the file upload. Now, to do that, we're
going to have to import a few more
things. And again, just bear with me.
I'm going to explain it step by step as
we go through that. And then we'll start
adding the users, a bunch of other stuff
as well. So, what we're going to do is
from our fast API import at the top,
actually, we're going to import file.
We're going to import upload file the 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
going to say async define upload file.
We're using async because we're going to
have some async operations in here where
we essentially wait for something to
occur. Fast API is asynchronous by
default. So you can always use the async
keyword for these functions. Now what
we're going to do is we're going to say
file and this is going to be upload file
is equal to file with three dots inside.
What this means is that we're going to
be able to receive a file object to this
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
file, you can send a form, you can send
a request body, you can send query
parameters. This is just another type.
So, we're going to accept the caption
from some form data. Okay. We're then
going to say our session is an async
session,
which is equal to depends, and this is
going to be get async session. Now, this
is going to look a little bit weird, but
in fast API, we have something called
dependency injection. This is
essentially a dependency injection. 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
want to essentially trigger another
function to run as a dependency for this
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
injection where you say this function
depends on this right here. It will run
the function. It will get the database
object. It will pass it to us and then
we'll be able to use it directly inside
of here. Okay. So now inside of here,
what we're going to do is we're going to
take the file and we're going to upload
it. That's going to take us a second to
be able to do because we need to use
image 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
session and then you commit the session.
It's important that you commit this
because committing it will actually save
it. adding it to the session is like
staging it saying hey this is ready to
be added but it won't actually fully be
written into the database unless you
commit the session. Now another thing
you can do after this is you can say
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
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
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
doing that is because we need to access
the database here in order to get all of
our posts. So we need to bring this in
as the dependency injection. Now quickly
I do need to import something. So I'm
going to say from SQL alchemy import
select because this is going to allow us
to select different posts. So now what
we're going to do is we're going to say
result is equal to await session. This
is our database.execute.
Okay. And this is how you can execute a
query. And we're going to say select and
we want to select on the post um what do
you call it object or post kind of
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
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
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
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
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
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
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
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.
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
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
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
importing a few things. We're going to
say fromv
import load.env.
We're going to say from imagekit.
Okay, IO import if we can spell this
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
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 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
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
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
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
handle all of the asset management for
us. Eventually, when we start uploading
stuff, we'll see in our media library
that it will show up here. We'll be able
to click into the various files. We can
view them here. We can view the
information. We can edit the tags, get
embeds, URLs, all of this kind of stuff.
Just makes it very easy to manage the
images. Now, in terms of using image
kit, uh what we can do is use a single
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
this. Let's go to the Python
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
this way so that we can securely control
what images we upload or which ones we
don't and have them stored on our
server. Whereas, if you were to upload
this directly from a front end or a
client like a JavaScript application or
something, 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.
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
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
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
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
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
weird. Again, just bear with me here.
We're going to say this is as temp file
and we're going to say this is as temp
file. What this is going to do is
essentially make a named temporary file
that ends in whatever the file name is
that was uploaded here. Then inside of
here, since we have this temporary file,
we're going to say the temp file path is
equal to the temp file.name. And we're
going to say 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,
for example, we create a temporary JPEG.
If they upload a MP4, we create a
temporary MP4. Okay? So what we do is we
create the temporary file. We then copy
the contents of this file into the
temporary file. And then that kind of
completes this 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
say the options is equal to and this is
going to be the upload file request
options. And in here we're going to say
use 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
that's literally how easy it is to use
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,
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 path okay or or sorry, not or and
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.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
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
bring open our terminal again. Let's
just restart this and let's go back here
and let's test this from our docs. Okay,
so let's go upload. All right, we're
going to go try it out. We're going to
put a file. So, we need to upload an
image. So, let me just upload one of
these images here. Let's go hello world
and let's send this. and it says module
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
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
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
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.
Try it out. Choose a file again. Some
media. Okay.
Hello world. And let's see. And here we
go. We get the response body. And now
what I want to check is the URL. So it
has a URL here. So let's copy this and
let's paste this in our browser. And we
should see that we get the image now and
it is uploaded. And importantly, if we
go to the media library, we should be
able to refresh here. And we should see
that now the images appear happening
twice 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
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
see there we go. So, we have this new
post, right? We have the image kit URL.
And now I just want to talk about kind
of the advantage of using image kit and
what we can do with these URLs that is
super cool, which I'm going to show you
in one second. So, I'm going to grab one
of these URLs, which I would recommend
that you do. 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,
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
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
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
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
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
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.
That's how we're getting the thumbnail.
We can do transformations on the
thumbnails. We can trim the videos so we
only get a certain amount of length from
it. And then of course the most
important thing in my opinion is the
optimization. So you can do image
optimization. So you can automatically
compress the images without losing
quality and load them significantly
faster. You also can do video
optimization and there's like so many
different things. So if you do care a
lot about image and video, definitely
check out these docs that I will leave
in the description. And one with video
specifically is like changing the
quality of the video so you're not
loading, you know, massive 4K videos or
something. 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
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
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
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
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
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
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
will delete the post. We're then going
to return and we'll just say success is
true. And then we can have some message
like post deleted successfully. And then
we're just going to have an 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
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
other one and let's do it. And there we
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
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
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
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
complicated part of most web
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
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
that Sally is the one who sent this
request. That's effectively what that
means. Sally signs in. She then gets
some token back. And this token is
Sally's token. Anyone that has this
token is Sally in the eyes of our API.
Okay. So, what Sally does is she now
stores this token or really her computer
stores it in the browser. She's not
going to do it manually. And now for the
rest of all of the requests that she
uses with our 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,
we can now go do this thing because
Sally's allowed to do that thing. That's
essentially how this works. This is an
oversimplified explanation. The point is
user signs in, they get some token, they
store this token, they then send that
with every request as they um kind of
hit our API and start interacting with
it. And that identifies them to us as
that user. And there you go. Right? That
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
other stuff to import as well. We're
going to say from fast API import
depends
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
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.
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
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
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
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
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
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
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
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
is going to be the async session equals
depends. Okay. Okay. And let's spell
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
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
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
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
the user and the UU IDU
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
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
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
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
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
user manager
with the user DB. So essentially we're
taking our database user kind of
injecting that inside of here. And now
we have this user manager class which
will allow us to manage the users in
fast API. We then are going to have the
bearer_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
get_jwt
strategy. And this is going to return
JWT strategy. We're going to pass our
secret and we're going to pass our
lifetime seconds. Now, the lifetime
seconds is how long you want a 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.
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
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
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
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
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
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
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
mean in a second, but we're going to say
the following. We're going to say at
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
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
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
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
do is we need to say from fast API
users import
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
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
be good to go. Okay. So that's that for
the registration routes. So again, same
thing. We're now going to have the
ability to register automatically be
created for us. I'm going to show you
this in the documentation in a second
from fast API users. Now if we wanted to
bring in other routes, we can do that.
So for example, we can say at
appincclude
routouter and then we can say fast API
users.get get resert reset password
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
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
get users router. So I'm going to say
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
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.
And then for the password, we'll go one,
two three four five six seven
eight. Okay, we'll just make is active
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
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
I send any requests, it's going to use
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
the way that I can make some of these
routes what's called protected is I can
add a dependency that forces the route
to get the current active user. And if
the current active user doesn't exist,
it won't let me call this endpoint. So
for example to be able to actually make
an upload well I need to have a user
right like I need to be signed in as a
user. So what I can do is I can just
change the function to be like this. So
I can just say user colon and then I can
say user and I can say this is equal to
depends and this is current active user
like that. For user I can just import
that from my database. So let's just
import user like so. And now what will
happen is if I try to call this function
and I'm not signed in and I cannot get
the current active user this function
will not work. So literally all you have
to do now is if you want to protect
these routes you just add this
dependency. So for example for the feed
same thing we add the user we say okay
you know 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
going to have to change some of the
content of these functions now so that
it actually saves the user that is doing
this operation. So let's go back to the
DB and for our post we're now
associating with users. Okay, so that's
all good. But for our app when we are
uploading images, we now need to
associate them with that user. So for
our upload result here, let's take user
ID and let's make this equal to the user
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
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
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
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
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
make it. Okay. And then I think that
should pretty much be good. But there's
one last thing actually that we will do.
Okay. So, for every single one of our
posts, I also want to include some basic
information about the user. And in this
case, that's going to be their email.
And what I'm going to do is I'm going to
get the user's email. So, I'm going to
say email. And then I'm going to say
this is post do user do email. And I
think that will work. Uh, but I'm not
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
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
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
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
to delete. Execute. And you see it says
you don't have permission to delete the
post. Perfect. Okay. So that wraps up
the API. The API 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
a new file. And I'm just going to call
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
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,
interesting. So, we'll fix that in a
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 image, and then we can delete this.
And we can also expand it directly from
here. Now, I want to talk about kind of
how this works. So, I'll walk through
the code a little bit, but again, just
copy the code from the link in
description. It's not really valuable
for me to write this out with you
because this is not a tutorial on
Streamlit. Now, first things first, the
JWT token is something that we store in
our session. That just means that we're
storing it. So, we're constantly using
it anytime we send a request to the back
end. Okay. The way that you send JWT
token requests is you include bearer
plus the JWT token. And then when you do
that, you're able to actually send an
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
passing a request to user/me so I can
make sure that I'm signed in
successfully. And I store that again in
my session state. Same thing for
registering. I send a request to
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
which include my JWT token that's then
able to upload the image and then show
it on the feed. Now, the cool part here
is when we start talking about actually
displaying the images with image kit
because it makes it super easy for us to
view the images and to actually perform
transformations on them. So, let me show
you with the UI. If I go here and I go
to upload. Okay, so let's go to upload.
Let's change this here and let's upload
here this one with 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
going to upload this file for me to my
backend API. And if I go back to the
feed now just takes a second to load.
We'll see the image appears here. And
one thing you'll notice if I make these
larger is that we have this caption that
I'm kind of putting directly on top of
the images by using image 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
do this. You can see that I have these
file types. So I check if it's an image,
which is all we've done right now, but
I'll show you videos in a second. Then
what we'll do is we will essentially
transform the URL to use the
transformation that puts the caption
directly on top of it that looks like
this. So this is essentially the text
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
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
doing that transformation with ImageKit
and displaying the images. So, let's go
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
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
shows up on the image. Now, if I want to
upload a video, I can totally do that as
well. So, let's go files and let me find
a video. Okay, so I just found this
screen recording that's not that big.
So, I'm just going to upload this screen
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
one second to load here and then there
you go. We get the video and we can
watch it, right? And it just shows up
directly here. Now, same thing with when
we transform the images, we can
transform the video. So, let me show you
a few examples of how we actually do
that. Let's go here. And what I'm going
to do is I'm just going to put in some
transformation parameters. Right now,
you can see that I'm just putting it
quality at max and width 300. But I can
adjust this to, for example, crop the
video. So, for example, if I just take
this transformation parameter, I'll just
show you what this does, but essentially
it crops the video and then adds a 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
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
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
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
video, make sure you leave a like,
subscribe to the channel, and I will see
you in the next one.
[Music]
ImageKit is an image and video API plus AI-Powered DAM that we use in this project to handle all of our image and video operations, check it out here: https://tinyurl.com/bdf3mxxx 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. This video is not designed for absolute beginners. DevLaunch is my mentorship program where I personally help developers go beyond tutorials, build real-world projects, and actually land jobs. No fluff. Just real accountability, proven strategies, and hands-on guidance. Learn more here - https://training.devlaunch.us/tim?video=SR5NYCdzKkc š Video Resources š ImageKit: https://tinyurl.com/bdf3mxxx ImageKit Docs: https://imagekit.io/docs/integration/python#generating-url-for-rendering-images-in-python-app FastAPI Users Docs: https://fastapi-users.github.io/fastapi-users/latest/ Code in this video: https://github.com/techwithtim/FastAPIPhotoVideoSharing UV Tutorial: https://www.youtube.com/watch?v=6pttmsBSi8M ā³ Timestamps ā³ 00:00:00 | Video Overview 00:00:35 | Project Demo 00:01:58 | Web App Architecture & Theory 00:14:07 | Project Setup & Install 00:20:45 | FastAPI Setup & Basics 00:28:10 | FastAPI Docs 00:30:10 | GET & Fetching Posts 00:32:25 | Path Parameters 00:33:49 | Raising Errors & Status Codes 00:34:56 | Query Parameter 00:38:34 | Request Body & POST 00:43:28 | Output Type & Pydantic Models 00:46:30 | Database Connection 00:59:57 | Creating Posts & Saving to Database 01:05:22 | Retrieving from Database 01:10:27 | Image & Video Upload (ImageKit) 01:27:08 | Deleting Posts 01:30:54 | User Authentication and JWT Tokens 01:52:02 | Protecting Endpoints 01:58:00 | Streamlit Frontend Hashtags #FastAPI #ImageKit #SoftwareEngineer