@divami-labs/nestjs-api-key-management
v0.0.2
Published
A NestJS library for API key management with service account support
Maintainers
Readme
API Key Management Library
A robust, production-ready NestJS library for managing API keys with service account authentication, soft delete functionality, and comprehensive key lifecycle management.
Features
✅ Two-Tier Authentication Architecture
- User → Service Account → API Keys hierarchy
- Client credential authentication (client_id + client_secret)
- Multiple API keys per service account
✅ Secure Key Management
- Base64 encoded random byte generation (32 bytes)
- Automatic service account creation
- Secure client secret handling
✅ Advanced Key Operations
- Create API keys with custom metadata (name, description)
- Validate keys with client credentials
- Update key expiry and active status
- Soft delete with audit trails
✅ Powerful Query Capabilities
- Page-based pagination (configurable limit, max 100)
- Search by name or description (case-insensitive)
- Sort by multiple fields (created_at, updated_at, expiry_date, name, is_active)
- Filter by client_id, status, and deleted state
✅ Enterprise-Ready
- TypeScript with full type definitions
- TypeORM integration for PostgreSQL
- Comprehensive error handling
- Audit logging (created_by, updated_by, deleted_by)
- Soft delete support
Architecture
┌─────────────────────────────────────────────────────────┐
│ User │
│ (user_id: 123) │
└───────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Service Account │
│ (sa_info) │
│ ┌───────────────────────────────────────────────────┐ │
│ │ client_id: "550e8400-e29b-41d4-a716-446655440000" │ │
│ │ client_secret: "base64_encoded_secret" │ │
│ │ user_id: 123 │ │
│ └───────────────────────────────────────────────────┘ │
└───────────────────┬─────────────────────────────────────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
┌───────┐ ┌───────┐ ┌───────┐
│ API │ │ API │ │ API │
│ Key 1 │ │ Key 2 │ │ Key 3 │
└───────┘ └───────┘ └───────┘Database Schema
sa_info (Service Accounts)
id(UUID, Primary Key)user_id(Integer, Foreign Key)client_secret(String, Unique, Base64 encoded)description(String, Nullable)created_at,updated_at,created_by,updated_bydeleted_at,deleted_by(Soft delete)
api_keys
id(Integer, Primary Key)sa_info_id(UUID, Foreign Key → sa_info.id)api_key(String, Base64 encoded, 32 random bytes)name(String, Required)description(String, Nullable)is_active(Boolean, Default: true)expires_at(Timestamp, Nullable)created_at,updated_at,created_by,updated_bydeleted_at,deleted_by(Soft delete)
Installation
npm install key-manager-libQuick Start
1. Module Setup
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { KeyManagerModule } from 'key-manager-lib';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'your_username',
password: 'your_password',
database: 'your_database',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true, // Disable in production
}),
KeyManagerModule,
],
})
export class AppModule {}2. Create Your First API Key
import { Controller, Post, Body } from '@nestjs/common';
import { KeyManagerService } from 'key-manager-lib';
@Controller('api/keys')
export class KeyController {
constructor(private readonly keyManager: KeyManagerService) {}
@Post()
async createKey(@Body() dto: any) {
return await this.keyManager.createApiKey(dto);
}
}Request:
POST /api/keys
Content-Type: application/json
{
"user_id": 123,
"name": "Production API Key",
"description": "Main production environment key",
"is_active": true,
"expires_at": "2027-12-31T23:59:59.000Z"
}Response:
{
"success": true,
"message": "API key created successfully",
"data": {
"key_id": 1,
"raw_key": "aGVsbG93b3JsZGJhc2U2NGVuY29kZWRrZXk=",
"client_id": "550e8400-e29b-41d4-a716-446655440000",
"client_secret": "bXlzZWNyZXRiYXNlNjRlbmNvZGVkc3RyaW5n",
"name": "Production API Key",
"description": "Main production environment key",
"is_active": true,
"created_at": "2026-01-30T10:00:00.000Z",
"expires_at": "2027-12-31T23:59:59.000Z",
"status": "active"
}
}⚠️ Important: Save
client_secretsecurely! It's only returned when the service account is first created.
3. Validate an API Key
@Post('validate')
async validateKey(@Body() dto: any) {
return await this.keyManager.validateKey(dto);
}Request:
POST /api/keys/validate
Content-Type: application/json
{
"client_id": "550e8400-e29b-41d4-a716-446655440000",
"client_secret": "bXlzZWNyZXRiYXNlNjRlbmNvZGVkc3RyaW5n",
"api_key": "aGVsbG93b3JsZGJhc2U2NGVuY29kZWRrZXk="
}Response:
{
"success": true,
"message": "API key is valid",
"code": "KEY_VALID",
"data": {
"key_id": 1,
"user_id": 123,
"client_id": "550e8400-e29b-41d4-a716-446655440000",
"expires_at": "2027-12-31T23:59:59.000Z",
"status": "active"
}
}API Endpoints
Create API Key
POST /api/keysCreates a new API key. Automatically creates a service account if this is the user's first key.
Body Parameters:
user_id(number, required): User identifiername(string, required): Display name for the keydescription(string, optional): Key descriptionis_active(boolean, optional): Active status (default: true)expires_at(ISO 8601, optional): Expiration timestamp
Validate API Key
POST /api/keys/validateValidates an API key with client credentials.
Body Parameters:
client_id(UUID, required): Service account client IDclient_secret(string, required): Service account client secretapi_key(string, required): The API key to validate
Update API Key
PUT /api/keys/:idUpdates an existing API key's expiry date or active status.
Body Parameters:
expires_at(ISO 8601, optional): New expiration timestampis_active(boolean, optional): Active status
Remove API Key
DELETE /api/keys/:idSoft deletes an API key (sets deleted_at timestamp).
List API Keys
GET /api/keys?page=1&limit=20&search=productionRetrieves API keys with advanced filtering and pagination.
Query Parameters:
client_id(UUID, optional): Filter by service accountstatus(string, optional): Filter by status (active, inactive, expired, deleted)page(number, optional): Page number (default: 1)limit(number, optional): Results per page (default: 10, max: 100)search(string, optional): Search in name/descriptionsort_by(string, optional): Sort field (created_at, updated_at, expiry_date, name, is_active)sort_order(string, optional): ASC or DESC (default: DESC)include_deleted(boolean, optional): Include soft-deleted keys (default: false)
Development
Prerequisites
- Node.js >= 14.x
- PostgreSQL >= 12.x
- npm or yarn
Setup
- Clone the repository:
git clone <repository-url>
cd API-Key-Management- Install dependencies:
npm installConfigure database connection in your environment or
TypeOrmModule.forRoot()Build the library:
npm run build- Run in development mode:
npm run start:devProject Structure
src/
├── entities/
│ ├── api-key.entity.ts # API key database model
│ └── sa-info.entity.ts # Service account database model
├── interfaces/
│ ├── dto.interface.ts # Data transfer object types
│ ├── model.interface.ts # Domain model types
│ └── service.interface.ts # Service contract types
├── models/
│ └── api-key.model.ts # Business logic models
├── services/
│ ├── key-generation.service.ts # Key generation logic
│ └── key-validation.service.ts # Key validation logic
├── utils/
│ └── logger.util.ts # Logging utilities
├── key-manager.module.ts # NestJS module definition
├── key-manager.service.ts # Main service implementation
└── index.ts # Public API exportsTesting
Import the included Postman collection for comprehensive API testing:
# Located at project root
postman_collection.jsonTest Coverage:
- Service account creation and reuse
- API key CRUD operations
- Validation with client credentials
- Soft delete scenarios
- Pagination and search
- Error handling
- Security tests (SQL injection prevention)
Error Handling
All endpoints return structured error responses:
{
"success": false,
"message": "API key not found or already deleted",
"code": "KEY_NOT_FOUND",
"timestamp": "2026-01-30T10:00:00.000Z"
}Common Error Codes
KEY_NOT_FOUND: API key doesn't exist or is deletedKEY_EXPIRED: API key has expiredKEY_INACTIVE: API key is not activeKEY_ALREADY_DELETED: Attempting to delete an already deleted keyINVALID_CLIENT_CREDENTIALS: Client ID or secret is incorrectSERVICE_ACCOUNT_NOT_FOUND: Service account doesn't existEXPIRY_DATE_PAST: Expiry date must be in the futureINVALID_LIMIT: Pagination limit exceeds maximum (100)
Best Practices
Store Credentials Securely
- Never commit
client_secretvalues to version control - Use environment variables or secure vaults
- Rotate secrets periodically
- Never commit
API Key Lifecycle
- Set appropriate expiration dates
- Monitor expiring keys proactively
- Remove unused keys promptly
Validation Flow
- Always validate both client credentials and API key together
- Cache validation results with short TTL (if needed)
- Log validation attempts for security monitoring
Pagination
- Use reasonable limit values (10-50) for better performance
- Implement cursor-based pagination for large datasets if needed
Soft Delete
- Soft-deleted records are excluded by default
- Use
include_deleted=trueonly when necessary - Implement hard delete policies for compliance (GDPR, etc.)
Configuration
Environment variables (optional):
# Database
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=your_username
DB_PASSWORD=your_password
DB_DATABASE=api_key_management
# Application
NODE_ENV=production
LOG_LEVEL=infoContributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Built with ❤️ using NestJS and TypeORM
