Sometimes you need to redirect users to external websites or non-Inertia pages (like OAuth providers, payment gateways, or external maps). Cross-Inertia provides the inertia.location() method for this.
Why External Redirects?
Regular Inertia navigation is designed for single-page app transitions within your application. However, for external URLs, you need a full page reload. The location() method handles this by:
- Returning a
409 Conflictresponse - Setting the
X-Inertia-Locationheader - Triggering the Inertia client to perform a full page navigation
Basic Usage
from fastapi import FastAPI
from cross_inertia.fastapi import InertiaDep
app = FastAPI()
@app.get("/auth/github")
async def github_oauth(inertia: InertiaDep):
"""Redirect to GitHub OAuth"""
oauth_url = f"https://github.com/login/oauth/authorize?client_id={CLIENT_ID}"
return inertia.location(oauth_url)Common Use Cases
OAuth Authentication
Redirect users to OAuth providers:
@app.get("/auth/google")
async def google_oauth(inertia: InertiaDep):
"""Redirect to Google OAuth"""
oauth_url = (
"https://accounts.google.com/o/oauth2/v2/auth"
f"?client_id={GOOGLE_CLIENT_ID}"
f"&redirect_uri={REDIRECT_URI}"
"&response_type=code"
"&scope=openid email profile"
)
return inertia.location(oauth_url)
@app.get("/auth/github")
async def github_oauth(inertia: InertiaDep):
"""Redirect to GitHub OAuth"""
oauth_url = (
f"https://github.com/login/oauth/authorize"
f"?client_id={GITHUB_CLIENT_ID}"
f"&redirect_uri={REDIRECT_URI}"
"&scope=user:email"
)
return inertia.location(oauth_url)Payment Gateways
Redirect to external payment providers:
@app.post("/checkout")
async def create_checkout(inertia: InertiaDep):
"""Create Stripe checkout session"""
form_data = await inertia.request.json()
# Create Stripe checkout session
session = stripe.checkout.Session.create(
payment_method_types=['card'],
line_items=[{
'price': form_data['price_id'],
'quantity': 1,
}],
mode='payment',
success_url=f'{BASE_URL}/success?session_id={{CHECKOUT_SESSION_ID}}',
cancel_url=f'{BASE_URL}/cancel',
)
# Redirect to Stripe
return inertia.location(session.url)External Maps
Redirect to mapping services:
@app.get("/shelter/{shelter_id}/directions")
async def get_directions(shelter_id: int, inertia: InertiaDep):
"""Get directions to a shelter"""
shelter = get_shelter(shelter_id)
# Redirect to Google Maps
address = shelter["address"].replace(" ", "+")
maps_url = f"https://maps.google.com/?q={address}"
return inertia.location(maps_url)
@app.get("/venue/{venue_id}/navigate")
async def navigate_to_venue(venue_id: int, inertia: InertiaDep):
"""Navigate to venue using Apple Maps"""
venue = get_venue(venue_id)
# Redirect to Apple Maps
apple_maps_url = f"maps://?q={venue['name']}&ll={venue['lat']},{venue['lng']}"
return inertia.location(apple_maps_url)External Documentation
Link to external documentation or help articles:
@app.get("/help/external/{article_id}")
async def external_help(article_id: str, inertia: InertiaDep):
"""Redirect to external help documentation"""
help_url = f"https://docs.example.com/articles/{article_id}"
return inertia.location(help_url)Download Files
Trigger file downloads from external URLs:
@app.get("/reports/{report_id}/download")
async def download_report(report_id: int, inertia: InertiaDep):
"""Download a generated report"""
report = get_report(report_id)
# Redirect to S3 or CDN URL
download_url = generate_presigned_url(report['file_key'])
return inertia.location(download_url)Real-World Example
Here's a complete example from a cat adoption platform:
from fastapi import FastAPI
from cross_inertia.fastapi import InertiaDep
app = FastAPI()
@app.get("/cats/{cat_id}")
async def show_cat(cat_id: int, inertia: InertiaDep):
"""Show cat profile with shelter information"""
cat = get_cat_by_id(cat_id)
shelter = get_shelter_by_name(cat["shelter_name"])
return inertia.render("CatProfile", {
"cat": cat,
"shelter": shelter,
})
@app.get("/shelter/{shelter_name}/directions")
async def get_shelter_directions(shelter_name: str, inertia: InertiaDep):
"""
External redirect to Google Maps for shelter directions.
Returns a 409 response with X-Inertia-Location header.
The Inertia client will automatically navigate to Google Maps.
"""
shelter = get_shelter_by_name(shelter_name)
if not shelter:
return inertia.render("Error", {
"message": f"Shelter '{shelter_name}' not found"
})
# URL-encode the address
address = shelter["address"].replace(" ", "+")
maps_url = f"https://maps.google.com/?q={address}"
return inertia.location(maps_url)Frontend component:
export default function CatProfile({ cat, shelter }) {
return (
<div>
<h1>{cat.name}</h1>
<div className="shelter-info">
<h2>{shelter.name}</h2>
<p>{shelter.address}</p>
{/* This link will trigger an external redirect */}
<a href={`/shelter/${shelter.name}/directions`}>
Get Directions
</a>
</div>
</div>
)
}How It Works
When you call inertia.location(url):
-
Server response: Returns
409 Conflictstatus with:X-Inertia-Location: <url>header
-
Client behavior: The Inertia client:
- Detects the
409status - Reads the
X-Inertia-Locationheader - Performs
window.location.href = url(full page navigation)
- Detects the
Internal vs External Redirects
When to use inertia.location() (External)
- OAuth providers (GitHub, Google, etc.)
- Payment gateways (Stripe, PayPal, etc.)
- External mapping services
- Third-party documentation
- File downloads from external URLs
- Any URL outside your Inertia app
When to use regular redirects (Internal)
For navigation within your Inertia app, use FastAPI's RedirectResponse:
from fastapi.responses import RedirectResponse
@app.post("/users")
async def create_user(inertia: InertiaDep):
# Create user...
# Internal redirect (stays in SPA)
return RedirectResponse(url=f"/users/{user.id}", status_code=303)The Inertia client automatically handles internal redirects without a full page reload.
Testing External Redirects
When testing, you can verify the redirect:
from fastapi.testclient import TestClient
def test_oauth_redirect():
client = TestClient(app)
response = client.get(
"/auth/github",
headers={"X-Inertia": "true"}
)
assert response.status_code == 409
assert "X-Inertia-Location" in response.headers
assert "github.com" in response.headers["X-Inertia-Location"]Security Considerations
- Validate URLs: Only redirect to trusted domains
- Use HTTPS: Always use HTTPS for OAuth and payment redirects
- Verify state: For OAuth, always verify state parameters
- Whitelist domains: Consider whitelisting allowed redirect domains
ALLOWED_DOMAINS = ["github.com", "accounts.google.com", "checkout.stripe.com"]
@app.get("/redirect/{url:path}")
async def safe_redirect(url: str, inertia: InertiaDep):
from urllib.parse import urlparse
parsed = urlparse(url)
if parsed.netloc not in ALLOWED_DOMAINS:
return inertia.render("Error", {
"message": "Invalid redirect domain"
})
return inertia.location(url)Next Steps
- History Encryption - Secure sensitive OAuth data
- Validation Errors - Handle payment errors
- Shared Data - Flash messages after OAuth