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

@owlmeans/native-db

v0.1.1

Published

The **@owlmeans/native-db** package provides React Native database functionality using AsyncStorage for OwlMeans Common Libraries, designed for fullstack microservices and microclients development with focus on security and proper authentication and autho

Downloads

23

Readme

@owlmeans/native-db

The @owlmeans/native-db package provides React Native database functionality using AsyncStorage for OwlMeans Common Libraries, designed for fullstack microservices and microclients development with focus on security and proper authentication and authorization.

Purpose

This package serves as the React Native database implementation that:

  • Provides persistent storage for React Native apps using AsyncStorage
  • Implements client database interface for consistency across platforms
  • Supports key-value storage operations with JSON serialization
  • Offers database service abstraction with context integration
  • Enables multi-store management with isolated namespaces
  • Provides data persistence for offline-capable mobile applications

Core Concepts

Native Database Service

A service that manages multiple AsyncStorage-based databases with namespace isolation, providing a consistent interface for data persistence in React Native applications.

AsyncStorage Integration

Leverages React Native's AsyncStorage for persistent data storage, automatically handling JSON serialization and deserialization of stored values.

Multi-Store Support

Supports multiple isolated storage instances (stores) within a single service, allowing for organized data separation by feature or domain.

Client Database Interface

Implements the standard ClientDb interface from @owlmeans/client-resource, ensuring compatibility with other OwlMeans client packages.

API Reference

Factory Functions

makeNativeDbService(alias?): NativeDbService

Creates a React Native database service that manages AsyncStorage-based databases.

import { makeNativeDbService } from '@owlmeans/native-db'

const dbService = makeNativeDbService('app-storage')

Parameters:

  • alias: string (optional) - Service alias, defaults to DEFAULT_ALIAS

Returns: NativeDbService instance

appendNativeDbService<C, T>(context, alias?): T

Registers a native database service with a client context.

import { appendNativeDbService } from '@owlmeans/native-db'
import { makeClientContext } from '@owlmeans/client-context'

const context = makeClientContext(config)
appendNativeDbService(context, 'storage')

Parameters:

  • context: T - Client context instance
  • alias: string (optional) - Service alias

Returns: Context with registered database service

Service Interface

NativeDbService

Database service interface extending ClientDbService.

interface NativeDbService extends ClientDbService {
  initialize(alias?: string): Promise<ClientDb>
  erase(): Promise<void>
}

Database Operations

initialize(alias?): Promise<ClientDb>

Initializes and returns a database instance for the specified alias.

const dbService = context.service('storage')
const userDb = await dbService.initialize('users')
const settingsDb = await dbService.initialize('settings')

Parameters:

  • alias: string (optional) - Database identifier

Returns: Promise resolving to ClientDb instance

erase(): Promise<void>

Clears all data from AsyncStorage.

await dbService.erase() // Clears all stored data

ClientDb Interface

The returned database instances implement the ClientDb interface:

get<T>(id): Promise<T | null>

Retrieves a value by ID.

const userData = await userDb.get<User>('current-user')

set<T>(id, value): Promise<void>

Stores a value with the specified ID.

await userDb.set('current-user', { id: '123', name: 'John Doe' })

has(id): Promise<boolean>

Checks if a value exists for the given ID.

const hasUser = await userDb.has('current-user')

del(id): Promise<boolean>

Deletes a value and returns whether it existed.

const wasDeleted = await userDb.del('current-user')

Types

NativeDbService

Main service interface for native database operations.

interface NativeDbService extends ClientDbService {}

Constants

DEFAULT_ALIAS

Default alias for the database service (imported from @owlmeans/client-resource).

const DEFAULT_ALIAS = DEFAULT_DB_ALIAS

Usage Examples

Basic Database Setup

import { makeClientContext, makeClientConfig } from '@owlmeans/client-context'
import { appendNativeDbService } from '@owlmeans/native-db'
import { AppType } from '@owlmeans/context'

// Create React Native app context
const config = makeClientConfig(AppType.Frontend, 'mobile-app')
const context = makeClientContext(config)

// Add native database service
appendNativeDbService(context, 'app-db')

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

// Use database service
const dbService = context.service('app-db')
const userDb = await dbService.initialize('users')

User Data Management

import { appendNativeDbService } from '@owlmeans/native-db'

interface User {
  id: string
  name: string
  email: string
  preferences: UserPreferences
}

const setupUserStorage = async (context) => {
  const dbService = context.service('app-db')
  const userDb = await dbService.initialize('users')
  
  // Store user data
  const saveUser = async (user: User) => {
    await userDb.set(`user:${user.id}`, user)
    await userDb.set('current-user', user.id)
  }
  
  // Load current user
  const getCurrentUser = async (): Promise<User | null> => {
    const currentUserId = await userDb.get<string>('current-user')
    if (!currentUserId) return null
    
    return await userDb.get<User>(`user:${currentUserId}`)
  }
  
  // Update user preferences
  const updatePreferences = async (preferences: UserPreferences) => {
    const user = await getCurrentUser()
    if (user) {
      user.preferences = preferences
      await saveUser(user)
    }
  }
  
  return { saveUser, getCurrentUser, updatePreferences }
}

