A Cloudflare Workers proxy to NASA's open APIs — APOD and NeoWs — built with Rust and compiled to WASM. Handles API key injection, per-IP rate limiting, D1 response caching, and SSRF protection so clients can call a single endpoint without managing NASA credentials.
- Rust + WASM on Cloudflare Workers (
workers-rs) - NASA API proxy — APOD (today, by date, date range) and NeoWs asteroid feed
- Server-side API key — clients never see the NASA key
- Per-IP rate limiting — 100 requests/hour, enforced before upstream calls
- D1 response cache — reduces upstream traffic, 30-minute TTL
- SSRF protection — hard-coded allowlist, defense-in-depth URL validation
- Retry with backoff — transient upstream failures are retried automatically
- Structured errors — consistent
{ code, message }JSON on every failure
Base path: /v2
| Endpoint | Description |
|---|---|
GET /v2/healthz |
Service health check |
GET /v2/nasa/apod/today |
Today's Astronomy Picture of the Day |
GET /v2/nasa/apod?date=YYYY-MM-DD |
APOD for a specific date |
GET /v2/nasa/apod/range?start_date=…&end_date=… |
APOD array (max 30 days) |
GET /v2/nasa/neows/feed?start_date=…&end_date=… |
Near-Earth asteroids (max 7 days) |
curl -s https://your-worker.workers.dev/v2/nasa/apod/today | jq .{
"date": "2024-10-05",
"title": "NGC 1365: A Barred Spiral Galaxy",
"explanation": "NGC 1365 is a giant barred spiral galaxy...",
"media_type": "image",
"url": "https://apod.nasa.gov/apod/image/2410/ngc1365.jpg",
"hdurl": "https://apod.nasa.gov/apod/image/2410/ngc1365_hd.jpg"
}Every error returns the same shape:
{
"code": "invalid_request",
"message": "Missing query parameter: date",
"retry_after_seconds": null
}| HTTP Status | code |
|---|---|
| 400 | invalid_request |
| 429 | rate_limit_exceeded |
| 500 | service_misconfigured |
| 502 | upstream_rate_limited, upstream_invalid |
| 503 | upstream_unavailable, service_unavailable |
- Rust 1.70+ with
wasm32-unknown-unknowntarget - Node.js 18+
- Wrangler CLI
- Cloudflare account with a D1 database
- NASA API key (free, instant)
git clone https://github.com/o4s/NASA-API-Proxy.git
cd NASA-API-Proxy
npm install
cargo install worker-build
rustup target add wasm32-unknown-unknownCreate a D1 database and update wrangler.toml with the database ID:
wrangler d1 create nasa_mirror_dbSet your NASA API key:
wrangler secret put NASA_API_KEYnpm run dev
# → http://localhost:8787/v2/nasa/apod/todaywrangler login
npm run deploysrc/
├── lib.rs # Entry point, router
├── handlers.rs # Request handlers, validation, pipeline
├── models.rs # AstronomyPicture, NeoFeedResponse, ApiError
├── nasa.rs # Upstream NASA client, caching, retry logic
└── ratelimit.rs # Per-IP rate limiter (D1-backed)
- Server-side API key injection (never exposed to clients)
- Per-IP rate limiting (100 req/hour, D1-backed)
- SSRF protection via hard-coded host allowlist + defense-in-depth URL re-parse
- Strict input validation (date format, allowed params, range bounds)
- Security headers (
X-Content-Type-Options: nosniff) - No PII in error responses; upstream details are never leaked
See CONTRIBUTING.md. In short: fork, branch, cargo fmt && cargo clippy && cargo test, open a PR.