glhd/linearavel

A fully-typed Linear API SDK for PHP and Laravel, generated from Linear's GraphQL schema.
1,479 14
Install
composer require glhd/linearavel
Latest Version:0.0.3
PHP:^8.2
License:MIT
Last Updated:Sep 5, 2026
Links: GitHub  ·  Packagist
Maintainer: inxilpro

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.

Related Packages

leroy-merlin-br/exacttarget-client

Easy way to interact with ExactTarget REST API in Laravel

11,450 3
orzcc/taobao-top-client

Taobao top client(SDK) for laravel

26,148 105
aimeos/aimeos-laravel

Cloud native, API first Laravel eCommerce package with integrated AI for ultra-f...

234,897 8,697
hkonnet/laravel-ebay

This package is wrapper for php Ebay sdk for laravel to automate all the configu...

53,063 39
sportbook/sdk

SportBook SDK PHP - Integration function

226 1