- JavaScript 61.4%
- CSS 24.6%
- HTML 12.1%
- Dockerfile 1.9%
Pinned 2026 toolchains (Go 1.26, Rust 1.98/edition 2024, Python 3.14 + uv, Node 24, Zig 0.16, NixOS 26.05), postgres 18 / mongo 8, lockfiles built from, non-root runtimes, .dockerignore, per-project LICENSE, READMEs with the git.devai.io clone line, checkout@v7 CI. Security fixes in the legacy Rust APIs (any-password login, self-assigned admin, hard-coded JWT secret), JWT alg/exp/sub enforcement across the blog series, safe markdown links in the frontends, and many smaller bugs — every project was built, run and exercised end to end. Adds scripts/publish.sh + a CI publish job that splits every folder into its own repo at git.devai.io/templates/<folder>. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01128fhuZbgivaSJvtMf4s1G |
||
|---|---|---|
| .github/workflows | ||
| public | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| compose.yaml | ||
| Dockerfile | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| server.js | ||
nasa-photo-proxy
NASA's Astronomy Picture of the Day, fetched through your own tiny Node server. The same "fetch some data and show it" idea as the browser tutorials — but now with a real backend, so you learn why apps have servers: to keep secrets like API keys out of the browser.
Run
Get it: git clone https://git.devai.io/templates/nasa-photo-proxy.git
With Node.js 22 or newer — there are no npm packages to install:
cp .env.example .env # optional: paste your own NASA key
node server.js
Then open http://localhost:8080.
Or run it like production:
docker compose up --build
Same address. Compose reads NASA_API_KEY from .env too, and falls back to
NASA's shared DEMO_KEY.
How it works
In the browser-only tutorials the page called the API directly. That's fine for keyless APIs, but many APIs need a secret key — and anything in a web page is visible to anyone who opens "View Source". So put a server in the middle:
Browser ──► /api/apod (your server) ──► api.nasa.gov/planetary/apod?api_key=SECRET
◄── title, date, image URL ◄──
server.js has two jobs:
- Serve the page. Requests like
/,/styles.cssand/app.jsread a file frompublic/— and anything that tries to climb out of it (/../server.js) is refused. - Be the proxy.
/api/apodcalls NASA withNASA_API_KEYfrom the environment (or.env, loaded with Node's built-inprocess.loadEnvFile()), and sends back only the fields the page needs.
public/app.js fetches /api/apod — its own server — never nasa.gov, so
the key never reaches the browser. DEMO_KEY is shared by everyone and allows
only a few dozen requests an hour; when NASA says "too many", the page tells
you to get a free key at https://api.nasa.gov.
Try it: cache NASA's answer for an hour so reloads don't call NASA, add
/api/apod?date=2022-07-11 (NASA accepts a date parameter), or swap NASA
for any other key-protected API — the proxy pattern is identical.
Layout
server.js the whole backend: static files + the /api/apod proxy
public/index.html the page
public/app.js calls /api/apod on our own server
public/styles.css the look; follows your system's light or dark mode
.env.example where the API key goes
package.json name + "start" script; the lockfile is empty on purpose
Deploy
Push to your own GitHub repo and the shipped workflow
(.github/workflows/ci.yml) tests the compose stack, publishes the image to
GHCR, and — once you set the DEPLOY_HOST / DEPLOY_USER variables and
DEPLOY_KEY secret — deploys it to your server over ssh. Put your real
NASA_API_KEY in .env next to compose.yaml on the server.
Part of devai.io — the APIs & Data track: fetch real data from the internet. Previous: ip-lookup.