App Settings Persistence

interface AppSettings {
  theme: 'light' | 'dark'
  notifications: boolean
  language: string
  privacy: PrivacySettings
}

const manageAppSettings = async (context) => {
  const dbService = context.service('app-db')
  const settingsDb = await dbService.initialize('settings')
  
  const defaultSettings: AppSettings = {
    theme: 'light',
    notifications: true,
    language: 'en',
    privacy: { analytics: false, crashReports: true }
  }
  
  // Load settings with defaults
  const loadSettings = async (): Promise<AppSettings> => {
    const settings = await settingsDb.get<AppSettings>('app-settings')
    return settings ? { ...defaultSettings, ...settings } : defaultSettings
  }
  
  // Save settings
  const saveSettings = async (settings: AppSettings) => {
    await settingsDb.set('app-settings', settings)
  }
  
  // Update specific setting
  const updateSetting = async <K extends keyof AppSettings>(
    key: K, 
    value: AppSettings[K]
  ) => {
    const settings = await loadSettings()
    settings[key] = value
    await saveSettings(settings)
  }
  
  return { loadSettings, saveSettings, updateSetting }
}

Offline Data Caching

interface CacheEntry<T> {
  data: T
  timestamp: number
  expiry?: number
}

const createCacheManager = async (context) => {
  const dbService = context.service('app-db')
  const cacheDb = await dbService.initialize('cache')
  
  const cache = {
    // Set cache entry with optional TTL
    set: async <T>(key: string, data: T, ttlMinutes?: number) => {
      const entry: CacheEntry<T> = {
        data,
        timestamp: Date.now(),
        expiry: ttlMinutes ? Date.now() + (ttlMinutes * 60 * 1000) : undefined
      }
      await cacheDb.set(key, entry)
    },
    
    // Get cache entry if not expired
    get: async <T>(key: string): Promise<T | null> => {
      const entry = await cacheDb.get<CacheEntry<T>>(key)
      if (!entry) return null
      
      // Check expiry
      if (entry.expiry && Date.now() > entry.expiry) {
        await cacheDb.del(key)
        return null
      }
      
      return entry.data
    },
    
    // Remove cache entry
    remove: async (key: string) => {
      return await cacheDb.del(key)
    },
    
    // Clean expired entries
    cleanup: async () => {
      // Note: AsyncStorage doesn't support key enumeration
      // This would require maintaining a separate index
      console.log('Cache cleanup would require key enumeration')
    }
  }
  
  return cache
}

Authentication State Management

interface AuthState {
  isAuthenticated: boolean
  token?: string
  refreshToken?: string
  user?: User
  expiresAt?: number
}

const manageAuthState = async (context) => {
  const dbService = context.service('app-db')
  const authDb = await dbService.initialize('auth')
  
  const auth = {
    // Save authentication state
    saveAuth: async (authState: AuthState) => {
      await authDb.set('auth-state', authState)
    },
    
    // Load authentication state
    loadAuth: async (): Promise<AuthState | null> => {
      const state = await authDb.get<AuthState>('auth-state')
      
      // Check if token is expired
      if (state?.expiresAt && Date.now() > state.expiresAt) {
        await auth.clearAuth()
        return null
      }
      
      return state
    },
    
    // Check if authenticated
    isAuthenticated: async (): Promise<boolean> => {
      const state = await auth.loadAuth()
      return state?.isAuthenticated ?? false
    },
    
    // Clear authentication
    clearAuth: async () => {
      await authDb.del('auth-state')
    },
    
    // Update user info
    updateUser: async (user: User) => {
      const state = await auth.loadAuth()
      if (state) {
        state.user = user
        await auth.saveAuth(state)
      }
    }
  }
  
  return auth
}

Form Draft Persistence

interface FormDraft {
  formId: string
  data: Record<string, any>
  timestamp: number
}

const createFormDraftManager = async (context) => {
  const dbService = context.service('app-db')
  const draftDb = await dbService.initialize('form-drafts')
  
  const drafts = {
    // Save form draft
    saveDraft: async (formId: string, data: Record<string, any>) => {
      const draft: FormDraft = {
        formId,
        data,
        timestamp: Date.now()
      }
      await draftDb.set(`draft:${formId}`, draft)
    },
    
    // Load form draft
    loadDraft: async (formId: string): Promise<Record<string, any> | null> => {
      const draft = await draftDb.get<FormDraft>(`draft:${formId}`)
      return draft?.data ?? null
    },
    
    // Remove form draft
    removeDraft: async (formId: string) => {
      return await draftDb.del(`draft:${formId}`)
    },
    
    // Check if draft exists
    hasDraft: async (formId: string): Promise<boolean> => {
      return await draftDb.has(`draft:${formId}`)
    }
  }
  
  return drafts
}

