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

@vantis/data

v0.2.0

Published

Vantis Data SDK — typed, server-only runtime client for the per-block Data API

Readme

@vantis/data

Store and query your app's data with full TypeScript types — no SQL, no connection strings.

import { data } from '@vantis/data';

await data.from('posts').insert({ title: 'Hello world' });
const { data: posts } = await data.from('posts').select('*');

That's a real, type-checked query. Here's how to get there.

Quickstart

1. Install

npm install @vantis/data

Zero dependencies.

2. Describe your data in vantis.schema.json:

{
  "version": 1,
  "tables": [
    {
      "name": "posts",
      "columns": [
        { "name": "id",         "type": "uuid",        "primary_key": true, "default": { "kind": "builtin", "builtin": "gen_random_uuid()" } },
        { "name": "title",      "type": "text",        "nullable": false },
        { "name": "body",       "type": "text",        "nullable": true },
        { "name": "created_at", "type": "timestamptz", "nullable": false, "default": { "kind": "builtin", "builtin": "now()" } }
      ]
    }
  ]
}

3. Generate types

vantis data build

Add the generated file to your tsconfig.json include array. Now every query is type-checked — wrong column names and missing required fields are compile errors, before you run anything.

Editor autocomplete while writing the schema: point your editor at the bundled JSON schema in .vscode/settings.json:

{ "json.schemas": [{ "fileMatch": ["vantis.schema.json"], "url": "./node_modules/@vantis/data/vantis.schema.schema.json" }] }

Read

const { data: posts, error } = await data.from('posts').select('*');

Filter, order, and page:

const { data: recent } = await data.from('posts')
  .select('id, title, created_at')
  .eq('published', true)
  .order('created_at', { ascending: false })
  .limit(20);

Pull in related rows — the shape follows your foreign keys: a single relationship comes back as an object (null if it can be absent), and the "many" side of a relationship comes back as an array:

const { data: withAuthor } = await data.from('posts').embed('users');
// withAuthor[0].users — the author object (or null if the post has no author)

Write

// Insert — omit a required field and it won't compile
await data.from('posts').insert({ title: 'Hello world' });

// Update the rows your filters match
await data.from('posts').eq('id', postId).update({ title: 'Edited' });

// Delete the rows your filters match
await data.from('posts').eq('id', postId).delete();

Chain .select() after a write to get the affected rows back:

const { data: created } = await data.from('posts').insert({ title: 'Hi' }).select('*');

Change a value safely

Increment a counter, toggle a flag, or merge JSON in one atomic call — no read-then-write, no race conditions:

import { data, increment, toggle } from '@vantis/data';

await data.from('posts').modify({ views: increment(1) }, { id: postId });
await data.from('posts').modify({ pinned: toggle() }, { id: postId });

Available helpers: increment, decrement, multiply, divide, min, max, toggle, serverTimestamp, concat, merge, jsonSet, setOnInsert.

Add a guard to change a row only when a condition still holds:

const res = await data.from('accounts').modify(
  { balance: decrement(10) },
  { id: accountId },
  { guard: { balance: { gte: 10 } } },
);
if (!res.error && res.data?.length === 0) {
  // guard missed — balance was too low, nothing changed
}

Or create the row on first touch and modify it every time after — no separate "does it exist yet?" check, and no need to repeat the id you already gave in the second argument:

await data.from('counters').modify(
  { count: increment(1) },
  { id: 'visits' },
  { upsert: {} }, // row doesn't exist yet → created with count starting at 1
);

guard and upsert are mutually exclusive — use one or the other.

Search

For tables you mark searchable, full-text search is one call:

const { data: hits } = await data.from('posts').search('climbing gear');

Call your functions

const { data: result } = await data.run.transfer({ from, to, amount });

When something goes wrong

Every call returns { data, error } — no try/catch needed. Check error first:

const { data: post, error } = await data.from('posts').select('*').eq('id', postId).limit(1);
if (error) {
  // error.message tells you what went wrong; error.code is a stable code you can branch on
  return;
}
use(post);

Keep your token on the server

@vantis/data reads its URL and token from your environment. The token grants access to your data — use it only on the server, never in the browser. Importing @vantis/data in browser code throws, on purpose.

The token is read when you run a query, not when you import — so server builds that bundle before your environment is set (like next build) work fine.

Need full control?

@vantis/data gives you a fast, typed, opinionated path. If you need raw database access or a different engine, run your own service on Vantis instead.

License

Apache-2.0