DEV Community

Wisdom Ehirim
Wisdom Ehirim

Posted on

Roast your LinkedIn Bio with FastAPI: A Fun Intro Guide to Building APIs that make you smile

Alright, here's a disclaimer... By the end of this article, you'll probably be in love with FastAPI. Now, this isn't sponsored (actually, I wish it were), but I wrote this because I recently used FastAPI for a project and loved every bit of it. I also found out that there weren't many engaging resources available for FastAPI.

They say project-based learning is the best. So for this article, we'll do something fun while still learning about APIs.

Introduction: Boring LinkedIn Bios

I'm a LinkedIn fan, but most bios sound corporate and sometimes like lies.

"Passionate and results-oriented professional leveraging synergies in cross-functional teams…"

LinkedIn lies meme

So, what if we built an API that turns your name, role, and tech stack into something funny, and maybe a little savage?

Welcome to RoastBio, a FastAPI-powered project that helps us learn FastAPI while roasting boring bios.

By the end of this article, you'll know:

  • Why FastAPI feels different from Flask and Django
  • How type hints, Pydantic, and async make your life easier
  • How to build a real API that serves spicy LinkedIn roasts — and a Gemini AI Integration

FastAPI vs Flask vs Django

Flask:

"I'm simple. You bring the tools; I'll bring the routes."

Django:

"I built a city for you: ORM, templates, and 100 rules."

FastAPI:

"I validate your data, document your API, and go async — all in plain Python."

FastAPI vs Flask vs Django comparison

Here's the difference:

  • Flask: It's minimal and great if you just want to set things up quickly, but you hand-roll validation, serialization, and docs
  • Django: It's great for full-stack apps and gives many features, but heavy if you just want an API. No beef with Django
  • FastAPI: API-first, powered by ASGI (Asynchronous Server Gateway Interface). Type hints aren't optional here because they're the backbone of data validation, auto-docs, and clean code. I'm getting the hang of it.

Step 1: Project Setup

Create your folder:

mkdir linkedin-roastbio
cd linkedin-roastbio
Enter fullscreen mode Exit fullscreen mode

Set up a virtual environment:

python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate.bat
Enter fullscreen mode Exit fullscreen mode

Install dependencies:

pip install fastapi uvicorn httpx python-dotenv
Enter fullscreen mode Exit fullscreen mode

Note:

"Yes, you actually need a virtual environment. You'll have issues deploying the project because package conflicts are much worse than you think."

Always use virtual environment meme

Step 2: Your First FastAPI App

Create main.py:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def read_root():
    return {"message": "Welcome to RoastBio — where LinkedIn bios get spicy!"}
Enter fullscreen mode Exit fullscreen mode

Run:

uvicorn main:app --reload
Enter fullscreen mode Exit fullscreen mode

Open http://127.0.0.1:8000/docs — and boom: interactive Swagger docs for your API!

Behind the scenes:

  • ASGI (via Uvicorn) handles concurrent requests
  • async def: Non-blocking I/O. While your API waits (for a DB or AI response), it can serve other requests
  • Docs auto-generated using type hints and Pydantic models

FastAPI Swagger documentation

But wait... Why do Type Hints Matter?

FastAPI leans on Python's type hints — those str, int, List[str] annotations you might have ignored.

FastAPI type hints explanation

Example:

async def greet_user(name: str) -> str:
    return f"Hello, {name}!"
Enter fullscreen mode Exit fullscreen mode
  • At runtime: FastAPI checks that name is a str
  • Docs: Swagger knows what to expect and builds clear API docs
  • Editor: Autocomplete and type checking get smarter

Think of type hints as contracts:

"Dear API, I promise to send you a str. If I lie, you throw a 422 and roast me."

Step 3: Validating Data with Pydantic

Let's validate incoming bio data with Pydantic. Create schemas.py:

from pydantic import BaseModel, Field, validator
from typing import List, Optional

