Personal project
moleXa 3D Molecular Visualization Platform
Next.js, TypeScript, Three.js, Express, node-cache, Supabase, Vercel
moleXa is a free, no-signup web app for exploring molecules in 3D in the browser. The product is two GitHub repositories that together form one application: a Next.js front end at molexa.org that does the rendering, and a small Node and Express API at molexa-api.vercel.app that sits in front of PubChem (the National Library of Medicine's open chemistry database; Kim et al. 2025; Nucleic Acids Research 53(D1):D1516) and turns its raw responses into something a browser can fetch directly and a student can read.
I am the sole author of both repositories. I started the project in July 2025 after running into another viewer's paywall on 3D-model export and decided that the same feature should be free.
Why two repositories
The split is functional, not stylistic. The browser cannot talk to PubChem directly for four reasons, and each one is fixed in the API.
First, PubChem's REST endpoints do not set permissive CORS headers, so a browser fetch from molexa.org is blocked by the Same-Origin Policy. The API runs server-side and adds the right headers on the way back to the user.
Second, PubChem's name autocomplete is not exposed in the public REST surface, only in their web UI. The API wraps an undocumented autocomplete endpoint behind a clean public route and caches results for one hour through node-cache.
Third, PubChem rate-limits at five requests per second per IP. A public web app would burn through that budget the moment two users searched simultaneously. The API absorbs the throttle on the server with express-rate-limit and cuts upstream load through a longer 24-hour cache for compound responses.
Fourth, PubChem returns raw fields. Students staring at "XLogP: 1.2" need to know what the number means before it helps them, so the API enriches the response with plain-English explanations of common properties (lipophilicity for XLogP, drug-likeness rules of thumb for hydrogen-bond donor and acceptor counts, and so on).
The educational layer is the part that made me keep the API as its own repository. It evolves on a different cadence than the frontend, and a future contributor (or me, a year from now) can swap the explanations without touching the renderer.
The 3D renderer
The renderer is in components/canvas-3d.tsx. PubChem returns molecule structure as an SDF (Structured Data File) blob, which encodes atoms with positions and bonds with start and end atom indices plus a bond-order field. The component parses the SDF, places one Three.js sphere per atom colored by element using the standard CPK palette, and draws a cylinder for each bond between the right two sphere centers, with the cylinder's length and orientation derived from the two endpoint coordinates. Double and triple bonds render as parallel cylinders offset along the perpendicular bond axis.
User interactions (drag to rotate, scroll to zoom, click to toggle labels) are wired through Three.js's OrbitControls plus a small label overlay. The download modal exports a high-resolution PNG of the current viewport plus an SDF or 3D-model file of the loaded molecule. That export is the feature MolView gates behind a paid account; in moleXa it is open and free.
A small Zustand store (lib/store.ts) holds the currently loaded molecule, the renderer's view state, and the most recent autocomplete suggestions. State changes propagate through immer for immutable updates so React's render path stays predictable.
The educational layer and the properties panel
The educational endpoint (/api/pubchem/compound/{name}/educational?type=name) merges PubChem's PUG REST and PUG View into one enriched response. PUG REST contributes the molecule's identifying fields (formula, CID, atom and bond counts, molecular weight, alternative names) and the property numbers (XLogP, TPSA, hydrogen-bond donor and acceptor counts, heavy-atom count, rotatable bond count). PUG View contributes the curated annotations that PubChem's web UI surfaces (safety, pharmacology, toxicity headings).
The API takes those two sources, runs them through a dictionary of explanations, and emits a single JSON blob the frontend can render directly. The properties panel shown above is what the frontend renders for morphine: each card carries the raw number plus a one-line description of what the value means and what range is typical for drug-like molecules. The drug-likeness thresholds are the part that turn a PubChem table into something a student can use to form a hypothesis.
Privacy posture
Analytics are optional and privacy-preserving. The API records request metadata (route, method, response time, hashed IP) into Supabase Postgres only when SUPABASE_URL, SUPABASE_ANON_KEY, and HASH_SALT are all configured; otherwise the analytics path is a no-op. IP addresses are hashed with HASH_SALT before storage, so the API cannot reconstruct them even with full database access. Row-level security policies (scripts/setup-rls.sql) limit the public anon key to read-only access on the public dashboard and block writes outside the admin path. Admin endpoints are gated by an ADMIN_TOKEN env var. A monthly Vercel cron (schedule: "0 0 1 * *") runs /api/admin/cleanup to archive the prior month's rows into a static archive directory in the repository, which keeps the Supabase free-tier row count bounded and means the live database only ever holds the current and previous month.
The frontend has no auth, no cookies, no session. An anonymous user can search, view, rotate, label, and download molecules without leaving anything on the server beyond a single hashed-IP analytics row per request.
Operations
Both repositories deploy to Vercel on push to main. The frontend uses Next.js's standard Vercel adapter; the API uses @vercel/node for the Express handler with a 30-second maxDuration per request. Cold starts on the API can take a couple of seconds, so the frontend pings /api/health on app boot and shows a small countdown modal (api-loading-screen.tsx) if the API is not ready yet, rather than letting the user hit a broken empty state. A CVE-fix pass in December 2025 picked up a Vercel-flagged React Server Components vulnerability, landed through PR-1 on the frontend repo.