python
52 lines · 8 steps
Building a signup endpoint in FastAPI
A user-registration route that validates input, checks for duplicates, persists to the database, and sends a welcome email in the background.
Explained by
highlit
1from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status
2from pydantic import BaseModel, EmailStr
3from sqlalchemy.orm import Session
4
5from .database import get_db
6from .models import User
7from .services.mailer import send_email
8
9router = APIRouter(prefix="/users", tags=["users"])
10
11
12class SignupRequest(BaseModel):
13 email: EmailStr
14 full_name: str
15
16
17class SignupResponse(BaseModel):
18 id: int
19 email: EmailStr
20
21 class Config:
22 from_attributes = True
23
24
25def send_welcome_email(email: str, full_name: str) -> None:
26 send_email(
27 to=email,
28 subject="Welcome aboard!",
29 template="welcome",
30 context={"name": full_name},
31 )
32
33
34@router.post("", response_model=SignupResponse, status_code=status.HTTP_201_CREATED)
35def signup(
36 payload: SignupRequest,
37 background_tasks: BackgroundTasks,
38 db: Session = Depends(get_db),
39) -> User:
40 if db.query(User).filter(User.email == payload.email).first():
41 raise HTTPException(
42 status_code=status.HTTP_409_CONFLICT,
43 detail="A user with this email already exists.",
44 )
45
46 user = User(email=payload.email, full_name=payload.full_name)
47 db.add(user)
48 db.commit()
49 db.refresh(user)
50
51 background_tasks.add_task(send_welcome_email, user.email, user.full_name)
52 return user
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Pydantic models validate incoming JSON and shape outgoing responses, keeping the endpoint body free of manual parsing.
- 2Depends lets FastAPI inject a per-request database session so the handler never manages its own connection lifecycle.
- 3BackgroundTasks defers slow work like email until after the response is sent, keeping the request fast.
Related explainers
typescript
import { registerLocaleData } from '@angular/common'; import localeFr from '@angular/common/locales/fr'; import localeFrExtra from '@angular/common/locales/extra/fr'; import localeDe from '@angular/common/locales/de';
Locale-aware bootstrapping in Angular
i18n
localization
dependency-injection
Intermediate
8 steps
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
typescript
import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import * as Joi from 'joi';
Validating env config at boot in NestJS
configuration
schema-validation
environment-variables
Intermediate
8 steps
java
@Component @Converter public class EncryptedStringConverter implements AttributeConverter<String, String> {
Transparent column encryption in Spring & JPA
encryption
aes-gcm
jpa-converter
Advanced
10 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
typescript
import { Inject, Injectable, Logger } from '@nestjs/common'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { Cache } from 'cache-manager'; import { InjectRepository } from '@nestjs/typeorm';
A cache-aside country lookup in NestJS
cache-aside
dependency-injection
batch-lookup
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/building-a-signup-endpoint-in-fastapi-explained-python-f7e8/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.