Loading video player...
Hey guys, welcome to my fast API crash
course. Yes, we're finally going to
write some code and and not just talk
about AI. Uh, I mean, we'll use AI a
little bit to save some typing, but I
want to start getting back into the
fundamental crash courses. And this one
I wanted to do on fast API, which is a
high performance Python framework for
building well fast APIs. So, you'll be
build powerful backends and
microservices, things like that. Now,
we're going to spend the first few
minutes just talking about what fast API
offers, some of its key features, and
then we're going to jump in and build an
API for an issue tracker. And we're
going to focus on learning the
fundamentals. So, the overall
architecture, creating routes,
middleware, pyantic data models, swagger
documentation, basically everything
that's included in the fast API standard
package. And everything that I show you
is going to be documented and included
in the description, including the full
code, the presentation document, uh even
a written version of this crash course
and so on. So, I hope you enjoy and
let's get into it.
So, when it comes to software
development, one of the most important
things we do are code reviews, but it's
also a super tedious task and prone to
mistakes since you never really know how
well someone's reviewing your code. So
today's sponsor, Code Rabbit, makes code
review so much easier. It's an AI
powered code review tool that integrates
into your development workflow by
automatically reviewing your poll
requests or you can even just use the
CLI before you commit your code. So Code
Rabbit doesn't replace the need for code
reviews. It just makes them more
efficient and catches things that humans
will often miss. So instead of bugging
teammates with things that don't need
their attention yet, you can have your
code reviewed in minutes and you can
catch bugs before asking a co-worker for
a final review. So let's take a a quick
look at this PR where code rabbit caught
some bugs. So it catches that we
initiated our client instance to our
apprite backend in two separate
components when instead we should have
created one and then used the import
throughout the app. So it immediately
caught this mistake and provided us with
suggestions for changes that we should
make. So one of the coolest features is
the ability to set custom rules for you
and your team. So for example, we have a
rule set that describes how headings
should be set in articles. And when a PR
is made, Code Rabbit will catch this.
Even though it's not technically a bug,
it's something that a teammate could
have easily missed. So, a code rabbit
will also learn from your entire
codebase and help enforce patterns and
and rules that you and your team set.
So, it's actually pretty awesome. So, if
you want to give Code Rabbit a try, make
sure you check out the link in the
description below. So, in 2026, I know
the world has changed and I know that a
lot of you are going to be using fast
API and other technologies along with AI
and you're going to generate a lot of
your code. However, I think it's crucial
to understand uh at the very least the
fundamentals of what you're using. And
that goes for for anything. Uh and for
my crash courses from here on out, what
I want to focus on is what I think you
absolutely need to know before you use
any AI, before you generate any code. So
in terms of fast API, that's going to be
understanding the overall architecture
of your API of your files and folders,
creating your routes, testing them,
knowing your your data models is very
important. So learning pyantic and and
of course Python um and and learning
type hints and so on. So that's what
I'll be focusing on in this crash
course. And I don't want it to be too
tedious, so I'm not going to type out
everything. I might use C-Pilot. I might
do some copying and pasting, but I'll
explain everything that's going on. So,
I used to create slides, but I'm going
to try this new format with these
Excaladraw documents. These are really
cool, and I'll have these available for
you guys to download every crash course.
So, for this one, if you look in the
description, you'll get this this
repository which has the final code, but
if you look in docs, you're going to
have the Excaladraw document. Okay, it's
actually a PNG version. And then you'll
also have a a complete written version
of the crash course with all the code
snippets. So that's kind of the new
format for for 2026. Moving on, I also
want to mention devsheets.io which has
cheat sheets for for so many different
technologies. There's one for fast API.
I'll link that in the description. And
it has a lot of the stuff we'll be going
over and it has both the quick reference
and detailed examples of of everything.
All right, so enough rambling. Let's
talk about what fast fast API actually
is. So it's a modern high-erformance web
framework that is obviously it's Python
and it's built on top of the Starlet
framework or toolkit which is an ASGI
framework or toolkit and ASGI stands for
asynchronous server gateway interface
and I'll talk more about that in a
second but basically Starlet handles uh
the basic web parts. So things like
routing, middleware, uh request response
and so on. And then fast API adds a
robust layer for for building powerful
APIs and also simplifying API
development and just giving you a better
develop uh developer experience out of
the box. Now, as far as features, of
course, uh speed and performance, I
mean, it's built right into the name. So
it's actually on par with Node.js JS
thanks to Starlet's uh ASGI environment.
Now again asgi is the it's the successor
to WSGI which is web server gateway
interface and WSGI is synchronous and
was designed for traditional web apps
where each request is handled one at a
time where ASGI on the other hand is
asynchronous and can handle multiple
requests concurrently. Now, for what
we're doing, building our issue tracker,
we're going to be using a just a custom
storage module with a JSON document for
storage. We won't get the full advantage
of this, but if you're using something
like SQL Alchemy uh 2.0, something that
has those asynchronous capabilities,
then you'll get the, you know, that full
advantage. So, in addition to being, you
know, very fast, you have automatic data
validation and serialization. And so it
uses paidantic to um to to validate your
API and it does this using Python type
hints. Okay. So basically if you send a
post request with a field that's not
supposed to be there, it's going to
automatically strip it out. Uh and then
it also adds to the next feature which
is the automatic interactive
documentation.
So you get uh both swagger and red do
bundled with the standard package and
swagger is what we'll be focusing on. So
it creates your documentation where it
shows all your different routes uh any
params or anything like that but it's
also interactive. So you can use it as
you would something like postman or
curl. So you can make your um your HTTP
requests using swagger. uh and that
includes post, put, delete, and that
includes adding JSON to the body or
adding custom headers, things like that.
So, it's it's really powerful. And then
you also have a dependency injection
system. So, this makes it easy to manage
components and database connections,
authentication.
Uh so, you can actually define
dependencies as functions and fast API
will will handle their life cycle. Then
you also have security utilities. So,
built-in tools for things like
authentication, common security
practices like OOTH 2, JSON web tokens.
We're not going to really focus on
authentication in this crash course cuz
I want to focus on just the kind of the
basics, the fundamentals. But if you
guys like this and uh you know, you
enjoy it and it does well, then I'll
move on and I'll do some more advanced
stuff. We'll look at JSON web tokens,
protecting routes, and so on. Developer
experience focus, so there's minimal
boilerplate. Everything is explicit and
readable. It leverages Python type
hints. And then you also have support
for websockets and streaming so you can
build real-time applications and so on.
So I'm not great at creating diagrams,
but I wanted to kind of give you guys
just a a very highlevel overview of of
just how all this stuff works. So
basically, you know, we have our front
end, right? So, we have our client, that
could be React, that could be Vue, that
could be a mobile app, a desktop app,
whatever it might be, that makes an HTTP
request to our server that's running
fast API. And usually you're going to
have, you know, EngineX there as kind of
your entry point using a proxy. Um, you
might have some static files. And then
Fast API is running with this ASGI
um server, which would be like uvicorn
or something like that. Uh and then of
course you're going to make an HTTP
request along with a specific method and
route. So let's say we make a get
request to API v1 issues. That's going
to fetch all the issues. So that that
will interact with your data whether
that's a you know postgres database or
or even just a JSON document like we're
using. Uh and then it's going to fetch
that data. Now on top of that you're
going to have your paidantic models. So,
we're going to have some issue models or
issue schemas with certain fields and
that's where our validation comes in and
that's all based on Python type hints
which is really cool. And then of course
in addition you have your interactive
documentation for testing. So we can
test all these routes using swagger and
uh you know we can attach form data or
JSON data. And that's pretty much uh
just a basic overview, very highlevel
overview of how all this works. And if
that's confusing, it'll make more sense
as we move along. All right, so let's
jump in now and let's create our issue
tracker API. All right, so we're going
to get started. And I'm assuming that
you guys have basic Python experience.
So I'm not going to explain basic Python
syntax or how to install it or anything
like that. Um, so if you if you're not
familiar with Python, I would suggest
watching my my crash course or just
anybody's basic uh Python tutorial. So
the first thing we want to do is just
just create an empty folder. I called
mine fast API issue tracker and then
open it in your ter not your terminal,
but open it in your text editor or your
IDE as well as your terminal. So I'm
using VS Code's integrated terminal and
we want to create uh what's called a
virtual environment and this is so when
we install our dependencies everything
gets installed it's it's localized in
this project in this folder rather than
global. So what we can do is run
python-m
venv and thenvev
for the folder name and that will create
this.veenv
folder that you see here on the left.
All right. Now we want to activate this
virtual environment and we can do that
on a Mac with source and then we want to
go dot slash.vev
and then bin and then activate. So if I
run that now you can see we have this
parentheses with the folder name so we
know it's activated. Now if you're on
Windows it's a bit different. You want
to do uh dot slash.ve
slashscripts
slash and then activate.
Okay. Okay. So, if you're on Windows,
run that and and that should activate
your environment. All right. Now, what
we want to do is create an entry point.
So, we're going to call this main.py.
And I know a lot of people that watch my
videos are JavaScript developers. So,
I'll be doing some comparison with
Node.js and Express because that is, you
know, that's what you would use for a
lot of the same stuff. Um, so in Node
and Express, you would have your
server.js or app.js and you would
initialize express there. We're doing
the same exact thing here. So, um,
before we can initialize it though, we
of course need to install it. So,
basically, I'm just going to use this
dev sheets.io for some reference. And
you can install uh fast API a couple
different ways. You can use the all
extra and that includes a bunch of
different dependencies or you can use
the standard which includes just the
things we we want that we're going to
need like paidantic and swagger and so
on. Um you can do it this way. So
install fast API and then u which is the
the ASGI server standard or you can
simply just do fast API with the
standard extra which is what I'm going
to do. So let's jump into the terminal
here and let's say pip which is the
Python package manager install and then
in double quotes I'm going to say fast
API and then in brackets we'll use um
standard.
Okay. And that'll install it locally.
It's not installing anything globally on
your machine because we're in a virtual
environment. All right. So, now that
we've done that, we can import, let's
say from fast API, we want to import
uppercase F fast API.
Okay. And then we can initialize that.
And I'm going to be using Copilot to do
my autocomplete just so I don't have to
type everything out. So we're just
initializing fast API setting it into
this app variable and then that's really
all we have to do. We can now actually
start creating routes. So we can do that
with a decorator like this. So we use
this at symbol and then the app variable
and then dot whatever method we want to
accept for this route. So in this case a
get request and then you would add your
URL or your endpoint here. So this would
be just the home route uh or home
endpoint. I'm actually going to create a
health endpoint so that we can just see
if the API is up and running. And then
what you would do is under that
decorator is create the function. So
we're going to define a function. This
doesn't have to be async for for what
we're doing. Um define a function called
health check or whatever you want to
call it. The name of this function is
irrelevant. And then we're just going to
simply return uh a dictionary which will
then get serialized into uh uh JSON. So
we'll just say status. Okay. And that's
it. We should be able to run our server
with uicorn. And there's actually a
wrapper that we can use to do that with
fast API dev and then the name of the
file. So main.py. And now our server is
running. So that's it. Just a couple
lines of code. Run our server. And now
it's running on localhost 8000. Now if I
go to just the home route, it's it says
not found because we didn't create that
route yet. But if we go to health, then
we see status. Okay. Now, this isn't
really how you want to how you want to
test your routes. You could use a tool
like Postman or curl, but remember fast
API has interactive documentation with
swagger, which is really cool. So, we
can just go to localhost 8000/doccks
and we can use swagger. Okay. Any route
we create just by adding that decorator
uh we're going to see here. So we see
our get request to /halth. It's going to
show us what the response should look
like, the code, uh any parameters, but
you can also try it out. You can also
hit that route from here. So I'm going
to hit execute. And now it sends a
request. We get the response 200
response status. Okay. And then we get
our headers. We get a curl command if
you want that. Um so it's pretty cool
stuff. And this is what we'll use to
test. So that's how we can create a
route. Um I do want to show you show you
some other simple routes before we move
on to our issue tracker. So let's say we
want to just have some items. So I'm
going to just add here let's say items.
We'll set that to a list or an array.
And let's just have one, two, and three.
Okay. Now I want to have a route that
will fetch these items and display them.
So let's come down here. We'll say
app.get get items and then create a
function. Doesn't matter what the
function's called. Get items is fine.
Typically in Python, you're going to use
the the underscore syntax. Uh and then
we'll return items. And now just by
doing that, if we go back to the browser
here and refresh, now we see get items,
right? And if I do try it out, I execute
now we can see it's fetching those
items. All right. Now let's say we
wanted to get uh a single item by its
ID. So we can do that as well. So let's
come down here. Let's say at at app get
items and then notice I have slash and
then curly braces item ID. Okay. So this
is a path param and and you can check
out again dev sheets has this stuff. So
right here path params. So we pass in
the item ID and then what we do is
create a function. doesn't have to be
asynchronous in this case. And then we
pass in that item ID and we add a type
hint. This is in this case it's it's an
int. And then we'll return that. So
let's do that here. So app.get
items ID. Create our function that also
takes in an item ID. And then as far as
the logic, we're just looping over the
items, checking if the ID matches
whatever's in the URL. Return that item.
And if it's not there, then return an
error. All right, so let's go ahead and
save that. And then we'll go back to
swagger and let's refresh. And now
you'll see we have that route right
here. Item slash item ID. And notice now
it it has an input. I can't click in
this yet. I have to click try it out
first. And then I can add the ID.
Execute. And now it gives me item one.
And if I were to put something that
doesn't exist, like let's say 100, then
I'm going to get that item not found. So
creating routes is is very simple. Now,
if you wanted a query string like let's
say you wanted I'm just going to type it
up here. So like slash items and then
maybe question mark page equals 1 and uh
I don't know limit equals 10. So if you
wanted to do that, you wanted to get
these values, I'll just show you on the
on the dev sheets here. If you go to
query parameters, you would just pass
those in. This one says skip. I put
page, but whatever it is, you would put
it in here. You would uh add the the
type hint. So, int and then set the
value uh default value. And then you
could access that in here, right? So, if
I put limit equals 10, pass in limit
here, and then I could access it. All
right? So now let's take a look at uh a
simple post request before we jump into
our our actual issues routes for our
API. So I'm just going to add down here
app.post items and then create an item.
And you could pass in the individual
fields like what do we have I think well
just a name really. Uh or you could pass
in the entire item as a dictionary which
is what we're doing here. And then we're
just simply appending that new item onto
that dictionary and then returning it.
So let's try that. I'll save it. We'll
jump back over to Swagger and let's see.
We should have a post route.
Did I save this?
So app post
refresh. There it is. Okay. So then the
post request is green. And then you'll
see here we have an input for our item.
It just has this additional prop one
just as a just a generic field. But
let's say try it out. And we'll get rid
of this and set this to let's say this
has a name and test item. We'll execute
send the post request and then we get
that that object back. Name test item.
Now since we don't have any models set
up, we have no schema. We didn't set up
paidantic yet. I can send anything here,
right? Like I could just send
foo and then for a value bar and execute
and that sends and then it responds with
name and that fu fuar value. Okay. When
you add a when you create schemas with
with paidantic you can validate this
right? So you can only send certain
things otherwise it'll get stripped out.
Same thing with the response. You can
send set a model for what you get back
in the response. And we'll get to to to
models soon. But what I want to do now
is show you how you can use the router
because you're not going to want to put
all your routes in the the main entry
point here. In fact, we can get rid of
all this item stuff. You can keep the
health check if you want. I'm just going
to get rid of it. Um, and I'm also going
to get rid of the items. And now we're
going to start to create our issue
routes. Now I'm going to put this in a
separate file. So, in the root here, I'm
actually going to create a folder called
app.
And then in app, I'm going to have uh a
folder. Let's create a folder called
routes. And then in routes, we're going
to have a file called issues.py. And
this is where any routes that have to do
with issues are going to go. Now, in
order to do this, to use a separate
file, we need to use the the API router,
which again, if you're coming from Node
and Express, it's very similar to how
the express router works. So, we're
going to bring that in. Let's say import
or not import, let's say from fast API.
We want to import API router. And then
we're going to initialize it into a
variable called router. And then when we
want to create routes, we then use the
at@ routouter decorator. Instead of
doing at app.get or post or whatever, we
do router.get or post. Now, we could do
it this way and just have slash issues,
but a better way to do this would be to
add a prefix in here of issues and then
we don't have to add it in each row like
that. Okay. And then tags. What that's
just going to help the documentation. So
when we when we view our swagger UI,
it'll be separated by the different
resources, issues, users, etc. Um, and
also for prefix, I usually like to do
slash API slash issues at least. And
sometimes you can do like the version.
So I'm going to put v1 in there. So we
know that this is version one of the
API. And then if you if you update it
and a lot of things change, you can then
have a version two, version three, etc.
All right. Now, for this get, let's go
ahead and create a function. So, we'll
just have get issues. And then, as far
as what I want to return, we don't need
all this. Let's just return an empty
array for now. Okay. And we don't have
um our our JSON file yet. So, we don't
have any data. So, we'll just have an
empty uh empty list or empty array.
Okay. Now, we should well, we shouldn't
see this just yet. As you can see, if I
refresh swagger, it doesn't show up
because we need to wire it up in our
main.py, which is very simple. It's just
two lines. So, one is going to be the
import where we can say from
app.routs.us
import router as issues router. And then
down here, we simply say app.incclude
router and pass in that issues router.
Now if you had like let's say users and
you had a users route you would do the
same thing except you would replace
issues here and here with users and you
would pass the users router in down here
as well. So now if we go back to swagger
and I refresh now we can see issues and
we can make a request here which it'll
just give us just an empty array. Okay.
Okay. So, now that we have that set up,
we can move on to our storage, right?
Because we want to be able to store uh
issues in a in a JSON file. So, let's
come back to VS Code. And I'm going to
put this in a file in the app folder
called storage.py.
And basically, this is just going to
have two functions. One to load data
from the JSON file and one to save data.
Now in a production project again you'd
probably be using something like SQL
model or SQL alchemy or or some kind of
OM like that along with uh like a
Postgres database but I wanted to keep
this focused on fast API and not
introduce too much extra stuff. So and
this will be pretty simple. So I just
want to have a couple imports here. We
want uh Path Lib from Pathlib. We're
importing path. And I'm just going to
shut off for a couple minutes. I'm going
to shut off um Copilot just so we can
type all this out without confusion. And
then since we're using JSON, we'll use
the JSON library. All right. And we
don't have to install anything extra for
this. So I'm going to define both the
data directory and the data file that
we're using. So data directory, I'm
going to set that to path and it's going
to be a directory called data. So it'll
be just in the root. And we don't have
to create this. So it'll be created
automatically when we insert or when we
create uh a new issue. And then data
let's say data file. So data file should
be data directory slash and then the
name of the file which is going to be
issues.json.
Okay. So basically I if you were to keep
it like this and you stick with JSON
files how the way we're doing it you'd
have a separate JSON file for each
resource like maybe users.json. JSON,
post.json, etc. All right, so now let's
create we're going to define a load
data function.
All right, and this is going to be
pretty simple. We're just going to first
off check to see if the data file
exists. So let's say if the data file
dot
exists,
then we want to open it. So let's say
with open
uh we want to open data file and we want
we're just reading it. So we're going to
pass in r for read and then as f and
then what we want to do is get the
content from it. So let's have a a
variable called content and let's set
that to uh the file dot and we'll use
the read method.
All right. And then I just want to strip
out any white space. So we can do that
by saying if content dot strip
then we want to return the JSON dot and
then we'll use loads and pass in
content. So that's it's just simply
looking at the file reading it returning
the the JSON and if it doesn't exist so
let's go right here and let's say return
just an empty array. So pretty simple
that's our load data. Now let's create
our save data. So save
data and that's going to take in data.
And what we want to do here
is first off let's let's create the
directory the data directory if it
doesn't exist. So we can do that with
data durd
and we can pass in here let's say
parents equals true and let's say ex uh
exists. Okay, set that to true. So it's
just it's just going to create it if
it's not there. Then we want to open it
to write to. So let's say with open and
pass in our data
file and we're going to pass in W here
because we want to write to it. And
we'll say as f.
Okay. Then we just want to use JSON.dump
to write to it. and pass in here the
data, the file, and the options, which
I'm going to set indent to two just to
make it a little prettier. And that's
it. That's our entire storage module to
to load data from the JSON file and to
to save it. Okay, we can close that up.
Now, we can go back to our routes and
start to um start to work with that. So,
let's go to issues and let's see. We
want to bring in uh couple things. Oh,
you know what? Before we do this, let's
set up our schemas. I didn't realize we
didn't do that yet. Um, so in app, we're
going to create a file called schemas.j
um js. You can tell what my main
language is. So, schemas.py.
And basically what this allows us to do
is use paidantic to map out data for
issues or or any resource in specific
situations. So when we make a post
request, we send in the body an issue
with a title uh a description, a
priority. So we want to create a schema
for creating an issue. When we do an
update, we also send in specific data.
We want to create that schema. But when
we get an issue back from our API in a
response, we want a schema for that as
well. Also, I want to have some enum
um enums for the status and the priority
because they need to be specific values.
For instance, a status should be like
open um in progress or closed. It
shouldn't be able to be anything else.
So, we're just setting strict rules for
our data for issues. So let's bring in
since we're using enum we're going to
say from enum import
enum from paidantic we want to bring in
base model. So basically to create these
schemas it inherits this class this base
model. And then we're also going to
bring in field from paidantic which
allows us to um to add validation
constraints like like min length and max
length things like that. And then from
typing, I'm going to import optional
because we're going to have some
optional fields specifically for the
update. Okay. So, now that we've brought
that stuff in, let's let's work on our
enum. So, issue status, that's actually
exactly what I want, except I I don't
like to use caps here. It's up to you.
You can if you want, but I just want to
use lowercase. So basically we're saying
that the status okay when we assign this
to the the status of the issue it has to
be one of these and then we'll do the
same with priority. So let's say class
uh issue priority
and all we're we're using enum and we're
saying it's these are also strings. So
priority has to be low, medium or high.
It can't be critical or or anything
else. If you want it to be able to be
critical, then you would add it here. So
now, now that we have those uh enum
classes, let's go ahead and create a
schema for creating an issue. So when
you make a post request, that data that
you send in the body. So we'll say
class, and I'm going to call this issue
create, which takes in a base model. And
I just want to type this stuff out
because it can be a little confusing uh
using C-pilot with big blocks of code
like this. So I'll just snooze it for a
little bit. And we're going to have a
title which is going to be a string. Set
that to field. Remember field allows us
to add validation constraints such as
min uh min length which I'll set to
three. And let's say max max length
we'll set 100 for the for the title.
And then let's do description.
And if you want to change any of these
values, of course you can. Whoops.
Description.
Um we're going to set that yeah to
string
and then to field.
So field let's set min length
to five and we'll say max
max length to I don't know th00and
and then let's do the priority.
So priority is going to be the the enum
class which is issue priority and then
we'll set that to issue priority
domedium.
Okay? Because we can set it to any of
these here. Low, medium, high. So, I'm
just setting it to medium by default.
So, that's that's what the schema looks
like to create an issue. Now, notice I
don't have status in here because status
is going to be hardcoded as open when we
create an issue.
Now, let's do the update, which is going
to be a bit different. So, let's say
issue
update
and base model.
And then we have
the title.
So title. Now I'm going to set this.
This is where this optional comes in
that we brought in. Spell that right.
Optional. Uh and it's going to be an
optional string. Now the reason it's
optional is because when you do an
update, let's say we just want to update
the title.
We don't want the description to be
required, right? Because we don't care
about updating the description. So when
we make that put request,
everything's going to be optional
because you you might only want to do
one or two things. So let's set this to
field and we'll set let's say default to
none. And if you're new to Python, you
can think of none as like null from
other from like JavaScript just means
empty. And then let's do max length of
what do we do? 100.
And then let's do description.
So description, same thing. It's going
to be optional and it's going to be str.
Set that to field. Set the default equal
to none.
And then let's do max
uh max length of
what do we want to do here? I think a
th00and.
Okay. And then we'll have a priority.
So priority is going to be set to
optional as well. However, it's also
going to be issue priority
and we'll set that directly to none by
default. And then we have status.
Status is going to be optional.
Um issue status and set that to none.
Okay. So that's the schema for updating
an issue. Now the last thing I want to
add here is the schema for the the
response the output. when you fetch
either um you know all issues or just a
single issue it's it needs a schema. So
let's call this one. We'll say class
issue out
and inherits base model.
It's going to have an ID which is going
to be a string. It's going to have title
string
description
string
and then also priority basically all the
fields. priority which is going to be
issue priority and then status which
will be issue status.
And that's it. That's that's all of our
scheme is. Again, we have our enums for
status and priority. We have our what
the issue should look like on a create,
what the issue should look like on an
update, and what it should look like on
output or response.
So, the next thing we want to do is move
back over to uh to our routes, right?
Issues.py.
And couple things that we want to bring
in here. Since we're not using a
database, we need to create our own ids.
So, I'm going to use uuid for that.
And then from fast API, a couple other
things we want to bring in. I want HTTP
exception and status. So that'll allow
us to um to set you know we'll set a 404
if it's not found things like that. And
then we also want to bring in from apps
schemas let's see we want to bring in
yeah issue create issue out and issue
update.
And then we also want to use our storage
modules. So let's say app.sto storage.
We want to import
our two functions which are load data
and save data. So those are all the
imports.
Next, let's uh see. So we'll go ahead
and and finish this route. Right now,
it's just returning an empty list. So
I'm going to add going to add a dock
string here. Just say retrieve all
issues. We're going to set a variable of
issues to to load data and then return
those issues. Let's get rid of this. And
then one thing I forgot here is we're
also going to pass into the get a
response model. And this is going to be
what this abides by, what we return,
which is going to be a list, right?
Because ultimately it's a JSON array. In
Python, it returns a list. And it's
going to use the issue out schema. Okay?
So again, which is going to be this this
stuff here. So each route that we
create, we'll also assign a response
model to. Now, let's do the post request
so we can actually create an issue and
it will create the the JSON file. So,
I'm going to say at@ routouter.post
and I'm going to use uh copilot here
just to save some time. There's going to
be a couple things I might change up,
but basically we're just making a post
to slash which in reality is API v1
issues. And then the model here is just
issue out because this is not a list.
This is this returns a single issue. You
add it and then it returns it. This up
here returns multiple issues. That's why
it's a list with the issue out schema.
This we just add the schema. And then we
can also add a status code. And in this
case, we're changing it to 2011. So this
static object can have these different
constants that represent different
status codes, 404, etc. All right. Then
we're going to add the function. Whoops.
We're going to add the function to
create an issue.
Okay. that takes in issue and the schema
of issue create. And then let's see, we
don't need to wrap this stuff in issue
out. It'll do that automatically. So, we
can actually get rid of this and just
make this a dictionary. So, add that.
And then we need to add our double
quotes.
Yeah. So, we just add double quotes. And
for the ID, it's going to create a UU
ID. title is coming from
uh right here. So, you know what? I
think in the in the final code I called
this payload. So, let's let's actually
change that to payload. And then change
these to payload as well. Yeah.
So, yeah, the payload is is coming from
the body of the request. Setting the ID,
title, description, status. Notice how
we're setting it to issue status.open,
open, not simply just a string of open.
And then we're appending that new issue
to the issues from the JSON file. And
then we're just saving all of them with
the new one. And then finally returning
the new one, which is going to uh you
know abide by this issue out schema. So
that should work. Let's try it out.
We'll save that and let's refresh. We
should see our post request here. And
I'm going to say try it out. And title
I'll just say issue one. And uh let's
see description I'll say this is a test
issue
and medium for the priority. Let's
execute. And there we go. So we get a
2011 because we remember we did that
status
whatever that constant was. And then we
get our ID, we get our title,
description, priority, status is open.
So yeah. So, and that should have
created, let's check it out. So, you can
see now there's a data folder with an
issues.json with that that object in it,
that issue in it. All right. So, that's
working good. So, now we just want to
finish up this this CRUD layer, right?
We want to be able to to create, update,
and and do all that stuff. So, let's
see. Let's do a single issue, though,
before we do any update or delete. So
I'm going to say at routouter
get take in the issue id the response
model since this is returning a single
issue response model is going to be
issue out and then let's create the
function.
So let's say uh defaf get issue and this
should be fine. So getting the issues
from the data looping through finding
the the right ID if it's there we return
it. If not, we're going to raise an HTTP
exception. This is something we brought
in from fast API. And then adding the
status of 404. We're using um status dot
and then that constant. And if it's not
there, you can have this as an error
message.
Okay. So, we'll save that and then let's
try it out. I'm going to come back here.
Let's refresh. And I'm going to copy
uh let's try it with an ID that doesn't
exist first. like we'll do 100 execute
and we get issue not found for the
detail for the error and then let's copy
an actual ID that works from this
response.
Uh actually,
you know what? Let's add another one.
Before I do that, I just want to show
you if we make a post request.
See, try it out. and let's say issue two
and for the description I'll say this is
another test and then I want to show you
if I add in something here that's not in
the schema right so again if I do foo
foo bar like I did earlier without using
any schemas
and we go ahead and execute
so it it goes through but what it does
is it just strips out and ignores this
because that's not in the schema. All
right, but now we should have two
issues. So if I come back up to the get
and execute, now I see issue one and
issue two. So let's copy the issue two
ID. Let's go down to the single that we
just created and let's add that here and
then execute. And now you can see it
shows me issue two.
All right, so that's that. Now let's do
the the update the put. So I'm going to
open go back to my issues.py and let's
say at routouter.put. We want to take in
the ID the response model. It's just
going to be issue out. Then we have our
function. Uh let me see if this looks
good. So we're getting the issues from
load data. Looping through checking the
ID copying the issue putting it into a
variable. I think I did this a little
bit different in the final repo, but not
much. And then we're checking for the
title description because it can be it
can be none. Um, if it's not none, it's
going to get set to whatever that
payload was for all of these. All right,
then we're going to save it to the file.
If it's not there, then we raise an
exception. Good. Let's save that. And
then let's try to update one of these.
So, I'll refresh and let's grab an ID.
So, I'm going to execute. Get get one of
these IDs. I'll just grab the first one.
All right. And then we're going to come
down to the put, which is orange. Try it
out. I'm going to put the ID in there.
And then I only want to change the
description. So, what I'm going to do is
get rid of this other stuff. And
remember, it's optional with the the
issue update schema. So, I'll say this
is the
updated
issue.
Execute. And we get back uh 402. That's
because I have a trailing comma. Let's
get rid of that. Execute. There we go.
So, now the description is this is the
updated issue. And if we were to go up
here and make this request to get all
the issues, you can see one has that
updated description.
Okay. Okay. And then the last thing we
want to do is the delete.
Let's just um
let's just create another one to delete.
So we'll go to post. Try it out. And
I'll just leave this stuff here. Just
string string whatever. Execute. And now
we have our third. And I'm just going to
grab that ID.
Okay. So grab that ID. And if we were to
fetch all of them, there should be three
now. So that's the third one. Now let's
go back and let's add the delete. So
let's say at routouter.delete.
And we want to return a 204 which is no
content because we're deleting it. We're
not going to return it. And then let's
see. We have our delete issue. It's
going to get from the JSON file. It's
going to loop through. it's going to pop
off the end um or pop off wherever the
index is, whichever one we're deleting
and then raise an exception if it's not
there. So, very simple.
And then let's come back over and let's
try to delete. So, delete is red. So,
now I'm going to say try it out. Put pop
the ID in there. Execute. We get back a
204.
Um,
we get a validation error.
Um,
let me see if see if that actually
deleted.
Execute. And yeah. Okay. So, it did
delete. We have two.
All right. So, now we have full CRUD.
So, we have full H full CRUD API.
Now there's a few other things that I
want to show you. I want to show you uh
how to add middleware. So every
framework has or every backend framework
just about has the concept of middleware
which is really just a a function or
code that runs before or after each
request and you have access to that
request and response object and you can
do things with it. So a lot of
authentication will have middleware
logging things like that. So, I figured
what we could do is just create a simple
piece of custom middleware. So, we're
going to have a timer uh that basically
will get the the timing of the request
and it will send that as a custom header
with the response. So, we could put it
right in the main.py, but I figure we'll
in the app folder, we'll create a folder
called middleware. That's what you would
do in a, you know, a larger project. And
in middleware, let's create a file. And
we'll call this timer.py.
Okay. And I'm just going to mute
gonna snooze copilot for this just
because I don't want it to confuse you
guys. So let's import time
and let's import or let's say from
fast API I want to import request
because we have access to that request
object and we want to use that. Um, and
we also want to send uh we want to
attach a custom header to the response
as well. So this is actually going to be
an async function because there's a a
specific function that we call called
next that is asynchronous. So let's say
async define a timing
timing middleware
and you can call this whatever you want.
The name of the function doesn't matter.
And then it's going to take in request
which we'll set to request. And then
it's also going to take in that call
next function.
And then in the function body let's
create a variable called start. We're
going to set that to time doperf
counter. And I'll explain this in a
minute. And then let's say um response
we want to set that to await. And then
call next.
and call next will take in the request.
Then I want to set the custom header. So
we can take the response object and we
can say dot header or headers and then
in brackets we can call this what we
want. I'm going to call it x. It's
typical convention to just do x dash. So
we'll do x dash process dash time. And
then let's set that to an string. And in
that string, I want to add um some
dynamic code here. So, we're going to
take our time.perf counter.
And then from that, we want to subtract
the start. And then I want to do four
decimal places. So, we can format that
with colon and then period 4. So, four
and then f. And I want to put an s at
the end because it's however many, you
know seconds.
um and and actually that s should go
inside the quote and then I simply want
to return that response.
Okay, so uh again just to kind of go
over this, we have our request that gets
passed in. Call next, which just means
call the next piece of middleware. If
you're coming from Nodeen Express, it's
very similar to the next function. And
then um this right here, time perf
counter. This is a high resolution
counter to measure the elapse time. And
what we're doing here setting the custom
header to uh a calculation which is the
time taken to process the request and
then we just add it as a custom header
and we return the the entire response.
Now to use this we need to wire this up
in main.py. So let's go there and then
we're going to bring in let's say from
app dot app.middware.timing
timing.
We want to import our timer. Uh, what
did I call it? Timer. Timing middleware.
That's what I called it, right?
Timing middleware. Yeah.
Why isn't it
could not be resolved? Um,
middleware. Oh, timer.
timer is the name of the file. All
right. And then all we need to do after
that, let's go right under where we
initialize our app and let's say app.
Middleware. And then we want to pass in
here http. And then we want to open
another set. And this is where we add
the name of our function which is going
to be timing
middleware.
All right. So now to test it, we can
just go to Swagger and just make a
request. Let's say let's use this get
request. Let's say try it out. Execute.
And then what we should see down here is
the X process time. All right. So we we
added this custom header there. So
0.0019
seconds. If I go ahead and try this
again, let's execute.
Now it's 0012. Okay. Okay. So, it's just
giving me that that response time. So,
very very simple, but I figured that
it's a you know, it's a good way to show
you guys how to uh how to implement
middleware. Now, another thing I want to
look at is cores. So, or cross origin
cross origin resource sharing. Because
right now if you were to use this API
and deploy it if you try to use it with
a different domain in the front end like
let's say this is at I don't know um
issue tracker.com or whatever or.io and
then you have I don't know myiss
issues.com as your front end your
reactor or view or whatever and you try
to make a request you're going to get a
cause error in the in the browser
console. So if you wanted to to open it
up, you could use the the cores
middleware. So to do that in main.py,
we can say from
from fast API
um actually yeah we want to do from fast
API middleware.cores
import this cores middleware
and then we'll go right after our custom
middleware and let's say app.add add
middleware and then coores middleware
and then you can add your origins your
different values here.
Okay, so what we're doing is allowing
all right we're allowing all origins
which means we're allowing all domains
to access this API. So if this was
public and you wanted just anyone to
access it then you would do this right
it was saying also all methods and all
headers HTTP methods and headers are
allowed as well. Um, if you wanted just
specific domains, then you could add
that into this list here. But as it is
now, it's it's just public and open. So,
I'm just going to keep it like this. All
right. So, yeah. I mean, that that's
kind of all I wanted to go over. I don't
want this video to be too too long. Uh,
I do want to do a deployment, though.
So, what I would suggest is render.com.
If you're not really good with DevOps
and you you don't know Docker and you
know, you don't really know AWS or
platforms like Digital Ocean, Render is
great because it does all that stuff for
you. And basically, you can just link up
your your GitHub repo and and they're
not sponsoring this or anything. They've
never sponsored me. It's just a service
that I really like and I think is great,
especially for beginners. So, or just
people that aren't into DevOps. So, what
we're going to do first before we even
go to render is go into the terminal
here.
And I'm just going to stop I'm just
going to stop the API.
Um, and and we want to generate a
requirements.ext
file. So basically this just has all the
dependencies that are required for this
project which are you know fast API
pyantic swagger all the stuff that was
installed with that standard extra that
standard package. Um it's kind of like u
like a package.json if you're if you're
coming from node express. So we can do
this with pip freeze
and then uh we want to do
requirements.txt
txt.
Okay. So, that'll generate it. Now, you
can see we have that file and it it just
has everything that that is in this this
project. All right. Now, once we do
that, we want to you want to push your
code to GitHub. Now, I'm not going to do
that because I already have a repo on
GitHub with this pretty much the same
thing that we just created. So, I'm just
going to use that. So, let's go to
render.com.
And you want to just log in. And I
believe you can just log in with GitHub.
Uh, and then we want to create a new web
service.
And then yeah, so my my repos right
here, fast API issue tracker. I'm going
to go ahead and select that.
And then let's see this stuff. We can
leave. Um,
what we do want to add is in the build
command right here. Actually, it's
already there. pip install record. So
what this does is it'll just install
those those packages those dependencies
that are in that requirements.
And then for the the start command we're
going to use uicorn. So let's say uicorn
um main colon app and then you add your
host. So d-host
which is local. So 0000
and then d-port
and just the port variable. So, money
sign uppercase port. So, that should be
the the star command by default. And
then that should do it. We can click on
the free package. So, you don't have to
pay for this. And then if you had
environment variables, which we don't,
you would put them in here. So, let's
say deploy web service.
And then that's probably going to take a
couple minutes to to deploy. You'll see
your logs here. So it's going through
and it's installing all of our packages,
all the dependencies.
And then this will be your URL. So right
here for me, it's going to be fast
apiissue tracker.on.com.
And I can copy that. Actually, we'll
just open in a new tab. And it says not
found because obviously it's not it's
not done deploying, but we'll test that
in a second. All right. So it says build
successful.
Okay. Okay, so it says available at your
primary URL. Let's go back here and
let's refresh. We get not found, which
is which is fine because this is the the
home endpoint, right? The index. So we
want to go to slash um what do we want
to go to? Let's say API slashv1
issues and we have an empty array. Now
we can make a post request. Actually we
should be able to use
can we use docs here? Yeah. So, we're on
the production domain
and and you don't have to deploy with
the docs, but I just want to be able to
test this. So, let's come down here and
let's say try it out
and
issue one test
execute.
Okay, there it is. Good.
Now, if I go back to my domain,
see API slashv1
issues. And there we go. All right. So,
our API is now live. So, you could now
create a front end, excuse me, and link
to it. Obviously, you'd want to add
authentication. I mean, this is just an
example to give you the fundamentals,
but that's it. Let me know if you guys
like this and if you want to do more
with fast API, I'd be happy to, you
know, to look into authentication and to
um use JSON web tokens. In fact, in the
final repo, if you look at docs and you
look at the crash course, the written
version, I do include authentication.
So, if we go all the way down cores,
that's what we left off with. Let's see.
Yeah. So, right here. So this shows you
how to add your your O schemas. So your
token token data user and proceed with
JSON web tokens. All right. So that's it
guys. Thank you so much. If you like
this, please leave a like and I will see
you in the next
Learn the fundamentals of creating APIs with Python using the FastAPI framework. Check out our sponsor Code Rabbit: https://coderabbit.link/traversymedia Final Code & Docs: https://github.com/bradtraversy/fastapi-issue-tracker Devsheets.io FastAPI Cheat Sheet: https://www.devsheets.io/sheets/fastapi Timestamps: 0:00 - Introduction 1:06 - Sponsor (Code Rabbit) 2:45 - What Is FastAPI? (Slides) 8:25 - High Level Overview 10:11 - Virtual Environment Setup 11:43 - FastAPI Install & Basic Setup 13:32 - Your First Route 15:09 - Swagger Interactive Docs 16:07 - Basic Item Route Examples 19:00 - POST Requests 20:50 - API Router Setup 24:00 - JSON Storage Module 28:39 - Pydantic Schemas 36:10 - Issue GET Route 37:50 - Issue POST Route 43:26 - Issue PUT Route 45:17 - Issue DELETE Route 47:00 - Middleware (Timing Example) 52:50 - CORS 54:37 - Deploying to Render