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

@monospace/sdk

v1.0.0

Published

Readme

@monospace/sdk

npm version license

TypeScript client for Monospace.

Generate a client from your workspace schema to query typed collections. Field selection, aliases, and included relations determine the result type.

const posts = await client.Post.readMany({
	fields: ['id', 'title'],
	filter: { views: { _gte: 1000 } },
	sort: [{ published_date: { direction: 'desc' } }],
	include: {
		author: { fields: ['name'] },
		comments: { fields: ['text'], limit: 3 },
	},
	limit: 10,
});

for (const post of posts) {
	post.author; // { name: string | null } | null
	post.comments; // { data: { text: string | null }[] } | null
	post.body; // compile error: `body` was not selected
}

Quick start

1. Install

Install the SDK and the monospace CLI. The CLI generates types and is a dev dependency.

npm install @monospace/sdk
npm install --save-dev @monospace/cli

With pnpm, Yarn, or Bun, use pnpm add, yarn add, or bun add (-D for the CLI).

2. Generate your client

npx @monospace/cli sdk init      # writes monospace.config.ts
npx @monospace/cli sdk generate  # writes ./src/generated/monospace/index.ts

sdk init asks for your instance URL, workspace, and output directory. You can also write the config yourself.

// monospace.config.ts
import { defineConfig } from '@monospace/sdk/config';

export default defineConfig({
	url: 'https://example.monospace.io',
	workspace: 'blog',
	output: './src/generated/monospace',
});

The generator needs an API key to read your schema. Set MONOSPACE_API_KEY in your environment or a .env file, or run npx @monospace/cli login once. Run sdk generate again whenever your schema changes to update the generated types.

3. Query

import { createClient } from './generated/monospace';

export const client = createClient({
	url: 'https://example.monospace.io',
	workspace: 'blog',
	apiKey: process.env.MONOSPACE_API_KEY,
});

const post = await client.Post.readOne({ key: 1, fields: ['title', 'body'] });

The generated module exports createClient, your Schema type, and helper types for each collection, such as PostKey, PostCreateOneInput, and PostReadManyResult.

For JavaScript, import createClient from @monospace/sdk and run the same queries without types. The CLI is optional.

Usage

Read

readMany, readOne (by primary key), and readFirst (first match or null) all accept the same query options. Collections without a primary key have no readOne.

Include typed relations at any depth in one request, each with its own filter, sort, limit, and offset.

const latest = await client.Post.readFirst({
	fields: ['id', 'title', 'published_date'],
	filter: { author: { active: { _eq: true } } },
	sort: [{ published_date: { direction: 'desc', nulls: 'last' } }],
});

Alias fields and relations

Use responseName:source to rename a field, or to include the same relation twice with different options.

const authors = await client.Author.readMany({
	fields: ['id', 'displayName:name'],
	include: {
		'latest:posts': { fields: ['title'], sort: [{ published_date: { direction: 'desc' } }], limit: 1 },
		'popular:posts': { fields: ['title'], sort: [{ views: { direction: 'desc' } }], limit: 3 },
	},
});

authors[0].displayName;
authors[0].latest?.data;
authors[0].popular?.data;

Filter

Filters support comparison, string, logical, and relational operators. TypeScript rejects unknown fields and operators unsupported by a field's type. To-many relations use _some, _every, and _none.

const posts = await client.Post.readMany({
	fields: ['id', 'title'],
	filter: {
		_or: [{ title: { _icontains: 'rust' } }, { score: { _gt: 4.5 } }],
		comments: { _some: { text: { _contains: 'thanks' } } },
	},
});

Write

Mutations accept fields and include to determine the result type. Nested writes can create, connect, disconnect, update, and delete related items in one transaction. Read-only collections have no write methods.

const post = await client.Post.createOne({
	data: {
		title: 'Hello, Monospace',
		views: 0,
		author: { _connect: { key: { id: authorId } } },
	},
	fields: ['id', 'title'],
	include: { author: { fields: ['name'] } },
});

await client.Post.updateOne({
	key: 42,
	data: { post_tags: [{ _connect: { keys: [{ id: 7 }] } }] },
});

await client.Post.deleteMany({ filter: { views: { _eq: 0 } } });

Handle errors

Failed requests throw a MonospaceError with the HTTP status. Authentication and permission failures have their own subclasses.

import { MonospaceAuthError, MonospaceError, MonospacePermissionError } from '@monospace/sdk';

try {
	await client.Post.readMany({ fields: ['id'] });
}
catch (error) {
	if (error instanceof MonospaceAuthError) {
		// 401: missing or invalid API key
	}
	else if (error instanceof MonospacePermissionError) {
		// 403: the key's policies don't allow this
	}
	else if (error instanceof MonospaceError) {
		console.error(error.status, error.message);
	}
}

Query collections not known at compile time

For collection names chosen at runtime, every method has an untyped $ variant that takes the collection name and your own result type.

const rows = await client.$readMany<{ id: string; name: string }>(collectionName, {
	fields: ['id', 'name'],
	limit: 50,
});

Types

64-bit integers are returned as strings to preserve precision.

Monospace permissions can hide any field from a caller, so every selected value is typed as possibly null by default. If your API key can always read the selected fields, set strictNull: false to use your schema's nullability. This setting applies to the whole client or a single query.

const client = createClient({ url, workspace, apiKey, strictNull: false });

const post = await client.Post.readOne({ key: 1, fields: ['title', 'body'] }, { strictNull: false });
// { title: string; body: string | null }

Documentation

docs.monospace.io has the SDK guides.

Requirements

  • TypeScript 5.7 or newer for generated types, with strict enabled so nullable fields keep their | null
  • A runtime with a global fetch

Versioning

@monospace/sdk follows Semantic Versioning. Breaking changes to the public API ship only in a new major version. Generated types target the SDK version they were generated with, so run sdk generate again after upgrading.

Feedback

Open an issue to report a bug or suggest a change.