🚀 Optimising Code Reusability: Creating a Private NPM Package for Shared AWS Services & Common Utilities

Introduction
When working with multiple repositories, maintaining shared logic across each project can lead to code duplication, inconsistencies, and increased maintenance overhead.
A better approach is to create a private NPM package that consolidates reusable logic into a single repository. This aligns with the DRY (Don't Repeat Yourself) principle, making your codebase more scalable, maintainable, and efficient.
In this blog, we’ll explore:
✅ Different ways to publish an NPM package
✅ The benefits of using a private package
✅ A step-by-step guide to creating and using a private NPM package
✅ Other reusable components that can be converted into an NPM package
⚡ Why Use an NPM Package for Shared Code?
Developers often duplicate logic across multiple repositories, making maintenance difficult. Instead of writing and updating the same code in different projects, we can extract commonly used code into a single NPM package and reuse it across repositories.
Some common examples of reusable code include:
✅ AWS Service Wrappers
S3 (Storage)
SQS (Message Queue)
SNS (Notifications)
Secrets Manager (Credential Storage)
AppConfig (Configuration Management)
EventBridge (Event-driven Communication)
CloudWatch (Monitoring & Logs)
✅ Common Utilities & Helpers
Masking sensitive data
Logging utilities
Exception handlers
Custom decorators (for authorization, validation, etc.)
Database query builders
✅ NestJS-Specific Reusable Components
Guards (Authentication, Role-based access control)
Filters (Global exception handling)
Interceptors (Logging, Request transformation)
Custom decorators
3rd-party service integrations (like Stripe, Twilio, etc.)
📦 How to Publish an NPM Package?
There are three main ways to publish an NPM package:
1️⃣ Public NPM Package
If the code is generic and doesn’t contain sensitive information, publish it as a public package on the NPM registry.
Example:
axios
,lodash
, etc.
2️⃣ Private NPM Package (Enterprise Registry)
If the package contains internal logic or proprietary code, a private package is a better option.
Requires a paid NPM Enterprise account (or services like GitHub Packages, AWS CodeArtifact, or Verdaccio).
3️⃣ Private Git Repository (Recommended Alternative)
If you don’t want to pay for an Enterprise NPM account, host the package in a private GitHub/GitLab repo.
Install it in projects using:
npm install git+ssh://git@github.com:your-org/your-private-package.git
You can configure scoped access to limit usage within your organization.
🔨 Step-by-Step: Creating a Private NPM Package
1️⃣ Initialize Your NPM Package
Create a new folder and initialize an NPM package:
mkdir shared-utils && cd shared-utils
npm init -y
This will generate a package.json
file.
2️⃣ Install Required Dependencies
npm install aws-sdk dotenv winston
3️⃣ Write AWS Service Wrappers
Example: S3 Service Utility
Create src/s3Service.ts
:
import AWS from 'aws-sdk';
export class S3Service {
private s3: AWS.S3;
constructor() {
this.s3 = new AWS.S3();
}
async uploadFile(bucket: string, key: string, file: Buffer) {
const params = { Bucket: bucket, Key: key, Body: file };
return this.s3.upload(params).promise();
}
}
Example: SQS Service Utility
Create src/sqsService.ts
:
import AWS from 'aws-sdk';
export class SQSService {
private sqs: AWS.SQS;
constructor() {
this.sqs = new AWS.SQS();
}
async sendMessage(queueUrl: string, message: string) {
const params = { QueueUrl: queueUrl, MessageBody: message };
return this.sqs.sendMessage(params).promise();
}
}
4️⃣ Add Common Utilities & Helpers
Example: Masking Sensitive Data
Create src/maskFields.ts
:
export function maskFields(obj: Record<string, any>, fieldsToMask: string[]) {
return Object.fromEntries(
Object.entries(obj).map(([key, value]) => [
key,
fieldsToMask.includes(key) ? '****' : value
])
);
}
Example: Logger Utility
Create src/logger.ts
:
import winston from 'winston';
export const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'app.log' })
]
});
5️⃣ Export Services from index.ts
Create src/index.ts
:
export { S3Service } from './s3Service';
export { SQSService } from './sqsService';
export { maskFields } from './maskFields';
export { logger } from './logger';
6️⃣ Compile the Package (If Using TypeScript)
If you’re using TypeScript, add a tsconfig.json
:
{
"compilerOptions": {
"outDir": "dist",
"module": "CommonJS",
"target": "ES6",
"declaration": true
}
}
Then compile:
tsc
7️⃣ Publish the Package
A) Publishing to NPM (Public/Private Registry)
Login to NPM:
npm login
Publish the package:
npm publish --access=public
If private:
npm publish --access=restricted
B) Publishing to GitHub (Alternative Method)
If you want to use a private GitHub repo, push the package:
git init
git add .
git commit -m "Initial commit"
git remote add origin git@github.com:your-org/shared-utils.git
git push origin main
Then, install it in your projects using:
npm install git+ssh://git@github.com:your-org/shared-utils.git
🚀 Using the Package in Another Project
Now that the package is published, install and use it in another repository:
npm install @your-org/shared-utils
Example usage:
import { S3Service, SQSService, maskFields, logger } from '@your-org/shared-utils';
const s3 = new S3Service();
await s3.uploadFile('my-bucket', 'test.txt', Buffer.from('Hello World'));
const maskedData = maskFields({ password: 'secret123' }, ['password']);
console.log(maskedData); // { password: '****' }
logger.info('Application started');
🎯 Conclusion
By creating a private NPM package for AWS services and common utilities, you can eliminate redundancy, improve maintainability, and simplify collaboration across multiple repositories.
💡 If your project has shared code, consider modularising it into an NPM package today! 🚀
🔗 Follow me for more tech insights! 🚀
Subscribe to my newsletter
Read articles from Shivam Agrawal directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
