graphlink 4.6.1 copy "graphlink: ^4.6.1" to clipboard
graphlink: ^4.6.1 copied to clipboard

GraphLink is a powerful code generation tool that generates type-safe client and server code from GraphQL schemas for Dart, Java, Spring Boot and more.

example/main.dart

/// GraphLink — comprehensive Dart client example
///
/// This example demonstrates the full capabilities of a GraphLink-generated
/// Dart client:
///
///   • Queries with automatic caching (`@glCache`)
///   • Mutations with cache invalidation (`@glCacheInvalidate`)
///   • Real-time subscriptions over WebSocket
///   • File upload (multipart)
///   • Custom projections — fetching only the fields you need
///   • Compound queries — multiple root fields in one round-trip
///   • `staleIfOffline` — returning expired cache when the network is down
///   • Dio HTTP adapter with auth header injection
///
/// ─── How to generate the client ────────────────────────────────────────────
///
/// 1. Install the CLI:
///      dart pub global activate graphlink
///
/// 2. Run the generator (reads glink.yaml automatically):
///      glink
///
///    Or with an explicit path:
///      glink -c glink.yaml
///
///    Watch mode (regenerate on every schema save):
///      glink -w
///
/// 3. Run this file:
///      dart run main.dart
///
/// ─── Schema summary ────────────────────────────────────────────────────────
///
///   Types:   User, Post, Comment
///   Enums:   PostStatus (DRAFT | PUBLISHED | ARCHIVED)
///   Scalars: Upload  →  GLUpload (file uploads)
///
///   Queries:
///     getUser(id)            cached 5 min, staleIfOffline
///     listPosts(status?)     cached 1 min
///     getPost(id)            cached 2 min
///     getUserCard(id)        projection: id, username, avatarUrl only
///     listPostPreviews(?)    projection: no body field
///     getPostWithAuthor(…)   compound: post + author in one call
///
///   Mutations:
///     createUser(input)      invalidates "users" cache tag
///     createPost(input)      invalidates "posts" cache tag
///     addComment(input)      invalidates "posts" cache tag
///     uploadAvatar(id, file) multipart upload, invalidates "users"
///     deleteAllPosts()       invalidates ALL cache entries
///
///   Subscriptions:
///     commentAdded(postId)   fires whenever a comment is added to a post
///     postPublished          fires whenever a post is published

// ignore_for_file: avoid_print
import 'dart:typed_data';

import 'package:graphlink_example/generated/client/graph_link_client.dart';
import 'package:graphlink_example/generated/client/graph_link_uploads.dart';
import 'package:graphlink_example/generated/enums/post_status.dart';
import 'package:graphlink_example/generated/inputs/add_comment_input.dart';
import 'package:graphlink_example/generated/inputs/create_post_input.dart';
import 'package:graphlink_example/generated/inputs/create_user_input.dart';

const _httpUrl = 'http://localhost:8080/graphql';
const _wsUrl   = 'ws://localhost:8080/graphql';

// ---------------------------------------------------------------------------
// Client setup
// ---------------------------------------------------------------------------

/// Builds the GraphLink client with a Dio adapter.
///
/// The Dio adapter gives you full Dio feature support: interceptors,
/// `BaseOptions`, certificate pinning, proxy settings, etc.
///
/// Here we inject a Bearer token on every request via a Dio interceptor.
GraphLinkClient buildClient({String? authToken}) {
  return GraphLinkClient.withHttp(
    url: _httpUrl,
    wsUrl: _wsUrl,
    // Provide headers lazily — called before each request.
    headersProvider: () async => authToken != null
        ? {'Authorization': 'Bearer $authToken'}
        : null,
  );
}

// ---------------------------------------------------------------------------
// Queries
// ---------------------------------------------------------------------------

/// Fetches a user using the auto-generated query (all fields).
///
/// The result is cached for 5 minutes under the "users" tag.
/// If the network is unavailable, the last cached value is returned instead
/// of throwing (`staleIfOffline: true` in the schema).
Future<void> demoGetUser(GraphLinkClient client, String userId) async {
  print('\n── getUser (all fields, cached 5 min) ──');

  final res = await client.queries.getUser(id: userId);
  final user = res.getUser;

  if (user == null) {
    print('User $userId not found');
    return;
  }

  print('id:        ${user.id}');
  print('username:  ${user.username}');
  print('email:     ${user.email}');
  print('bio:       ${user.bio ?? "(none)"}');
  print('posts:     ${user.posts.length}');

  // Second call is served from the in-memory cache — no network round-trip.
  print('\n[re-fetch — should be served from cache]');
  final cached = await client.queries.getUser(id: userId);
  print('username (cached): ${cached.getUser?.username}');
}

