← 返回首页
FastAPI Tutorial in Visual Studio Code

Documentation

Topics Overview Overview Linux macOS Windows VS Code for the Web Raspberry Pi Network Additional Components Uninstall VS Code Tutorial Copilot Quickstart User Interface Personalize VS Code Install Extensions Tips and Tricks Intro Videos Overview Setup Quickstart Overview Language Models Context Tools Agents Customization Trust & Safety Overview Agents Tutorial Agents Window Planning Memory Tools Subagents Local Agents Copilot CLI Cloud Agents Third-Party Agents Overview Chat Sessions Add Context Inline Chat Review Edits Checkpoints Artifacts Panel Debug Chat Interactions Prompt Examples Overview Instructions Prompt Files Custom Agents Agent Skills Language Models MCP Hooks Plugins Context Engineering Customize AI Test-Driven Development Edit Notebooks with AI Test with AI Test Web Apps with Browser Tools Debug with AI MCP Dev Guide OpenTelemetry Monitoring Inline Suggestions Smart Actions Best Practices Security Troubleshooting FAQ Cheat Sheet Settings Reference MCP Configuration Workspace Context Display Language Layout Keyboard Shortcuts Settings Settings Sync Extension Marketplace Extension Runtime Security Themes Profiles Overview Voice Interactions Command Line Interface Telemetry Basic Editing IntelliSense Code Navigation Refactoring Snippets Overview Multi-Root Workspaces Workspace Trust Tasks Debugging Debug Configuration Testing Port Forwarding Integrated Browser Overview Quickstart Staging & Committing Branches & Worktrees Repositories & Remotes Merge Conflicts Collaborate on GitHub Troubleshooting FAQ Getting Started Tutorial Terminal Basics Terminal Profiles Shell Integration Appearance Advanced Overview Enterprise Policies AI Settings Extensions Telemetry Updates Overview JavaScript JSON HTML Emmet CSS, SCSS and Less TypeScript Markdown PowerShell C++ Java PHP Python Julia R Ruby Rust Go T-SQL C# .NET Swift Working with JavaScript Node.js Tutorial Node.js Debugging Deploy Node.js Apps Browser Debugging Angular Tutorial React Tutorial Vue Tutorial Debugging Recipes Performance Profiling Extensions Tutorial Transpiling Editing Refactoring Debugging Quick Start Tutorial Run Python Code Editing Linting Formatting Debugging Environments Testing Python Interactive Django Tutorial FastAPI Tutorial Flask Tutorial Create Containers Deploy Python Apps Python in the Web Settings Reference Getting Started Navigate and Edit Refactoring Formatting and Linting Project Management Build Tools Run and Debug Testing Spring Boot Modernizing Java Apps Application Servers Deploy Java Apps GUI Applications Extensions FAQ Intro Videos GCC on Linux GCC on Windows GCC on Windows Subsystem for Linux Clang on macOS Microsoft C++ on Windows Build with CMake CMake Tools on Linux CMake Quick Start C++ Dev Tools for Copilot Editing and Navigating Debugging Configure Debugging Refactoring Settings Reference Configure IntelliSense Configure IntelliSense for Cross-Compiling FAQ Intro Videos Get Started Navigate and Edit IntelliCode Refactoring Formatting and Linting Project Management Build Tools Package Management Run and Debug Testing FAQ Overview Node.js Python ASP.NET Core Debug Docker Compose Registries Deploy to Azure Choose a Dev Environment Customize Develop with Kubernetes Tips and Tricks Overview Jupyter Notebooks Data Science Tutorial Python Interactive Data Wrangler Quick Start Data Wrangler PyTorch Support Azure Machine Learning Manage Jupyter Kernels Jupyter Notebooks on the Web Data Science in Microsoft Fabric Foundry Toolkit Overview Foundry Toolkit Copilot Tools Create Agents Models Playground Agent Builder Agent Inspector Evaluation Tool Catalog Fine-Tuning (Automated Setup) Fine-Tuning (Project Template) Model Conversion Tracing Profiling (Windows ML) FAQ File Structure Manual Model Conversion Manual Model Conversion on GPU Setup Environment Without Foundry Toolkit Template Project Migrating from Visualizer to Agent Inspector Overview Getting Started Resources View Deployment VS Code for the Web - Azure Containers Azure Kubernetes Service Kubernetes MongoDB Remote Debugging for Node.js Overview SSH Dev Containers Windows Subsystem for Linux GitHub Codespaces VS Code Server Tunnels SSH Tutorial WSL Tutorial Tips and Tricks FAQ Overview Tutorial Attach to Container Create Dev Container Advanced Containers devcontainer.json Dev Container CLI Tips and Tricks FAQ Default Keyboard Shortcuts Default Settings Substitution Variables Tasks Schema
Copy as Markdown

