Search by

glhd / linearavel

inxilpro

A fully-typed Linear API SDK for PHP and Laravel, generated from Linear's GraphQL schema.

Package info

github.com/glhd/linearavel

pkg:composer/glhd/linearavel

Statistics

Installs: 1 479

Dependents: 0

Suggesters: 0

Stars: 14

Open Issues: 0

0.0.3 2024-06-26 21:04 UTC

This package is auto-updated.

Last update: 2026-09-05 19:48:46 UTC


README

Build Status Coverage Status Latest Stable Release MIT Licensed Follow @inxilpro on Twitter

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.