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/colocation-tools

v0.10.2

Published

Colocation utilities for soda-gql fragments

Readme

@soda-gql/colocation-tools

Utilities for colocating GraphQL fragments with components in soda-gql. This package provides tools for fragment composition and data masking patterns.

Features

  • Fragment colocation - Keep GraphQL fragments close to components that use them
  • Data projection - Create typed projections from fragment data
  • Type safety - Full TypeScript support for fragment composition

Installation

npm install @soda-gql/colocation-tools
# or
bun add @soda-gql/colocation-tools

Usage

Fragment Colocation Pattern

import { createProjection, createExecutionResultParser } from "@soda-gql/colocation-tools";
import { userFragment } from "./graphql-system";

// Create a projection with paths and handle function
const userProjection = createProjection(userFragment, {
  paths: ["$.user"],
  handle: (result) => {
    if (result.isError()) return { error: result.error, user: null };
    if (result.isEmpty()) return { error: null, user: null };
    const [user] = result.unwrap(); // tuple of values for each path
    return { error: null, user };
  },
});

// Use with execution result parser
const parser = createExecutionResultParser({
  user: userProjection,
});

Spreading Fragments

Fragments can be spread in operations:

import { gql } from "./graphql-system";
import { userFragment } from "./UserCard";

export const getUserQuery = gql.default(({ query }) =>
  query.operation({
    name: "GetUser",
    fields: ({ f }) => ({ ...f.user({ id: "1" })(({ f }) => ({ ...userFragment.spread() })) }),
  }),
);

Using with $colocate

When composing multiple fragments in a single operation, use $colocate to prefix field selections with labels. The createExecutionResultParser will use these same labels to extract the corresponding data.

Complete Workflow

Step 1: Define component fragments

// UserCard.tsx
export const userCardFragment = gql.default(({ fragment, $var }) =>
  fragment.Query({
    variables: { ...$var("userId").ID("!") },
    fields: ({ f, $ }) => ({ ...f.user({ id: $.userId })(({ f }) => ({ ...f.id(), ...f.name(), ...f.email() })) }),
  }),
);

export const userCardProjection = createProjection(userCardFragment, {
  paths: ["$.user"],
  handle: (result) => {
    if (result.isError()) return { error: result.error, user: null };
    if (result.isEmpty()) return { error: null, user: null };
    const [user] = result.unwrap();
    return { error: null, user };
  },
});

Step 2: Compose operation with $colocate

// UserPage.tsx
import { userCardFragment, userCardProjection } from "./UserCard";
import { postListFragment, postListProjection } from "./PostList";

export const userPageQuery = gql.default(({ query, $var, $colocate }) =>
  query.operation({
    name: "UserPage",
    variables: { ...$var("userId").ID("!") },
    fields: ({ $ }) => $colocate({
      userCard: userCardFragment.spread({ userId: $.userId }),
      postList: postListFragment.spread({ userId: $.userId }),
    }),
  }),
);

Step 3: Create parser with matching labels

const parseUserPageResult = createExecutionResultParser({
  userCard: userCardProjection,
  postList: postListProjection,
});

Step 4: Parse execution result

const result = await executeQuery(userPageQuery);
const { userCard, postList } = parseUserPageResult(result);
// userCard and postList contain the projected data

The labels in $colocate (userCard, postList) must match the labels in createExecutionResultParser for proper data routing.

API

createProjection

Creates a typed projection from a fragment definition with specified paths and handler.

import { createProjection } from "@soda-gql/colocation-tools";

const projection = createProjection(fragment, {
  // Field paths to extract (must start with "$.")
  paths: ["$.user"],
  // Handler to transform the sliced result (receives tuple of values for each path)
  handle: (result) => {
    if (result.isError()) return { error: result.error, data: null };
    if (result.isEmpty()) return { error: null, data: null };
    const [user] = result.unwrap();
    return { error: null, data: user };
  },
});

createProjectionAttachment

Combines fragment definition and projection into a single export using attach(). This eliminates the need for separate projection definitions.

import { createProjectionAttachment } from "@soda-gql/colocation-tools";
import { gql } from "./graphql-system";

export const postListFragment = gql
  .default(({ fragment, $var }) =>
    fragment.Query({
      variables: { ...$var("userId").ID("!") },
      fields: ({ f, $ }) => ({ ...f.user({ id: $.userId })(({ f }) => ({ ...f.posts({})(({ f }) => ({ ...f.id(), ...f.title() })) })) }),
    }),
  )
  .attach(
    createProjectionAttachment({
      paths: ["$.user.posts"],
      handle: (result) => {
        if (result.isError()) return { error: result.error, posts: null };
        if (result.isEmpty()) return { error: null, posts: null };
        const [posts] = result.unwrap();
        return { error: null, posts: posts ?? [] };
      },
    }),
  );

// The fragment now has a .projection property
postListFragment.projection;

Benefits:

  • Single export for both fragment and projection
  • Fragment can be passed directly to createExecutionResultParser
  • Reduces boilerplate when projection logic is simple

Using with createExecutionResultParser:

const parseResult = createExecutionResultParser({
  userCard: { projection: userCardProjection }, // Explicit projection
  postList: postListFragment,                    // Fragment with attached projection
});

Both patterns work with the parser - it automatically detects fragments with attached projections.

createExecutionResultParser

Creates a parser from labeled projections to process GraphQL execution results.

import { createExecutionResultParser } from "@soda-gql/colocation-tools";

const parser = createExecutionResultParser({
  userData: userProjection,
  postsData: postsProjection,
});

const results = parser(executionResult);
// results.userData, results.postsData

createDirectParser

For single fragment operations (like mutations), use createDirectParser for simpler parsing without $colocate:

import { createDirectParser } from "@soda-gql/colocation-tools";

// Fragment with attached projection
const productFragment = gql
  .default(({ fragment }) => fragment.Mutation({ ... }))
  .attach(createProjectionAttachment({ ... }));

// Direct parser - no labels needed
const parseResult = createDirectParser(productFragment);
const result = parseResult(executionResult);
// result is the projected type directly

Related Packages

License

MIT