Database Migration

interface Migration {
  version: number
  migrate: (dbService: NativeDbService) => Promise<void>
}

const createMigrationManager = async (context) => {
  const dbService = context.service('app-db')
  const metaDb = await dbService.initialize('meta')
  
  const migrations: Migration[] = [
    {
      version: 1,
      migrate: async (dbService) => {
        // Initial setup
        const userDb = await dbService.initialize('users')
        await userDb.set('schema-version', 1)
      }
    },
    {
      version: 2,
      migrate: async (dbService) => {
        // Add settings structure
        const settingsDb = await dbService.initialize('settings')
        await settingsDb.set('schema-version', 2)
      }
    }
  ]
  
  const migrate = async () => {
    const currentVersion = await metaDb.get<number>('db-version') ?? 0
    
    for (const migration of migrations) {
      if (migration.version > currentVersion) {
        console.log(`Running migration ${migration.version}`)
        await migration.migrate(dbService)
        await metaDb.set('db-version', migration.version)
      }
    }
  }
  
  return { migrate }
}

Integration Patterns

With React Native Components

import React, { useEffect, useState } from 'react'
import { useContext } from '@owlmeans/client'

const useNativeStorage = <T>(key: string, defaultValue: T) => {
  const context = useContext()
  const [value, setValue] = useState<T>(defaultValue)
  const [loading, setLoading] = useState(true)
  
  useEffect(() => {
    const loadValue = async () => {
      try {
        const dbService = context.service('app-db')
        const db = await dbService.initialize('app-data')
        const stored = await db.get<T>(key)
        setValue(stored ?? defaultValue)
      } catch (error) {
        console.error('Failed to load value:', error)
        setValue(defaultValue)
      } finally {
        setLoading(false)
      }
    }
    
    loadValue()
  }, [key])
  
  const updateValue = async (newValue: T) => {
    try {
      const dbService = context.service('app-db')
      const db = await dbService.initialize('app-data')
      await db.set(key, newValue)
      setValue(newValue)
    } catch (error) {
      console.error('Failed to save value:', error)
    }
  }
  
  return { value, updateValue, loading }
}

// Usage in component
const SettingsScreen: React.FC = () => {
  const { value: settings, updateValue: updateSettings, loading } = 
    useNativeStorage('app-settings', defaultSettings)
  
  if (loading) return <LoadingSpinner />
  
  return (
    <SettingsForm 
      settings={settings} 
      onUpdate={updateSettings} 
    />
  )
}

With Redux/State Management

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'

// Async thunks for database operations
export const loadUserData = createAsyncThunk(
  'user/loadData',
  async (_, { getState }) => {
    const { context } = getState() as { context: any }
    const dbService = context.service('app-db')
    const userDb = await dbService.initialize('users')
    return await userDb.get('current-user')
  }
)

export const saveUserData = createAsyncThunk(
  'user/saveData',
  async (userData: User, { getState }) => {
    const { context } = getState() as { context: any }
    const dbService = context.service('app-db')
    const userDb = await dbService.initialize('users')
    await userDb.set('current-user', userData)
    return userData
  }
)

const userSlice = createSlice({
  name: 'user',
  initialState: { data: null, loading: false },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(loadUserData.fulfilled, (state, action) => {
        state.data = action.payload
        state.loading = false
      })
      .addCase(saveUserData.fulfilled, (state, action) => {
        state.data = action.payload
      })
  }
})

Performance Considerations

AsyncStorage Limitations

  • Synchronous operations - All operations are asynchronous and should be awaited
  • JSON serialization - All data is serialized/deserialized as JSON
  • Storage limits - Be mindful of device storage limitations
  • Key enumeration - AsyncStorage doesn't support key enumeration natively

Best Practices

  1. Use appropriate data structures - Store related data together when possible
  2. Implement error handling - Always handle AsyncStorage failures gracefully
  3. Consider data size - Large objects may impact performance
  4. Use namespacing - Use different database aliases for different data domains
  5. Clean up old data - Implement cleanup mechanisms for temporary data

Error Handling

The package handles various error scenarios:

  • AsyncStorage failures - When the underlying storage system fails
  • JSON parsing errors - When stored data cannot be parsed as JSON
  • Context errors - When the service context is not properly configured
  • Memory limitations - When device storage is full

Security Considerations

Data Security

  • Sensitive data - AsyncStorage is not encrypted by default
  • Secure storage - Consider using react-native-keychain for sensitive data
  • Data validation - Always validate data retrieved from storage
  • Access control - Implement proper access control for different data types

Dependencies

This package depends on:

  • @owlmeans/context - For service creation and registration
  • @owlmeans/client-resource - For client database interface
  • @owlmeans/client-context - For client context types
  • @react-native-async-storage/async-storage - For persistent storage

Related Packages