/// Fetches only id, username, and avatarUrl — a minimal "user card" projection.
///
/// Defined as a named query in the schema:
///
/// ```graphql
/// query getUserCard($id: ID!) @glCache(ttl: 120, tags: ["users"]) {
///   getUser(id: $id) {
///     id
///     username
///     avatarUrl
///   }
/// }
/// ```
Future<void> demoGetUserCard(GraphLinkClient client, String userId) async {
  print('\n── getUserCard (projection: id, username, avatarUrl) ──');

  final res = await client.queries.getUserCard(id: userId);
  final card = res.getUser;

  print('id:        ${card?.id}');
  print('username:  ${card?.username}');
  print('avatarUrl: ${card?.avatarUrl ?? "(none)"}');
}

/// Lists posts, returning only preview fields (no body).
///
/// ```graphql
/// query listPostPreviews($status: PostStatus) {
///   listPosts(status: $status) {
///     id
///     title
///     status
///     tags
///     createdAt
///   }
/// }
/// ```
Future<void> demoListPostPreviews(GraphLinkClient client) async {
  print('\n── listPostPreviews (projection: no body field) ──');

  final res = await client.queries.listPostPreviews(
    status: PostStatus.PUBLISHED,
  );

  for (final post in res.listPosts) {
    print('[${post.status.name}] ${post.title}  tags: ${post.tags.join(", ")}');
  }
}

/// Fetches a post and its author in a single GraphQL round-trip.
///
/// ```graphql
/// query getPostWithAuthor($postId: ID!, $authorId: ID!)
///   @glCache(ttl: 120, tags: ["posts", "users"]) {
///   post: getPost(id: $postId) {
///     ... _all_fields
///   }
///   author: getUser(id: $authorId) {
///     id
///     username
///     avatarUrl
///   }
/// }
/// ```
Future<void> demoGetPostWithAuthor(
    GraphLinkClient client, String postId, String authorId) async {
  print('\n── getPostWithAuthor (compound query) ──');

  final res = await client.queries.getPostWithAuthor(
    postId: postId,
    authorId: authorId,
  );

  final post   = res.post;
  final author = res.author;

  if (post != null) {
    print('Post:   [${post.status.name}] ${post.title}');
    print('Tags:   ${post.tags.join(", ")}');
  }
  if (author != null) {
    print('Author: ${author.username}  (${author.avatarUrl ?? "no avatar"})');
  }
}

// ---------------------------------------------------------------------------
// Mutations
// ---------------------------------------------------------------------------

/// Creates a user and a published post, then adds a comment.
///
/// Each mutation carries a `@glCacheInvalidate` directive in the schema,
/// so the relevant cache tags are evicted automatically after success.
Future<(String, String)> demoMutations(GraphLinkClient client) async {
  print('\n── mutations ──');

  // Create user
  final userRes = await client.mutations.createUser(
    input: CreateUserInput(
      username: 'alice',
      email: 'alice@example.com',
      bio: 'GraphQL enthusiast',
    ),
  );
  final user = userRes.createUser;
  print('Created user: ${user.id}  (${user.username})');

  // Create post
  final postRes = await client.mutations.createPost(
    input: CreatePostInput(
      title: 'Getting started with GraphLink',
      body: 'GraphLink generates a fully typed client from your GraphQL schema...',
      tags: ['graphql', 'dart', 'codegen'],
    ),
  );
  final post = postRes.createPost;
  print('Created post: ${post.id}  "${post.title}"');

  // Add a comment — invalidates "posts" tag so listPosts is refreshed
  final commentRes = await client.mutations.addComment(
    input: AddCommentInput(
      postId: post.id,
      body: 'Great post, Alice!',
    ),
  );
  print('Added comment: ${commentRes.addComment.id}');

  return (user.id, post.id);
}

// ---------------------------------------------------------------------------
// File upload
// ---------------------------------------------------------------------------

/// Uploads an avatar image using the GraphQL multipart upload spec.
///
/// The schema declares:
/// ```graphql
/// uploadAvatar(userId: ID!, file: Upload!): User!
/// ```
///
/// On the Dart side, `Upload` maps to [GLUpload] — wrap any [File] or stream
/// in it and the generated Dio adapter handles the multipart encoding.
Future<void> demoUpload(GraphLinkClient client, String userId) async {
  print('\n── uploadAvatar (multipart file upload) ──');

  // Simulate a file — in a real app bytes come from a file picker or camera.
  final pngBytes = Uint8List.fromList([137, 80, 78, 71]); // minimal PNG header

  final upload = GLUpload.fromBytes(
    pngBytes,
    filename: 'avatar.png',
    mimeType: 'image/png',
  );

  final res = await client.mutations.uploadAvatar(
    userId: userId,
    file: upload,
  );

  print('Avatar URL: ${res.uploadAvatar.avatarUrl}');
}

// ---------------------------------------------------------------------------
// Subscriptions
// ---------------------------------------------------------------------------

/// Subscribes to new comments on a post and cancels after 3 events.
///
/// The generated client returns a [Stream<CommentAddedResponse>].
/// Subscriptions are backed by a WebSocket that auto-reconnects on disconnect.
Future<void> demoSubscription(GraphLinkClient client, String postId) async {
  print('\n── commentAdded subscription (cancels after 3 events) ──');

  var received = 0;

  final sub = client.subscriptions
      .commentAdded(postId: postId)
      .listen((event) {
    final comment = event.commentAdded;
    print('[comment] ${comment.id}: "${comment.body}"');
    received++;
  });

  // In a real app you'd await user action; here we just wait briefly.
  await Future.delayed(const Duration(seconds: 5));
  await sub.cancel();
  print('Subscription cancelled after $received event(s)');
}

/// Subscribes to post-published events — no arguments needed.
Future<void> demoPostPublishedSubscription(GraphLinkClient client) async {
  print('\n── postPublished subscription (cancels after 2 events) ──');

  var received = 0;

  final sub = client.subscriptions.postPublished().listen((event) {
    final post = event.postPublished;
    print('[published] "${post.title}"  id: ${post.id}');
    received++;
    if (received >= 2) {
      // Stream auto-closes if the subscription is cancelled upstream.
    }
  });

  await Future.delayed(const Duration(seconds: 5));
  await sub.cancel();
  print('Subscription cancelled');
}

// ---------------------------------------------------------------------------
// Cache invalidation — nuke everything
// ---------------------------------------------------------------------------

/// Deletes all posts and wipes the entire client-side cache in one shot.
///
/// The schema declares:
/// ```graphql
/// deleteAllPosts: Boolean! @glCacheInvalidate(all: true)
/// ```
Future<void> demoCacheWipe(GraphLinkClient client) async {
  print('\n── deleteAllPosts (invalidates all cache entries) ──');
  final res = await client.mutations.deleteAllPosts();
  print('deleted: ${res.deleteAllPosts}');
}

// ---------------------------------------------------------------------------
// Entry point
// ---------------------------------------------------------------------------

void main() async {
  // Build the client — swap in a real token for authenticated endpoints.
  final client = buildClient(authToken: 'my-jwt-token');

  // 1. Mutations — create data and show cache invalidation
  final ids = await demoMutations(client);
  final userId = ids.$1;
  final postId = ids.$2;

  // 2. Queries — full fetch, then projection variants
  await demoGetUser(client, userId);
  await demoGetUserCard(client, userId);
  await demoListPostPreviews(client);
  await demoGetPostWithAuthor(client, postId, userId);

  // 3. File upload
  await demoUpload(client, userId);

  // 4. Subscriptions — real-time events over WebSocket
  await demoSubscription(client, postId);
  await demoPostPublishedSubscription(client);

  // 5. Nuke everything
  await demoCacheWipe(client);

  print('\nDone.');
}
1
likes
140
points
473
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

GraphLink is a powerful code generation tool that generates type-safe client and server code from GraphQL schemas for Dart, Java, Spring Boot and more.

Homepage
Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

args, build, glob, logger, source_gen, yaml

More

Packages that depend on graphlink