gql.tada
gql.tada is a powerful and spec-compliant GraphQL document authoring library designed for TypeScript environments. Its primary function is to infer the result and variables types of GraphQL queries, mutations, and fragments directly within the TypeScript type system, eliminating the need for separate code generation steps. Currently at version 1.9.2, the library maintains an active development pace with frequent patch and minor releases, as evidenced by recent changelogs. A key differentiator is its deep integration with TypeScript, providing on-the-fly type safety, editor feedback, and auto-completion without external build steps, especially when paired with a language server like GraphQLSP. It achieves this by parsing GraphQL documents directly in TypeScript, deriving a schema from introspection and scalar configuration, and then mapping these definitions to robust result and variables types, including advanced features like fragment masks and gradual fragment unwrapping.
Common errors
-
Module '"gql.tada"' has no exported member 'graphql'.
cause The `@gql.tada/codegen-env` import or the `graphql-env.d.ts` file is missing, incorrectly configured, or `@gql.tada/cli` has not generated the necessary environment types for the TypeScript language server.fixEnsure `@gql.tada/cli` is installed and run `npx gql-tada init` followed by `npx gql-tada generate`. Verify your `tsconfig.json` includes the generated `graphql-env.d.ts` file. -
Type 'string' is not assignable to type 'DocumentNode<any, any>'.
cause TypeScript is not recognizing the `graphql` template literal tag as a special type-generating construct. This typically indicates that the `codegen-env` or schema types provided by `gql.tada` are not correctly loaded or interpreted by TypeScript.fixCheck your `tsconfig.json` to ensure `graphql-env.d.ts` is included and that `moduleResolution` and `module` settings are compatible (e.g., `NodeNext` or `Bundler`). Restart your TypeScript language server or IDE to refresh type definitions. -
Property 'someField' does not exist on type '{ /* ... */ }'.cause The GraphQL query within the `graphql` tag does not match the actual schema, or the introspected schema used by `gql.tada` for type inference is outdated or incorrect, leading to a mismatch between the query and the inferred type.fixReview the GraphQL query for typos or mismatches against your schema. If your schema has changed, run `npx gql-tada generate` to update your local schema definition file and then restart your TypeScript language server/IDE.
Warnings
- breaking gql.tada requires TypeScript version ^5.0.0 || ^6.0.0 as a peer dependency. Using older versions of TypeScript (e.g., v4.x) will lead to compilation errors and prevent the library's core type inference functionality from working correctly.
- gotcha Full type inference and editor feedback in gql.tada relies on an introspected GraphQL schema being available to the TypeScript language server. This is typically achieved by using `@gql.tada/cli` to generate a `graphql-env.d.ts` file. Without this pre-generated schema environment, the `graphql` tagged template literal will not provide comprehensive type safety for your queries.
- gotcha Prior to `@gql.tada/cli-utils@1.7.1` (which ships with `gql.tada@1.8.13` and above), there was an issue where external scalar types might not be correctly handled in the generated `graphql` definition files, leading to incorrect type inferences for custom scalars. If using custom scalars with older versions, type safety might be compromised.
- gotcha While gql.tada is optimized for performance, very large and complex GraphQL schemas, especially when combined with older TypeScript versions or less performant build setups, can potentially lead to increased type checking times due to the intensive type system operations involved in on-the-fly inference.
Install
-
npm install gql.tada -
yarn add gql.tada -
pnpm add gql.tada
Imports
- graphql
const graphql = require('gql.tada');import { graphql } from 'gql.tada'; - readFragment
import { useFragment } from 'gql.tada';import { readFragment } from 'gql.tada'; - FragmentOf
import type { FragmentOf } from 'gql.tada';
Quickstart
import { graphql } from 'gql.tada';
import type { FragmentOf } from 'gql.tada';
// Ensure you have configured gql.tada via its CLI (e.g., `npx gql-tada init` and `npx gql-tada generate`)
// and have a 'graphql-env.d.ts' file at your project root or as specified in tsconfig.
// This setup allows TypeScript to understand the `graphql` tagged template literal
// and infer types from your GraphQL schema.
// Define a GraphQL fragment for reusable fields
const userFragment = graphql(`
fragment UserProfile on User {
id
name
email
}
`);
// Define a GraphQL query that incorporates the fragment
const viewerQuery = graphql(`
query Viewer {
viewer {
...UserProfile
status
}
}
${userFragment} // Fragments must be included in the document
`);
// Infer the TypeScript type of the query result
type ViewerQueryResult = typeof viewerQuery;
// Expected type: { viewer: { id: string, name: string, email: string, status: string | null } | null }
// Infer the TypeScript type of the fragment
type UserProfileType = FragmentOf<typeof userFragment>;
// Expected type: { id: string, name: string, email: string }
// Example function to process query results with type safety
function displayViewerData(data: ViewerQueryResult) {
if (data?.viewer) {
// Directly access fields, including those from the fragment
console.log(`Viewer ID: ${data.viewer.id}`);
console.log(`Viewer Name: ${data.viewer.name}`);
console.log(`Viewer Email: ${data.viewer.email}`);
console.log(`Viewer Status: ${data.viewer.status ?? 'N/A'}`);
} else {
console.log("No viewer data available.");
}
}
// Mock data for demonstration purposes
const mockData: ViewerQueryResult = {
viewer: {
id: "user-123",
name: "Alice Wonderland",
email: "alice@example.com",
status: "active"
}
};
displayViewerData(mockData);
// Example of a component prop type using a fragment
interface UserCardProps {
user: UserProfileType; // Type-safe prop based on the fragment
}
const UserCard = ({ user }: UserCardProps) => {
return `<div class="user-card"><h2>${user.name}</h2><p>Email: ${user.email}</p></div>`;
};
// Usage of the UserCard component
if (mockData.viewer) {
const cardHtml = UserCard({ user: mockData.viewer });
console.log(cardHtml);
}