npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@owlmeans/mongo-resource

v0.1.1

Published

MongoDB resource implementation for OwlMeans Common applications. This package provides a complete MongoDB integration for the OwlMeans resource system, offering document storage, querying, indexing, and schema validation with the familiar OwlMeans resour

Downloads

133

Readme

@owlmeans/mongo-resource

MongoDB resource implementation for OwlMeans Common applications. This package provides a complete MongoDB integration for the OwlMeans resource system, offering document storage, querying, indexing, and schema validation with the familiar OwlMeans resource interface.

Overview

The @owlmeans/mongo-resource package extends the base @owlmeans/resource system with MongoDB-specific functionality. It provides:

  • MongoDB Resource Implementation: Complete resource interface backed by MongoDB collections
  • Document Management: Full CRUD operations with MongoDB ObjectId handling
  • Schema Integration: AJV schema validation with MongoDB document structure
  • Index Management: Automated index creation and management
  • Query Support: MongoDB query capabilities with pagination and sorting
  • Locking Mechanisms: Document-level locking for concurrent access control
  • Collection Lifecycle: Automated collection and index initialization
  • Type Safety: Full TypeScript support with MongoDB-specific types

This package is part of the OwlMeans database integration ecosystem, providing MongoDB as a storage backend for resources.

Installation

npm install @owlmeans/mongo-resource mongodb ajv

Dependencies

This package requires:

  • @owlmeans/resource: Core resource system
  • @owlmeans/server-context: Server context management
  • mongodb: MongoDB Node.js driver (peer dependency)
  • ajv: JSON Schema validation (peer dependency)

Core Concepts

MongoDB Resource

A MongoDB resource wraps a MongoDB collection with the OwlMeans resource interface, providing seamless integration between MongoDB documents and the resource system.

ObjectId Handling

The package automatically converts MongoDB ObjectIds to string IDs in the resource interface while maintaining MongoDB-native ObjectId usage internally.

Schema Validation

JSON Schema validation is applied to documents before storage, ensuring data integrity and consistency.

Collection Management

Collections are automatically created and configured with indexes during resource initialization.

API Reference

Types

MongoResource<T extends ResourceRecord>

Main MongoDB resource interface that extends the base Resource interface with MongoDB-specific capabilities.

interface MongoResource<T extends ResourceRecord> extends Resource<T>, ResourceLocker<T> {
  name?: string                                    // Collection name override
  schema?: AnySchema                              // AJV schema for validation
  indexes?: Array<IndexDefinition>               // Index definitions
  collection: Collection                          // MongoDB collection instance
  db(): Promise<Db>                              // Get database instance
  client(): Promise<MongoClient>                 // Get MongoDB client
  index<Type extends MongoResource<T>>(          // Add index definition
    name: string, 
    index: IndexSpecification, 
    options?: CreateIndexesOptions
  ): Type
  getDefaults(): Partial<T>                      // Get default values from schema
}

MongoDbService

Database service interface for MongoDB operations and connection management.

interface MongoDbService extends ResourceDbService<Db, MongoClient>, DbLocker<ResourceRecord> {
  // Inherits database service methods and locking capabilities
}

IndexDefinition

Type for defining MongoDB indexes.

interface IndexDefinition {
  name: string                        // Index name
  index: IndexSpecification          // MongoDB index specification
  options?: CreateIndexesOptions     // Index creation options
}

Factory Functions

makeMongoResource<R extends ResourceRecord, T extends MongoResource<R>>(alias: string, dbAlias?: string, serviceAlias?: string, makeCustomResource?: ResourceMaker<R, T>): T

Creates a MongoDB resource instance with full CRUD capabilities.

Parameters:

  • alias: Resource alias for registration
  • dbAlias: Database alias (default: 'mongo')
  • serviceAlias: Service alias (default: same as dbAlias)
  • makeCustomResource: Optional custom resource factory

Returns: MongoResource instance

Example:

import { makeMongoResource } from '@owlmeans/mongo-resource'

interface User extends ResourceRecord {
  id?: string
  name: string
  email: string
  createdAt?: Date
}

const userResource = makeMongoResource<User>('users')

// With custom collection and service
const customResource = makeMongoResource<User>('users', 'primary-db', 'mongo-service')

MongoDB Resource Methods

get(id: string, field?: string, opts?: LifecycleOptions): Promise<T>

Retrieves a document by ID or specified field. Throws UnknownRecordError if not found.

Parameters:

  • id: Document identifier
  • field: Field to search by (default: '_id')
  • opts: Lifecycle options

Returns: Promise resolving to the document

Example:

// Get by MongoDB ObjectId (converted from string)
const user = await userResource.get('507f1f77bcf86cd799439011')

// Get by custom field
const userByEmail = await userResource.get('[email protected]', 'email')

load(id: string, field?: string, opts?: LifecycleOptions): Promise<T | null>

Loads a document by ID or specified field. Returns null if not found.

Example:

const user = await userResource.load('507f1f77bcf86cd799439011')
if (user) {
  console.log('User found:', user.name)
} else {
  console.log('User not found')
}

create(record: Partial<T>, opts?: LifecycleOptions): Promise<T>

Creates a new document. Automatically applies schema defaults and generates ObjectId.

Parameters:

  • record: Document data (id will be generated)
  • opts: Lifecycle options

Returns: Promise resolving to the created document

Throws: RecordExists if record already has an ID

Example:

const newUser = await userResource.create({
  name: 'John Doe',
  email: '[email protected]'
})

console.log('Created user with ID:', newUser.id)

save(record: Partial<T>, opts?: Getter): Promise<T>

Saves a document (creates if new, updates if exists).

Example:

// Create new user (no id)
const user1 = await userResource.save({
  name: 'Jane Doe',
  email: '[email protected]'
})

// Update existing user (with id)
const user2 = await userResource.save({
  id: user1.id,
  name: 'Jane Smith'
})

update(record: Partial<T>, opts?: Getter): Promise<T>

Updates an existing document. Throws UnknownRecordError if not found.

Example:

const updatedUser = await userResource.update({
  id: '507f1f77bcf86cd799439011',
  name: 'Updated Name'
})

delete(id: string | T, opts?: Getter): Promise<T | null>

Deletes a document by ID or document object.

Parameters:

  • id: Document ID or document object
  • opts: Additional options or field name

Returns: Promise resolving to deleted document or null

Example:

// Delete by ID
const deleted = await userResource.delete('507f1f77bcf86cd799439011')

// Delete by document
const deleted2 = await userResource.delete(userObject)

// Delete by custom field
const deleted3 = await userResource.delete('[email protected]', 'email')

pick(id: string | T, opts?: Getter): Promise<T>

Deletes and returns a document. Throws UnknownRecordError if not found.

Example:

try {
  const removedUser = await userResource.pick('507f1f77bcf86cd799439011')
  console.log('Removed user:', removedUser.name)
} catch (error) {
  console.error('User not found for removal')
}

list(criteria?: ListOptions | ListCriteria, opts?: ListOptions): Promise<ListResult<T>>

Lists documents with optional filtering, pagination, and sorting.

Parameters:

  • criteria: MongoDB query criteria or list options
  • opts: Additional list options

Returns: Promise resolving to paginated results

Example:

// List all users with pagination
const result = await userResource.list({
  pager: { page: 0, size: 10 }
})

// List with MongoDB query
const activeUsers = await userResource.list({
  criteria: { status: 'active' },
  pager: { page: 0, size: 20, sort: [['createdAt', false]] }
})

console.log(`Found ${result.pager?.total} users`)
result.items.forEach(user => console.log(user.name))

Database Access Methods

db(): Promise<Db>

Gets the MongoDB database instance.

Example:

const db = await userResource.db()
const stats = await db.stats()
console.log('Database stats:', stats)

client(): Promise<MongoClient>

Gets the MongoDB client instance.

Example:

const client = await userResource.client()
const admin = client.db().admin()
const serverStatus = await admin.serverStatus()

Index Management

index<Type>(name: string, index: IndexSpecification, options?: CreateIndexesOptions): Type

Adds an index definition to the resource. Indexes are created during resource initialization.

Parameters:

  • name: Index name
  • index: MongoDB index specification
  • options: Index creation options

Returns: Resource instance for method chaining

Example:

// Single field index
userResource.index('email-unique', { email: 1 }, { unique: true })

// Compound index
userResource.index('name-email', { name: 1, email: 1 })

// Text index
userResource.index('text-search', { name: 'text', email: 'text' })

// TTL index
userResource.index('expire-sessions', { createdAt: 1 }, { expireAfterSeconds: 3600 })

Schema and Defaults

getDefaults(): Partial<T>

Gets default values from the AJV schema.

Example:

// Assuming schema has default values
const defaults = userResource.getDefaults()
console.log('Default values:', defaults)

// Defaults are automatically applied during create()

Locking Methods

lock(record: Partial<T>, fields?: string[]): Promise<T>

Locks specified fields of a document for concurrent access control.

Parameters:

  • record: Document to lock
  • fields: Fields to lock (default: secure fields from schema)

Returns: Promise resolving to locked document

unlock(record: Partial<T>, fields?: string[]): Promise<T>

Unlocks specified fields of a document.

Parameters:

  • record: Document to unlock
  • fields: Fields to unlock (default: secure fields from schema)

Returns: Promise resolving to unlocked document

Example:

// Lock user for critical update
const lockedUser = await userResource.lock({ id: userId }, ['balance'])

try {
  // Perform critical operations
  await updateUserBalance(lockedUser)
} finally {
  // Always unlock
  await userResource.unlock({ id: userId }, ['balance'])
}

Helper Functions

getSchemaSecureFeilds(schema: AnySchema): string[]

Extracts fields marked as secure in the AJV schema.

Parameters:

  • schema: AJV schema object

Returns: Array of secure field names

Example:

import { getSchemaSecureFeilds } from '@owlmeans/mongo-resource'

const schema = {
  type: 'object',
  properties: {
    password: { type: 'string', secure: true },
    balance: { type: 'number', secure: true },
    name: { type: 'string' }
  }
}

const secureFields = getSchemaSecureFeilds(schema)
// ['password', 'balance']

Constants

const DEFAULT_DB_ALIAS = 'mongo'     // Default database alias
const DEFAULT_PAGE_SIZE = 10         // Default pagination size

Usage Examples

Basic Resource Setup

import { makeMongoResource } from '@owlmeans/mongo-resource'
import { makeServerContext } from '@owlmeans/server-context'

// Define document interface
interface Product extends ResourceRecord {
  id?: string
  name: string
  price: number
  category: string
  inStock: boolean
  createdAt?: Date
}

// Create context with MongoDB configuration
const context = makeServerContext({
  service: 'product-service',
  dbs: [{
    service: 'mongo',
    alias: 'primary',
    host: 'localhost',
    port: 27017,
    schema: 'products_db'
  }]
})

// Create and configure resource
const productResource = makeMongoResource<Product>('products', 'primary')

// Add indexes
productResource
  .index('category-price', { category: 1, price: -1 })
  .index('name-text', { name: 'text' })
  .index('inStock', { inStock: 1 })

// Register resource with context
context.registerResource(productResource)

// Initialize context
await context.configure().init()

CRUD Operations

// Create product
const newProduct = await productResource.create({
  name: 'Gaming Laptop',
  price: 1299.99,
  category: 'electronics',
  inStock: true
})

// Get product
const product = await productResource.get(newProduct.id!)

// Update product
const updatedProduct = await productResource.update({
  id: product.id,
  price: 1199.99
})

// List products with filtering
const electronicsResult = await productResource.list({
  criteria: { category: 'electronics', inStock: true },
  pager: { 
    page: 0, 
    size: 20,
    sort: [['price', true]] // Sort by price descending
  }
})

// Delete product
await productResource.delete(product.id!)

Schema Validation Integration

import Ajv, { JSONSchemaType } from 'ajv'

// Define schema with validation and defaults
const productSchema: JSONSchemaType<Product> = {
  type: 'object',
  properties: {
    id: { type: 'string', nullable: true },
    name: { type: 'string', minLength: 1, maxLength: 100 },
    price: { type: 'number', minimum: 0 },
    category: { type: 'string', enum: ['electronics', 'clothing', 'books'] },
    inStock: { type: 'boolean', default: true },
    createdAt: { type: 'string', format: 'date-time', nullable: true }
  },
  required: ['name', 'price', 'category'],
  additionalProperties: false
}

// Create resource with schema
const productResource = makeMongoResource<Product>('products')
productResource.schema = productSchema

// Now all operations will validate against schema
try {
  await productResource.create({
    name: '', // Will fail validation (minLength: 1)
    price: -10, // Will fail validation (minimum: 0)
    category: 'invalid' // Will fail validation (not in enum)
  })
} catch (error) {
  console.error('Validation failed:', error)
}

Advanced Querying

// Complex MongoDB queries
const advancedResults = await productResource.list({
  criteria: {
    $and: [
      { price: { $gte: 100, $lte: 1000 } },
      { category: { $in: ['electronics', 'books'] } },
      { inStock: true }
    ]
  },
  pager: {
    page: 0,
    size: 15,
    sort: [['createdAt', false], ['price', true]]
  }
})

// Text search (requires text index)
const searchResults = await productResource.list({
  criteria: { $text: { $search: 'gaming laptop' } }
})

// Aggregation via direct database access
const db = await productResource.db()
const categoryStats = await db.collection('products').aggregate([
  { $group: { _id: '$category', count: { $sum: 1 }, avgPrice: { $avg: '$price' } } }
]).toArray()

Index Management

// Add various types of indexes
productResource
  // Unique index
  .index('sku-unique', { sku: 1 }, { unique: true })
  
  // Compound index for common queries
  .index('category-instock-price', { 
    category: 1, 
    inStock: 1, 
    price: -1 
  })
  
  // Text search index
  .index('search', { 
    name: 'text', 
    description: 'text' 
  }, { 
    weights: { name: 10, description: 5 } 
  })
  
  // TTL index for temporary records
  .index('temp-expire', { 
    createdAt: 1 
  }, { 
    expireAfterSeconds: 86400 // 24 hours
  })
  
  // Partial index
  .index('active-products', { 
    category: 1, 
    price: 1 
  }, { 
    partialFilterExpression: { inStock: true } 
  })

// Indexes are automatically created during resource initialization

Document Locking

// Schema with secure fields
const userSchema = {
  type: 'object',
  properties: {
    id: { type: 'string', nullable: true },
    username: { type: 'string' },
    balance: { type: 'number', secure: true }, // Secure field
    password: { type: 'string', secure: true } // Secure field
  },
  required: ['username']
}

const userResource = makeMongoResource<User>('users')
userResource.schema = userSchema

// Lock user for balance update
const userId = 'user123'
const lockedUser = await userResource.lock({ id: userId })

try {
  // Perform balance update
  const user = await userResource.get(userId)
  await userResource.update({
    id: userId,
    balance: user.balance + 100
  })
} finally {
  // Always unlock
  await userResource.unlock({ id: userId })
}

Multiple Database Support

// Configure multiple MongoDB databases
const context = makeServerContext({
  dbs: [
    {
      service: 'mongo',
      alias: 'primary',
      host: 'primary-mongo.example.com',
      schema: 'main_db'
    },
    {
      service: 'mongo', 
      alias: 'analytics',
      host: 'analytics-mongo.example.com',
      schema: 'analytics_db'
    }
  ]
})

// Create resources for different databases
const userResource = makeMongoResource<User>('users', 'primary')
const analyticsResource = makeMongoResource<AnalyticsRecord>('events', 'analytics')

// Both resources work independently with their respective databases

Error Handling

import { 
  UnknownRecordError, 
  RecordExists, 
  RecordUpdateFailed,
  MisshapedRecord 
} from '@owlmeans/resource'

try {
  // Various operations that can fail
  const user = await userResource.get('nonexistent-id')
} catch (error) {
  if (error instanceof UnknownRecordError) {
    console.error('User not found:', error.id)
  }
}

try {
  await userResource.create({ id: 'existing-id', name: 'Test' })
} catch (error) {
  if (error instanceof RecordExists) {
    console.error('User already exists')
  }
}

Advanced Features

Custom Collection Names

// Override collection name
const customResource = makeMongoResource<User>('users')
customResource.name = 'custom_users_collection'

Direct MongoDB Operations

// Access MongoDB collection directly for advanced operations
const collection = userResource.collection

// Use MongoDB-specific features
const bulkOps = collection.initializeUnorderedBulkOp()
bulkOps.insert({ name: 'User 1' })
bulkOps.insert({ name: 'User 2' })
await bulkOps.execute()

// Aggregation pipelines
const pipeline = [
  { $match: { active: true } },
  { $group: { _id: '$department', count: { $sum: 1 } } }
]
const results = await collection.aggregate(pipeline).toArray()

Schema Defaults Integration

const schemaWithDefaults = {
  type: 'object',
  properties: {
    status: { type: 'string', default: 'active' },
    createdAt: { type: 'string', format: 'date-time', default: new Date().toISOString() },
    settings: { 
      type: 'object', 
      default: { theme: 'light', notifications: true }
    }
  }
}

const resource = makeMongoResource<MyRecord>('records')
resource.schema = schemaWithDefaults

// Defaults are automatically applied during creation
const record = await resource.create({ name: 'Test' })
// record.status === 'active'
// record.settings === { theme: 'light', notifications: true }

Performance Considerations

  • Indexing: Create appropriate indexes for your query patterns
  • Pagination: Use pagination for large result sets
  • Connection Pooling: MongoDB driver handles connection pooling automatically
  • Schema Validation: Validation happens before database operations
  • Document Size: MongoDB has a 16MB document size limit
  • Query Optimization: Use MongoDB explain() to optimize queries

Best Practices

  1. Index Strategy: Create indexes that match your query patterns
  2. Schema Design: Design schemas that reflect your document structure
  3. Error Handling: Handle MongoDB-specific errors appropriately
  4. Resource Cleanup: Properly close MongoDB connections
  5. Security: Use secure fields for sensitive data that requires locking
  6. Validation: Always use schema validation for data integrity
  7. Pagination: Implement pagination for list operations

Integration with OwlMeans Ecosystem

Server Context Integration

import { makeServerContext } from '@owlmeans/server-context'

// MongoDB resources integrate seamlessly with server context
const context = makeServerContext(config)
context.registerResource(mongoResource)

Service Integration

import { createDbService } from '@owlmeans/resource'

// MongoDB service provides database access
const mongoService = createDbService('mongo', mongoConfig)
context.registerService(mongoService)

Authentication Integration

// MongoDB resources work with authentication scopes
const authenticatedResource = makeMongoResource<ProtectedRecord>('protected')
// Access control is handled at the application level

Related Packages