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

@stork-tools/zod-async-storage

v1.0.1

Published

Zod-validated wrapper around @react-native-async-storage/async-storage with type-safe APIs

Readme

@stork-tools/zod-async-storage

npm version License: MIT TypeScript

A type-safe and validated wrapper around @react-native-async-storage/async-storage using Zod schemas. Enjoy the benefits of runtime validation, automatic type inference, and better developer experience when working with AsyncStorage in React Native and Expo applications. This library is a drop-in replacement for @react-native-async-storage/async-storage with added type safety.

✨ Features

  • 🛡️ Type Safety: Full TypeScript support with automatic type inference from Zod schemas
  • 🔄 Drop-in Replacement: Maintains the same API as AsyncStorage with added type safety
  • 🔄 Incremental adoption: You can start with a single schema and add more later
  • Runtime Validation: Automatic validation of stored/retrieved data using Zod schemas
  • 🔒 Strict Mode: Strict mode enabled by default to prevent access to undefined keys
  • 🧹 Error Handling: Configurable behavior for invalid data (clear or throw)
  • 🚀 Zero Runtime Overhead: Only validates data when schemas are provided
  • 📱 React Native & Expo: Compatible with both React Native and Expo projects

📦 Installation

# Using pnpm (recommended)
pnpm add @stork-tools/zod-async-storage zod @react-native-async-storage/async-storage

# Using npm
npm install @stork-tools/zod-async-storage zod @react-native-async-storage/async-storage

# Using yarn
yarn add @stork-tools/zod-async-storage zod @react-native-async-storage/async-storage

# Using bun
bun add @stork-tools/zod-async-storage zod @react-native-async-storage/async-storage

🚀 Quick Start

import { z } from "zod";
import { createAsyncStorage } from "@stork-tools/zod-async-storage";

// Define your schemas
const schemas = {
  user: z.object({
    id: z.string(),
    name: z.string(),
    email: z.string().email(),
  }),
  settings: z.object({
    theme: z.enum(["light", "dark"]),
    notifications: z.boolean(),
  }),
};

// Create a single instance of type-safe storage
export const AsyncStorage = createAsyncStorage(schemas);

import { AsyncStorage } from "~/async-storage";

// Use with full type safety
await AsyncStorage.setItem("user", {
  id: "123",
  name: "John Doe",
  email: "[email protected]",
});

const user = await AsyncStorage.getItem("user"); // Type: User | null

📖 API Reference

createAsyncStorage(schemas, options?)

Creates a type-safe AsyncStorage instance with validation.

Parameters

  • schemas: Record<string, ZodSchema> - Object mapping keys to Zod schemas
  • options: GlobalOptions (optional) - Configuration options

Global Options

type GlobalOptions = {
  strict?: boolean;        // Enforce only defined keys (default: true)
  onFailure?: "clear" | "throw"; // Handle zod validation failures (default: "clear")
  debug?: boolean;         // Enable debug logging (default: false)
  onValidationError?: (key: string, error: z.ZodError, value: unknown) => void; // Callback on validation failure
};

Instance Methods

All methods maintain the same signature as AsyncStorage but with added type safety:

getItem(key, options?, callback?)

Retrieves and validates an item from storage.

const user = await AsyncStorage.getItem("user");
// Type: { id: string; name: string; email: string } | null

// Per-operation options
const user = await AsyncStorage.getItem("user", { onFailure: "throw" });

setItem(key, value, callback?)

Stores an item with automatic serialization and type validation.

await AsyncStorage.setItem("user", {
  id: "123",
  name: "John Doe",
  email: "[email protected]",
}); // ✅ Type-safe

await AsyncStorage.setItem("user", { invalid: "data" }); // ❌ TypeScript error

multiGet(keys, options?, callback?)

Retrieves multiple items with type safety for each key.

const results = await AsyncStorage.multiGet(["user", "settings"]);
// Type: [["user", User | null], ["settings", Settings | null]]

multiSet(keyValuePairs, callback?)

Sets multiple items with type validation.

await AsyncStorage.multiSet([
  ["user", { id: "123", name: "John", email: "[email protected]" }],
  ["settings", { theme: "dark", notifications: true }],
]);

Other Methods

  • removeItem(key, callback?) - Remove an item
  • clear(callback?) - Clear all storage
  • getAllKeys(callback?) - Get all keys
  • multiRemove(keys, callback?) - Remove multiple items
  • mergeItem(key, value, callback?) - Merge with existing item
  • multiMerge(keyValuePairs, callback?) - Merge multiple items
  • flushGetRequests() - Flush pending get requests

🎯 Usage Examples

Basic Usage

import { z } from "zod";
import { createAsyncStorage } from "@stork-tools/zod-async-storage";

const schemas = {
  user: z.object({
    id: z.string(),
    name: z.string(),
    preferences: z.object({
      theme: z.enum(["light", "dark"]),
      language: z.string(),
    }),
  }),
};

// Create a single instance of type-safe storage
export const AsyncStorage = createAsyncStorage(schemas);

import { AsyncStorage } from "~/async-storage";

// Set data
await AsyncStorage.setItem("user", {
  id: "u1",
  name: "Alice",
  preferences: {
    theme: "dark",
    language: "en",
  },
});