class BioRequest(BaseModel):
    name: str = Field(..., min_length=2, description="Your full name")
    role: str = Field(..., min_length=2, description="Your professional title")
    tech_stack: Optional[List[str]] = Field(None, description="Technologies you work with")

    @validator("name")
    def validate_name(cls, value):
        if value.lower() in ["user", "admin", "anonymous"]:
            raise ValueError("Pick a name that's not as boring as 'Admin'.")
        return value.title()

class BioResponse(BaseModel):
    bio: str
    roast_level: str = "standard"
Enter fullscreen mode Exit fullscreen mode

What's happening in the code?

  • ... means that the field is required and is not optional
  • BaseModel: Automatically validates and serializes (converting JSON to Python and vice-versa) your data, preparing it for the API and documentation. The validation prevents errors and handles error management
  • Field: Adds rules (min_length) and docs. If this were Django, what would it be called?
  • Validators: Custom rules that run before your endpoint logic

Bad data? FastAPI replies with:

{
  "detail": [
    {"loc": ["body", "name"], "msg": "Pick a name that's not as boring as 'Admin'."}
  ]
}
Enter fullscreen mode Exit fullscreen mode

"Yes, even your name can get rejected. Deal with it."

FastAPI Pydantic validation in action

Step 4: The Roast Engine

Create generator_logic.py:

import random
from typing import Optional, List

def generate_bio(name: str, role: str, tech_stack: Optional[List[str]] = None) -> str:
    easter_eggs = {
        "Null": "Name is 'Null'? Did your parents forget to call the constructor?",
        "Test User": "Living proof that QA never ends.",
        "root": "All hail the sysadmin overlord. May sudo be with you."
    }

    if name in easter_eggs:
        return f"{name} — {role}. {easter_eggs[name]}"

    roasts = [
        "ships features faster than you can say 'merge conflict'",
        "believes Docker errors are modern riddles",
        "debugs Kubernetes clusters like a digital exorcist"
    ]

    tech_roasts = {
        "Python": "Indentation errors build character.",
        "JavaScript": "May your console.logs be ever in your favor.",
        "FastAPI": "Built APIs so fast even requests can't keep up."
    }

    tech_comment = " ".join(
        tech_roasts.get(tech, f"Probably good at {tech}… or at least Googling it.") 
        for tech in tech_stack or []
    )

    return f"{name} — {role}. {random.choice(roasts)}. {tech_comment}".strip()
Enter fullscreen mode Exit fullscreen mode

Step 5: Building the Endpoint

Update main.py:

from fastapi import FastAPI, Query
from schemas import BioRequest, BioResponse
from generator_logic import generate_bio

app = FastAPI(title="RoastBio API", description="Roast your LinkedIn bio with FastAPI")

@app.post("/generate-bio", response_model=BioResponse)
async def create_bio(request: BioRequest, premium: bool = Query(False)):
    bio = generate_bio(request.name, request.role, request.tech_stack)
    return BioResponse(bio=bio)
Enter fullscreen mode Exit fullscreen mode

Test it using Swagger Docs:

Request:

{
  "name": "Ada Lovelace",
  "role": "Backend Developer",
  "tech_stack": ["Python", "FastAPI"]
}
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "bio": "Ada Lovelace — Backend Developer. ships features faster than you can say 'merge conflict'. Indentation errors build character. Built APIs so fast even requests can't keep up.",
  "roast_level": "standard"
}
Enter fullscreen mode Exit fullscreen mode

Swagger return

Testing the FastAPI endpoint in Swagger UI

