Releasing a closed-source, reusable JavaScript/TypeScript package for internal use across frontend and backend is a common challenge, especially when you want to automate it, keep things modular, and avoid unnecessary leakage of code. Here’s how I do it, step by step, using only what’s needed for a stable, repeatable workflow.
Why GitHub Packages (and Not npmjs.org)?Most teams reach for npmjs.org by default, but if your utilities are strictly internal - or have some private contract processing logic you’re not ready to open-source - GitHub’s own registry is more than enough:
I've used this for smart contract SDKs referenced by both the frontend app and the NestJS API.
Directory StructureI keep only my distributable code in /package, separate from internal scripts/docs, to avoid accidentally leaking dev files.
|-- .github/ |-- src/ |-- package/ # <--- Only your published files live here |-- package.json |-- dist/ |-- index.js |-- ...Pro tip: npm publish runs only in /package, not at the repo root.
Manual Releases Triggered From GitHub ReleasesEvery package update is explicitly tagged as a release in GitHub's UI, which helps prevent accidental releases of incomplete work.
\
Action Workflow FileBelow is the full workflow that gets the job done.
name: Publish package on GitHub Packages on: release: types: [created] jobs: publish: runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: 18 registry-url: "https://npm.pkg.github.com" scope: "@your-user-name" always-auth: true - name: Install dependencies run: npm ci - name: Build package run: npm run package - name: Install package dependencies working-directory: ./package run: npm ci - name: Publish package working-directory: ./package run: npm publish env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}Place it in github/workflows/publish.yml
Key PartsLet’s say you update a Smart Contracts ABI in /src, then run your internal build (maybe via a simple "package" script) to output to /package/dist.
Only that transpiled, dependency-free version ships.
Your API team can safely pull it via:
npm install @user/package-name --registry=https://npm.pkg.github.com
From both backend and frontend, with no npmjs exposure.
All Rights Reserved. Copyright , Central Coast Communications, Inc.