// Get data (fully typed)
const user = await AsyncStorage.getItem("user");
if (user) {
  console.log(user.preferences.theme); // TypeScript knows this exists
}

Strict Mode (Default)

By default, strict mode is enabled to prevent access to undefined keys:

const AsyncStorage = createAsyncStorage(schemas); // strict: true by default

await AsyncStorage.getItem("user");        // ✅ OK
await AsyncStorage.getItem("someUndefinedKey");   // ❌ TypeScript error

Loose Mode

Disable strict mode to allow access to any key while maintaining type safety for schema-defined keys. This is useful if you are migrating to @stork-tools/zod-async-storage and want to maintain access to keys that are not yet defined in schemas.

const AsyncStorage = createAsyncStorage(schemas, { strict: false });

await AsyncStorage.getItem("user");      // Type: User | null (validated)
await AsyncStorage.getItem("any-key");   // Type: string | null (loose autocomplete, no validation)

With strict: false, you get:

  • Loose autocomplete: Access any string key
  • Type-safe returns: Keys matching schemas return validated types
  • Raw string fallback: Unknown keys return string | null

Error Handling

Configure how validation failures are handled:

// Clear invalid data (default)
const AsyncStorage = createAsyncStorage(schemas, { onFailure: "clear" });

// Throw errors on invalid data
const AsyncStorage = createAsyncStorage(schemas, { onFailure: "throw" });

// Per-operation override
const user = await AsyncStorage.getItem("user", { onFailure: "throw" });

Validation Error Callbacks

Get notified when validation fails using the onValidationError callback:

const AsyncStorage = createAsyncStorage(schemas, {
  onFailure: "clear",
  onValidationError: (key, error, value) => {
    // Log validation failures for monitoring
    console.warn(`Validation failed for key "${key}":`, error.message);
    
    // Send to analytics
    analytics.track('validation_error', {
      key,
      errors: error.issues,
      invalidValue: value
    });
  }
});

// Per-operation callback override
const user = await AsyncStorage.getItem("user", {
  onValidationError: (key, error, value) => {
    // Handle this specific validation error differently
    showUserErrorMessage(`Invalid user data: ${error.message}`);
  }
});

The callback receives:

  • key: The storage key that failed validation
  • error: The Zod validation error with detailed issues
  • value: The raw parsed value that failed validation

Note: The callback is only called for Zod schema validation failures, not for JSON parsing errors.

Working with Raw Strings

Keys without schemas work with raw strings:

const schemas = {
  user: z.object({ name: z.string() }),
  // 'token' has no schema
};

const AsyncStorage = createAsyncStorage(schemas);

await AsyncStorage.setItem("user", { name: "John" });  // Validated object
await AsyncStorage.setItem("token", "abc123");         // Raw string

🪝 React Hooks

import { createAsyncStorage, createUseAsyncStorage } from "@stork-tools/zod-async-storage";

const AsyncStorage = createAsyncStorage(schemas);
const { useAsyncStorage } = createUseAsyncStorage(AsyncStorage);

function UserProfile() {
  const { getItem, setItem, mergeItem, removeItem } = useAsyncStorage("user");
  
  const loadUser = async () => {
    const user = await getItem(); // Type: User | null
  };
  
  const saveUser = async () => {
    await setItem({ id: "123", name: "John", email: "[email protected]" });
  };
  
  const updateUser = async () => {
    await mergeItem({ name: "Updated Name" });
  };
  
  const clearUser = async () => {
    await removeItem();
  };
}

Hook Methods

  • getItem(options?, callback?) - Retrieve item with type safety
  • setItem(value, callback?) - Store item with validation
  • mergeItem(value, callback?) - Merge with existing data
  • removeItem(callback?) - Remove item from storage

All methods support the same options and callbacks as the storage instance.

🔧 Advanced Configuration

Debug Mode

Enable debug logging to monitor validation failures:

export const AsyncStorage = createAsyncStorage(schemas, {
  debug: true,
  onFailure: "clear",
});

// When invalid data is found and cleared, you'll see:
// console.warn("Cleared invalid item", key);

🤝 Contributing

We welcome contributions from the community! Whether you're fixing bugs, adding features, improving documentation, or sharing feedback, your help makes this project better.

Quick Start for Contributors

  1. Fork the repository on GitHub
  2. Clone your fork locally:
    git clone https://github.com/YOUR_USERNAME/stork.git
    cd stork
  3. Install dependencies:
    pnpm install
  4. Create a feature branch:
    git checkout -b feature/your-feature-name
  5. Make your changes following our coding standards
  6. Add a changeset (for user-facing changes):
    pnpm changeset
  7. Commit and push your changes
  8. Open a Pull Request - CI will handle testing and validation

Types of Contributions

  • 🐛 Bug Reports: Use our bug report template
  • Feature Requests: Use our feature request template
  • 💻 Code Contributions: Follow our coding standards and include tests
  • 📚 Documentation: Help improve our docs and examples
  • 🧪 Testing: Add or improve test coverage
  • 💬 Discussions: Share ideas in GitHub Discussions

Key Guidelines

  • Type Safety: No any types, use type guards over casting
  • Testing: Include tests for new features and bug fixes
  • Changesets: Run pnpm changeset for user-facing changes
  • Code Style: Follow existing patterns, JSDoc for public APIs

For detailed contributing guidelines, see CONTRIBUTING.md.

📝 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments