CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-nestjs--mongoose

NestJS module that provides seamless integration between NestJS and Mongoose ODM for MongoDB database operations

80

1.02x
Quality

Pending

Does it follow best practices?

Impact

80%

1.02x

Average score across 10 eval scenarios

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

utility-functions.mddocs/

Utility Functions

Helper functions for generating dependency injection tokens, handling connection retry logic, and managing raw object definitions. These utilities support the core functionality of the NestJS Mongoose integration.

Capabilities

Token Generation Functions

Utility functions for creating dependency injection tokens used by the NestJS container.

getModelToken

Generates the dependency injection token used for model injection.

/**
 * Generates dependency injection token for a model
 * @param model - Model name (typically the class name)
 * @param connectionName - Optional connection name for multi-database setup
 * @returns String token for dependency injection
 */
function getModelToken(model: string, connectionName?: string): string;

Usage Examples:

import { getModelToken } from '@nestjs/mongoose';

// Basic model token
const userToken = getModelToken('User');
// Returns: 'UserModel'

// Model token with named connection
const userTokenWithConnection = getModelToken('User', 'users-db');
// Returns: 'users-db/UserModel'

// Use in custom providers
import { Module, Provider } from '@nestjs/common';

const customProviders: Provider[] = [
  {
    provide: 'USER_REPOSITORY',
    useFactory: (userModel: Model<User>) => {
      return new UserRepository(userModel);
    },
    inject: [getModelToken('User')],
  },
  {
    provide: 'USER_CACHE_SERVICE',
    useFactory: (userModel: Model<User>, cacheService: CacheService) => {
      return new UserCacheService(userModel, cacheService);
    },
    inject: [getModelToken('User'), 'CACHE_SERVICE'],
  },
];

@Module({
  providers: [...customProviders],
  exports: ['USER_REPOSITORY', 'USER_CACHE_SERVICE'],
})
export class UserRepositoryModule {}

// Testing with model tokens
import { Test, TestingModule } from '@nestjs/testing';

describe('UserService', () => {
  let service: UserService;
  let mockUserModel: Model<User>;

  beforeEach(async () => {
    const mockModel = {
      find: jest.fn(),
      findById: jest.fn(),
      create: jest.fn(),
      findByIdAndUpdate: jest.fn(),
      findByIdAndDelete: jest.fn(),
    };

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UserService,
        {
          provide: getModelToken('User'),
          useValue: mockModel,
        },
      ],
    }).compile();

    service = module.get<UserService>(UserService);
    mockUserModel = module.get<Model<User>>(getModelToken('User'));
  });

  it('should create a user', async () => {
    const userData = { name: 'John Doe', email: 'john@example.com' };
    mockUserModel.create = jest.fn().mockResolvedValue(userData);

    const result = await service.create(userData);
    expect(mockUserModel.create).toHaveBeenCalledWith(userData);
    expect(result).toEqual(userData);
  });
});

getConnectionToken

Generates the dependency injection token used for connection injection.

/**
 * Generates dependency injection token for a connection
 * @param name - Optional connection name for multi-database setup
 * @returns String token for dependency injection
 */
function getConnectionToken(name?: string): string;

Usage Examples:

import { getConnectionToken } from '@nestjs/mongoose';

// Default connection token
const defaultToken = getConnectionToken();
// Returns: 'DatabaseConnection'

// Named connection token
const namedToken = getConnectionToken('analytics-db');
// Returns: 'analytics-db'

// Use in custom providers
const databaseProviders: Provider[] = [
  {
    provide: 'DATABASE_HEALTH_CHECK',
    useFactory: (connection: Connection) => {
      return new DatabaseHealthCheck(connection);
    },
    inject: [getConnectionToken()],
  },
  {
    provide: 'ANALYTICS_DB_MANAGER',
    useFactory: (connection: Connection) => {
      return new DatabaseManager(connection);
    },
    inject: [getConnectionToken('analytics-db')],
  },
];

// Multiple connections in service
@Injectable()
export class MultiDatabaseService {
  constructor(
    @Inject(getConnectionToken()) private defaultConnection: Connection,
    @Inject(getConnectionToken('analytics-db')) private analyticsConnection: Connection,
  ) {}

  async getConnectionInfo() {
    return {
      default: {
        name: this.defaultConnection.name,
        readyState: this.defaultConnection.readyState,
      },
      analytics: {
        name: this.analyticsConnection.name,
        readyState: this.analyticsConnection.readyState,
      },
    };
  }
}

Connection Retry Utility

RxJS operator for handling connection retry logic with configurable attempts and delays.

handleRetry

Creates an RxJS operator for connection retry logic.

/**
 * RxJS operator for connection retry logic
 * @param retryAttempts - Number of retry attempts (default: 9)
 * @param retryDelay - Delay between retries in milliseconds (default: 3000)
 * @param verboseRetryLog - Enable verbose retry logging (default: false)
 * @returns RxJS operator function
 */
function handleRetry(
  retryAttempts?: number,
  retryDelay?: number,
  verboseRetryLog?: boolean
): <T>(source: Observable<T>) => Observable<T>;

Usage Examples:

import { handleRetry } from '@nestjs/mongoose';
import { Observable, throwError } from 'rxjs';
import { retryWhen } from 'rxjs/operators';

// Basic retry configuration
const connectWithRetry = (uri: string) => {
  return mongoose.connect(uri).pipe(
    retryWhen(handleRetry(5, 2000, true)) // 5 attempts, 2 second delay, verbose logging
  );
};

// Custom connection service with retry logic
@Injectable()
export class ConnectionService {
  private connect(uri: string): Observable<Connection> {
    return new Observable(subscriber => {
      mongoose.connect(uri)
        .then(connection => {
          subscriber.next(connection);
          subscriber.complete();
        })
        .catch(error => {
          subscriber.error(error);
        });
    }).pipe(
      retryWhen(handleRetry(10, 5000, true)) // 10 attempts, 5 second delay
    );
  }

  async establishConnection(uri: string): Promise<Connection> {
    return this.connect(uri).toPromise();
  }
}

// Integration with module configuration
export class DatabaseConnectionFactory {
  static createConnectionProvider(
    uri: string,
    options: { retryAttempts?: number; retryDelay?: number; verboseRetryLog?: boolean } = {}
  ): Provider {
    return {
      provide: 'DATABASE_CONNECTION',
      useFactory: async () => {
        const { retryAttempts = 9, retryDelay = 3000, verboseRetryLog = false } = options;
        
        return new Observable(subscriber => {
          mongoose.connect(uri)
            .then(connection => subscriber.next(connection))
            .catch(error => subscriber.error(error));
        }).pipe(
          retryWhen(handleRetry(retryAttempts, retryDelay, verboseRetryLog))
        ).toPromise();
      },
    };
  }
}

Raw Object Definition Utility

Function for marking object definitions as raw to bypass type transformation.

raw

Marks an object definition as raw, preventing Mongoose from applying type transformations.

/**
 * Marks an object definition as raw (bypasses type transformation)
 * @param definition - Object definition to mark as raw
 * @returns Object definition with raw marker
 */
function raw(definition: Record<string, any>): Record<string, any>;

Usage Examples:

import { raw } from '@nestjs/mongoose';

@Schema()
export class FlexibleDocument {
  @Prop({ required: true })
  name: string;

  // Raw nested object - no type enforcement
  @Prop(raw({
    metadata: { type: mongoose.Schema.Types.Mixed },
    settings: {
      theme: { type: String, default: 'light' },
      notifications: { type: Boolean, default: true },
      preferences: mongoose.Schema.Types.Mixed
    },
    analytics: {
      views: { type: Number, default: 0 },
      clicks: { type: Number, default: 0 },
      customEvents: [mongoose.Schema.Types.Mixed]
    }
  }))
  dynamicData: {
    metadata: any;
    settings: {
      theme: string;
      notifications: boolean;
      preferences: any;
    };
    analytics: {
      views: number;
      clicks: number;
      customEvents: any[];
    };
  };

  // Raw array of mixed objects
  @Prop(raw([{
    type: { type: String, required: true },
    data: mongoose.Schema.Types.Mixed,
    timestamp: { type: Date, default: Date.now }
  }]))
  events: Array<{
    type: string;
    data: any;
    timestamp: Date;
  }>;
}

// Complex raw schema for configuration
@Schema()
export class ApplicationConfig {
  @Prop({ required: true })
  appName: string;

  @Prop(raw({
    database: {
      mongodb: {
        uri: String,
        options: mongoose.Schema.Types.Mixed
      },
      redis: {
        host: String,
        port: Number,
        options: mongoose.Schema.Types.Mixed
      }
    },
    services: [{
      name: { type: String, required: true },
      enabled: { type: Boolean, default: true },
      config: mongoose.Schema.Types.Mixed
    }],
    features: mongoose.Schema.Types.Mixed,
    customFields: mongoose.Schema.Types.Mixed
  }))
  configuration: {
    database: {
      mongodb: {
        uri: string;
        options: any;
      };
      redis: {
        host: string;
        port: number;
        options: any;
      };
    };
    services: Array<{
      name: string;
      enabled: boolean;
      config: any;
    }>;
    features: any;
    customFields: any;
  };
}

// Usage in service
@Injectable()
export class ConfigService {
  constructor(
    @InjectModel(ApplicationConfig.name) 
    private configModel: Model<ApplicationConfig>
  ) {}

  async updateConfig(appName: string, configUpdate: any): Promise<ApplicationConfig> {
    // Raw objects allow any structure in configUpdate
    return this.configModel.findOneAndUpdate(
      { appName },
      { 
        $set: { 
          'configuration.features': configUpdate.features,
          'configuration.customFields': configUpdate.customFields
        }
      },
      { new: true }
    ).exec();
  }

  async addService(appName: string, service: { name: string; config: any }): Promise<ApplicationConfig> {
    return this.configModel.findOneAndUpdate(
      { appName },
      { 
        $push: { 
          'configuration.services': {
            name: service.name,
            enabled: true,
            config: service.config // Any structure allowed
          }
        }
      },
      { new: true }
    ).exec();
  }
}

Constants

Pre-defined constants used throughout the NestJS Mongoose integration.

/** Default connection name when none is specified */
const DEFAULT_DB_CONNECTION = 'DatabaseConnection';

/** Injection token for Mongoose module options */
const MONGOOSE_MODULE_OPTIONS = 'MongooseModuleOptions';

/** Injection token for connection name */
const MONGOOSE_CONNECTION_NAME = 'MongooseConnectionName';

/** Symbol used to mark raw object definitions */
const RAW_OBJECT_DEFINITION = 'RAW_OBJECT_DEFINITION';

Usage Examples:

import { 
  DEFAULT_DB_CONNECTION, 
  MONGOOSE_MODULE_OPTIONS, 
  MONGOOSE_CONNECTION_NAME,
  RAW_OBJECT_DEFINITION 
} from '@nestjs/mongoose';

// Check if using default connection
export function isDefaultConnection(connectionName?: string): boolean {
  return !connectionName || connectionName === DEFAULT_DB_CONNECTION;
}

// Custom provider using constants
const configProvider: Provider = {
  provide: 'CUSTOM_MONGOOSE_CONFIG',
  useFactory: (options: MongooseModuleOptions, connectionName: string) => {
    return {
      options,
      connectionName,
      isDefault: connectionName === DEFAULT_DB_CONNECTION,
    };
  },
  inject: [MONGOOSE_MODULE_OPTIONS, MONGOOSE_CONNECTION_NAME],
};

// Utility to check raw definitions
export function isRawDefinition(definition: any): boolean {
  return definition && definition[RAW_OBJECT_DEFINITION] === true;
}

Advanced Utility Patterns

Token Generation for Dynamic Modules

export class DynamicTokenGenerator {
  static generateModelTokens(models: string[], connectionName?: string): string[] {
    return models.map(model => getModelToken(model, connectionName));
  }

  static generateConnectionTokens(connectionNames: string[]): string[] {
    return connectionNames.map(name => getConnectionToken(name));
  }

  static createProviderMap(
    models: string[], 
    connectionName?: string
  ): Record<string, string> {
    return models.reduce((map, model) => {
      map[model] = getModelToken(model, connectionName);
      return map;
    }, {} as Record<string, string>);
  }
}

// Usage in dynamic modules
@Module({})
export class DynamicDatabaseModule {
  static forFeature(models: string[], connectionName?: string): DynamicModule {
    const tokens = DynamicTokenGenerator.generateModelTokens(models, connectionName);
    const providerMap = DynamicTokenGenerator.createProviderMap(models, connectionName);
    
    return {
      module: DynamicDatabaseModule,
      providers: [
        {
          provide: 'MODEL_TOKENS',
          useValue: tokens,
        },
        {
          provide: 'PROVIDER_MAP',
          useValue: providerMap,
        },
      ],
      exports: ['MODEL_TOKENS', 'PROVIDER_MAP'],
    };
  }
}

docs

dependency-injection.md

index.md

module-configuration.md

schema-definition.md

schema-factories.md

utility-functions.md

validation-pipes.md

tile.json