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-biocreate_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