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

@doync/drizzle

v0.4.0

Published

doync Drizzle flavor: createDoyncDrizzle one-call entry for schema + queries + mutations

Downloads

1,120

Readme

@doync/drizzle

The Drizzle flavor of doync's DX surface: one call, createDoyncDrizzle, takes your Drizzle schema and drizzle-kit migrations and returns everything a Drizzle consumer needs — a typed db, the derived doync schema, a bound Drizzle-flavored defineQuery / defineMutation, and the plain-core registry helpers.

Under the hood the flavor still produces the same DoyncSchema, QueryDefinition, and MutationDefinition values plain @doync/core defines. The engine never sees Drizzle — only compiled SQL for query identity and Membership, plus an opaque row decoder so useQuery yields the same nested shape Drizzle would. You can mix Drizzle-authored definitions with plain-core ones in the same tree.

Install

pnpm add @doync/drizzle @doync/core drizzle-orm

| Package | Role | | --- | --- | | @doync/drizzle | This flavor | | @doync/core | Schema / query / mutation definition API the flavor builds on | | drizzle-orm | Peer>=0.30.0 (developed and tested against ^0.45) |

drizzle-orm is this package's peer on purpose: @doync/core carries none. You already need it to author tables; pin a version you trust and let the corpus canaries break loudly on a bad upgrade rather than silently mis-detect. Pair with drizzle-kit in your app (or workspace) to generate the migrations journal createDoyncDrizzle consumes — drizzle-kit is not a runtime dependency of this package.

For a browser or mobile client you will also want @doync/web / @doync/mobile and @doync/react. For Cloudflare Durable Objects, add @doync/server.

createDoyncDrizzle

import { createDoyncDrizzle } from '@doync/drizzle'
import * as drizzleSchema from './db/drizzle-schema'
import migrations from './db/migrations/migrations.js'

export const ctxValidationSchema = z.object({ userId: z.string() })

export const {
  schema, // DoyncSchema (tables + migration track)
  defineQuery, // Drizzle-flavored, bound to `db`
  defineQueries, // plain @doync/core tree helper
  defineMutation, // Drizzle-flavored ({ args, ctx, db }) bodies
  defineMutations, // plain @doync/core tree helper
  // optional `db` — SqliteRemoteDatabase over your schema for local reads
} = createDoyncDrizzle({
  schema: drizzleSchema,
  migrations,
  ctxValidationSchema,
  // optional `name` — Database name (default 'doync') — must match the client
})

Options

| Field | Required | Notes | | --- | --- | --- | | schema | yes | Consumer Drizzle tables + relations() exports. A namespace import (import * as schema from './…') works; inference keeps db.query.* populated. | | migrations | yes | drizzle-kit's migrations.js default export: { journal: { entries: [{ idx, tag }] }, migrations: { m0000: sql, … } }. Extra journal fields are ignored. Statement-breakpoint markers are stripped before the track reaches the engine. Backfill SQL the kit cannot diff belongs as a Kit custom migration in this same journal (drizzle-kit generate --custom --name=…). | | ctxValidationSchema | yes | Standard Schema whose output is your auth-context shape: what authenticated ctx looks like in query resolvers and mutation bodies (null when anonymous). | | name | no | Database name this db resolves imperative local reads against. Defaults to 'doync'. Must match the name the topology client was constructed with; two databases in one app get two createDoyncDrizzle calls with distinct names. |

Result (CreateDoyncDrizzleResult)

| Field | Type / role | | --- | --- | | schema | Derived DoyncSchema: every SQLiteTable (name + columns) plus the ordered migration track. Hand to Origin / Mirror / client constructors. | | defineQueries / defineMutations | They register the defined queries and mutations by name. | | defineQuery | Bound Drizzle-flavored query definer (see below). | | defineMutation | Bound Drizzle-flavored mutation definer (see below). | | db | SqliteRemoteDatabase for the consumer schema. await db.query… is a Local read — typed rows as fresh as the last re-dump, not a live Subscription. Writes on this db always refuse (LOCAL_READ_WRITE_MESSAGE). |

Defining a Drizzle schema

Author tables the usual Drizzle way. Every synced table needs a primary key (inline or composite); relations() exports are used for RQB nesting and filtered out of the doync table list.

// db/drizzle-schema.ts
import { relations } from 'drizzle-orm'
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'

export const task = sqliteTable('task', {
  id: text('id').primaryKey(),
  title: text('title').notNull(),
  done: integer('done').notNull().default(0),
  ownerId: text('owner_id'),
})

export const taskRelations = relations(task, () => ({}))

Generate migrations with drizzle-kit and import the journal the kit emits. For seed/backfill SQL the generated diff cannot express, add a custom migration so it sits in the journal at the right order:

drizzle-kit generate --custom --name=default_project
# edit the empty .sql the kit wrote, then wire it into migrations.js

Queries

defineQuery(argsValidationSchema?, ({ args, ctx, db }) => builder) — the resolver returns a real Drizzle RQB builder. Do not annotate the return type; row shape and one-ness are inferred from the builder (findMany → multi row; findFirst → one-query; mixing the two across return paths is a compile error — one-ness is static per definition). Nested with and columns projections flow through to useQuery.

