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

@zeroin.earth/appwrite-ts

v1.0.2

Published

TypeScript Server Plugin + CLI that generates fully typed collections from your Appwrite database schema

Readme

@zeroin.earth/appwrite-ts

A TypeScript Server Plugin + CLI that generates fully typed collections from your Appwrite database schema. Point it at your appwrite.config.json and get const-typed declarations, then use the exported utility types to build type-safe wrappers for your data layer.

How It Works

  1. Pull your Appwrite project config using the Appwrite CLI:

    appwrite pull all
    # or just the parts you need:
    appwrite pull collections

    This writes (or updates) appwrite.config.json in your project root, which includes a tables array describing every collection and its columns.

  2. The TSS plugin (or CLI) reads that JSON and generates an appwrite-config.d.ts file with const-typed literal tuples.

  3. You import typeof appwriteConfig and pass it as a Config parameter to the library's utility types — CollectionType, AppwriteDocument, etc. — to get precise, per-collection field types.

Whenever your Appwrite schema changes, run appwrite pull again. The TSS plugin watches the file and regenerates the declaration automatically; if you're using the CLI, re-run appwrite-ts generate.

Setup

bun add @zeroin.earth/appwrite-ts

Peer dependency: typescript ^5

TypeScript Server Plugin

Register the plugin in tsconfig.json so your editor regenerates the declaration file automatically whenever appwrite.config.json changes:

{
  "compilerOptions": {
    "plugins": [
      {
        "name": "@zeroin.earth/appwrite-ts/plugin",
        "configPath": "./appwrite.config.json",
        "outputPath": "./appwrite-config.d.ts"
      }
    ]
  }
}

| Option | Description | Default | |---|---|---| | configPath | Path to the Appwrite config JSON | ./appwrite.config.json | | outputPath | Path for the generated .d.ts file | ./appwrite-config.d.ts |

VS Code setup: Make sure VS Code is using your workspace TypeScript (not its bundled copy). Add this to .vscode/settings.json:

{
  "js/ts.tsdk.path": "node_modules/typescript/lib"
}

Then select the workspace TypeScript version: open any .ts file, click the TypeScript version in the bottom-right status bar, and choose "Use Workspace Version".

After initial setup, restart TS Server once (Cmd/Ctrl+Shift+P → "TypeScript: Restart TS Server"). The plugin will then watch for changes automatically — no restart needed after appwrite pull.

CLI

You can also generate the declaration file manually or in CI:

# One-shot generation
appwrite-ts generate ./appwrite.config.json -o ./appwrite-config.d.ts

# Watch mode — regenerates on every change to the config file
appwrite-ts watch ./appwrite.config.json -o ./appwrite-config.d.ts

Exported Types

CollectionType<Id, Config?>

Maps a collection $id to an object type of its fields. Pass your literal Config for precise types; omit it for a generic fallback.

AppwriteDocument<Id, Config?>

AppwriteMeta & CollectionType<Id, Config> — a fully-typed document including Appwrite system fields.

AppwriteRow

AppwriteMeta & { readonly [key: string]: unknown } — an untyped row with an index signature, useful for generic operations like iterating keys or stripping metadata.

AppwriteMeta

Appwrite system fields present on every document:

| Field | Type | |---|---| | $id | string | | $createdAt | string | | $updatedAt | string | | $permissions | string[] | | $databaseId | string | | $tableId | string | | $sequence | number (optional) |

Column, Table, AppwriteConfig

Runtime shapes for the config JSON. Used internally by the generator and available for tooling that needs to inspect the schema at runtime.

Column Type Mapping

| Appwrite column type | TypeScript type | |---|---| | boolean | boolean | | integer, double | number | | varchar, text, mediumtext, string | string | | string with format: "enum" | Union of literal element strings | | Any column with array: true | T[] | | Any column with required: false | T \| null |

Usage

Binding Your Config

The core pattern: import the generated declaration and pass its type as the Config parameter.

import type appwriteConfig from './appwrite-config'
import type { AppwriteDocument, CollectionType } from '@zeroin.earth/appwrite-ts'

// Bind the literal config
type Config = typeof appwriteConfig

// Extract the union of all collection IDs
type CollectionId = Config['tables'][number]['$id']
// → "users" | "posts" | "comments" | ...

// Get precise field types for a specific collection
type PostFields = CollectionType<'posts', Config>
// → { title: string; body: string; authorId: string; published: boolean; views: number | null }

// Get a full Appwrite document (system fields + collection fields)
type PostDoc = AppwriteDocument<'posts', Config>
// → AppwriteMeta & PostFields

Building Typed Wrappers

In practice, you'll want to build a small set of wrapper types in your project that bind Config once and give you convenient aliases for the rest of your codebase. This is where the real value is — you define these once and every data-access function, mapper, and persister gets full type safety for free.

// types.ts — define once, import everywhere
import type appwriteConfig from './appwrite-config'
import type { AppwriteDocument, AppwriteMeta } from '@zeroin.earth/appwrite-ts'

type Config = typeof appwriteConfig

/** Union of all collection $id strings. */
export type CollectionId = Config['tables'][number]['$id']

/** A fully-typed Appwrite document for a given collection. */
export type Doc<Id extends CollectionId> = AppwriteDocument<Id, Config>

/** An untyped row with index signature for generic operations (stripping meta, etc). */
export type UntypedRow = AppwriteMeta & { [key: string]: unknown }

Now you can use Doc<'posts'>, Doc<'comments'>, etc. throughout your project with zero extra ceremony:

import type { CollectionId, Doc, UntypedRow } from './types'

// Typed function — knows exactly which fields exist on a post
function formatPost(doc: Doc<'posts'>): string {
  return `${doc.title} by ${doc.authorId}`  // ← autocomplete + type checking
}

// Generic function — works with any collection's documents
function stripMeta(row: UntypedRow): Record<string, unknown> {
  const result: Record<string, unknown> = {}
  for (const [key, value] of Object.entries(row)) {
    if (!key.startsWith('$')) result[key] = value
  }
  return result
}

Distinguishing Singletons from Multi-Row Collections

If your schema has both single-document collections (e.g., user settings, app config) and multi-row collections (e.g., posts, comments), you can encode that distinction:

type SingletonIds = 'user_settings' | 'app_config'

/** Pulled data shape — singletons are a single doc or undefined, arrays for the rest. */
export type PulledCollections = {
  [K in CollectionId]: K extends SingletonIds ? Doc<K> | undefined : Doc<K>[]
}

This gives you a single type representing an entire pull from Appwrite, with each collection correctly typed as either a document or an array of documents.

Notes

  • The TSS plugin watches the directory containing appwrite.config.json (not the file itself) so that atomic writes like those from appwrite pull (write tmp → rename) are detected reliably.
  • The plugin uses export = init (CommonJS) as required by the TypeScript Server Plugin API. Your root tsconfig should exclude the plugin's source directory (e.g. packages/*/src) to avoid module format conflicts.
  • Peer dependency: typescript ^5.
  • The CLI uses Commander for argument parsing.

Development

bun run build        # Compile with tsc
bun run dev          # Watch mode
bun test             # Run tests
bun run typecheck    # Type-check without emitting
bun run clean        # Remove dist/