FastAPI

Introduction to FastAPI

  • Modern, high-performance web framework for building APIs with Python.
  • External Library. Installed as: pip install fastapi[standard]
  • Start Server as: fastapi dev main.py
  • Terminologies:
    1. Web Application: A software application that runs on a web server and is accessed through a web browser or API client.
    2. Web Framework: A software framework that provides tools and libraries to build web applications and APIs. E.g: FastAPI, Django, Flask etc.
    3. API (Application Programming Interface): A set of rules that allows different software applications to communicate with each other.

Typical API Terminologies

  • Endpoint: A URL route that performs a specific function (e.g. /students).
  • HTTP Methods: Actions performed on endpoints. GET, POST, PUT, DELETE.
  • Request Body: Data sent by the client to the server. JSON payload.
  • Response: Data sent back to client after processing a request by server.
  • Status Codes: Codes indicating result of a request. Eg. 200, 404 Not Found.
  • Query Parameters: Key-value pairs in the URL used for filtering or additional info. E.g: /students?age=20
  • Path Parameters: Dynamic segments in the URL that capture values. E.g: /students/{student_id}

Basic FastAPI Example

  • from fastapi import FastAPI app = FastAPI() @app.get("/") def read_root(): return {"Hello": "World"} # Run with: fastapi dev main.py

Helper Functions for Database Interaction

  • Define helper functions to interact with the database.
  • This abstracts away database logic from endpoint definitions.
  • Example:
    1. import sqlite3 con = sqlite3.connect("students.db") cur = con.cursor() def run_query(sql_query, type="SELECT"): cursor = con.cursor() cursor.execute(sql_query) if type == "SELECT": return cursor.fetchall()

Creating GET Endpoint

  • Used to retrieve data from the server.
  • Data is sent back in the response body.
  • Example:
    1. @app.get("/students") def get_students_endpoint(): students = run_query("SELECT * FROM students") return {"students": students}
    2. @app.get("/students/{student_id}") def get_student(student_id: int): student = run_query(f"SELECT * FROM students WHERE id={student_id}") if student: return {"student": student[0]} else: return {"error": "Student not found"}
    3. @app.get("/students/search/") def search_students(age: int = None, name: str = None): query = "SELECT * FROM students WHERE 1=1" if age: query = query + f" AND age={age}" if name: query = query + f" AND name LIKE '%{name}%'" students = run_query(query) return {"students": students}

Using GET Endpoint with Query Parameters

  • Query parameters are used to filter information in GET requests.
  • They are included in the URL after ? and are in the form of key-value pairs.
  • Example:
    1. import requests url = "http://localhost:8000/students/search/" params = {"age": 20, "name": "John"} response = requests.get(url, params=params) # params is optional data = response.json() print(data)

Creating POST Endpoint

  • Used to submit data to the server.
  • Data is sent in the request body.
  • Example:
    1. @app.post("/students") def add_student(student: dict): sql_query = f"""INSERT INTO students (name, age) VALUES ('{student['name']}', {student['age']})""" run_query(sql_query, type="INSERT") return {"message": "Student added successfully"}
    Testing POST Endpoint with Python Client
    1. import requests url = "http://localhost:8000/students" student_data = {"name": "Alice", "age": 22} response = requests.post(url, json=student_data) data = response.json() print(data)
    Validating with GET
    1. params = {"name": "Alice", "age": 22} response = requests.get("http://localhost:8000/students", params=params) data = response.json() print(data)

Creating PUT Endpoint

  • Used to update existing data on the server.
  • Data is sent in the request body.
  • Example:
    1. @app.put("/students/{student_id}") def update_student(student_id: int, student: dict): sql_query = f"""UPDATE students SET name='{student['name']}', age={student['age']} WHERE id={student_id}""" run_query(sql_query, type="UPDATE") return {"message": "Student updated successfully"}
    Testing PUT Endpoint with Python Client
    1. import requests url = "http://localhost:8000/students/1" updated_data = {"name": "Alice Smith", "age": 23} response = requests.put(url, json=updated_data) data = response.json() print(data)
    Validating with GET
    1. response = requests.get("http://localhost:8000/students/1") data = response.json() print(data)

Creating DELETE Endpoint

  • Used to delete existing data on the server.
  • Example:
    1. @app.delete("/students/{student_id}") def delete_student(student_id: int): sql_query = f"""DELETE FROM students WHERE id={student_id}""" run_query(sql_query, type="DELETE") return {"message": "Student deleted successfully"}
    Testing DELETE Endpoint with Python Client
    1. import requests url = "http://localhost:8000/students/1" response = requests.delete(url) data = response.json() print(data)
    Validating with GET
    1. response = requests.get("http://localhost:8000/students/1") data = response.json() print(data)

HTTP Status Codes

  • Indicate the result of a client's request to the server.
  • Specified in the response sent by the server.
  • Range: 100 - 599, first digit indicates category of response. - 1xx: Informational - 2xx: Success - 3xx: Redirection - 4xx: Client Error - 5xx: Server Error

Common Status Codes and Their Meanings

Common HTTP Status Codes:
Status CodeMeaningUse Case
200 OKRequest was successful.General success response.
201 CreatedResource was successfully created.Successful POST request.
204 No ContentRequest was successful but no content to return.Successful DELETE request.
400 Bad RequestClient sent an invalid request.Malformed request syntax or invalid data.
404 Not FoundRequested resource does not exist.Resource not found at the specified URL.
500 Internal Server ErrorServer encountered an error processing the request.Unexpected server error.
Common HTTP status codes used in API responses.

Concept of Asynchronous Endpoint

  • Allow the server to handle multiple requests concurrently without blocking.
  • Implemented using async def and await in FastAPI.
  • Useful for I/O-bound operations like database queries or external API calls.
  • Analogy: Chef boiling pasta while preparing sauce, instead of waiting.
Async vs Sync API handling
Illustration of how asynchronous endpoints allow for concurrent request handling compared to synchronous endpoints.

Demo Code for Synchronous Endpoint

  • def get_data_from_site(name): print(f"Start fetching {name}") time.sleep(3) # simulating network delay print(f"Finished fetching {name}") return f"Data from {name}" @app.get("/sync") def sync_demo(): result1 = get_data_from_site("Site A") result2 = get_data_from_site("Site B") return {"results": [result1, result2]}

Demo Code for Asynchronous Endpoint

  • import asyncio async def get_data_from_site(name): print(f"Start fetching {name}") await asyncio.sleep(3) # simulating network delay print(f"Finished fetching {name}") return f"Data from {name}" @app.get("/async") async def async_endpoint(): task1 = asyncio.create_task(get_data_from_site("Site A")) task2 = asyncio.create_task(get_data_from_site("Site B")) result1 = await task1 result2 = await task2 return {"results": [result1, result2]}