Building several basic API endpoints offers practical experience with FastAPI. This demonstrates path operations, HTTP methods, and how FastAPI handles requests.We will continue working with the main.py file introduced in the previous section. Remember to run your application using uvicorn main:app --reload in your terminal. The --reload flag is convenient during development as it automatically restarts the server when you save changes to your code.Creating a Basic GET EndpointThe most common HTTP method is GET, used to retrieve data from a specified resource. In FastAPI, you define a GET endpoint using the @app.get() decorator above an async function.Let's modify your main.py to include a root GET endpoint that returns a simple JSON message:# main.py from fastapi import FastAPI app = FastAPI() @app.get("/") async def read_root(): """ This endpoint returns a message. It serves as the root or index of the API. """ return {"message": "To the ML Model API"} Here's a breakdown:@app.get("/"): This decorator tells FastAPI that the function read_root below it is responsible for handling GET requests made to the path / (the root path).async def read_root():: This defines an asynchronous function named read_root. FastAPI supports both synchronous (def) and asynchronous (async def) route handlers. Using async def allows leveraging Python's asyncio for concurrent operations, which we'll discuss more in Chapter 5. For now, know that it's the standard way to define path operation functions in FastAPI.return {"message": "To the ML Model API"}: FastAPI automatically converts this Python dictionary into a JSON response.Save the file. If uvicorn is running with --reload, it should restart automatically. Open your web browser and navigate to http://127.0.0.1:8000/. You should see the JSON response: {"message":"Hello to the ML Model API"}.You can also access the automatically generated interactive API documentation by navigating to http://127.0.0.1:8000/docs. This interface (Swagger UI) allows you to explore and test your API endpoints directly from the browser. Find the / endpoint, expand it, and click "Try it out" then "Execute".Adding Path ParametersOften, you need to retrieve a specific item based on an identifier included in the URL path. These are called path parameters. Let's create an endpoint to retrieve information about a specific item ID.# main.py (add this function below the previous one) from fastapi import FastAPI app = FastAPI() @app.get("/") async def read_root(): return {"message": "To the ML Model API"} @app.get("/items/{item_id}") async def read_item(item_id: int): """ Retrieves an item based on its ID provided in the path. """ # In a real application, you would fetch data based on item_id return {"item_id": item_id, "description": f"Details for item {item_id}"} Important Changes:@app.get("/items/{item_id}"): The path now includes {item_id}. This shows a path parameter named {item_id}.async def read_item(item_id: int):: The function now accepts an argument named item_id. Crucially, we've added a type hint : int. FastAPI uses this hint to:Validate that the value received in the path for item_id can be converted to an integer.Convert the value (which is initially a string from the URL) into an integer.Provide editor support (like autocompletion and type checking).Save the file and test this endpoint. Go to http://127.0.0.1:8000/items/42 in your browser. You should see: {"item_id":42,"description":"Details for item 42"}.Now, try accessing http://127.0.0.1:8000/items/abc. FastAPI automatically intercepts this request before it reaches your function code because "abc" cannot be converted to an integer. It returns a helpful JSON error response indicating the validation failure:{ "detail": [ { "loc": [ "path", "item_id" ], "msg": "value is not a valid integer", "type": "type_error.integer" } ] }This automatic data validation is a major benefit of using FastAPI and type hints.Adding Query ParametersQuery parameters are optional key-value pairs appended to the URL after a question mark (?), often used for filtering, sorting, or pagination. Unlike path parameters, they are not part of the path structure itself.Let's modify the /items/ endpoint (or create a new one) to accept optional skip and limit query parameters for pagination.# main.py (modify or add as needed) from fastapi import FastAPI from typing import Optional # Import Optional app = FastAPI() # ... (previous endpoints) ... @app.get("/users/") async def read_users(skip: int = 0, limit: int = 10): """ Retrieves a list of users, with optional pagination. Uses query parameters 'skip' and 'limit'. """ # Simulate fetching users from a data source all_users = [{"user_id": i, "name": f"User {i}"} for i in range(100)] return all_users[skip : skip + limit] @app.get("/items/{item_id}") async def read_item(item_id: int, q: Optional[str] = None): """ Retrieves an item by ID, with an optional query parameter 'q'. """ response = {"item_id": item_id, "description": f"Details for item {item_id}"} if q: response.update({"query_param": q}) return response Explanation:read_users(skip: int = 0, limit: int = 10):Parameters skip and limit are defined as function arguments without being in the path string (/users/). FastAPI recognizes them as query parameters.Type hints (: int) provide validation and conversion.Default values (= 0, = 10) make these parameters optional. If the client doesn't provide them in the URL, these defaults are used.read_item(item_id: int, q: Optional[str] = None):We added a new parameter q.Optional[str] indicates that q can be a string or None.= None sets its default value to None, making it optional.Test these:http://127.0.0.1:8000/users/: Uses defaults (skip=0, limit=10).http://127.0.0.1:8000/users/?skip=10&limit=5: Uses provided values.http://127.0.0.1:8000/items/5: No query parameter q.http://127.0.0.1:8000/items/5?q=somequery: Includes the query parameter q.Again, try providing invalid types, like skip=abc, to see the automatic validation errors.Creating a Basic POST EndpointThe POST method is used to send data to the server to create or update a resource. Defining a POST endpoint is similar to GET, using the @app.post() decorator.Handling the body of a POST request (the data being sent) typically involves Pydantic models, which we'll cover in detail in Chapter 2. For this practice, let's create a simple POST endpoint that just acknowledges the request without processing complex input data yet.# main.py (add this function) from fastapi import FastAPI from typing import Optional app = FastAPI() # ... (all previous endpoints) ... @app.post("/items/") async def create_item(): """ Placeholder endpoint for creating a new item. Currently just returns a confirmation message. Data reception will be handled in Chapter 2. """ # In Chapter 2, we'll learn how to receive data here return {"message": "Item received (but not processed yet)"} Since browsers typically only make GET requests directly via the address bar, you'll need a different tool to test this POST endpoint easily:FastAPI Docs: Navigate to http://127.0.0.1:8000/docs, find the POST /items/ endpoint, expand it, click "Try it out", and then "Execute". You should see the success response.curl (Command Line): Open your terminal and run:curl -X POST http://127.0.0.1:8000/items/ -H "accept: application/json"The -X POST flag specifies the HTTP method. The -H adds a header indicating we accept JSON responses. You should receive {"message":"Item received (but not processed yet)"}.Complete Code ExampleHere is the complete main.py incorporating all the endpoints we created:# main.py from fastapi import FastAPI from typing import Optional app = FastAPI(title="Simple API Practice", version="0.1.0") @app.get("/") async def read_root(): """ Root endpoint returning a message. """ return {"message": "To the ML Model API"} @app.get("/users/") async def read_users(skip: int = 0, limit: int = 10): """ Retrieves a list of users, with optional pagination. Uses query parameters 'skip' and 'limit'. """ all_users = [{"user_id": i, "name": f"User {i}"} for i in range(100)] return all_users[skip : skip + limit] @app.get("/items/{item_id}") async def read_item(item_id: int, q: Optional[str] = None): """ Retrieves an item by ID (path parameter), with an optional query parameter 'q'. """ response = {"item_id": item_id, "description": f"Details for item {item_id}"} if q: response.update({"query_param": q}) return response @app.post("/items/") async def create_item(): """ Placeholder endpoint for creating a new item via POST. """ return {"message": "Item received (but not processed yet)"} # To run: uvicorn main:app --reload You have now successfully created endpoints using different HTTP methods (GET, POST) and learned how to handle path and query parameters with automatic validation provided by FastAPI's type hinting system. You also saw the utility of the interactive API documentation. In the next chapter, we will significantly enhance our ability to handle data, especially complex request bodies for POST requests, using Pydantic models.