Deploying to Vercel
FastAPI Interactions is designed for Vercel's serverless Python runtime. Each interaction is handled as a stateless HTTP request with no persistent connections or startup overhead.
Project structure
Vercel's Python runtime automatically detects a FastAPI instance named app at your entrypoint. Since bot.app exposes a FastAPI instance, you just need to export it.
See Vercel's FastAPI documentation for more details.
Environment variables
Your bot needs Discord credentials. Never hardcode these — use environment variables instead.
Local Development: Create a .env file and use environs to load it:
.env:
Production (Vercel): Configure variables in the Vercel dashboard under Settings → Environment Variables. Scope DISCORD_BOT_TOKEN and DISCORD_PUBLIC_KEY to Production only so preview deployments don't affect your live bot.
| Variable | Description |
|---|---|
DISCORD_APP_ID |
Your Discord application ID |
DISCORD_PUBLIC_KEY |
Ed25519 public key from the Developer Portal |
DISCORD_BOT_TOKEN |
Bot token, prefixed with Bot |
Application setup
Entrypoint
Create main.py to initialize your bot and expose the FastAPI app:
# main.py
from fastapi_interactions import Bot
from .commands import router
from environs import env
env.read_env()
bot = Bot(
app_id=env.int('DISCORD_APP_ID'),
public_key=env.str(DISCORD_PUBLIC_KEY'),
bot_token=env.star('DISCORD_BOT_TOKEN')
)
bot.attach_router(router)
app = bot.app
Warning
The variable must be named app and must be a FastAPI instance. Vercel's runtime detects it by name.
Commands
Define your commands in commands.py:
#commands.py
from fastapi_interactions.commands import CommandRouter, Option
router = CommandRouter('my-router')
@router.command(name='username', description='Retrieve your username')
async def username(ctx):
return f'Hello {ctx.user.username}'
@router.command(name='echo', description='Echo a text')
@Option.string(name='text', description='Text to echo')
async def echo(ctx, text: str):
return text
Syncing commands
Command syncing happens at deploy time, not at runtime. Create sync_commands.py:
# sync_commands.py
from fastapi_interactions import Bot
from .commands import router
from environs import env
import sys
env.read_env()
vercel_env = env.str('VERCEL_ENV')
if vercel_env != 'Production':
print(f'Skipping command sync: VERCEL_ENV={vercel_env!r}')
sys.exit(0)
bot = Bot(
app_id=env.int('DISCORD_APP_ID'),
public_key=env.str(DISCORD_PUBLIC_KEY'),
bot_token=env.star('DISCORD_BOT_TOKEN')
)
bot.attach_router(router)
bot.sync_commands()
This script skips syncing on preview builds, preventing them from overwriting your live commands.
Configure build command
In the Vercel dashboard under Settings → Build & Development Settings, set the build command to:
Commands are synced automatically on every production deploy. Global command registration can take up to an hour to propagate. During development, use guild-scoped routers for instant updates:
After deployment
Once your app is live, set your Discord interactions endpoint URL in the Developer Portal:
Discord will verify the endpoint by sending a PING handshake. Once confirmed, your bot is ready.
Performance
Vercel's serverless functions spin up on demand. Discord requires a response within 3 seconds. The framework adds minimal overhead — no database connections, no gateway handshakes — so cold starts are typically well within that window.
Warning
Long-running operations don't work with deferred responses on serverless because the container terminates after the HTTP response is sent. If a command needs more than 3 seconds, either complete the work within the response window, or deploy to traditional hosting. Most Discord bots don't need this pattern.