Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Sailor Laravel Package

spawnia/sailor

Sailor generates typed PHP classes from your GraphQL queries, giving Laravel apps autocompletion and static analysis instead of hand-written query strings. Point it at your schema, define operations in .graphql files, and it builds a reusable, type-safe client for your API.

View on GitHub
Deep Wiki
Context7

Getting Started

Sailor is a codegen-first GraphQL client: you don't hand-write HTTP calls or decode arrays, you write .graphql operation documents and generate typesafe PHP classes from them.

Minimal setup:

  1. composer require spawnia/sailor
  2. Publish the config (php artisan vendor:publish --tag=sailor in Laravel, or copy sailor.php manually) and set two things: where the schema comes from (an introspection endpoint or a local .graphql SDL file) and where generated code should live (path + PSR-4 namespace).
  3. Point it at your operation files — a graphql/ directory of *.graphql documents is the conventional layout.
  4. Run the generator (php artisan sailor:generate, or the package binary outside Laravel) and commit the generated directory.
  5. Execute an operation from anywhere in your app:
# graphql/User.graphql
query UserById($id: ID!) {
  user(id: $id) {
    id
    name
    email
  }
}
$user = \App\GraphQL\UserById::execute(['id' => $id])->user;

Look first at: the published sailor.php config (schema source, headers, output namespace), the README's codegen section, and sailor:generate --help. Then read one generated class end-to-end — it is the contract you'll actually code against, and it tells you more about the ergonomics than the docs do.

First use case: a read-only query with one variable, executed from a controller or artisan command. Get the generate → execute → autocomplete loop working before touching mutations.

Implementation Patterns

Organize operations by domain, not by screen. One operation per file, named after the operation (UserById.graphql containing query UserById). This keeps generated class names stable, makes diffs reviewable, and avoids collisions when two features need "the same" query with different fields.

Lean on fragments for shared selections. Extract fragment UserCard on User { ... } and spread it across queries. Sailor generates a nested result class per selection set, so fragments become reusable typed building blocks rather than copy-paste.

Treat generation as a build step. The healthiest workflow is: schema changes → regenerate → commit generated code → CI regenerates and fails on a dirty diff. That catches "someone changed the schema and forgot to update the client" before runtime. Pin the Sailor version; codegen output can shift between releases.

Wire auth and environment at the config level. Put the endpoint URL in env, and inject tokens via configurable headers or a client middleware/factory rather than per-call. For multi-tenant or per-user tokens, resolve the client from the container with the token bound, not a global singleton.

Mutations and inputs. Input objects become typed PHP classes/arrays, enums become PHP enums. Build the input from your domain model at the boundary, then pass it straight through — resist mapping to associative arrays "just this once."

Service-layer wrapper. Don't call ::execute() directly from twenty controllers. Wrap each operation in a small repository/service method that owns variable shaping, error translation, and caching. This is also the seam where you swap in a fake client for tests.

Testing. Because operations are classes, you can assert on generated classes and mock the HTTP layer beneath them. For integration tests, point the config at a test schema file and run against a local GraphQL server rather than the real API.

Laravel integration tips. Register the generated namespace for autoloading, keep the generated directory out of your IDE's "exclude from index" list (you want completion), and use queued jobs for mutations that can be retried — the client is synchronous per call unless you add your own concurrency.

Gotchas and Tips

  • Introspection is often disabled in production. Don't make CI depend on hitting a live endpoint. Export the schema SDL, commit it, and generate from the file. This also makes generation reproducible and fast.
  • Never hand-edit generated files. They'll be overwritten and the diff will lie to you. Put a header comment or .gitattributes marker and extend via wrappers instead.
  • Stale generated code is the #1 failure mode. Symptom: "class not found" or "undefined property" right after a schema deploy. Fix: regenerate, and add the CI dirty-diff check.
  • Partial data plus errors. GraphQL can return data and errors together. Decide explicitly whether your wrapper throws on any error, throws only when data is null, or surfaces errors to the caller. Centralize that decision — inconsistent handling across call sites is a common source of swallowed failures.
  • Nullability is encoded in the types, not enforced at runtime. A nullable nested field means the generated property can be null; don't assume non-null because "the API always returns it."
  • Unknown enum values returned by a newer server can break strict enum decoding. Know your client's fallback behavior before you deploy server-first.
  • Variable name/type drift. Renaming a variable in .graphql silently changes the generated method signature. Treat generated-class signature changes as breaking changes in review.
  • Operation and fragment names must be globally unique across your operation files; duplicates produce confusing codegen errors.
  • Custom scalars (DateTime, JSON, money) come through as strings or untyped unless you register mappings. Decide early and map them to value objects at the service boundary.
  • Debugging: when an operation fails oddly, dump the raw HTTP request/response first, then check whether the generated class matches the current schema. Most "Sailor bugs" are schema drift or an auth header that isn't being applied.
  • Extension points: the config's client factory, custom scalar mappings, and the codegen class-name/namespace settings are where you customize. Prefer these over subclassing generated code.
  • Version churn risk. With ~95 stars and a small maintainer surface, budget for reading the changelog on upgrades and keeping a smoke test that generates and executes one operation. That single test will catch most breaking changes.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo