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

@vyrel/graphql-client

v0.1.4

Published

Type-safe, operation-driven GraphQL mutations and optimistic cache updates for Apollo React

Readme

@vyrel/graphql-client

Small, type-safe React helpers for Apollo mutations and optimistic cache updates. The package is operation-driven: pass only the mutation at the call site. Its GraphQL Codegen plugin generates the gql.tada fragment types and canonical CRUD collection registry without generating one hook per resource.

bun | yarn | pnpm add @vyrel/graphql-client @apollo/client graphql react

Why

Apollo already provides an excellent normalized cache and typed hooks. The repetitive part is connecting optimistic responses to that cache:

  • wrapping an entity in the correct mutation response field;
  • repeating __typename, temporary IDs and unchanged fields;
  • manually inserting and removing items from query results;
  • keeping optimistic and server-result updates consistent;
  • adding casts around fragment-masked gql.tada documents.

@vyrel/graphql-client handles those mechanical steps while leaving domain values, GraphQL operations and Apollo options in application code.

Create

import { useOptimisticCreate } from "@vyrel/graphql-client";

const [createTask] = useOptimisticCreate(CreateTaskDocument, {
  optimistic: ({ input }) => ({
    description: input.description ?? null,
    imageFull: null,
    imageThumb: null,
    title: input.title,
  }),
});

The callback variables come from CreateTaskDocument. The generated registry connects the mutation's ...TaskListItem spread to ResultOf<typeof TaskListItemFragment>, so its selected fields are available to TypeScript without passing the fragment at every call site. At runtime the package reads the same fragment definitions from the gql.tada document, infers Task, creates a temporary id, adds conventional createdAt and updatedAt values when selected and builds the mutation response. The generated registry maps Task to ListTasksDocument and maps its organizationId variable to CreateTask.input.organizationId, so both the optimistic and real entity are prepended automatically.

The optimistic callback intentionally remains explicit. A generic library cannot safely invent a title, price, status or other domain value.

Update on demand

Each call site chooses only the fields it wants to change.

const [renameTask] = useOptimisticUpdate(UpdateTaskDocument, {
  current: task,
  optimistic: ({ input }) => ({
    title: input.title ?? task.title,
  }),
});

Another screen can update a different patch without defining another resource:

const [editTask] = useOptimisticUpdate(UpdateTaskDocument, {
  current: task,
  optimistic: ({ input }) => ({
    description: input.description ?? task.description,
    title: input.title ?? task.title,
  }),
});

Apollo normalizes the partial optimistic entity using its typename and ID, so every query containing the same entity sees the update. A list rewrite is not needed for ordinary updates. current must contain every field selected by the mutation fragment; TypeScript enforces this so Apollo never receives an incomplete optimistic response. The optimistic callback remains a partial on-demand patch.

Delete

const [deleteTask] = useOptimisticDelete(DeleteTaskDocument, {
  id: ({ input }) => input.taskId,
});

The package builds the scalar optimistic response, finds Task through the generated mutation registry, removes the item from every cached argument variant of the canonical tasks collection and evicts its normalized entity.

Server freshness

The package deliberately does not refetch queries. The component that owns an Apollo query also owns its exact filters, pagination and refetch function:

const { refetch } = useQuery(ListTasksDocument, { variables: filters });
const [createTask] = useOptimisticCreate(CreateTaskDocument, options);

await createTask({ variables: { input } });
await refetch();

This keeps network policy in application code and optimistic cache mechanics in the package, without reconstructing query variables in another abstraction.

Apollo options and escape hatches

Normal useMutation options stay at the top level and keep their Apollo types:

useOptimisticCreate(CreateTaskDocument, {
  optimistic: ({ input }) => ({ title: input.title }),
  onCompleted: () => notifySuccess(),
  onError: (error) => notifyError(error.message),
  refetchQueries: [DashboardDocument],
  update: (cache, result, context) => {
    // Runs after @vyrel/graphql-client's built-in cache behavior.
  },
});

Pass field when a mutation has multiple top-level fields. Cache key fields are generated from the plugin configuration (and default to id); keyField remains an override, while optimisticId supplies a custom temporary-ID strategy.

Reads continue to use Apollo's useQuery. It is already concise and fully typed; wrapping it would add an abstraction without removing meaningful boilerplate.

Required codegen

The package is designed around gql.tada and uses the official GraphQL Code Generator document scanner. Install its CLI as a development dependency:

bun add --dev @graphql-codegen/cli

The @vyrel/graphql-client/codegen-plugin custom plugin receives the parsed schema and documents from GraphQL Codegen. It only implements Vyrel-specific knowledge: fragment type augmentation, canonical list discovery, CRUD mutation association and mutation-to-query variable binding.

import type { CodegenConfig } from "@graphql-codegen/cli";

const config: CodegenConfig = {
  schema: "schema.graphql",
  documents: ["src/**/*.{ts,tsx}", "!src/graphql/generated/**"],
  pluckConfig: { globalGqlIdentifierName: ["graphql"] },
  generates: {
    "src/graphql/generated/client-schema.ts": {
      plugins: [
        {
          "@vyrel/graphql-client/codegen-plugin": {
            keyFields: { Organization: "slug" },
            scalars: { DateTime: "string" },
          },
        },
      ],
    },
  },
};

export default config;

Documents follow the gql.tada export convention: fragment TaskListItem is exported as TaskListItemFragment, and operation ListTasks as ListTasksDocument. Codegen validates these export names. A mutation can spread multiple fragments and select multiple CRUD root fields; their result types and per-field variable bindings are generated independently.

Register the generated runtime registry once for each Apollo cache. The /cache entry is isomorphic and does not import the React client bundle:

import { configureGraphqlClientCache } from "@vyrel/graphql-client/cache";
import {
  graphqlClientRegistry,
  graphqlClientTypePolicies,
} from "./generated/client-schema";

const cache = configureGraphqlClientCache(
  new InMemoryCache({
    typePolicies: graphqlClientTypePolicies,
  }),
  graphqlClientRegistry
);
import type { GraphqlClientModel } from "./client-schema";

type Task = GraphqlClientModel<"Task">;

Task has autocomplete for every field currently present in the schema. The generated artifact also records enum values, nested list/nullability structure, configured cache keys and custom scalar mappings. It exports graphqlClientTypePolicies, keeping Apollo normalization aligned with the generated optimistic registry.

The generated artifact also exports schema metadata for ModelOf extensions. The /codegen and /codegen-plugin exports never enter the React bundle.

V1 boundaries

  • Apollo Client 4 with React 18.2 or React 19 is supported.
  • Mutations may contain multiple CRUD root fields; field selects the one used by a hook. Canonical collection queries contain one top-level list field.
  • Canonical collections are top-level arrays. When multiple list queries return the same entity, the unique List<Field> operation is canonical; unresolved ambiguity fails code generation.
  • Optimistic create/update/delete are included. Offline queues, undo, conflict resolution and UI notifications remain outside the core.
  • The package does not generate operations or replace Apollo or gql.tada.

See docs/flow-a-z.md for the complete server-to-client flow and docs/contract-v1.md for the architectural contract.