my-library
Build & deploy
Running it locally, the environment it needs, and what happens on a push to main.
Local setup
npm install
cp .env.local.example .env.local # fill in the two Neon strings
npm run db:migrate # apply migrations over DIRECT_URL
npm run create:user -- "mblrc" # prints a token, once
npm run devThe dev server runs on port 3001 in this workspace — port 3000 belongs to mblrc-web, and the two are frequently up together.
Environment
| Variable | Value | Needed by |
|---|---|---|
DATABASE_URL | Neon pooled string — host contains -pooler | Runtime. Set in Vercel for Production and Preview. |
DIRECT_URL | Neon direct string — no -pooler | drizzle-kit only. Local; Vercel never runs migrations. |
.env* is git-ignored with one exception — !.env.local.example — because the example is documentation and needs to survive a fresh clone. That negation is load-bearing: the pattern would otherwise swallow it, and the file only stayed in the repo because it had been committed before the rule was added.
Every command
| Command | Does |
|---|---|
npm run dev | Dev server. |
npm run build | Production build. |
npm run typecheck | tsc --noEmit. |
npm test | The pglite suite. |
npm run db:generate | schema.ts → migration SQL. |
npm run db:migrate | Apply migrations. |
npm run db:studio | Browse the database. |
npm run create:user | Mint a user and a token. |
npm run create:token | Mint another token. |
npm run enrich:library | CSV → OpenLibrary → JSON shards. No database. |
npm run import:library:commit | Shards → Postgres, deduped. |
npm run scan:local | Catalogue local ebook files. |
npm run import:local:commit | Trusted local matches → Postgres. |
Deployment
Vercel’s Git integration, not a workflow. Push to main and production builds; pull requests get preview URLs; rollback is a click. A GitHub Action deploying via vercel deploy would reimplement all of that and add a token to rotate.
git push origin main
→ build starts within ~15s
→ ~40s later, live at mylibrary.mblrc.meMigrations are not part of it
The build does not migrate. A build step that touches the schema can half-migrate production on a bad push, and on Vercel it would run on every preview too. Migrate locally over DIRECT_URL first, then push — which also means the ordering is a decision somebody makes rather than a side effect.
Its own project, its own subdomain
my-library is a separate Vercel project from mblrc-web, matching the pattern the other apps follow: one app, one repository, one project, one subdomain. Since mblrc.me uses third-party nameservers, adding mylibrary.mblrc.me meant a CNAME at the registrar pointing at cname.vercel-dns.com — Vercel shows Invalid Configuration until it propagates, then flips itself.
Two CLI details worth remembering
A subdomain must name its project: vercel domains add mylibrary.mblrc.me my-library. Only apex domains can be added without one, because Vercel otherwise cannot know which deployment should answer.
And check .vercel/project.json before running vercel in a directory. This folder was once linked to mblrc-web, which would have deployed the book API over the main site. It is git-ignored, so it is purely a local hazard — the dashboard’s import flow avoids it entirely.
What is in the repository
Ignored, deliberately: .env* (except the example), .vercel/, postman/, and every import working file — scripts/csv/, scripts/enriched/, books.json and the local-scan output. The last group is derived data measured in megabytes, reproducible from the scripts, and of no interest to anyone reading the code.
Verifying a deployment
curl -sI https://mylibrary.mblrc.me/ # 200
curl -s -o /dev/null -w '%{http_code}' \
https://mylibrary.mblrc.me/api/v1/books # 401 without a token
npx newman run postman/my-library.postman_collection.json \
--env-var "baseUrl=https://mylibrary.mblrc.me" --env-var "apiToken=$TOKEN"The newman run is the thorough version — 51 assertions across every endpoint and error path, and it cleans up after itself. See Testing.