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 dev

The dev server runs on port 3001 in this workspace — port 3000 belongs to mblrc-web, and the two are frequently up together.

Environment

VariableValueNeeded by
DATABASE_URLNeon pooled string — host contains -poolerRuntime. Set in Vercel for Production and Preview.
DIRECT_URLNeon direct string — no -poolerdrizzle-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

CommandDoes
npm run devDev server.
npm run buildProduction build.
npm run typechecktsc --noEmit.
npm testThe pglite suite.
npm run db:generateschema.ts → migration SQL.
npm run db:migrateApply migrations.
npm run db:studioBrowse the database.
npm run create:userMint a user and a token.
npm run create:tokenMint another token.
npm run enrich:libraryCSV → OpenLibrary → JSON shards. No database.
npm run import:library:commitShards → Postgres, deduped.
npm run scan:localCatalogue local ebook files.
npm run import:local:commitTrusted 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.me

Migrations 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.