On this page there are 9 sections

FastAPI Tutorial in Visual Studio Code

FastAPI is a modern high-performant web framework for building APIs with Python. It is designed to make it easy to build APIs quickly and efficiently while providing features like automatic validation, serialization, and documentation of your API, making it a popular choice for building web services and microservices.

In this FastAPI tutorial, we will create a grocery list app using FastAPI. By the end of the tutorial, you will understand how to work with FastAPI in the Visual Studio Code terminal, editor, and debugger. This tutorial is not a FastAPI deep dive. For that, you can refer to the official FastAPI documentation.

If this is your first time using Python, we recommend you to start with our Python tutorial to get familiar with the language and VS Code's Python support. This tutorial is more suited for those who are already familiar with Python and want to learn how to work with FastAPI in VS Code.

The completed code project from this FastAPI tutorial can be found on GitHub: python-sample-vscode-fastapi-tutorial.

If you have any problems, you can search for answers or ask a question on the Python extension Discussions Q&A.

Set up the project

There are different ways you can set up your project for this tutorial. We will cover how you can set it up in GitHub Codespaces and in VS Code on your local machine.

GitHub Codespaces

You can set up this project to develop in GitHub Codespaces, where you can code, debug, and run your app remotely in a codespace. A codespace provides a fully configured development environment hosted in the cloud, eliminating the need for local setup. This environment includes your project's dependencies, tools, and extensions, ensuring a consistent and reproducible development experience. It streamlines collaboration by providing real-time editing, integrated version control, and easy access to debugging and testing tools, all while maintaining the security and reliability of your project.

Note: All GitHub.com accounts have a monthly quota of free use of GitHub Codespaces included in the Free or Pro plan. For more information, go to About billing for GitHub Codespaces.

To set up a codespace for this tutorial, navigate to this project's GitHub repository. This codespace contains all the necessary configurations and dependencies to quickly get started with FastAPI development.

For this tutorial, select the dictionarybased branch:

Then, select Code > Codespaces > Create Codespace on <dictionarybased> branch to create and open a codespace for your project.

Once you're done, you can continue with the Replace the database section below.

Locally in VS Code

To successfully complete this tutorial in VS Code, you first need to set up your Python development environment. Specifically, this tutorial requires:

In this section, we will create a folder to open as a workspace in VS Code, set up a Python virtual environment, and install the project's dependencies.

  1. In your file system, create a project folder for this tutorial, such as groceries-plugin.

  2. Open this new folder in VS Code (File > Open Folder…).

  3. When the Workspace Trust prompt shows up, select Yes, I trust the authors to allow the workspace to access necessary resources and extensions. You can learn more about Workspace Trust in the documentation.

Now, let's create a requirements.txt file that lists the dependencies we wish to install for the application. The requirements.txt file is a common practice in Python development, used to specify the libraries that your project relies on and their versions. This file helps ensure that anyone working on the project can recreate a similar development environment, making it a convenient component for maintaining consistency.

We will install FastAPI for creating the app, uvicorn to work as the server, and Redis and type-redis for handling data storage and interacting with a Redis database.

  1. Create a new file in VS Code (File > New Text File or ⌘N (Windows, Linux Ctrl+N)).

  2. Add the following content to it:

    from fastapi import FastAPI app = FastAPI() @app.get("/") def root(): return {"message": "Hello World"}
  3. Run the code by starting up the debugger (F5).

  4. From the dropdown menu, select the FastAPI configuration option from the list:

    This automatically creates a debug configuration that invokes uvicorn to start the application server through the debugger and allows you to step through the source code to inspect its behavior. You should see something like the following in the terminal:

    Tip: In the case where your default port is already in use, stop the debugger and open the Command Palette (⇧⌘P (Windows, Linux Ctrl+Shift+P)), search for Debug: Add Configuration, select Python Debugger, and then FastAPI. This will create a custom config file in .vscode/launch.json that you can edit. Add the following to "args":[] to set a custom port: "--port=5000". Save the file, and restart the debugger using (F5).

  5. Ctrl+Click the http://127.0.0.1:8000/ URL in the terminal to open your default browser to that address:

    Congratulations! Your FastAPI app is up and running!

  6. Stop the debugger by using the Stop button in the debug toolbar, or through ⇧F5 (Windows, Linux Shift+F5).

Create a model for grocery list items

Now that we have the FastAPI app working, we can define our grocery list items by using Pydantic, a data validation and parsing library that integrates seamlessly with FastAPI. Pydantic lets you define data models using Python classes with type hints for automatic validation and parsing of incoming data (called "payloads") in API requests.

Let's create a model for our grocery list items. We will use the ItemPayload model to define the data structure of the items to add to the grocery list. This model will have three fields: item_id, item_name, and quantity.

  1. Create a new Python file with File > New File… and then select Python File.

  2. Add the following lines to the file, and then save it in the groceries-plugin folder as models.py (⇧⌘S (Windows, Linux Ctrl+Shift+S)):

    from fastapi import FastAPI, HTTPException from models import ItemPayload
  3. Now add the following line right below app = FastAPI():

    # Route to add an item @app.post("/items/{item_name}/{quantity}") def add_item(item_name: str, quantity: int): if quantity <= 0: raise HTTPException(status_code=400, detail="Quantity must be greater than 0.") # if item already exists, we'll just add the quantity. # get all item names items_ids = {item.item_name: item.item_id if item.item_id is not None else 0 for item in grocery_list.values()} if item_name in items_ids.keys(): # get index of item_name in item_ids, which is the item_id item_id = items_ids[item_name] grocery_list[item_id].quantity += quantity # otherwise, create a new item else: # generate an ID for the item based on the highest ID in the grocery_list item_id = max(grocery_list.keys()) + 1 if grocery_list else 0 grocery_list[item_id] = ItemPayload( item_id=item_id, item_name=item_name, quantity=quantity ) return {"item": grocery_list[item_id]}

    If you have enabled type hints in the previous section, you might notice Pylance adds inlay hints with the function return type, as well as the types for item_ids and item_id. You can optionally double-click on each suggestion to insert them into the code:

    Now let's check if this route is working as expected. The fastest way to do so is to use both VS Code's debugger as well as FastAPI's /docs endpoint, which provides information about all the available API routes and lets you interact with the API to explore their parameters and responses. This documentation is generated dynamically based on the metadata and type hints defined in the FastAPI application.

  4. Add a breakpoint next to the if quantity <= 0 statement, by clicking on the left margin of the line number (or F9). The debugger will stop prior to the execution of that line, so you can inspect the code line by line.

  5. Start the debugger (F5), and then navigate to http://127.0.0.1:8000/docs in the browser.

    There should be a Swagger interface with the two endpoints available in the app: /items and root (/).

  6. Select the down arrow next to the /items route to expand it, and then the Try it out button that appears on the right side.

  7. Add a grocery list item by passing a string to the item_name field and a number to quantity. For example, you could provide apple as the item_name and 2 as the quantity.

  8. Select Execute.

  9. Open VS Code again and notice the debugger has stopped at the breakpoint you set earlier.

    On the left side, all local and global variables defined at this point are displayed in the Variables window, under the Run and Debug view. In our example, item_name is set to 'apple' and quantity is set to 2 under the locals variable view, as well as an empty grocery_list dictionary under the globals variable view.

    Now let's use VS Code's Debug Console to do some exploration.

  10. Select the quantity <= 0 statement, right-click on the editor and select Evaluate in Debug Console:

    This opens the Debug Console and runs the selected expression. As expected in our example, the expression evaluates to False.

    The Debug Console can be a powerful tool to quickly test expressions and better understand the state of your code at the time of a breakpoint. You can also use it to run arbitrary code, such as calling functions or printing variables. You can learn more about Python debugging in VS Code in the Python tutorial.

    You can now continue the execution of the code by selecting Continue in the Debug view tool bar, or by pressing F5.

    Finally, let's add the remaining routes for the application so we can list all items or specific items, as well as remove them from our grocery list. You can leave the debugger running as it will automatically reload the application when you save the changes you make in the next step.

  11. Replace the content in main.py with the code below:

    "postCreateCommand": "pip3 install --user -r requirements.txt",

    You can learn about postCreateCommand and more lifecycle scripts in the Development Containers Specification.

    Now we will use the customizations property to add the VS Code extensions we want installed in the container.

  12. Add the following setting to devcontainer.json:

    redis_client = redis.StrictRedis(host='0.0.0.0', port=6379, db=0, decode_responses=True)

    Pylance will display an error message because Redis hasn't been imported yet.

  13. Put the cursor on "redis" in the editor, and click on the displayed light bulb (or ⌘. (Windows, Linux Ctrl+.)). Then select Add 'import redis'.

    Tip: You can set up Pylance to automatically add imports by looking for the Auto Import Completions setting in the Settings editor (⌘, (Windows, Linux Ctrl+,)) and enabling it.

    We now have a Redis client object that connects to a Redis server running on the local host (host="0.0.0.0") and listening on port 6379 (port=6379). The db parameter specifies the Redis database to use. Redis supports multiple databases, and in this code we're going to use database 0, which is the default database. We're also passing decode_responses=True for the responses to be decoded as strings (instead of bytes).

    Let's do some more replacements in the first route add_item. Instead of looking at all the keys from the dictionary to find the item name that has been provided, we can fetch that information directly from a Redis hash.

    We'll assume that the item_name_to_id hash already exists, mapping item names to their IDs (don't worry, we'll add this code shortly!). We can then get the ID of the item name we're receiving in the request by invoking the hget method from Redis, which will return the item ID if the requested name already exists in the hash, or None if it doesn't.

  14. Delete the line with the content below:

    item_id = redis_client.hget("item_name_to_id", item_name)

    Notice that Pylance raises a problem with this change. This is because the hget method returns either str, or None (if the item doesn't exist). However, the lines below the code that we haven't replaced yet expect item_id to be of type int. Let's address this warning by renaming the item_id symbol.

  15. Rename item_id to item_id_str.

  16. If you have inlay hints enabled, Pylance should show a variable type hint next to item_id_str. You can optionally double-click to accept it:

  17. If the item doesn't exist, then item_id_str is None. So now we can delete the line with the following content:

    if item_id_str is not None:

    Now that we have the item ID as a string, we need to convert it to an int and update the quantity for the item. Currently, our Redis hash only maps item names to their IDs. To also map item IDs to their names and quantities, we will create a separate Redis hash for each item, using "item_id:{item_id}" as our hash name to make retrieval by ID easier. We'll also add item_name and quantity fields for each of these hashes.

  18. Delete the code within the if block:

    item_id = int(item_id_str) redis_client.hincrby(f"item_id:{item_id}", "quantity", quantity)

    We now only need to replace the code for when the item does not exist, when item_id_str is None. In this case, we generate a new item_id, create a new Redis hash for the item, and then add the provided item name and quantity.

    To generate a new item_id, let's use the incr method from Redis, passing a new hash called "item_ids". This hash is used to store the last generated ID, so we can increment it each time we create a new item, ensuring that they all have a unique ID.

  19. Delete the line with the following content:

    item_id: int = redis_client.incr("item_ids")

    When this incr call is run for the first time with the item_ids key, Redis creates the key and maps it to the value 1. Then, each subsequent time it's run, it increments the stored value by 1.

    Now we will add the item to the Redis hash, using the hset method and by providing a mapping for the fields (item_id, item_name, and quantity), and the values (the item's newly created ID, and its provided name and quantity).

  20. Delete the line with the following content:

    redis_client.hset( f"item_id:{item_id}", mapping={ "item_id": item_id, "item_name": item_name, "quantity": quantity, })

    Now we only need to map the newly created ID to the item name by setting the hash we referenced in the beginning, item_name_to_id.

  21. Add this line to the end of the route, inside the else block:

    return {"item": grocery_list[item_id]}

    And replace it with:

    import redis from fastapi import FastAPI, HTTPException from models import ItemPayload app = FastAPI() redis_client = redis.StrictRedis(host="0.0.0.0", port=6379, db=0, decode_responses=True) # Route to add an item @app.post("/items/{item_name}/{quantity}") def add_item(item_name: str, quantity: int) -> dict[str, ItemPayload]: if quantity <= 0: raise HTTPException(status_code=400, detail="Quantity must be greater than 0.") # Check if item already exists item_id_str: str | None = redis_client.hget("item_name_to_id", item_name) if item_id_str is not None: item_id = int(item_id_str) redis_client.hincrby(f"item_id:{item_id}", "quantity", quantity) else: # Generate an ID for the item item_id: int = redis_client.incr("item_ids") redis_client.hset( f"item_id:{item_id}", mapping={ "item_id": item_id, "item_name": item_name, "quantity": quantity, }, ) # Create a set so we can search by name too redis_client.hset("item_name_to_id", item_name, item_id) return { "item": ItemPayload(item_id=item_id, item_name=item_name, quantity=quantity) } # Route to list a specific item by ID but using Redis @app.get("/items/{item_id}") def list_item(item_id: int) -> dict[str, dict[str, str]]: if not redis_client.hexists(f"item_id:{item_id}", "item_id"): raise HTTPException(status_code=404, detail="Item not found.") else: return {"item": redis_client.hgetall(f"item_id:{item_id}")} @app.get("/items") def list_items() -> dict[str, list[ItemPayload]]: items: list[ItemPayload] = [] stored_items: dict[str, str] = redis_client.hgetall("item_name_to_id") for name, id_str in stored_items.items(): item_id: int = int(id_str) item_name_str: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") if item_name_str is not None: item_name: str = item_name_str else: continue # skip this item if it has no name item_quantity_str: str | None = redis_client.hget( f"item_id:{item_id}", "quantity" ) if item_quantity_str is not None: item_quantity: int = int(item_quantity_str) else: item_quantity = 0 items.append( ItemPayload(item_id=item_id, item_name=item_name, quantity=item_quantity) ) return {"items": items} # Route to delete a specific item by ID but using Redis @app.delete("/items/{item_id}") def delete_item(item_id: int) -> dict[str, str]: if not redis_client.hexists(f"item_id:{item_id}", "item_id"): raise HTTPException(status_code=404, detail="Item not found.") else: item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") redis_client.hdel("item_name_to_id", f"{item_name}") redis_client.delete(f"item_id:{item_id}") return {"result": "Item deleted."} # Route to remove some quantity of a specific item by ID but using Redis @app.delete("/items/{item_id}/{quantity}") def remove_quantity(item_id: int, quantity: int) -> dict[str, str]: if not redis_client.hexists(f"item_id:{item_id}", "item_id"): raise HTTPException(status_code=404, detail="Item not found.") item_quantity: str | None = redis_client.hget(f"item_id:{item_id}", "quantity") # if quantity to be removed is higher or equal to item's quantity, delete the item if item_quantity is None: existing_quantity: int = 0 else: existing_quantity: int = int(item_quantity) if existing_quantity <= quantity: item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") redis_client.hdel("item_name_to_id", f"{item_name}") redis_client.delete(f"item_id:{item_id}") return {"result": "Item deleted."} else: redis_client.hincrby(f"item_id:{item_id}", "quantity", -quantity) return {"result": f"{quantity} items removed."}
  22. Re-run the debugger to test this application by interacting with the /docs route. You can stop the debugger once you're done.

Congrats! You now have a working FastAPI application with routes to add, list, and delete items from a grocery list, and the data is persisted in a Redis database.

Optional: Set up database deletion

With the data now persisted by Redis, you might want to create a script to erase all testing data. To do so, create a new file called flushdb.py with the following content: