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

@soda-gql/core

v0.10.1

Published

Core types and utilities for zero-runtime GraphQL query generation

Readme

@soda-gql/core

npm version License: MIT

Core GraphQL types and utilities for the soda-gql ecosystem. This package provides the foundational building blocks for writing type-safe GraphQL operations.

Installation

bun add @soda-gql/core

Core Concepts

soda-gql uses two main building blocks for constructing GraphQL operations:

Fragments

Reusable type-safe field selections. Fragments define how to select fields from a GraphQL type and can be spread in operations.

Operations

Complete GraphQL operations (query/mutation/subscription) with field selections. Operations define variables, select fields, and can spread fragments for reusable field selections.

Usage

All soda-gql definitions use the gql.default() pattern, which is provided by the generated GraphQL system:

import { gql } from "@/graphql-system";

Writing Fragments

Fragments define reusable field selections for a specific GraphQL type:

export const userFragment = gql.default(({ fragment, $var }) =>
  fragment.User({
    variables: { ...$var("includeEmail").Boolean("?") },
    fields: ({ f, $ }) => ({
      ...f.id(),
      ...f.name(),
      ...f.email({ if: $.includeEmail }),
    }),
  }),
);

Writing Operations

Operations define complete GraphQL queries, mutations, or subscriptions:

export const getUserQuery = gql.default(({ query, $var }) =>
  query.operation({
    name: "GetUser",
    variables: { ...$var("id").ID("!") },
    fields: ({ f, $ }) => ({
      ...f.user({ id: $.id })(({ f }) => ({ ...f.id(), ...f.name() })),
    }),
  }),
);

// Operation with spread fragment
export const getUserWithFragment = gql.default(({ query, $var }) =>
  query.operation({
    name: "GetUserWithFragment",
    variables: { ...$var("id").ID("!"), ...$var("includeEmail").Boolean("?") },
    fields: ({ f, $ }) => ({
      ...f.user({ id: $.id })(({ f }) => ({ ...userFragment.spread({ includeEmail: $.includeEmail }) })),
    }),
  }),
);

Variable Type Syntax

Variables are declared using a string-based type syntax:

| Syntax | Meaning | GraphQL Equivalent | |--------|---------|-------------------| | "ID:!" | Required ID | ID! | | "String:?" | Optional String | String | | "Int:![]!" | Required list of required Int | [Int!]! | | "String:![]?" | Optional list of required Strings | [String!] | | "MyInput:!" | Required custom input type | MyInput! |

Field Selection Patterns

| Pattern | Description | |---------|-------------| | ...f.id() | Basic field selection | | ...f.posts({ limit: 10 }) | Field with arguments | | ...f.posts()(({ f }) => ({ ... })) | Nested selection (curried) | | ...f.id(null, { alias: "uuid" }) | Field with alias | | ...f.email({ if: $.includeEmail }) | Conditional field | | ...userFragment.spread({}) | Use fragment fields |

Understanding the Inject Module

The inject module ({schema}.inject.ts) bridges your GraphQL schema with TypeScript types.

Why hand-written?

  • Custom scalar types (DateTime, JSON, etc.) need explicit TypeScript type mappings
  • Version-controlled to keep type behavior explicit and reviewable

What it contains:

  • scalar: TypeScript type definitions for each GraphQL scalar

Scaffolding:

bun run soda-gql codegen --emit-inject-template ./src/graphql-system/default.inject.ts

This creates a template with standard scalars (ID, String, Int, Float, Boolean) that you can customize.

Defining Custom Scalars

Scalars define the TypeScript types for GraphQL scalar values:

import { defineScalar } from "@soda-gql/core";

export const scalar = {
  // Built-in scalars
  ...defineScalar<"ID", string, string>("ID"),
  ...defineScalar<"String", string, string>("String"),
  ...defineScalar<"Int", number, number>("Int"),
  ...defineScalar<"Float", number, number>("Float"),
  ...defineScalar<"Boolean", boolean, boolean>("Boolean"),

  // Custom scalars - defineScalar<Name, InputType, OutputType>(name)
  ...defineScalar<"DateTime", string, Date>("DateTime"),
  ...defineScalar<"JSON", Record<string, unknown>, Record<string, unknown>>("JSON"),
} as const;

Alternative callback syntax:

export const scalar = {
  ...defineScalar("DateTime", ({ type }) => ({
    input: type<string>(),
    output: type<Date>(),
    directives: {},
  })),
} as const;

Type Inference

Extract TypeScript types from soda-gql elements using $infer:

// Fragment types
type UserInput = typeof userFragment.$infer.input;
type UserOutput = typeof userFragment.$infer.output;

// Operation types
type QueryVariables = typeof getUserQuery.$infer.input;
type QueryResult = typeof getUserQuery.$infer.output.projected;

Element Extensions

The attach() method allows extending gql elements with custom properties. This is useful for colocating related functionality with fragment or operation definitions.

Basic Usage

import type { GqlElementAttachment } from "@soda-gql/core";

// Define an attachment
const myAttachment: GqlElementAttachment<typeof userFragment, "custom", { value: number }> = {
  name: "custom",
  createValue: (element) => ({ value: 42 }),
};

// Attach to a fragment
export const userFragment = gql
  .default(({ fragment }) => fragment.User({ fields: ({ f }) => ({ ...f.id(), ...f.name() }) }))
  .attach(myAttachment);

// Access the attached property
userFragment.custom.value; // 42

Type Safety

Attachments are fully typed. The returned element includes the new property in its type:

const fragment = gql.default(...).attach({ name: "foo", createValue: () => ({ bar: 1 }) });
// Type: Fragment<...> & { foo: { bar: number } }

Metadata

Metadata allows you to attach runtime information to operations. This is useful for HTTP headers and application-specific values.

Metadata Structure

All metadata types share two base properties:

| Property | Type | Purpose | |----------|------|---------| | headers | Record<string, string> | HTTP headers to include with the GraphQL request | | custom | Record<string, unknown> | Application-specific values (auth requirements, cache settings, etc.) |

Defining Metadata

Metadata is defined on operations:

// Operation with metadata
export const getUserQuery = gql.default(({ query, $var }) =>
  query.operation({
    name: "GetUser",
    variables: { ...$var("id").ID("!") },
    metadata: ({ $, document }) => ({
      headers: { "X-Request-ID": "user-query" },
      custom: {
        requiresAuth: true,
        cacheTtl: 300,
        trackedVariables: [$var.getInner($.id)],
      },
    }),
    fields: ({ f, $ }) => ({ ...f.user({ id: $.id })(({ f }) => ({ ...f.id(), ...f.name() })) }),
  }),
);

Variable Utilities

The $var object provides utility functions for inspecting variable references:

| Method | Description | |--------|-------------| | $var.getName(ref) | Get variable name from a VarRef | | $var.getValue(ref) | Get const value from a nested-value VarRef | | $var.getNameAt(ref, selector) | Get variable name at a specific path | | $var.getValueAt(ref, selector) | Get const value at a specific path | | $var.getVariablePath(ref, selector) | Get path segments to a variable |

// Example: getVariablePath with nested structure
const varRef = createVarRefFromVariable("user");
$var.getVariablePath(varRef, (p) => p.profile.name);
// Returns: ["$user", "name"] (variable name + path after variable reference point)

Adapter

Adapters customize the behavior of your GraphQL system with helpers, metadata configuration, and document transformation.

Basic Usage

import { defineAdapter } from "@soda-gql/core/adapter";

const adapter = defineAdapter({
  helpers: {
    auth: {
      requiresLogin: () => ({ requiresAuth: true }),
    },
  },
  metadata: {
    aggregateFragmentMetadata: (fragments) => ({
      count: fragments.length,
    }),
    schemaLevel: { apiVersion: "v2" },
  },
  transformDocument: ({ document, operationType }) => {
    // Schema-wide document transformation
    return document;
  },
});

Document Transform

Operations can also define their own transform with typed metadata:

gql.default(({ query, $var }) =>
  query.operation({
    name: "GetUser",
    metadata: () => ({ cacheHint: 300 }),
    transformDocument: ({ document, metadata }) => {
      // metadata is typed as { cacheHint: number }
      return document;
    },
    fields: ({ f, $ }) => ({ ... }),
  }),
);

Best Practice: Define transform logic in helpers for reusability:

const adapter = defineAdapter({
  helpers: {
    transform: {
      addCache: (ttl: number) => ({ document }) => {
        // Transform logic here
        return document;
      },
    },
  },
});

// Use helper in operation
query.operation({
  transformDocument: transform.addCache(300),
  ...
});

Runtime Exports

The /runtime subpath provides utilities for operation registration and retrieval:

import { gqlRuntime } from "@soda-gql/core/runtime";

// Retrieve registered operations (typically handled by build plugins)
const operation = gqlRuntime.getOperation("canonicalId");

TypeScript Support

This package requires TypeScript 5.x or later for full type inference support.

Related Packages

License

MIT