Your resource for web content, online publishing
and the distribution of digital products.
«  
  »
S M T W T F S
 
 
 
 
 
1
 
2
 
3
 
4
 
5
 
6
 
7
 
8
 
9
 
10
 
11
 
12
 
13
 
14
 
15
 
16
 
17
 
18
 
19
 
20
 
21
 
22
 
23
 
24
 
25
 
26
 
27
 
28
 
29
 
30
 
31
 
 
 
 
 
 
 

Releasing Utilities Package to GitHub Packages: A Guide

DATE POSTED:May 19, 2025

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:

  • Integrated with your repository: No extra accounts or keys to manage.
  • Scoped access: Control exactly who gets your code.
  • Familiar workflows: Your team’s already on GitHub; why hop away?

I've used this for smart contract SDKs referenced by both the frontend app and the NestJS API.

Directory Structure

I 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 Releases

Every package update is explicitly tagged as a release in GitHub's UI, which helps prevent accidental releases of incomplete work.

\n github release package page

\

Action Workflow File

Below 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 Parts
  • Scopes, Not Monorepos: No workspaces, no publishing the entire repo.
  • No Source Leakage: Only files in /package are seen by consumers—no accidental pushes of TS, docs, or git history.
  • Manual Trigger: The process kicks off only when you create a GitHub Release, not on every push or PR.
Real-World Example

Let’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.