Step 6: How about we add AI (just for AI-trend's sake)

AI integration with Gemini

For AI-powered roasts, create ai_roaster.py. First, visit the Gemini AI Studio and obtain your API key. Create a .env file and store your secret key:

.env file:

GEMINI_API_KEY=your_actual_api_key_here
Enter fullscreen mode Exit fullscreen mode

ai_roaster.py:

import os
import httpx
from typing import Optional, List
from dotenv import load_dotenv

load_dotenv()

GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")

async def generate_ai_roast(name: str, role: str, tech_stack: Optional[List[str]] = None) -> str:
    if not GEMINI_API_KEY:
        return "AI roast unavailable — you're safe for now."

    tech_list = ', '.join(tech_stack) if tech_stack else 'None'
    prompt = (
        f"Write a witty, roasty LinkedIn bio for {name}, a {role}. "
        f"Tech stack: {tech_list}. "
        "Make it clever, professional, and full of inside tech jokes. Keep it under 2 sentences."
    )

    try:
        async with httpx.AsyncClient(timeout=30.0) as client:
            response = await client.post(
                "https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent",
                headers={
                    "Content-Type": "application/json",
                    "x-goog-api-key": GEMINI_API_KEY
                },
                json={
                    "contents": [{"parts": [{"text": prompt}]}],
                    "generationConfig": {
                        "temperature": 0.9,
                        "maxOutputTokens": 150
                    }
                }
            )

            if response.status_code == 200:
                result = response.json()
                return result["candidates"][0]["content"]["parts"][0]["text"].strip()
            else:
                return "AI roast generator is having a coffee break ☕"

    except Exception:
        return "AI roast temporarily unavailable — even robots need debugging sometimes! 🤖"
Enter fullscreen mode Exit fullscreen mode

Update main.py:

from ai_roaster import generate_ai_roast

@app.post("/generate-bio", response_model=BioResponse)
async def create_bio(request: BioRequest, premium: bool = Query(False)):
    if premium:
        roast = await generate_ai_roast(request.name, request.role, request.tech_stack)
        return BioResponse(bio=roast, roast_level="premium")

    bio = generate_bio(request.name, request.role, request.tech_stack)
    return BioResponse(bio=bio)
Enter fullscreen mode Exit fullscreen mode

Why async?

  • API calls to Gemini are I/O-bound
  • async lets FastAPI handle other requests while waiting for Gemini's reply

Example request:

POST /generate-bio?premium=true
Enter fullscreen mode Exit fullscreen mode

AI response:

{
  "bio": "Grace Hopper — Software Engineer. Debugging Kubernetes is like pulling endless rabbits out of YAML hats. Python whisperer and breaker of production at 3 AM.",
  "roast_level": "premium"
}
Enter fullscreen mode Exit fullscreen mode

Nothing wrong here meme

FastAPI Request Lifecycle: What Happens Behind the Scenes

FastAPI request lifecycle diagram

Here's what happens when someone hits your /generate-bio endpoint:

  1. Uvicorn (ASGI server) receives the request
  2. Routing matches /generate-bio → create_bio
  3. Dependency injection handles query params (premium)
  4. Pydantic validation parses JSON → BioRequest. Invalid? Auto 422
  5. Business logic runs: generate_bio() or generate_ai_roast()
  6. Serialization: BioResponse ensures clean JSON output

FastAPI logic flow

What We Built

Hmm, I have more ideas... Maybe deploy and connect with a frontend. But let's wrap up here, save some brain power.

In this tutorial we built:

  • A FastAPI app that roasts LinkedIn bios
  • Pydantic validation that refuses boring inputs
  • AI-powered roast mode for premium sass
  • Swagger docs that make testing fun

Conclusion

Thanks for staying to the end. Please don't end your journey here; go crazy about APIs and be creative with them. I'm Wisdom, and I intend to become an expert in FastAPI.

FastAPI isn't just another web framework — it's a paradigm shift toward modern, type-safe, self-documenting APIs. The skills you've learned here apply to any API project, from simple microservices to complex enterprise applications.

Ready to dive deeper? You can read the FastAPI documentation here — it's some of the best technical documentation you'll ever read.


If this article helped you fall in love with FastAPI, give it a ❤️ and share it with fellow developers who are tired of boring API tutorials!

Top comments (2)

Collapse
 
princess_chidera_646210cf profile image
Princess Chidera •

I'm not a developer but this was an interesting read. Thank you for making it fun and relatable to read

Collapse
 
wisdomehirim06 profile image
Wisdom Ehirim •

Thank you very much Princess. Promise to write more