Getting Started

This guide walks you through setting up soda-gql in your project.

Installation

# Install core packages
bun add @soda-gql/core
bun add -D @soda-gql/tools @soda-gql/config

# Install peer dependencies
bun add neverthrow
TIP

soda-gql supports queries, mutations, and subscriptions. Field directives (@skip, @include) work in both tagged template and callback builder syntax. See the Tagged Template Syntax Guide for a feature comparison.

Setup

1. Initialize Project

Run the init command to scaffold all necessary files:

bun run soda-gql init

This creates:

  • soda-gql.config.ts - Configuration file
  • schema.graphql - Sample GraphQL schema
  • graphql-system/default.inject.ts - Scalars and adapter definitions
  • graphql-system/.gitignore - Ignore generated files

2. Edit Your Schema

Replace the sample schema.graphql with your actual GraphQL schema:

type Query {
  user(id: ID!): User
}

type User {
  id: ID!
  name: String!
  email: String!
}

3. Customize Scalars and Adapter (Optional)

Edit graphql-system/default.inject.ts to add custom scalars:

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

export const scalar = {
  ...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"),
  // Add custom scalars
  ...defineScalar<"DateTime", string, Date>("DateTime"),
} as const;

export const adapter = defineAdapter({
  helpers: {},
  metadata: {
    aggregateFragmentMetadata: (fragments) => fragments.map((m) => m.metadata),
  },
});

4. Generate GraphQL System

bun run soda-gql codegen schema

This generates the type-safe GraphQL system in the graphql-system/ directory.

Basic Usage

Define a Fragment

Fragments specify reusable field selections using tagged template syntax:

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

export const userFragment = gql.default(({ fragment }) =>
  fragment("UserFragment", "User")`{
    id
    name
  }`(),
);

Create an Operation

Operations can also use tagged template syntax, with fragment spreads via interpolation:

// Tagged template with fragment spread
export const getUserQuery = gql.default(({ query }) =>
  query("GetUser")`($userId: ID!) {
    user(id: $userId) {
      ...${userFragment}
    }
  }`(),
);

For advanced features like field aliases, directives, or $colocate, use the callback builder syntax:

// Options-object path (for advanced features)
export const getUserQuery = gql.default(({ query }) =>
  query("GetUser")({
    variables: `($userId: ID!)`,
    fields: ({ f, $ }) => ({
      ...f("user", { id: $.userId })(({ f }) => ({
        ...userFragment.spread(),
      })),
    }),
  })({}),
);

Execute the Query

import { getUserQuery } from "@/queries/user.query";
import { graphqlClient } from "./client";

const result = await graphqlClient({
  document: getUserQuery.document,
  variables: { userId: "42" },
});

Build Plugin Setup

soda-gql requires a build plugin to transform your code. Choose the one that matches your setup:

PluginUse Case
@soda-gql/babelBabel-based builds (use /plugin export)
@soda-gql/tscTypeScript compiler (use /plugin export)
@soda-gql/webpack-pluginWebpack projects
@soda-gql/vite-pluginVite projects
@soda-gql/metro-pluginReact Native / Expo

See the Recipes section for framework-specific setup guides.

Next Steps