Skip to content

Fix: gql.tada Not Working — Types Not Inferred, Schema Not Found, or IDE Not Showing Completions

FixDevs ·

Quick Answer

How to fix gql.tada issues — schema introspection, type-safe GraphQL queries, fragment masking, urql and Apollo Client integration, IDE setup, and CI type checking.

The Problem

graphql() returns unknown instead of typed data:

import { graphql } from 'gql.tada';

const UserQuery = graphql(`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
    }
  }
`);
// TypeScript shows the result type as unknown — no inference

Or the IDE shows no autocompletion inside the template literal:

const query = graphql(`
  query {
    |  // No suggestions after typing
  }
`);

Or the schema file can’t be found:

Error: Could not find schema — or —
gql.tada: No schema found in tsconfig.json

Why This Happens

gql.tada infers GraphQL types at the TypeScript level — no codegen step needed. But it requires specific configuration:

  • The GraphQL schema must be available to TypeScript — gql.tada reads a schema file (introspection JSON or SDL) referenced in tsconfig.json. Without this, it can’t infer field types from queries.
  • The TypeScript plugin must be configured — gql.tada uses a TypeScript language service plugin for IDE autocompletion. Without the plugin in tsconfig.json, the IDE doesn’t know about the GraphQL schema.
  • Schema must be up to date — gql.tada reads the schema at build/typecheck time. If the API changed but the local schema file is outdated, types don’t match reality.
  • Template literal types have limits — TypeScript’s template literal inference works with graphql() from gql.tada’s specific import. Using a different graphql tag or wrapping it in another function breaks inference.

Fix 1: Setup and Schema Introspection

npm install gql.tada
npm install -D @0no-co/graphqlsp  # TypeScript LSP plugin for IDE support
# Generate introspection schema from your GraphQL API
npx gql.tada generate-schema "http://localhost:4000/graphql" --output schema.graphql

# Or from a file
npx gql.tada generate-schema ./schema.graphql --output introspection.ts

# Generate the output file used by TypeScript
npx gql.tada generate-output --tsconfig ./tsconfig.json
// tsconfig.json — configure the TypeScript plugin
{
  "compilerOptions": {
    "strict": true,
    "plugins": [
      {
        "name": "@0no-co/graphqlsp",
        "schema": "./schema.graphql",
        "tadaOutputLocation": "./src/graphql-env.d.ts"
      }
    ]
  }
}
// src/graphql/tada.ts — initialize gql.tada
import { initGraphQLTada } from 'gql.tada';
import type { introspection } from '../graphql-env';

export const graphql = initGraphQLTada<{
  introspection: introspection;
  scalars: {
    DateTime: string;
    JSON: Record<string, unknown>;
  };
}>();

export type { FragmentOf, ResultOf, VariablesOf } from 'gql.tada';
export { readFragment } from 'gql.tada';

Fix 2: Type-Safe Queries

import { graphql, type ResultOf, type VariablesOf } from '@/graphql/tada';

// Query — types are inferred from the schema
const GetUsersQuery = graphql(`
  query GetUsers($limit: Int, $offset: Int) {
    users(limit: $limit, offset: $offset) {
      id
      name
      email
      role
      createdAt
      posts {
        id
        title
      }
    }
  }
`);

// ResultOf<typeof GetUsersQuery> = {
//   users: {
//     id: string;
//     name: string;
//     email: string;
//     role: string;
//     createdAt: string;
//     posts: { id: string; title: string }[];
//   }[];
// }

// VariablesOf<typeof GetUsersQuery> = {
//   limit?: number | null;
//   offset?: number | null;
// }

// Mutation
const CreateUserMutation = graphql(`
  mutation CreateUser($input: CreateUserInput!) {
    createUser(input: $input) {
      id
      name
      email
    }
  }
`);

// Variables are type-checked
type CreateUserVars = VariablesOf<typeof CreateUserMutation>;
// { input: { name: string; email: string; role?: string } }

Fix 3: Fragments

import { graphql, readFragment, type FragmentOf } from '@/graphql/tada';

// Define a fragment
const UserFragment = graphql(`
  fragment UserFields on User {
    id
    name
    email
    avatar
  }
`);

// Use fragment in a query
const GetUserQuery = graphql(`
  query GetUser($id: ID!) {
    user(id: $id) {
      ...UserFields
      posts {
        id
        title
      }
    }
  }
`, [UserFragment]);  // Pass fragments as second argument

