glhd / linearavel
A fully-typed Linear API SDK for PHP and Laravel, generated from Linear's GraphQL schema.
Requires
- ext-json: *
- guzzlehttp/guzzle: ^7.8
- illuminate/support: ^9|^10|11.x-dev|dev-master
- nikic/php-parser: ^5.0
- saloonphp/saloon: ^3.0
- spatie/laravel-data: ^4.2
- webonyx/graphql-php: ^15.10
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.34
- mockery/mockery: ^1.6
- orchestra/testbench: ^6.24|^7.10|^8|9.x-dev|10.x-dev|dev-master
- phpunit/phpunit: ^10.3
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-05 19:48:46 UTC
README
Linearavel
Linearavel is a fully-featured Linear SDK for PHP and Laravel.
Every query, mutation, input and data object in this package is generated by walking Linear's GraphQL schema and building the matching PHP syntax tree, so the PHP API matches the GraphQL API one for one. A scheduled job rebuilds the package whenever Linear changes their schema.
Installation
composer require glhd/linearavel
Then add your API key to your .env file:
LINEAR_API_KEY=lin_api_...
You can create a key under Settings → Security & access → Personal API keys in Linear. To publish the config file:
php artisan vendor:publish --tag=linearavel-config
Usage
Linear uses GraphQL, which tends not to be particularly compatible with how PHP applications interact with APIs. This package bridges that gap—making API calls feel fluent while still exposing all the power of the Linear API.
Queries
A typical API call looks something like:
use Glhd\Linearavel\Facades\Linear; $viewer = Linear::viewer() // "viewer" is the name of the GraphQL query ->with('organization', 'id', 'name') // `with` lets you quickly retrieve nested fields ->get('id', 'name', 'active', 'avatarUrl', 'timezone'); // `get` defines the fields to retrieve
Calling get() gives you the query results directly:
assert($viewer instanceof Glhd\Linearavel\Data\User); assert($viewer->name === 'Chris Morrell'); assert($viewer->organization instanceof Glhd\Linearavel\Data\Organization); assert($viewer->organization->name === 'InterNACHI');
Call get() with no arguments to fetch a sensible default set of fields, or pass '*'
alongside your own to get the defaults plus extras.
There is also a linear() helper, if you prefer it to the facade:
$teams = linear()->teams(first: 10)->get();
Filtering and ordering
Query arguments are typed, so your editor can tell you what each query accepts:
use Glhd\Linearavel\Data\Enums\PaginationOrderBy; use Glhd\Linearavel\Requests\Inputs\DateComparatorInput; use Glhd\Linearavel\Requests\Inputs\IssueFilterInput; $issues = linear() ->issues( filter: new IssueFilterInput( createdAt: new DateComparatorInput(gt: now()->subWeek()), ), orderBy: PaginationOrderBy::updatedAt, first: 50, ) ->get();
Arguments are sent as GraphQL variables rather than being written into the query string, so enums, dates, lists and nested input objects all serialize correctly.
Mutations
Mutations follow the same shape, with a Mutation suffix on the method name:
use Glhd\Linearavel\Requests\Inputs\IssueCreateInput; $result = linear() ->issueCreateMutation(new IssueCreateInput( teamId: $team->id, title: 'Something is broken', description: 'It broke.', )) ->get('success', 'issue.id', 'issue.identifier', 'issue.url'); assert($result->success === true); echo $result->issue->url;
Responses
Instead of get(), call response() to get a LinearResponse—a custom
Saloon object that exposes things like status() and
headers(), and that you can resolve() into a fully-typed Linear data object.
$response = linear()->viewer()->response('id', 'name'); $response->status(); // 200 $response->header('X-RateLimit-Requests-Remaining'); $response->resolve(); // Glhd\Linearavel\Data\User
Errors
Linear reports GraphQL errors with a 200 status code, so this package inspects the
response body as well as the status. Any request that comes back with errors throws a
LinearRequestException:
use Glhd\Linearavel\Exceptions\LinearRequestException; try { linear()->issue($id)->get(); } catch (LinearRequestException $exception) { $exception->messages(); // Collection of human-readable messages $exception->codes(); // Collection of Linear error codes, e.g. "RATELIMITED" $exception->errors(); // The raw GraphQL errors $exception->getResponse(); // The Saloon response }
Union types
A handful of Linear queries return a union. Those resolve to whichever member type came back, and every member implements an interface named after the union:
use Glhd\Linearavel\Data\Contracts\OrganizationInviteDetailsPayload; use Glhd\Linearavel\Data\OrganizationInviteFullDetailsPayload; $details = linear()->organizationInviteDetails($id)->get(); assert($details instanceof OrganizationInviteDetailsPayload); if ($details instanceof OrganizationInviteFullDetailsPayload) { echo $details->organizationName; }
Keeping up with Linear
The local.graphql file in this repository is Linear's schema, and everything under
src/Data, src/Requests and src/Responses is generated from it. A scheduled
workflow fetches the live schema every day and, when it changes, rebuilds the package
and tags a release. Removed or narrowed schema types move the minor version; additions
move the patch version.
To rebuild locally:
LINEAR_API_KEY=lin_api_... composer fetch-schema composer generate-data composer fix-style
Contributing
Generated code should never be edited by hand—change the transformers under
src/Support/CodeGeneration and re-run composer generate-data instead. Run the test
suite with composer test and the style checks with composer check-style.