Day 20 of 80

Building a Simple API Server

Phase 2: Software Foundations

What You'll Build Today

Welcome to Day 20! Today is a massive milestone. For the last 19 days, you have been writing Python scripts—code that runs on your computer, does a job, and then stops.

But think about the apps you use every day. Instagram, Google Maps, or ChatGPT aren't just scripts running on a laptop; they are Servers. They stay awake, waiting for requests, and send back answers.

Today, you will transform your Python skills into a web service. You are going to build your first API (Application Programming Interface).

You will build a "Math & Utility Server" that includes:

* FastAPI: The modern engine that powers high-performance web servers (used by Netflix, Uber, and Microsoft).

* Endpoints (GET and POST): The specific "doors" users knock on to get data or send instructions.

* Data Validation: Automatic checking to ensure users send numbers when you ask for numbers (so your code doesn't crash).

* Automatic Documentation: A professional website that documents your code for you, automatically.

By the end of this session, your code won't just run locally; it will be ready to talk to the rest of the world.

---

The Problem

Let's imagine you wrote a brilliant Python function that predicts housing prices based on your previous days' work. Your friend, who is building a mobile app for real estate, wants to use your logic.

How do you give it to them?

  • Email the script? Your friend builds mobile apps; they don't know how to run Python scripts. They just want an answer.
  • Rewrite it in their language? That takes too much time.
  • Build a server using standard tools?
  • Let's try to build a server using Python's built-in "socket" library, which is the raw way computers talk to each other.

    Look at this code. Do not run this. Just look at the pain involved in simply trying to say "Hello" to a web browser.

    # The "Old School" Painful Way
    

    import socket

    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    server.bind(('0.0.0.0', 8000))

    server.listen(5)

    print("Listening on port 8000...")

    while True:

    # 1. Accept the connection

    client, addr = server.accept()

    # 2. Receive the raw bytes (0s and 1s)

    request = client.recv(1024).decode('utf-8')

    # 3. Manually parse the text to find what the user wants # We have to handle strings like "GET /hello HTTP/1.1" manually!

    headers = request.split('\n')

    first_line = headers[0].split()

    if len(first_line) > 1 and first_line[1] == '/hello':

    # 4. Manually construct the HTTP response headers

    response = 'HTTP/1.0 200 OK\n\nHello World'

    else:

    response = 'HTTP/1.0 404 NOT FOUND\n\nNot Found'

    # 5. Send bytes back and close

    client.sendall(response.encode('utf-8'))

    client.close()

    Why this is painful:

    * String Parsing: You have to manually chop up text to figure out what the user wants.

    * Byte Handling: You have to encode and decode text constantly.

    * No Validation: If the user sends bad data, you have to write dozens of if statements to catch it.

    * Concurrency: If two people hit this server at once, it might freeze.

    There has to be a better way. We want to write a Python function and simply say, "Hey Internet, this function is available."

    Enter FastAPI.

    ---

    Let's Build It

    We are going to use a library called FastAPI. It handles all the messy socket connections, byte decoding, and error handling for us. We just write functions.

    Step 1: Installation and Setup

    First, we need to install FastAPI and a tool called uvicorn.

    * FastAPI: The framework used to build the API.

    * Uvicorn: The server implementation that runs your FastAPI application (think of it as the engine).

    Open your terminal/command prompt and run:

    ``bash

    pip install fastapi uvicorn

    
    

    Now, create a file named main.py.

    Step 2: The "Hello World" of APIs

    In main.py, we will create an instance of the app and one "endpoint." An endpoint is like a specific URL (like /home or /profile).

    from fastapi import FastAPI
    
    # Create the app instance
    

    app = FastAPI()

    # Create a route (endpoint) # This decorator tells FastAPI: "When someone visits the root URL (/), run this function."

    @app.get("/")

    def read_root():

    return {"message": "Welcome to my API!"}

    How to run it:

    You cannot run this with the "Run" button in your IDE usually. You need to run the server command in your terminal:

    bash

    uvicorn main:app --reload

    
    

    * main: The name of your file (main.py).

    * app: The variable name inside the file (app = FastAPI()).

    * --reload: Makes the server restart automatically whenever you save code changes (great for development).

    The Result:

    Open your web browser and go to http://127.0.0.1:8000.

    You should see: {"message":"Welcome to my API!"}

    Step 3: Path Parameters

    Sometimes we want the URL itself to contain data, like /items/5 or /user/josh. This is called a Path Parameter.

    Add this to main.py (you don't need to stop the server if you used --reload, just save the file):

    @app.get("/echo/{name}")
    

    def echo_name(name: str):

    return {"greeting": f"Hello, {name}!"}

    Try it:

    Go to http://127.0.0.1:8000/echo/Alice in your browser.

    Result: {"greeting": "Hello, Alice!"}

    FastAPI takes the {name} from the URL and passes it into your function as the argument name.

    Step 4: Query Parameters and Type Conversion

    What if we want to pass configuration options, like /calculate?num1=10&num2=20? These are called Query Parameters.

    If you define function arguments that aren't in the URL path (like {name} was), FastAPI assumes they are query parameters.

    @app.get("/add")
    

    def add_numbers(a: int, b: int):

    return {

    "number_a": a,

    "number_b": b,

    "result": a + b

    }

    Try it:

    Go to http://127.0.0.1:8000/add?a=10&b=50

    Result: {"number_a": 10, "number_b": 50, "result": 60}

    The Magic:

    Try going to http://127.0.0.1:8000/add?a=10&b=hello.

    You will get a clear error message saying b must be an integer. FastAPI did that validation for you automatically because you added the type hint : int.

    Step 5: POST Requests and Pydantic Models

    So far, we have used GET requests. These are for getting data. They send data in the URL, which is not secure for passwords or good for large text blocks.

    To send data to the server (like a form submission), we use POST. To handle the data cleanly, we use Pydantic Models. A Pydantic model defines what the data structure should look like.

    Add this import at the top of your file:

    from pydantic import BaseModel
    

    Now add this code:

    # 1. Define the shape of the data we expect
    

    class Item(BaseModel):

    name: str

    price: float

    is_offer: bool = None # Optional field

    # 2. Create a POST endpoint

    @app.post("/items/")

    def create_item(item: Item):

    # We can access item.name, item.price just like a standard Python object

    calculated_tax = item.price * 0.15

    return {

    "item_name": item.name,

    "price_with_tax": item.price + calculated_tax,

    "on_offer": item.is_offer

    }

    How to test this?

    You can't test POST requests easily in the browser address bar. We need a tool. Luckily, FastAPI builds one for us.

    Step 6: The Automatic Documentation (Swagger UI)

    This is the best part of FastAPI.

  • Keep your server running.
  • Go to http://127.0.0.1:8000/docs.
  • You will see a professional, interactive dashboard listing all your endpoints (/, /echo, /add, /items).

    * Click on the POST /items/ row.

    * Click Try it out.

    * Edit the JSON in the box.

    * Click Execute.

    You will see the server response right there. This "Swagger UI" is how developers test APIs.

    ---

    Now You Try

    It's time to expand your API. Add these three new endpoints to your main.py file.

    1. The String Reverser (GET)

    Create an endpoint /reverse/{word}.

    * It should take a word from the path.

    * It should return a JSON object with the original word and the reversed version.

    Hint:* Python strings can be reversed with
    [::-1]. 2. The Age Validator (GET)

    Create an endpoint /check-age.

    * It should accept a query parameter age (integer).

    * If age is 18 or over, return {"status": "allowed"}.

    * If under 18, return {"status": "blocked"}.

    3. The User Registration (POST)

    Create a Pydantic model called User with username (str) and email (str).

    Create a POST endpoint /register.

    * Accept the User model.

    * Return a message saying {"message": "User [username] registered with email [email]"}.

    ---

    Challenge Project: The Mock Chatbot

    We are preparing for the next phase where we connect to real AI. Today, let's build the structure for a chat endpoint.

    The Goal: Create a
    /chat endpoint that accepts a user's message and returns a "fake" AI response. Requirements:
  • Create a Pydantic model named ChatInput containing:
  • * message (str)

    * model_name (str) - e.g., "gpt-4" or "claude"

  • Create a Pydantic model named ChatOutput containing:
  • * user_message (str)

    * ai_response (str)

  • Create a POST endpoint at /chat.
  • Inside the function:
  • * If message contains the word "hello", the ai_response should be "Hi there! I am a simple bot."

    * If message contains "bye", the response should be "Goodbye!"

    * Otherwise, the response should be "I don't understand, but I'm listening."

  • Return the ChatOutput.
  • Example Input (JSON):
    json

    {

    "message": "hello computer",

    "model_name": "basic-bot"

    }

    
    Example Output (JSON):
    
    json

    {

    "user_message": "hello computer",

    "ai_response": "Hi there! I am a simple bot."

    }

    ` Hints:

    * Remember to import BaseModel.

    * Use if "hello" in input_data.message.lower(): for case-insensitive matching.

    * Test this using the /docs page (Swagger UI).

    ---

    What You Learned

    Today you moved from writing scripts to building Services.

    * FastAPI: You initialized a web server in 3 lines of code.

    * Decorators: You used @app.get and @app.post` to route traffic.

    * JSON: You learned that APIs speak in dictionaries (JSON).

    * Pydantic: You learned how to enforce data rules (Schema) so your server doesn't crash on bad input.

    Why This Matters:

    When we start using OpenAI or HuggingFace in the next few days, you will notice something: They work exactly like the API you just built. You will send them a POST request with a JSON body (the prompt), and they will return a JSON body (the answer). By building your own API today, you now understand how the entire AI ecosystem communicates.

    Phase 2 Complete!

    You have mastered Python basics, data structures, files, and now APIs. You have the software foundations required to be an AI Engineer.

    Tomorrow: We enter Phase 3. We will look at The LLM Landscape—understanding the massive AI models that are changing the world, and how we can choose the right one for our projects.