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/resource

v0.1.1

Published

A comprehensive data persistence and resource management library for OwlMeans Common applications. This package provides abstract interfaces and implementations for handling database operations, resource management, and data access patterns across differe

Downloads

426

Readme

@owlmeans/resource

A comprehensive data persistence and resource management library for OwlMeans Common applications. This package provides abstract interfaces and implementations for handling database operations, resource management, and data access patterns across different storage backends.

Overview

The @owlmeans/resource package is a foundational data access library in the OwlMeans Common ecosystem that provides:

  • Database Abstraction: Generic interfaces for different database backends
  • CRUD Operations: Complete Create, Read, Update, Delete functionality
  • Resource Management: Advanced resource lifecycle management
  • Query System: Flexible query and filtering capabilities
  • Pagination Support: Built-in pagination and sorting functionality
  • Validation Integration: Schema validation with JSON Schema support
  • Caching & TTL: Time-to-live and caching mechanisms
  • Multi-Database Support: Handle multiple database configurations

Installation

npm install @owlmeans/resource

Core Concepts

Resource Interface

The Resource<T> interface provides the primary data access API for working with records that extend ResourceRecord.

Database Service

The ResourceDbService interface provides an abstraction for database-specific implementations, allowing different storage backends (MongoDB, Redis, etc.) to be used interchangeably.

Resource Records

All data entities extend ResourceRecord, which provides a basic structure with an optional id field.

List Operations

Advanced listing functionality with criteria-based filtering, pagination, and sorting capabilities.

API Reference

Types

Resource<T extends ResourceRecord>

Primary interface for resource operations.

interface Resource<T extends ResourceRecord> extends BasicResource {
  get: <Type extends T>(id: string, field?: Getter, opts?: LifecycleOptions) => Promise<Type>
  load: <Type extends T>(id: string, field?: Getter, opts?: LifecycleOptions) => Promise<Type | null>
  list: <Type extends T>(criteria?: ListOptions | ListCriteria, opts?: ListOptions) => Promise<ListResult<Type>>
  save: <Type extends T>(record: Partial<Type>, opts?: Getter) => Promise<Type>
  create: <Type extends T>(record: Partial<Type>, opts?: LifecycleOptions) => Promise<Type>
  update: <Type extends T>(record: Partial<Type>, opts?: Getter) => Promise<Type>
  delete: <Type extends T>(id: string | T, opts?: Getter) => Promise<Type | null>
  pick: <Type extends T>(id: string | T, opts?: Getter) => Promise<Type>
}

ResourceRecord

Base interface for all resource entities.

interface ResourceRecord {
  id?: string
}

ListOptions

Configuration for list operations.

interface ListOptions {
  pager?: ListPager
  criteria?: ListCriteria
}

ListPager

Pagination and sorting configuration.

interface ListPager {
  sort?: ListSort[]
  page?: number
  size?: number
  total?: number
}

ListResult<T>

Result structure for list operations.

interface ListResult<T extends ResourceRecord> {
  items: T[]
  pager?: ListPager
}

DbConfig

Database configuration structure.

interface DbConfig<P extends {} = {}> {
  service: string
  alias?: string
  host: string | string[]
  port?: number
  user?: string
  secret?: string
  schema?: string
  resourcePrefix?: string
  entitySensitive?: boolean
  serviceSensitive?: boolean
  encryptionKey?: string
  meta?: P
}

Factory Functions

createDbService<Db, Client, Service>(alias: string, override: Partial<Service>, init?: InitMethod<Service>): Service

Creates a database service with the specified configuration.

Parameters:

  • alias: Service alias for registration
  • override: Partial service implementation
  • init: Optional initialization method

Returns: Configured database service

Helper Functions

createListSchema<T>(schema: JSONSchemaType<T>): JSONSchemaType<ListResult<T>>

Creates a JSON Schema for list results based on a record schema.

prepareListOptions(defPageSize?: number, criteria?: ListOptions | ListCriteria, opts?: ListOptions): ListOptions

Prepares and normalizes list options with default pagination.

filterObject<T>(obj: T, keep?: string[]): T

Filters an object, removing null/undefined values except for specified keys.

CRUD Operations

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

Retrieves a record by ID. Throws UnknownRecordError if not found.

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

Loads a record by ID. Returns null if not found.

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

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

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

Creates a new record. Throws RecordExists if record already exists.

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

Updates an existing record. Throws UnknownRecordError if not found.

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

Deletes a record by ID or record object.

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

Retrieves a record, throwing UnknownRecordError if not found.

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

Lists records with optional filtering, pagination, and sorting.

Error Types

The package provides a comprehensive error hierarchy for resource operations:

ResourceError

