AstroVista
Open source app that explores NASA's space images and videos, archiving daily APOD images dating back to 1995.
Since 1995 NASA has published one image a day on APOD (Astronomy Picture of the Day). That is more than ten thousand images, each with an explanation written by astronomers. The official API exists, but it is slow, has an aggressive rate limit and is useless for browsing: you ask for a date and you get a date.
AstroVista turns that into a browsable archive, with search, date filtering and a gallery.
The problem with the official API
Hitting NASA's API on every request had three practical problems:
- Rate limit. The free key allows 1000 requests per hour. A paginated gallery burns through that in minutes.
- Latency. Between 800ms and 3s per call, with no caching on their side.
- No search. There is no full-text search endpoint. Only by date.
The fix was to stop calling NASA on the user's path.
Sync as the only writer
A daily job fetches new images and upserts them into MongoDB. The app reads only from the database. It never calls NASA during a request.
const existing = await Apod.findOne({ date }).lean();
if (existing) return { skipped: true };
const data = await fetchApod(date);
await Apod.updateOne({ date }, { $set: data }, { upsert: true });That changed the shape of the app entirely: response time became the time of an indexed Mongo query, and the rate limit stopped existing as a concept.
Why Hono and not a Next route
The backend is a separate Hono service. The reason was not performance. It was the sync job. It has to run outside the request cycle, with a long timeout and retries, and Next Route Handlers on serverless are hostile to that.
Splitting it also made the API reusable: today the same endpoint feeds the site and a bot that posts the picture of the day.
What I would do differently
I would store the images in my own bucket from day one. AstroVista serves NASA's URLs directly, and older images occasionally disappear from their server. The record survives, the image does not.