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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mongrov/db

v0.2.5

Published

Database utilities for @mongrov apps: KVStore + RxDB

Readme

@mongrov/db

Database utilities for @mongrov apps:

  • KVStore — Unified async key-value storage (MMKV + SecureStore)
  • RxDB — Document database with reactive queries and offline-first support

Installation

pnpm add @mongrov/db react-native-mmkv

# For secure storage (optional)
npx expo install expo-secure-store

# For RxDB document database (optional)
pnpm add rxdb rxdb-premium

KVStore

Unified async API over MMKV (fast) and SecureStore (secure).

Basic Usage

import { createKVStore } from '@mongrov/db'

// Default MMKV store for preferences
const prefs = createKVStore()
await prefs.set('theme', 'dark')
const theme = await prefs.get('theme') // 'dark'

// Store objects (JSON serialized)
await prefs.setObject('user', { id: 1, name: 'Alice' })
const user = await prefs.getObject<User>('user')

Secure Storage

Use for tokens, secrets, and sensitive data:

const tokens = createKVStore({ secure: true })
await tokens.set('accessToken', 'jwt...')
await tokens.set('refreshToken', 'refresh...')

Instance Isolation

Create isolated stores with custom instance IDs:

const cache = createKVStore({ instanceId: 'api-cache' })
const settings = createKVStore({ instanceId: 'user-settings' })

// These don't interfere with each other
await cache.set('data', 'cached')
await settings.set('data', 'settings')

API

interface KVStore {
  // String operations
  get(key: string): Promise<string | null>
  set(key: string, value: string): Promise<void>
  delete(key: string): Promise<void>

  // Object operations (JSON serialization)
  getObject<T>(key: string): Promise<T | null>
  setObject<T>(key: string, value: T): Promise<void>

  // Utility
  clear(): Promise<void>
  getAllKeys(): Promise<string[]>
}

interface KVStoreConfig {
  secure?: boolean    // Use SecureStore (default: false)
  instanceId?: string // MMKV instance ID (default: 'mongrov-kv')
}

Configuration Options

| Option | Default | Description | |--------|---------|-------------| | secure | false | Use expo-secure-store instead of MMKV | | instanceId | 'mongrov-kv' | MMKV instance ID for isolation (ignored when secure=true) |

When to Use Each Backend

| Backend | Use For | Characteristics | |---------|---------|-----------------| | MMKV (default) | Preferences, theme, caches, flags | Fast, synchronous, large capacity | | SecureStore | Tokens, secrets, credentials | Encrypted, platform keychain, 2KB limit per value |

Auth Integration

Use createTokenStore to integrate with @mongrov/auth:

import { createKVStore, createTokenStore } from '@mongrov/db'
import { createAuthClient } from '@mongrov/auth'

// Create secure KVStore for tokens
const secureStore = createKVStore({ secure: true })

// Bridge to auth's TokenStore interface
const tokenStore = createTokenStore(secureStore)

// Use with auth client
const authClient = createAuthClient({
  adapter: myAdapter,
  tokenStore, // Tokens now stored via KVStore
})

The createTokenStore function wraps a KVStore to implement auth's TokenStore interface:

interface TokenStore {
  getAccessToken(): Promise<string | null>
  setAccessToken(token: string): Promise<void>
  getRefreshToken(): Promise<string | null>
  setRefreshToken(token: string): Promise<void>
  clear(): Promise<void>
}

RxDB Database

Create offline-first document databases with reactive queries.

Creating a Database

import { createDatabase } from '@mongrov/db'
import { getRxStorageSQLite } from 'rxdb-premium/plugins/storage-sqlite'

// Define your schema
const messageSchema = {
  version: 0,
  primaryKey: 'id',
  type: 'object',
  properties: {
    id: { type: 'string', maxLength: 100 },
    content: { type: 'string' },
    conversationId: { type: 'string' },
    createdAt: { type: 'number' },
  },
  required: ['id', 'content', 'conversationId'],
  indexes: ['conversationId', 'createdAt'],
}

