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 🙏

© 2025 – Pkg Stats / Ryan Hefner

villus-cachebay

v0.8.0

Published

[![CI](https://github.com/lockvoid/villus-cachebay/actions/workflows/test.yml/badge.svg)](https://github.com/lockvoid/villus-cachebay/actions/workflows/test.yml) [![npm](https://img.shields.io/npm/v/villus-cachebay.svg)](https://www.npmjs.com/package/vill

Downloads

59

Readme

Cachebay for Villus

CI npm Coverage Bundlephobia License: MIT

Pragmatic normalized cache x Relay-style connections for Villus.

A tiny (11 kB gzipped) cache layer for Villus:

  • Small & focused APIs. Fragments, optimistic edits, and Relay connections — without ceremony.
  • Fast rendering and excellent performance. Microtask-batched updates; stable Relay views that don't churn arrays and minimize re-renders.
  • Relay-style connections — append/prepend/replace, edge de-duplication by node key, reactive, and no array churn.
  • Optimistic updates that stack — layered commits/reverts for entities and connections (add/remove/update pageInfo) with clean rollback.
  • SSR that just works — dehydrate/hydrate; first client mount renders from cache without a duplicate request; clean Suspense behavior.
  • Fragments APIidentify, readFragment, writeFragment (interfaces supported), plus reactive materialized proxies.
  • Tiny composablesuseFragment, useFragments, useCache
  • Suspense — first-class support.
  • Compiller mode (alpha) — boost performance by pre-compiling fragments and queries.

Documentation

  • Relay connections@connection directive, append/prepend/replace, de-dup, policy matrix
  • Optimistic updates — layering, rollback, entity ops, connection ops (addNode / removeNode / patch)
  • Fragmentsidentify(), readFragment(), writeFragment()
  • ComposablesuseCache(), useFragment()
  • SSR — dehydrate/hydrate, one-time cache render, Suspense notes

Keynotes

A quick architectural overview of how Cachebay works — see Keynotes.


Demo app

Nuxt 4 Demo App ϟ

or try live https://harrypotter.exp.lockvoid.com/


Install

Package installation using npm or pnpm to add Villus and Cachebay dependencies to your project.

npm i villus villus-cachebay
# or
pnpm add villus villus-cachebay

Quick start

Basic setup for creating a Cachebay-enabled Villus client with normalized caching and network transport configuration.

// client.ts
import { createClient } from 'villus'
import { createCache } from 'villus-cachebay'
import { fetch as fetchPlugin } from 'villus'

export const cache = createCache({
  // e.g. keys: { Post: (post) => post.id }
})

export const client = createClient({
  url: '/graphql',

  cachePolicy: 'cache-and-network',

  use: [
    cache,
    fetchPlugin(),
  ],
})

Cachebay Options

Configuration options for customizing Cachebay behavior including entity identification, interface mapping, and SSR/Suspense timeouts.

import { createCache } from 'villus-cachebay'

const cache = createCache({
  keys: {
    // e.g. AudioPost: (post) => post?.id ?? null
  },

  interfaces: {
    // e.g. Post: ['AudioPost','VideoPost']
  },

  hydrationTimeout: 100, // default
  suspensionTimeout: 1000, // default
})
  • keys / interfaces: how identity and interface reads work → see Fragments.
  • hydrationTimeout / suspensionTimeout: how SSR & Suspense windows are handled → see SSR.

Basic Query with Relay Connection

Example of using the @connection directive for cursor-based pagination with automatic edge management and page merging.

import { useQuery } from 'villus'

const { data } = useQuery({
  query: `
    query Posts($first: Int, $after: String) {
      posts(first: $first, after: $after) @connection {
        pageInfo {
          endCursor
          hasNextPage
        }

        edges {
          node {
            id
            title
          }
        }
      }
    }
  `,

  variables: {
    first: 20
  },
})

SSR

Server-side rendering support through cache snapshots that enable seamless hydration and prevent duplicate requests on first client mount.

// Server:
const snapshot = cache.dehydrate()

// Client:
cache.hydrate(snapshot)

On first client mount after hydrate, cache-and-network renders from cache without a duplicate request. After that, it behaves normally (cached + revalidate).


Usage with Nuxt 4

Integration pattern for Nuxt 4 using plugins to manage cache lifecycle, state synchronization between server and client, and proper hydration handling.

Minimal pattern: one cache instance per SSR request, dehydrate to a Nuxt state, hydrate on the client, and expose Villus + Cachebay via plugins.

1) Nuxt plugin (client & server)

// plugins/villus.ts
import { createClient } from 'villus'
import { createCache } from 'villus-cachebay'
import { fetch as fetchPlugin, dedup as dedupPlugin } from 'villus'

export default defineNuxtPlugin((nuxtApp) => {
  const cache = createCache()

  const state = useState('cachebay', () => null)

   if (import.meta.server) {
    nuxtApp.hook("app:rendered", () => {
      useState("cachebay").value = cachebay.dehydrate();
    });
  };

  if (import.meta.client) {
    const state = useState('cachebay').value;

    if (state) {
      cachebay.hydrate(state);
    }
  }

  const client = createClient({
    url: '/graphql',

    cachePolicy: 'cache-and-network',

    // Recommended plugin order: cachebay → dedup() → fetch()
    use: [
      cache,
      dedupPlugin(),
      fetchPlugin(),
    ],
  })

  nuxtApp.vueApp.use(client)
  nuxtApp.vueApp.use(cache)
})

2) Use it in components

