GQLKit generates fully typed TypeScript GraphQL client SDKs from a GraphQL schema. The generated SDK uses a builder pattern with type-safe field selection — only selected fields appear in the return type. Accessing unselected fields is a compile-time error.
- Go 1.21+ (required for code generation)
- Node.js 18+
- A GraphQL API endpoint or
.graphqlschema file
- Step 1: Install the CLI tools
- Step 2: Get your GraphQL schema
- Step 3: Install the runtime library
- Step 4: Configure scalar type mappings
- Step 5: Generate the SDK
- Step 6: Use the generated SDK
- Examples
- Error Handling
- Client Options
Option A — Quick install (macOS / Linux):
# Install gqlkit (SDK generator)
curl -sL https://raw.githubusercontent.com/khanakia/gqlkit/main/gqlkit/install.sh | sh
# Install gqlkit-sdl (schema fetcher)
curl -sL https://raw.githubusercontent.com/khanakia/gqlkit/main/gqlkit-sdl/install.sh | shOption B — Download from GitHub Releases:
Go to Releases and download the binary for your platform.
If you already have a .graphql schema file, skip this step.
To fetch a schema from a running GraphQL endpoint via introspection:
gqlkit-sdl fetch \
--url https://your-api.example.com/graphql \
--output schema.graphqlWith authentication:
gqlkit-sdl fetch \
--url https://your-api.example.com/graphql \
--output schema.graphql \
-H "Authorization: Bearer YOUR_TOKEN"npm install gqlkit-tsThis provides the runtime classes: GraphQLClient, BaseBuilder, FieldSelection, and GraphQLErrors.
Create a config.jsonc file to map custom GraphQL scalars to TypeScript types:
Built-in scalars (String, Int, Float, Boolean, ID) are mapped automatically. Unmapped custom scalars default to any.
Note: The generator itself is written in Go even for the TypeScript SDK. You need Go installed to run the code generation step.
There are two ways to generate the SDK:
gqlkit generate-ts \
--schema schema.graphql \
--output ./sdk \
--config config.jsoncCLI flags:
| Flag | Short | Default | Description |
|---|---|---|---|
--schema |
-s |
(required) | Path to .graphql schema file |
--output |
-o |
./sdk |
Output directory for generated SDK |
--config |
-c |
(required) | Path to config.jsonc file |
Create a Go file (e.g., cmd/generate/main.go):
package main
import (
"fmt"
"gqlkit/pkg/clientgents"
)
func main() {
config := &clientgents.Config{
SchemaPath: "cmd/generate/schema.graphql",
OutputDir: "./sdk",
ConfigPath: "cmd/generate/config.jsonc",
}
gen, err := clientgents.New(config)
if err != nil {
fmt.Printf("failed to create generator: %v\n", err)
return
}
if err := gen.Generate(); err != nil {
fmt.Printf("failed to generate SDK: %v\n", err)
return
}
fmt.Println("TypeScript SDK generation completed.")
}Then run it:
go run cmd/generate/main.goEither option creates the sdk/ directory:
sdk/
├── builder/index.ts # Re-exports from gqlkit-ts
├── enums/index.ts # TypeScript enums
├── fields/ # Field selector classes (one per type)
│ ├── index.ts
│ ├── todo.ts
│ ├── user.ts
│ └── ...
├── inputs/index.ts # Input type interfaces
├── mutations/ # Mutation builders + MutationRoot
│ ├── index.ts
│ ├── root.ts
│ └── ...
├── queries/ # Query builders + QueryRoot
│ ├── index.ts
│ ├── root.ts
│ └── ...
├── scalars/index.ts # Scalar type aliases
└── types/index.ts # TypeScript interfaces
import { GraphQLClient } from "gqlkit-ts";
import { QueryRoot } from "./sdk/queries";
import { MutationRoot } from "./sdk/mutations";
// 1. Create the client
const client = new GraphQLClient("https://your-api.example.com/graphql", {
authToken: "YOUR_TOKEN",
headers: { "X-Custom-Header": "value" },
});
// 2. Create query/mutation root
const qr = new QueryRoot(client);
const mr = new MutationRoot(client);
// 3. Execute queries
const todos = await qr
.todos()
.filter({ done: false })
.pagination({ limit: 10, offset: 0 })
.select((t) =>
t.id().text().done().user((u) => u.id().name())
)
.execute();
console.log(todos);
// Only selected fields are available — accessing unselected fields is a compile-time errorconst pingResult = await qr.ping().execute();
const echoResult = await qr.echo().message("Hello from SDK!").execute();
const sumResult = await qr.sum().a(10).b(20).execute();const users = await qr
.users()
.select((u) => u.id().name().email().role())
.execute();const todo = await qr
.todo()
.id("todo-1")
.select((t) => t.id().text().done().user((u) => u.id().name()))
.execute();const result = await qr
.todosConnection()
.filter({ done: false })
.pagination({ limit: 10, offset: 0 })
.select((conn) =>
conn
.totalCount()
.edges((e) =>
e.cursor().node((t) =>
t.id().text().done().priority().user((u) => u.id().name().email())
)
)
.pageInfo((p) =>
p.hasNextPage().hasPreviousPage().startCursor().endCursor()
)
)
.execute();// Create
const created = await mr
.createTodo()
.input({ text: "Buy milk", userId: "user-1" })
.select((t) => t.id().text().done())
.execute();
// Delete
const deleted = await mr.deleteTodo().id("todo-1").execute();
// Scalar mutation
const completedCount = await mr.completeAllTodos().execute();const rawData = await qr.ping().executeRaw();
// rawData is Record<string, unknown>import { GraphQLErrors } from "gqlkit-ts";
try {
const result = await qr.todos().select(/* ... */).execute();
} catch (err) {
if (err instanceof GraphQLErrors) {
// Structured GraphQL errors
for (const e of err.errors) {
console.error("GraphQL error:", e.message);
console.error(" Path:", e.path);
console.error(" Extensions:", e.extensions);
}
} else {
// Network or other error
console.error("Error:", err);
}
}const client = new GraphQLClient("https://your-api.example.com/graphql", {
// Bearer token authentication
authToken: "YOUR_TOKEN",
// Custom headers
headers: {
"X-Request-ID": "abc123",
"X-Custom": "value",
},
// Custom fetch implementation (e.g., for Node.js < 18 or custom middleware)
fetch: customFetchFunction,
});
{ "bindings": { "Time": "string", "JSON": "Record<string, unknown>", "Cursor": "string" } }