// Create the database
const db = await createDatabase({
  name: 'myapp',
  storage: getRxStorageSQLite(),
  collections: [
    { name: 'messages', schema: messageSchema },
    { name: 'conversations', schema: conversationSchema },
  ],
  logger: console, // Optional: log database operations
})

React Integration

Use the DatabaseProvider and hooks for reactive data access:

import {
  DatabaseProvider,
  useDatabase,
  useCollection,
  useQuery,
  useDocument,
} from '@mongrov/db'

// Wrap your app with the provider
function App() {
  const [db, setDb] = useState(null)

  useEffect(() => {
    createDatabase(config).then(setDb)
    return () => db?.destroy()
  }, [])

  return (
    <DatabaseProvider database={db}>
      <MyApp />
    </DatabaseProvider>
  )
}

// Access database and collections
function MessageList({ conversationId }) {
  const { db, isReady } = useDatabase()
  const messages = useCollection('messages')

  // Reactive query with automatic updates
  const { data, isLoading, error } = useQuery('messages', {
    selector: { conversationId },
    sort: [{ createdAt: 'desc' }],
    limit: 50,
  })

  if (isLoading) return <Loading />
  if (error) return <Error message={error.message} />

  return <FlatList data={data} renderItem={renderMessage} />
}

// Fetch single document by ID
function MessageDetail({ messageId }) {
  const { data: message, isLoading } = useDocument('messages', messageId)

  if (isLoading) return <Loading />
  if (!message) return <NotFound />

  return <MessageView message={message} />
}

Schema Migrations

Handle schema upgrades with migration strategies:

const collections = [
  {
    name: 'messages',
    schema: messageSchemaV2, // version: 2
    migrationStrategies: {
      1: (oldDoc) => ({ ...oldDoc, readStatus: 'unread' }),
      2: (oldDoc) => ({ ...oldDoc, reactions: [] }),
    },
  },
]

API Reference

// Database factory
createDatabase(config: DatabaseConfig): Promise<RxDatabase>
destroyDatabase(db: RxDatabase): Promise<void>

// React hooks
useDatabase(): { db: RxDatabase | null, isReady: boolean }
useCollection<T>(name: string): RxCollection<T> | null
useQuery<T>(collection: string, query?: MangoQuery): QueryResult<T>
useDocument<T>(collection: string, id: string | null): DocumentResult<T>

// Result types
interface QueryResult<T> {
  data: T[]
  isLoading: boolean
  error: Error | null
  refetch: () => void
}

interface DocumentResult<T> {
  data: T | null
  isLoading: boolean
  error: Error | null
  refetch: () => void
}

Replication

Sync collections with remote backends using push/pull handlers:

import { createReplicationState, cancelReplication } from '@mongrov/db'

// Create replication with push/pull handlers
const replication = createReplicationState({
  replicationIdentifier: 'messages-sync',
  collection: db.messages,

  // Push local changes to remote
  push: {
    handler: async (docs) => {
      await api.sendMessages(docs)
    },
    batchSize: 50,
  },

  // Pull remote changes
  pull: {
    handler: async (checkpoint, batchSize) => {
      const result = await api.getMessages({ since: checkpoint, limit: batchSize })
      return {
        documents: result.messages,
        checkpoint: result.lastTimestamp,
      }
    },
    batchSize: 100,
  },

  live: true, // Enable real-time sync
  logger: console,
})

// Listen for errors
replication.error$.subscribe((error) => {
  console.error('Sync error:', error)
})

// Trigger manual sync
await replication.reSync()

// Stop replication when done
await cancelReplication(replication)

The replication helper is a thin wrapper over RxDB's replication plugin. Your push/pull handlers connect to your backend (REST API, WebSocket, or @mongrov/collab adapter).

Peer Dependencies

  • react-native-mmkv (required for KVStore)
  • expo-secure-store (optional — only if using secure: true)
  • rxdb (optional — only if using database features)

License

MIT