Getting Started

From zero to generated code in under 5 minutes.

Step 1 — Download the CLI

The GraphLink CLI is distributed as a single self-contained binary called glink. No runtime, no package manager, no JVM required.

# Download the latest release
curl -fsSL https://github.com/Oualitsen/graphlink/releases/latest/download/glink-linux-x64 -o glink

# Make it executable
chmod +x glink

# Move to your PATH
sudo mv glink /usr/local/bin/glink

# Verify
glink --version
# Download via PowerShell
Invoke-WebRequest `
  -Uri "https://github.com/Oualitsen/graphlink/releases/latest/download/glink-windows-x64.exe" `
  -OutFile "glink.exe"

# Add to PATH or run from current directory
.\glink.exe --version
Visit: https://github.com/Oualitsen/graphlink/releases/latest

Download the binary for your platform:
  glink-linux-x64      — Linux (x86_64)
  glink-linux-arm64    — Linux (ARM64 / Raspberry Pi)
  glink-macos-x64      — macOS (Intel)
  glink-macos-arm64    — macOS (Apple Silicon)
  glink-windows-x64.exe — Windows (x86_64)

Place the binary somewhere on your PATH and make it executable.

Step 2 — Write your schema

Create a schema/ directory and save your GraphQL schema as one or more .graphql or .gql files. Here is the schema used throughout this documentation:

schema/schema.graphql GraphQL
# An enum — GraphLink generates serialization in both directions
enum FuelType {
  GASOLINE
  DIESEL
  ELECTRIC
  HYBRID
}

# A type — generates a model class with fromJson/toJson (Dart) or builder + getters (Java)
type Person {
  id: ID!        # ID maps to String in both Dart and Java
  name: String!
  email: String!
  vehicles: [Vehicle!]!  # nested list — fully typed
}

type Vehicle {
  id: ID!
  brand: String!
  model: String!
  year: Int!
  fuelType: FuelType!
  ownerId: ID    # nullable — String? in Dart, @Nullable String in Java
}

# Input types — generate immutable input classes with builders
input AddPersonInput {
  name: String!
  email: String!
}

input AddVehicleInput {
  brand: String!
  model: String!
  year: Int!
  fuelType: FuelType!
  ownerId: ID
}

# Queries — @glCache caches the result; ttl in seconds; tags for group invalidation
type Query {
  getPerson(id: ID!): Person                              # returns nullable Person
  getVehicle(id: ID!): Vehicle!  @glCache(ttl: 120, tags: ["vehicles"])
  listVehicles: [Vehicle!]!      @glCache(ttl: 60,  tags: ["vehicles"])
}

# Mutations — @glCacheInvalidate evicts all entries with matching tags on success
type Mutation {
  addPerson(input: AddPersonInput!): Person!
  addVehicle(input: AddVehicleInput!): Vehicle! @glCacheInvalidate(tags: ["vehicles"])
}

# Subscriptions — backed by a WebSocket connection
type Subscription {
  vehicleAdded: Vehicle!
}

The schema above exercises every major GraphLink feature: types, enums, input types, nullable vs non-nullable fields, nested lists, and caching directives.

Step 3 — Configure the generator

Create a config.json file in your project root. The config tells GraphLink where to find the schema, where to write output, and what language/framework to target.

{
  "schemaPaths": ["schema/*.graphql"],
  "mode": "client",
  "typeMappings": {
    "ID":      "String",
    "String":  "String",
    "Float":   "double",
    "Int":     "int",
    "Boolean": "bool",
    "Null":    "null"
  },
  "outputDir": "lib/generated",
  "clientConfig": {
    "dart": {
      "packageName": "my_app",
      "generateAllFieldsFragments": true,
      "autoGenerateQueries": true,
      "nullableFieldsRequired": false,
      "immutableInputFields": true,
      "immutableTypeFields": true
    }
  }
}
{
  "schemaPaths": ["schema/*.graphql"],
  "mode": "client",
  "typeMappings": {
    "ID":      "String",
    "String":  "String",
    "Float":   "Double",
    "Int":     "Integer",
    "Boolean": "Boolean",
    "Null":    "null"
  },
  "outputDir": "src/main/java/com/example/generated",
  "clientConfig": {
    "java": {
      "packageName": "com.example.generated",
      "generateAllFieldsFragments": true,
      "autoGenerateQueries": true,
      "nullableFieldsRequired": false,
      "immutableInputFields": true,
      "immutableTypeFields": true
    }
  }
}
{
  "schemaPaths": ["schema/*.graphql"],
  "mode": "server",
  "typeMappings": {
    "ID":      "String",
    "String":  "String",
    "Float":   "Double",
    "Int":     "Integer",
    "Boolean": "Boolean",
    "Null":    "null"
  },
  "outputDir": "src/main/java/com/example/generated",
  "serverConfig": {
    "spring": {
      "basePackage": "com.example.generated",
      "generateControllers": true,
      "generateInputs": true,
      "generateTypes": true,
      "generateRepositories": false,
      "immutableInputFields": true,
      "immutableTypeFields": false
    }
  }
}

