GraphLink¶
Define your GraphQL schema once. Get fully typed client and server code — for Dart, Flutter, Java, TypeScript, and Spring Boot — in milliseconds.
No runtime. No boilerplate. No schema drift.
In production¶
Measured in Optidialysis — a multi-tenant dialysis clinic management platform running GraphLink-generated code.
| Metric | Value |
|---|---|
| Spring Boot files generated | 72% |
| Spring Boot lines generated | 64% |
| Flutter codebase generated | 21.5% |
| Files written by hand (entire backend) | 135 files (~11.8k lines) |
Controllers, service interfaces, DTOs, input classes, enums — all generated. Every generated file compiles, ships, and runs with zero runtime dependency on GraphLink.
Why GraphLink?¶
No generics at the Java call site.
Every other Java GraphQL client makes you write TypeReference<GraphQLResponse<Map<String,Object>>>. GraphLink generates fully-resolved return types:
// Other clients
GraphQLResponse<GetUserData> res = client.execute(query, vars,
new TypeReference<GraphQLResponse<GetUserData>>() {});
User user = res.getData().getGetUser();
// GraphLink
User user = client.queries.getUser("42").getUser();
Cache control belongs in your schema.
Declare caching once with @glCache and @glCacheInvalidate — the generated client handles TTL, tag-based invalidation, partial query caching, and offline fallback automatically.
Only what the server needs. GraphLink generates minimal, precise query strings — no full-schema dumps that break Spring Boot's strict GraphQL validation.
Single source of truth.
One .graphql file drives the Dart client, the Java client, and the Spring Boot controllers. Add a field once, regenerate, both ends stay in sync.
Supported targets¶
| Target | Status |
|---|---|
| Dart client | ✅ Stable |
| Flutter client | ✅ Stable |
| Java client | ✅ Stable |
| Spring Boot server | ✅ Stable |
| TypeScript client | ✅ Stable |
| Express / Node.js | 📋 Planned |
| Go, Kotlin | 📋 On demand |
Installation¶
Download the single self-contained binary — no JVM, no package manager required.
Quick Start¶
1. Write your schema¶
type Vehicle {
id: ID!
brand: String!
model: String!
year: Int!
fuelType: FuelType!
}
enum FuelType { GASOLINE DIESEL ELECTRIC HYBRID }
input AddVehicleInput {
brand: String!
model: String!
year: Int!
fuelType: FuelType!
}
type Query {
getVehicle(id: ID!): Vehicle! @glCache(ttl: 120, tags: ["vehicles"])
listVehicles: [Vehicle!]! @glCache(ttl: 60, tags: ["vehicles"])
}
type Mutation {
addVehicle(input: AddVehicleInput!): Vehicle! @glCacheInvalidate(tags: ["vehicles"])
}
2. Configure¶
{
"schemaPaths": ["schema/*.graphql"],
"mode": "client",
"typeMappings": { "ID": "String", "Float": "Double", "Int": "Integer", "Boolean": "Boolean" },
"outputDir": "src/main/java/com/example/generated",
"clientConfig": {
"java": {
"packageName": "com.example.generated",
"generateAllFieldsFragments": true,
"autoGenerateQueries": true
}
}
}
{
"schemaPaths": ["schema/*.graphql"],
"mode": "client",
"typeMappings": { "ID": "string", "Float": "number", "Int": "number", "Boolean": "boolean" },
"outputDir": "src/generated",
"clientConfig": {
"typescript": {
"generateAllFieldsFragments": true,
"autoGenerateQueries": true,
"httpAdapter": "fetch"
}
}
}
{
"schemaPaths": ["src/main/resources/graphql/*.graphqls"],
"mode": "server",
"typeMappings": { "ID": "String", "Float": "Double", "Int": "Integer", "Boolean": "Boolean" },
"outputDir": "src/main/java/com/example/generated",
"serverConfig": {
"spring": {
"basePackage": "com.example.generated",
"generateControllers": true,
"generateInputs": true,
"generateTypes": true
}
}
}
schemaPaths:
- src/main/resources/graphql/*.graphqls
mode: server
typeMappings:
ID: String
Float: Double
Int: Integer
Boolean: Boolean
outputDir: src/main/java/com/example/generated
serverConfig:
spring:
basePackage: com.example.generated
generateControllers: true
generateInputs: true
generateTypes: true
3. Generate¶
glink # auto-discovers glink.json / glink.yaml / glink.yml
glink -c glink.json # explicit config path
glink -w # watch mode — regenerate on every save
Documentation¶
- Philosophy — Why pure code generation? Why no runtime abstractions?
- Getting Started — Zero to generated code in 5 minutes.
- Dart / Flutter Client — Typed queries, mutations, subscriptions. Adapter pattern.
- Java Client — No generics. No casting. Builder pattern on all inputs.
- TypeScript Client — Typed client for Angular, React, Vue, and Node.
- Spring Boot — Generated controllers, service interfaces, types, inputs.
- Caching —
@glCacheand@glCacheInvalidate. Tag-based invalidation. - Directives — Complete reference for all GraphLink directives.
- Configuration — Every
glink.json/glink.yamloption explained. - AI Agents — Ready-to-copy instructions for Claude Code, Gemini CLI, and others.
FAQ¶
Does the generated code have a runtime dependency on GraphLink?
No. If you stop using GraphLink tomorrow, every generated file continues to compile and work exactly as before. The output is ordinary Dart, Java, or TypeScript code — you own it completely.
Does the Java client use generics at the call site?
Never. GraphLink generates fully-resolved return types for every query and mutation.
The call site is just client.queries.getUser(id).getUser() — no TypeReference,
no casting, no generic parameters.
What happens when I add a field to the schema?
Run glink again (or glink -c glink.json) (or let watch mode pick it up automatically).
All affected files are regenerated and the new field is immediately available as a
typed property. One file to edit — GraphLink handles the rest.
How does the built-in caching work?
Cache behaviour is declared in the schema using two directives.
@glCache(ttl: 300, tags: ["cars"]) caches a query result for 300 seconds under the
tag "cars". @glCacheInvalidate(tags: ["cars"]) on a mutation evicts all entries
tagged "cars" when the mutation succeeds. Individual fields inside a compound query
can each carry their own TTL — if one tag is invalidated, the others stay warm.
Is GraphLink production-ready?
Yes. In one production deployment (multi-tenant SaaS, dialysis clinic management), 72% of Spring Boot files and 64% of lines are generated — only 135 files (~11.8k lines) were written by hand across the entire backend. On the Flutter side, 21.5% of the codebase is generated, covering all DTOs, input classes, enums, and GraphQL client wiring.
Can I use GraphLink with an existing project?
Yes. Point schemaPaths at your existing .graphql files and set outputDir to
wherever you want the generated files. GraphLink does not modify any of your existing
source files.
What languages and frameworks are supported?
Dart and Flutter (client), Java (client), TypeScript (client), and Spring Boot (server). Express/Node.js server generation and Go/Kotlin targets are planned based on community demand.
Issues and contributions welcome at github.com/Oualitsen/graphlink.