// Component that accepts fragment data
function UserCard({ user }: { user: FragmentOf<typeof UserFragment> }) {
  // readFragment unmasks the fragment data
  const data = readFragment(UserFragment, user);
  // data = { id: string, name: string, email: string, avatar: string }

  return (
    <div>
      <img src={data.avatar} alt={data.name} />
      <h3>{data.name}</h3>
      <p>{data.email}</p>
    </div>
  );
}

// Nested fragments
const PostFragment = graphql(`
  fragment PostFields on Post {
    id
    title
    body
    author {
      ...UserFields
    }
  }
`, [UserFragment]);

Fix 4: urql Integration

npm install urql gql.tada
'use client';

import { useQuery, useMutation } from 'urql';
import { graphql, type ResultOf } from '@/graphql/tada';

const PostsQuery = graphql(`
  query Posts($limit: Int!) {
    posts(limit: $limit) {
      id
      title
      excerpt
      author {
        name
      }
    }
  }
`);

function PostList() {
  const [result] = useQuery({
    query: PostsQuery,
    variables: { limit: 10 },
    // Variables are type-checked automatically
  });

  if (result.fetching) return <div>Loading...</div>;
  if (result.error) return <div>Error: {result.error.message}</div>;

  // result.data is fully typed
  return (
    <ul>
      {result.data?.posts.map(post => (
        <li key={post.id}>
          <h3>{post.title}</h3>
          <p>{post.excerpt}</p>
          <span>by {post.author.name}</span>
        </li>
      ))}
    </ul>
  );
}

// Mutation
const CreatePostMutation = graphql(`
  mutation CreatePost($input: CreatePostInput!) {
    createPost(input: $input) {
      id
      title
    }
  }
`);

function CreatePostForm() {
  const [, createPost] = useMutation(CreatePostMutation);

  async function handleSubmit(data: { title: string; body: string }) {
    const result = await createPost({
      input: data,  // Type-checked against CreatePostInput
    });

    if (result.data) {
      console.log('Created:', result.data.createPost.id);
    }
  }

  return <form onSubmit={/* ... */}>...</form>;
}

Fix 5: Apollo Client Integration

import { useQuery, useMutation } from '@apollo/client';
import { graphql, type ResultOf, type VariablesOf } from '@/graphql/tada';

const GetUserQuery = graphql(`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
    }
  }
`);

function UserProfile({ userId }: { userId: string }) {
  const { data, loading, error } = useQuery(GetUserQuery, {
    variables: { id: userId },
  });

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  // data is typed as ResultOf<typeof GetUserQuery>
  return (
    <div>
      <h1>{data?.user?.name}</h1>
      <p>{data?.user?.email}</p>
    </div>
  );
}

Fix 6: CI/CD Schema Validation

// package.json
{
  "scripts": {
    "schema:update": "gql.tada generate-schema http://localhost:4000/graphql --output schema.graphql",
    "graphql:generate": "gql.tada generate-output --tsconfig ./tsconfig.json",
    "graphql:check": "gql.tada check --tsconfig ./tsconfig.json",
    "predev": "npm run graphql:generate",
    "prebuild": "npm run graphql:generate"
  }
}
# .github/workflows/graphql-check.yml
name: GraphQL Type Check
on: [pull_request]
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci
      - run: npx gql.tada generate-output
      - run: npx tsc --noEmit  # Type-check all queries against schema

Still Not Working?

Types are unknown — no inference — the schema isn’t configured. Check tsconfig.json has the @0no-co/graphqlsp plugin with the correct schema path. Run npx gql.tada generate-output to create the graphql-env.d.ts file. Ensure you import graphql from your initialized tada.ts, not from gql.tada directly.

No autocompletion in the IDE — VS Code must use the workspace TypeScript version. Open Command Palette → “TypeScript: Select TypeScript Version” → “Use Workspace Version”. The @0no-co/graphqlsp plugin only runs with the workspace TS.

Schema is outdated — run npx gql.tada generate-schema <url> to re-download the schema. The introspection result reflects the API at download time. Add schema:update to your CI or pre-dev scripts.

Fragment types don’t propagate — fragments must be passed as the second argument to graphql(): graphql(\query { …UserFields }`, [UserFragment])`. Without this, gql.tada can’t resolve fragment spreads and the fields are missing from the result type.

For related GraphQL issues, see Fix: GraphQL Yoga Not Working and Fix: Pothos Not Working.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles