Skip to content

meshql-access-cache (@meshql/access-cache)

Cache access permission results per user so expensive rules (DB lookups, dynamic field lists) run once per TTL window.

Terminal window
npm install meshql-access-cache meshql-access meshql-core
# or
npx jsr add @meshql/access-cache @meshql/access @meshql/core

Published on npm as meshql-access-cache and JSR as @meshql/access-cache.

import { createMesh } from "@meshql/core";
import { withAccessCache } from "@meshql/access-cache";
const mesh = createMesh({ entities: { /* … */ } });
const { invalidate } = withAccessCache(
mesh,
{
rules: {
"user.email": (ctx) => ctx.role === "admin",
},
rowAccess: {
post: async (ctx, entityId) => {
// expensive DB check — cached for 60s per user + post id
return canReadPost(ctx.userId, entityId);
},
},
},
{ ttlSeconds: 60 },
);
// After role change or content update:
await invalidate.invalidateUser(userId);
import { createRedisAccessCacheStore, withAccessCache } from "@meshql/access-cache";
const store = createRedisAccessCacheStore({
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});
withAccessCache(mesh, accessOptions, { store, ttlSeconds: 60 });

Upstash is Redis-compatible over HTTPS — no native Redis client required. Row access and dynamic field rules use the async store; static field rules fall back to live evaluation when the store is async-only.

Method Purpose
invalidateUser(userId) Clear cache for one user
invalidateRole(role) Clear cache for one role principal
invalidateAll() Clear entire access cache prefix

Call these after role changes, permission updates, or content publishes that affect row-level access.

  • TTL: 60 seconds
  • Key prefix: meshql:access:
  • Principal: userId:role:tenantId from QueryContext

See also access and persisted-queries.