Key configuration options explained

KeyDescription
schemaPathsGlob patterns for schema files. You can split the schema across multiple files.
mode"client" generates client code; "server" generates Spring Boot scaffolding.
typeMappingsMaps GraphQL scalar types to language types. Add entries for custom scalars.
outputDirWhere to write generated files. Existing files are overwritten.
generateAllFieldsFragmentsGenerates a _all_fields_TypeName fragment per type, used by autoGenerateQueries.
autoGenerateQueriesGenerates query strings for every Query/Mutation/Subscription field automatically.
immutableInputFieldsInput class fields are final (Dart) or final (Java). Inputs become builder-only.
immutableTypeFieldsResponse type fields are final. For Spring server, set to false so Spring can set fields.
generateControllersSpring only. Generates @Controller classes wired with @QueryMapping etc.
generateRepositoriesSpring only. Generates JpaRepository interfaces for types annotated with @glRepository.

Step 4 — Run the generator

Point glink at your config file and let it run:

Terminal bash
glink -c config.json

For the Dart client config, the generator produces 21 files. For the Java client, 38 files. For Spring Boot, 9 files. Here is the Dart output tree:

lib/generated/
client/
graph_link_client.dart
enums/
fuel_type.dart
inputs/
add_person_input.dart add_vehicle_input.dart
types/
vehicle.dart person.dart get_vehicle_response.dart list_vehicles_response.dart get_person_response.dart add_vehicle_response.dart add_person_response.dart vehicle_added_response.dart graph_link_error.dart graph_link_payload.dart ... + 6 internal support files

And the Spring Boot output:

src/main/java/com/example/generated/
controllers/
PersonServiceController.java VehicleServiceController.java
services/
PersonService.java VehicleService.java
types/
Person.java Vehicle.java
inputs/
AddPersonInput.java AddVehicleInput.java
enums/
FuelType.java

Highlighted files () are the ones you interact with. Controllers are generated and never touched. Service interfaces are implemented by you. Types, inputs, and enums are plain data classes.

Step 5 — What just happened?

The generator processed each section of your schema and produced a corresponding set of files:

Types → model classes

type Vehicle became vehicle.dart (Dart) and Vehicle.java (Java). Each has all fields, a constructor, and JSON serialization. In Dart, fields are final and the constructor uses named parameters with required. In Java, an inner Builder class is generated.

Enums → enum classes with serialization

enum FuelType became fuel_type.dart and FuelType.java, each with toJson() and fromJson() methods that map to and from the GraphQL string representation.

Inputs → immutable input classes

input AddVehicleInput became add_vehicle_input.dart and AddVehicleInput.java. Required fields are enforced at construction time. The Java version uses a builder with Objects.requireNonNull for required fields.

Queries/Mutations/Subscriptions → response types + client

Each operation generates a response wrapper (e.g. GetVehicleResponse) that holds the typed result. The GraphLinkClient class exposes client.queries, client.mutations, and client.subscriptions with one method per operation.

Cache directives → wired into the client automatically

The @glCache and @glCacheInvalidate directives you wrote in the schema are reflected in the generated client code. No application-level code is needed to enable caching.

Watch mode

During development, add -w to watch your schema files and regenerate automatically on every save:

Terminal bash
glink -c config.json -w

# Output:
# Watching schema/*.graphql for changes...
# [14:32:01] Change detected in schema/schema.graphql
# [14:32:01] Regenerating... done (21 files, 312ms)

This integrates naturally with Flutter's hot reload workflow — schema change, save, and your Dart types are updated before you switch back to the emulator.

Next steps

You now have generated code. The next step depends on your target: