FastAPI Crash Course - Modern Python API Development
Chapters20
This chapter introduces FastAPI, outlining its high performance features and the plan to build an issue tracker API while covering fundamentals like routing, middleware, Pydantic models, and Swagger docs.
Traversy Media’s FastAPI crash course teaches core API concepts, from routing and Pydantic models to middleware, CORS, and real-time docs, with a hands-on issue-tracker project.
Summary
Brad Traversy walks through FastAPI’s fundamentals, emphasizing a strong grasp of architecture, routing, data validation with Pydantic, and automatic Swagger documentation. He builds a complete issue-tracker API, starting from a simple health check route and progressively introducing routers, a storage layer with JSON files, and Pydantic schemas for create/update/out responses. The course contrasts FastAPI with Node/Express to help developers new to Python land, and demonstrates practical steps like creating a virtual environment, installing FastAPI with the standard extras, and wiring routers with prefixes (e.g., API/v1/issues). Traversy also shows how to test endpoints via Swagger UI, implement CRUD operations for issues, and add middleware (a timing header) and CORS configuration. He explores deployment considerations using Render, including generating a requirements.txt and pushing to GitHub for hosting, along with notes on authentication and JWTs for future videos. The takeaway is to build confidence with the essentials before introducing AI-generated code, focusing on architecture, data models, and testing First before automation. He ends by inviting viewers to download the Excalidraw docs, code, and crash-course write-up in the description.
Key Takeaways
- Install FastAPI with the standard extras in a Python venv to keep dependencies project-scoped (pip install 'fastapi[standard]').
- Create and wire a modular API using APIRouter with a v1 prefix (e.g., /api/v1/issues) to keep routes organized and documented.
- Define strong Pydantic schemas (IssueCreate, IssueUpdate, IssueOut) plus enums for status and priority to enforce data integrity and validation.
Who Is This For?
Essential viewing for Python developers new to FastAPI who want a concrete, end-to-end project (an issue tracker) to learn routing, data modeling with Pydantic, docs, and basic middleware before diving into authentication or deployment.
Notable Quotes
"So what we want to do is create an entry point, main.py, and wire up FastAPI with a couple routes to get started."
—Brad introduces the basic app structure and the initial FastAPI setup.
"Swagger is what we’ll be focusing on. It creates your documentation where it shows all your different routes… and you can try them out."
—Demonstrating the interactive API docs provided by FastAPI.
"We’re going to build an issue tracker API… with a JSON document storage, just to keep things simple for the crash course."
—Describes the data storage approach used in the tutorial.
"Full CRUD API. We’ve got create, read, update, delete—now we’re talking about a real backend."
—Highlights the completion of CRUD capabilities for the project.
"If you want to open it up, you could use the CORS middleware to allow all origins which is great for demos but you’d tighten it for production."
—Brief note on CORS configuration and production considerations.
Questions This Video Answers
- how does FastAPI automatic data validation work with Pydantic schemas?
- how to structure FastAPI projects with APIRouter for versioning?
- how to add middleware in FastAPI and what are common use cases?
- how to deploy a FastAPI app to render.com or similar platforms?
- what are the steps to generate a requirements.txt from a FastAPI project?
Full Transcript
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 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. 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 description 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 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 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. 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
More from Traversy Media
Get daily recaps from
Traversy Media
AI-powered summaries delivered to your inbox. Save hours every week while staying fully informed.





![Applied Data Science With Python Full Course 2026 [Free] | Python For Data Science | Simplilearn thumbnail](https://rewiz.app/images?url=https://i.ytimg.com/vi/AyxgXZ7XAlM/sddefault.jpg?v=69cea44d)



