Skip to content

Working on the repo

  • Directorysling_gql/
    • .tool-versions flutter 3.47 · nodejs 24 (asdf)
    • Directorypackages/
      • Directorysling_gql/ runtime — Accessor, Selection, Cache, SlingClient, QueryBuilder
        • …
      • Directorysling_gql_gen/ generator — introspection JSON → Dart
        • …
      • Directorysling_gql_test/ test helpers — MockGraphQLServer, pumpUntilSettled
        • …
      • Directorysling_gql_hooks/ flutter_hooks adapter — useSlingQuery, useSlingMutation
        • …
      • Directorysling_gql_link/ gql_link adapter — a Link chain as the transport
        • …
    • Directorymock-api/ graphql-yoga server the example and its tests run against
      • …
    • Directoryexample/ Flutter app (iOS + Android + web)
      • …
    • Directorywebsite/ this site (Astro + Starlight)
      • …
    • AGENTS.md conventions and design decisions, read it first
  1. Install the toolchains. Flutter and Node are pinned with asdf; run every command from inside the repo so the pins apply.

    Terminal window
    git clone https://github.com/helloprimary/sling_gql && cd sling_gql
    asdf install
    dart pub global activate melos
    melos bootstrap # pub workspace: one `pub get` for all packages
  2. Start the mock API. Space-themed, ~180 launches, cursor and offset pagination, 400 ms of artificial latency so skeletons are visible.

    Terminal window
    cd mock-api && bun install && bun start
    # → http://localhost:4000/graphql (GraphiQL)
  3. Run the tests.

    Terminal window
    melos run test # all the gates below
    melos run test:runtime # runtime, pure Dart + widget tests
    melos run test:gen # generator
    melos run test:example # e2e; starts the mock API itself if needed
    melos run test:example:graphql-http # the same e2e on graphql-http + graphql-sse
    melos run test:website # docs build

    test:runtime includes a short soak on a large generated schema (packages/sling_gql_sqflite/test/soak_test.dart); the long one is flutter test benchmark/soak_bench_test.dart in that package, by hand.

    melos run bench runs the read-path and persistence benchmarks five times each and fails when a median regressed by more than 1.5× against scripts/bench-baseline.json. The metrics are ratios to reference work measured in the same run (jsonDecode of the same response, plain sqflite inserts and reads), so they hold across machines. After an intended slowdown, melos run bench:update and commit the baseline.

    The example test asserts exact request counts after each interaction. It is the one test that catches waterfalls — keep it exact.

  4. Run the example.

    Terminal window
    cd example && flutter run -d <ios-simulator-id> # or an Android emulator
    # or, from the repo root, with the mock API in the page (no server):
    (cd mock-api && bun run build:browser) && cd example && flutter run -d chrome

    Every request is printed to the console and listed in-app (N requests, top right).

  5. Change the schema. mock-api/schema.graphql is the contract; everything else is derived from it.

    Terminal window
    cd mock-api && bun run introspect # → example/graphql/schema.json
    cd .. && melos run generate # example/lib/generated/schema.dart
  6. Work on the website.

    Terminal window
    cd website && bun install && bun run dev # http://localhost:4321/sling_gql/

    It deploys to GitHub Pages on every push to main that touches website/.

  7. CI and releases. Every push and pull request runs the gates above (.github/workflows/ci.yml), plus analyze, format, a check that the example’s generated file is up to date, a debug build of the example’s Android app and the benchmarks (melos run bench).

    Commits follow Conventional Commits scoped by package (feat(sling_gql): …, fix(sling_gql_gen): …, docs(website): …), because releases are cut by melos from them:

    Terminal window
    melos run publish:dry-run # pub.dev validation
    melos version # bump + CHANGELOG + commit + `<pkg>-vX.Y.Z` tags
    git push --follow-tags # publish.yml publishes each tagged package to pub.dev

    What a release may change is the versioning policy: a breaking change is a major (! + a BREAKING CHANGE: footer), deprecations come first and live at least one minor.

  • Runtime stays dependency-light: package:http and package:meta only. No gql, no ferry, no build_runner.
  • Runtime machinery on public types is @internal, Accessor’s helpers @protected; building blocks apps never need go to package:sling_gql/internal.dart.
  • Every runtime behaviour change ships with a test in packages/sling_gql/test, using MockClient from package:http/testing.dart rather than the network.
  • The hand-written accessor classes at the top of packages/sling_gql/test/core_test.dart are the contract the generator must produce. Change both together.
  • example/lib/generated/schema.dart is committed and never edited by hand.
  • In example widgets, read every field you will need at the top of build(), into locals.