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

@kequnyang/dopdb

v0.1.202606290834

Published

One schema, two equivalent engines (Go + TypeScript), one wire protocol. Declare a collection once and get native types, runtime validation, a typed browser client (fetch), and a Node + MongoDB server — no codegen, no double-write. Redis-compatible data s

Readme

dopdb

One schema, two equivalent engines (Go + TypeScript), one wire protocol. Declare a collection once and get native types, runtime validation, a typed browser client (fetch), and a Node + MongoDB server — no codegen, no writing it twice.

This package is the TypeScript engine: a complete, standalone implementation (not a client SDK for a Go backend). It runs the server on Node and provides a typed browser client; it speaks the same URL wire protocol, command vocabulary, @-binding, isolation, and permission model as the Go engine, so the two are interchangeable.

npm install @kequnyang/dopdb
# server only: also install the peer
npm install mongodb

Requires Node ≥ 20 (ESM). mongodb is an optional peer dependency — only dopdb/server needs it; dopdb and dopdb/client are browser-safe.

Entry points

| Import | For | Pulls in | |---|---|---| | dopdb | the shared schema (collection, f, permission constants) | nothing node/mongodb — browser-safe | | dopdb/client | the browser fetch client | nothing node/mongodb | | dopdb/server | the Node + MongoDB server (serve, createNextHandler, serverDb, defineApi) | mongodb |

One schema, everywhere

// schema.ts — imported by client, server, and Next.js alike
import { collection, f, HGet, HGetAll, HSet, HDel } from "@kequnyang/dopdb";

export const schema = {
  notes: collection({
    _id:   f.string(),
    owner: f.string().bind("@uid"),          // owner comes from the JWT uid; the client can't change it
    text:  f.string().required(),
  })
    .named("notes")
    .ownerScope("owner")                      // row-level isolation
    .httpOn(HGet | HGetAll | HSet | HDel),    // expose + authorize (no args = All, debug only)
};

Browser client — no fetch code, no API layer

import { clientDb } from "@kequnyang/dopdb/client";
import { schema } from "./schema";

const db = clientDb(schema, {
  baseUrl: "https://api.example.com",
  getToken: () => localStorage.token,
});

await db.notes.hset("@uuid", { text: "buy milk" }); // create — "@uuid" => the server generates the id
const mine = await db.notes.hgetall();               // only ever returns my own notes
await db.notes.hdel(id);

db.notes.* is fully typed from the schema. There is no controller/service/DAO and no hand-written endpoint — the client safely operates on the database, and the framework enforces auth, isolation, and routing.

Server

In Next.js (App Router) — zero config

// app/api/[...slug]/route.ts
import { createNextHandler } from "@kequnyang/dopdb/server";
import { schema } from "@/schema";

export const { GET, POST, OPTIONS } = createNextHandler({
  schema,
  mongo: { uri: process.env.MONGO_URI!, db: "appdb" },
  jwtSecret: process.env.JWT_SECRET!,         // HS256 secret or RS256 PEM public key
});
export const runtime = "nodejs";              // the MongoDB driver is not Edge-compatible

This takes over /api/hget/notes, /api/find/..., /api/<fn>, watch (SSE), etc. The prefix follows the folder you place it in (rename to app/db/[...slug] for /db/*, no code change).

Standalone Node

import { serve, serverDb } from "@kequnyang/dopdb/server";
const srv = await serve({ schema, mongo: { uri, db: "appdb" }, jwtSecret, port: 8080 });

// trusted, in-process reads/writes (no scope/JWT):
const db = serverDb(schema, srv.mongo);
await db.notes.hset("u1", { text: "hi" });

What you get

  • Zero glue code: no endpoints, no fetch wrappers — the frontend calls database methods.
  • One set of types front-and-back: change a field and both sides move together (a compile error, not a runtime surprise).
  • Multi-tenancy by default: @-binding + .ownerScope() mean each user only ever sees their own rows, and the client can't widen it.
  • Permissions in one line: .httpOn(flags) exposes + authorizes a collection (off by default); Perm constants are exported (as BigInt) and bit-compatible with the Go engine.
  • Redis-compatible data structures on MongoDB: Hash, plus String / List / Set / ZSet — every command verified to behave identically across the Go and TypeScript engines.

| Type | Commands | |---|---| | Hash | HGet/HSet/HSetNX/HDel/HExists/HGetAll/HKeys/HVals/HLen/HIncrBy/HIncrByFloat/HMSet/HMGet/HScan/HScanNoValues/HRandField | | String | STRGET/STRSET/STRSETALL/STRGETALL/STRDEL (+ TTL) | | List | LPUSH/RPUSH/LPOP/RPOP/LRANGE/LLEN/LINDEX/LSET/LREM/LTRIM/LINSERTBEFORE/LINSERTAFTER | | Set | SADD/SREM/SMEMBERS/SISMEMBER/SCARD | | ZSet | ZADD/ZREM/ZSCORE/ZCARD/ZCOUNT/ZINCRBY/ZRANGE/ZREVRANGE/ZRANGEBYSCORE/ZREVRANGEBYSCORE/ZRANK/ZREVRANK/ZPOPMIN/ZPOPMAX/ZREMRANGEBYRANK/ZREMRANGEBYSCORE |

Blocking ops (BLPOP/BRPOP/BRPOPLPUSH) are intentionally not implemented — MongoDB has no native blocking; use watch (change streams) for subscriptions. watch needs MongoDB running as a replica set.

Security model (brief)

Keys are always strings (JS loses precision on large integers). The framework strips any @-prefixed key the client sends and injects the verified context, so identity/ownership can't be forged. find filters and sort/projection reject $-operator injection. JWT is HS256 or RS256 (none rejected). A collection that hasn't called .httpOn() returns 403 for its data commands.

License

MIT. See LICENSE. Source and issues: https://github.com/doptime/dopdb