Skip to content

Commit 9e48472

Browse files
committed
feat: add FastAPI example with all 10 domains, async endpoints, error handling
1 parent a852794 commit 9e48472

1 file changed

Lines changed: 228 additions & 0 deletions

File tree

examples/fastapi_app.py

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
"""
2+
Example FastAPI server using the roxy-sdk Python package.
3+
4+
Demonstrates all 10 domains with async endpoints, error handling,
5+
connection lifecycle, and environment-based API key configuration.
6+
7+
Setup:
8+
pip install roxy-sdk fastapi uvicorn
9+
10+
export ROXY_API_KEY="your-api-key" # get one at https://roxyapi.com/pricing
11+
export ROXY_BASE_URL="https://roxyapi.com/api/v2" # optional, this is the default
12+
uvicorn examples.fastapi_app:app --reload --port 8001
13+
14+
Test with curl:
15+
curl localhost:8001/horoscope/aries
16+
curl localhost:8001/horoscope/leo?lang=es
17+
curl -X POST localhost:8001/chart -H "Content-Type: application/json" \
18+
-d '{"date":"1990-07-15","time":"14:30:00","latitude":40.7128,"longitude":-74.006}'
19+
curl localhost:8001/tarot/draw/3
20+
curl localhost:8001/tarot/daily
21+
curl localhost:8001/numerology/life-path/1990/1/15
22+
curl localhost:8001/crystal/amethyst
23+
curl localhost:8001/crystals/zodiac/aries
24+
curl localhost:8001/iching/reading
25+
curl localhost:8001/dreams/symbol/water
26+
curl localhost:8001/angel-number/1111
27+
curl localhost:8001/vedic/nakshatras
28+
curl localhost:8001/location/search?q=Mumbai
29+
curl localhost:8001/usage
30+
"""
31+
from __future__ import annotations
32+
33+
import os
34+
from contextlib import asynccontextmanager
35+
36+
from fastapi import FastAPI, Query
37+
from fastapi.responses import JSONResponse
38+
from pydantic import BaseModel
39+
40+
from roxy_sdk import RoxyAPIError, create_roxy
41+
42+
# Read config from environment (never hardcode keys in production)
43+
API_KEY = os.environ.get("ROXY_API_KEY", "")
44+
BASE_URL = os.environ.get("ROXY_BASE_URL", "https://roxyapi.com/api/v2")
45+
46+
if not API_KEY:
47+
print("WARNING: ROXY_API_KEY not set. Get one at https://roxyapi.com/pricing")
48+
print(" export ROXY_API_KEY='your-key-here'")
49+
50+
# Create client at module level for connection reuse
51+
roxy = create_roxy(api_key=API_KEY, base_url=BASE_URL) if API_KEY else None
52+
53+
54+
@asynccontextmanager
55+
async def lifespan(app: FastAPI):
56+
yield
57+
# Clean up HTTP connections on shutdown
58+
if roxy:
59+
await roxy.aclose()
60+
61+
62+
app = FastAPI(
63+
title="RoxyAPI Example",
64+
description="Example FastAPI server powered by roxy-sdk",
65+
lifespan=lifespan,
66+
)
67+
68+
69+
# ---------------------------------------------------------------------------
70+
# Error handling
71+
# ---------------------------------------------------------------------------
72+
73+
74+
@app.exception_handler(RoxyAPIError)
75+
async def handle_roxy_error(request, exc: RoxyAPIError):
76+
"""Forward API errors to the client with the original status code."""
77+
return JSONResponse(
78+
status_code=exc.status_code,
79+
content={"error": exc.error, "code": exc.code},
80+
)
81+
82+
83+
# ---------------------------------------------------------------------------
84+
# Western Astrology
85+
# ---------------------------------------------------------------------------
86+
87+
88+
@app.get("/horoscope/{sign}")
89+
async def daily_horoscope(sign: str, lang: str | None = None):
90+
"""Daily horoscope for any zodiac sign. Supports 8 languages via ?lang= query param."""
91+
return await roxy.astrology.get_daily_horoscope_async(sign=sign, lang=lang)
92+
93+
94+
class ChartRequest(BaseModel):
95+
date: str # YYYY-MM-DD
96+
time: str # HH:MM:SS
97+
latitude: float
98+
longitude: float
99+
timezone: float = 0.0 # UTC offset in hours (e.g., -5 for EST, 5.5 for IST)
100+
101+
102+
@app.post("/chart")
103+
async def natal_chart(req: ChartRequest):
104+
"""Generate a natal birth chart from birth data."""
105+
return await roxy.astrology.generate_natal_chart_async(
106+
date=req.date,
107+
time=req.time,
108+
latitude=req.latitude,
109+
longitude=req.longitude,
110+
timezone=req.timezone,
111+
)
112+
113+
114+
@app.get("/moon-phase")
115+
async def moon_phase():
116+
"""Current moon phase with illumination and zodiac sign."""
117+
return await roxy.astrology.get_current_moon_phase_async()
118+
119+
120+
# ---------------------------------------------------------------------------
121+
# Tarot
122+
# ---------------------------------------------------------------------------
123+
124+
125+
@app.get("/tarot/draw/{count}")
126+
async def draw_cards(count: int):
127+
"""Draw N tarot cards from the full 78-card Rider-Waite-Smith deck."""
128+
return await roxy.tarot.draw_cards_async(count=count)
129+
130+
131+
@app.get("/tarot/daily")
132+
async def daily_card():
133+
"""Daily tarot card with interpretation."""
134+
return await roxy.tarot.get_daily_card_async()
135+
136+
137+
# ---------------------------------------------------------------------------
138+
# Numerology
139+
# ---------------------------------------------------------------------------
140+
141+
142+
@app.get("/numerology/life-path/{year}/{month}/{day}")
143+
async def life_path(year: int, month: int, day: int):
144+
"""Calculate Life Path number from birth date."""
145+
return await roxy.numerology.calculate_life_path_async(year=year, month=month, day=day)
146+
147+
148+
# ---------------------------------------------------------------------------
149+
# Crystals
150+
# ---------------------------------------------------------------------------
151+
152+
153+
@app.get("/crystal/{slug}")
154+
async def crystal_detail(slug: str):
155+
"""Crystal properties, healing info, and zodiac pairings by slug."""
156+
return await roxy.crystals.get_crystal_async(id=slug)
157+
158+
159+
@app.get("/crystals/zodiac/{sign}")
160+
async def crystals_by_zodiac(sign: str):
161+
"""Crystals associated with a zodiac sign."""
162+
return await roxy.crystals.get_crystals_by_zodiac_async(sign=sign)
163+
164+
165+
# ---------------------------------------------------------------------------
166+
# I Ching
167+
# ---------------------------------------------------------------------------
168+
169+
170+
@app.get("/iching/reading")
171+
async def iching_reading():
172+
"""Cast an I Ching reading with coin toss simulation."""
173+
return await roxy.iching.cast_reading_async()
174+
175+
176+
# ---------------------------------------------------------------------------
177+
# Dreams
178+
# ---------------------------------------------------------------------------
179+
180+
181+
@app.get("/dreams/symbol/{symbol_id}")
182+
async def dream_symbol(symbol_id: str):
183+
"""Look up a dream symbol meaning."""
184+
return await roxy.dreams.get_dream_symbol_async(id=symbol_id)
185+
186+
187+
# ---------------------------------------------------------------------------
188+
# Angel Numbers
189+
# ---------------------------------------------------------------------------
190+
191+
192+
@app.get("/angel-number/{number}")
193+
async def angel_number(number: str):
194+
"""Angel number meaning and spiritual significance."""
195+
return await roxy.angel_numbers.get_angel_number_async(number=number)
196+
197+
198+
# ---------------------------------------------------------------------------
199+
# Vedic Astrology
200+
# ---------------------------------------------------------------------------
201+
202+
203+
@app.get("/vedic/nakshatras")
204+
async def nakshatras():
205+
"""List all 27 Vedic nakshatras (lunar mansions)."""
206+
return await roxy.vedic_astrology.list_nakshatras_async()
207+
208+
209+
# ---------------------------------------------------------------------------
210+
# Location (helper for chart endpoints)
211+
# ---------------------------------------------------------------------------
212+
213+
214+
@app.get("/location/search")
215+
async def search_cities(q: str = Query(..., description="City name to search")):
216+
"""Search cities to get coordinates for birth chart calculations."""
217+
return await roxy.location.search_cities_async(q=q)
218+
219+
220+
# ---------------------------------------------------------------------------
221+
# Usage
222+
# ---------------------------------------------------------------------------
223+
224+
225+
@app.get("/usage")
226+
async def usage_stats():
227+
"""Check API usage and subscription info."""
228+
return await roxy.usage.get_usage_stats_async()

0 commit comments

Comments
 (0)