Base error class for all resource-related errors.

UnknownRecordError

Error thrown when a requested record is not found.

class UnknownRecordError extends ResourceError {
  constructor(id: string)
  get id(): string  // Extract the record ID from the error
}

MisshapedRecord

Error for malformed or invalid record data.

RecordExists

Error thrown when attempting to create a record that already exists.

RecordUpdateFailed

Error for failed update operations.

UnsupportedArgumentError

Error for unsupported method arguments.

UnsupportedMethodError

Error for unsupported operations.

Usage Examples

Basic Resource Operations

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

// Create a new user
const newUser: User = await userResource.create({
  name: 'John Doe',
  email: '[email protected]'
})

// Get a user by ID
const user = await userResource.get('user123')

// Update a user
const updatedUser = await userResource.update({
  id: 'user123',
  name: 'John Smith'
})

// Delete a user
await userResource.delete('user123')

List Operations with Pagination

// Basic list
const users = await userResource.list()

// List with pagination
const pagedUsers = await userResource.list({
  pager: {
    page: 0,
    size: 10,
    sort: [['name', true], 'createdAt']  // Sort by name asc, then createdAt
  }
})

// List with criteria
const filteredUsers = await userResource.list({
  criteria: {
    status: 'active',
    role: ['admin', 'moderator']
  },
  pager: { size: 20 }
})

Error Handling

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

try {
  const user = await userResource.get('nonexistent')
} catch (error) {
  if (error instanceof UnknownRecordError) {
    console.log(`User not found: ${error.id}`)
  }
}

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

Database Configuration

import { createBasicContext } from '@owlmeans/context'

const context = createBasicContext({
  dbs: [
    {
      service: 'mongo-db',
      alias: 'primary',
      host: 'localhost',
      port: 27017,
      schema: 'myapp',
      user: 'dbuser',
      secret: 'dbpass',
      resourcePrefix: 'app_',
      entitySensitive: true
    },
    {
      service: 'redis-db',
      alias: 'cache',
      host: 'localhost',
      port: 6379
    }
  ]
})

Custom Resource Implementation

import { Resource, ResourceRecord } from '@owlmeans/resource'

interface Product extends ResourceRecord {
  name: string
  price: number
  category: string
}

class ProductResource implements Resource<Product> {
  // Implement all required methods
  async get(id: string): Promise<Product> {
    // Custom implementation
  }
  
  async list(criteria?: ListOptions): Promise<ListResult<Product>> {
    // Custom implementation with business logic
  }
  
  // ... other methods
}

TTL and Caching

// Load with TTL
const user = await userResource.load('user123', {
  ttl: 300  // Cache for 5 minutes
})

// Load with expiration date
const user = await userResource.load('user123', {
  ttl: new Date(Date.now() + 300000)  // Expire in 5 minutes
})

Advanced Features

Multi-Database Support

The package supports multiple database configurations within a single application:

// Different databases for different resource types
const userResource = makeUserResource('primary-db')
const cacheResource = makeCacheResource('redis-db')
const analyticsResource = makeAnalyticsResource('analytics-db')

Schema Validation

Integration with JSON Schema for record validation:

import { createListSchema } from '@owlmeans/resource'

const userSchema: JSONSchemaType<User> = {
  type: 'object',
  properties: {
    id: { type: 'string', nullable: true },
    name: { type: 'string' },
    email: { type: 'string', format: 'email' }
  },
  required: ['name', 'email'],
  additionalProperties: false
}

const userListSchema = createListSchema(userSchema)

Resource Locking

Support for record locking mechanisms:

interface ResourceLocker<T extends ResourceRecord> {
  lock: (record: Partial<T>, fields?: string[]) => Promise<T>
  unlock: (record: Partial<T>, fields?: string[]) => Promise<T>
}

Integration with OwlMeans Ecosystem

The @owlmeans/resource package integrates with:

  • @owlmeans/context: Service registration and configuration management
  • @owlmeans/error: Resilient error handling system
  • @owlmeans/mongo-resource: MongoDB-specific implementation
  • @owlmeans/redis-resource: Redis-specific implementation
  • @owlmeans/client-resource: Client-side resource management
  • @owlmeans/static-resource: Static file resource management

Performance Considerations

  • Use load() instead of get() when records might not exist
  • Implement appropriate pagination for large datasets
  • Use TTL for frequently accessed but rarely changed data
  • Consider database-specific optimizations for complex queries
  • Implement proper indexing strategies for list criteria

Security Features

  • Entity-sensitive configurations for multi-tenant applications
  • Service-sensitive configurations for microservice architectures
  • Encryption key support for sensitive data
  • Input validation through schema integration

Fixes #32.