<script setup lang="ts">
import { useQuery } from 'villus'

const POSTS_QUERY = /* GraphQL */ `
  query Posts($first: Int, $after: String) {
    posts(first: $first, after: $after) @connection {
      pageInfo {
        endCursor
        hasNextPage
      }

      edges {
        node {
          id
          title
        }
      }
    }
  }
`

const { data } = await useQuery({
  query: POSTS_QUERY,

  variables: {
    first: 10,
  }
})
</script>

<template>
  <ul>
    <li v-for="e in data?.posts?.edges" :key="e.node.id">
      {{ e.node.title }}
    </li>
  </ul>
</template>

Fragments

Reactive fragment system for reading and writing normalized entities. Returns Vue proxies that automatically update when underlying data changes, enabling granular cache management and optimistic updates.

Reactive proxies are returned by reads; writes update normalized state immediately.

import { useCache } from 'villus-cachebay'
const { identify, readFragment, writeFragment } = useCache()

identify({ __typename: 'Post', id: 42 }) // → "Post:42"

const post = readFragment({
  id: 'Post:42',

  fragment: `
    fragment PostFields on Post {
      id
      name
    }
  `,
})

writeFragment({
  id: 'Post:42',

  fragment: `
    fragment PostFields on Post {
      id
      title
    }
  `,

  data: {
    title: 'New title'
  },
})

See Cache fragments for a concise API (identify, readFragment, writeFragment) and simple examples.


Optimistic updates (entities & connections)

Layered optimistic updates let you stage cache changes immediately while a request is in flight. You can patch/delete entities and edit connections (add/remove/patch) with zero array churn—then finalize with commit(data?) or undo with revert().

const tx = cache.modifyOptimistic((o, ctx) => {
  // Entities
  o.patch('Post:999', { title: 'New (optimistic)' })
  o.delete('Post:999')

  // Connections
  const c = o.connection({ parent: 'Query', key: 'posts' })

  // Add (dedup by node key)
  c.addNode({ __typename: 'Post', id: '999', title: 'New' }, { position: 'start' })
  c.addNode({ __typename: 'Post', id: '100', title: 'Before 123' }, { position: 'before', anchor: 'Post:123' })
  c.addNode({ __typename: 'Post', id: '101', title: 'After 123'  }, { position: 'after',  anchor: { __typename: 'Post', id: '123' } })

  // Remove
  c.removeNode('Post:999')

  // Patch connection meta / pageInfo (shallow)
  c.patch(prev => ({ pageInfo: { ...prev.pageInfo, hasNextPage: false } }))
})

// Finalize this layer (optionally using server data, e.g. replace a temp id)
tx.commit({ id: '123' })

// Or undo this layer only:
tx.revert()
  • commit(data?) finalizes the layer: your builder runs once in write-through mode with { phase: 'commit', data }, and the layer is dropped (nothing left to replay).
  • revert() undoes only that layer; other layers remain and continue to apply in order.
  • Layers stack in insertion order.
  • addNode de-dups by entity key and refreshes edge meta in place.
  • position: "start" | "end" | "before" | "after" (for the latter two, provide an anchor).

See Optimistic updates for more details and patterns.

MIT © LockVoid Labs ~●~