import { eq } from 'drizzle-orm'
import * as z from 'zod/mini'

import { task } from './db/drizzle-schema'
import { db, defineQuery, defineQueries } from './schema'

export const queries = defineQueries({
  // No args — findMany ⇒ multi-row Subscription
  allTasks: defineQuery(({ db }) =>
    db.query.task.findMany({ orderBy: (t, { asc }) => [asc(t.id)] }),
  ),

  // Args + standard-schema validator; findFirst ⇒ one-query
  taskById: defineQuery(z.string(), ({ args: id, db }) =>
    db.query.task.findFirst({ where: eq(task.id, id) }),
  ),

  // ctx-filtered: anonymous / user resolve to DIFFERENT statement identities
  myOpen: defineQuery(({ ctx, db }) => {
    if (ctx === null) {
      return db.query.task.findMany({ where: (t, { eq }) => eq(t.id, '') })
    }
    return db.query.task.findMany({
      where: (t, { and, eq }) => and(eq(t.done, 0), eq(t.ownerId, ctx.userId)),
    })
  }),
})

Identity and Membership come only from the builder's compiled SQL (.toSQL(){ sql, params }). The engine parses that statement the same way it would a hand-written one. At runtime the flavor attaches a row decoder, so the nested shape useQuery returns matches Drizzle's own sqlite-proxy result.

Mutations

defineMutation(argsValidationSchema?, ({ args, ctx, db }) => …) — same single-object input shape as queries. The bound db is a per-invocation sqlite-proxy over the engine's MutationExec: writes, .returning(), core selects, and RQB reads all run through it. Bodies must be deterministic and DB-only (no ID minting, no external I/O) — external work belongs in server overrides via defineMutations(shared, serverOverrides).

import { eq } from 'drizzle-orm'
import * as z from 'zod/mini'

import { task } from './db/drizzle-schema'
import { defineMutation, defineMutations } from './schema'

const createArgs = z.object({
  id: z.string(),
  title: z.string(),
  ownerId: z.string(),
})

export const mutations = defineMutations({
  task: {
    create: defineMutation(createArgs, async ({ args, db }) => {
      // ids arrive in args — client-generated, never minted here
      await db.insert(task).values({
        id: args.id,
        title: args.title,
        ownerId: args.ownerId,
        done: 0,
      })
    }),

    rename: defineMutation(
      z.object({ id: z.string(), title: z.string() }),
      async ({ args, db }) => {
        await db
          .update(task)
          .set({ title: args.title })
          .where(eq(task.id, args.id))
      },
    ),
  },
})

Fail-closed inside a body: db.transaction, db.batch, and any BEGIN / COMMIT / ROLLBACK / SAVEPOINT / RELEASE arriving as raw SQL throw — a mutation already runs inside one transaction.

Relationship to plain @doync/core

| Concern | Plain @doync/core | @doync/drizzle | | --- | --- | --- | | Schema | Plain DoyncSchema into createDoync | Derived by createDoyncDrizzle from Drizzle tables + drizzle-kit journal | | Query body | ({ args, ctx, sql }) => sql`…` / sql.one`…` | ({ args, ctx, db }) => db.query….findMany/findFirst(…) | | Mutation body | ({ args, ctx, sql }) => sql.exec(…) | ({ args, ctx, db }) => db.insert/update/… | | Registry trees | defineQueries / defineMutations | Same helpers, re-exported on the result | | Produced types | DoyncSchema, QueryDefinition, MutationDefinition | Identical — engines and hooks are flavor-blind |

Use this package when your app already models tables in Drizzle. Stay on plain core when you want hand-written SQL and no drizzle-orm peer. Both flavors hand the same shape to @doync/server, @doync/web, @doync/mobile, and @doync/react.

Local reads

After a client registers a local-read source under the same Database name, the shared db answers imperative Local reads:

// Web tab after createWebClient({ name: 'doync', … }) —
// as fresh as the last re-dump, not a live Subscription.
const open = await db.query.task.findMany({
  where: (t, { eq }) => eq(t.done, 0),
})

Writes on this db refuse with LOCAL_READ_WRITE_MESSAGE ('doync: this db is read-only — writes go through mutations'). Mutation bodies receive a different, write-capable db and are unaffected.

Public surface

The main entry (.) is governed by semver. Public symbols:

  • Runtime: createDoyncDrizzle
  • Types: CreateDoyncDrizzleOptions, CreateDoyncDrizzleResult, DrizzleCompiled DrizzleDefineMutation, DrizzleDefineQuery, DrizzleMigrations, DrizzleMutationBody, DrizzleMutationInput, DrizzleOne, DrizzleQueryInput, DrizzleResolver, DrizzleRow

Internal (@doync/drizzle/internal)

Anything imported from @doync/drizzle/internal is not part of the semver surface. It may change or disappear in any release, including patches, without notice. Sibling @doync/* packages reach internals through that specifier when they must; application code should not. Import the Drizzle wrappers from @doync/drizzle.