Skip to content

Commit 76e4d18

Browse files
committed
Update README
1 parent ecddeeb commit 76e4d18

File tree

1 file changed

+205
-34
lines changed

1 file changed

+205
-34
lines changed

README.md

Lines changed: 205 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,241 @@
1-
# GraphQL client for Shopify.
1+
# GraphQL client for Shopify
22

33
[![Latest Version on Packagist](https://img.shields.io/packagist/v/luminarix/laravel-shopify-graphql.svg?style=flat-square)](https://packagist.org/packages/luminarix/laravel-shopify-graphql)
44
[![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/luminarix/laravel-shopify-graphql/run-tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/luminarix/laravel-shopify-graphql/actions?query=workflow%3Arun-tests+branch%3Amain)
55
[![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/luminarix/laravel-shopify-graphql/fix-php-code-style-issues.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/luminarix/laravel-shopify-graphql/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain)
66
[![Total Downloads](https://img.shields.io/packagist/dt/luminarix/laravel-shopify-graphql.svg?style=flat-square)](https://packagist.org/packages/luminarix/laravel-shopify-graphql)
77

8-
This is a work in progress package to provide a GraphQL client for Shopify.
8+
A Laravel package for interacting with Shopify's GraphQL Admin API. Built on [Saloon](https://github.com/saloonphp/saloon) with automatic rate limiting and retry handling.
99

1010
## Installation
1111

12-
You can install the package via composer:
13-
1412
```bash
1513
composer require luminarix/laravel-shopify-graphql
1614
```
1715

18-
You can publish the config file with:
16+
Publish the config file:
1917

2018
```bash
2119
php artisan vendor:publish --tag="shopify-graphql-config"
2220
```
2321

24-
## Usage
22+
## Basic Usage
2523

2624
```php
2725
use Luminarix\Shopify\GraphQLClient\Facades\GraphQLClient;
2826
use Luminarix\Shopify\GraphQLClient\Authenticators\ShopifyApp;
2927

30-
$graphql = GraphQLClient::factory();
31-
32-
$authenticator = new ShopifyApp($shopDomain, $accessToken, $apiVersion);
33-
34-
$client = $graphql->create($authenticator)
28+
$authenticator = new ShopifyApp($shopDomain, $accessToken);
29+
$client = GraphQLClient::factory()->create($authenticator);
3530

3631
// Query
37-
$query = 'query {
38-
node(id: "gid://shopify/Order/148977776") {
39-
id
40-
... on Order {
41-
name
32+
$response = $client->query('
33+
query {
34+
shop {
35+
name
36+
email
37+
}
4238
}
43-
}
44-
}';
39+
');
4540

46-
$response = $client->query($query);
41+
$data = $response->toArray();
4742

4843
// Mutation
49-
$mutation = 'mutation orderMarkAsPaid($input: OrderMarkAsPaidInput!) {
50-
orderMarkAsPaid(input: $input) {
51-
order {
52-
# Order fields
44+
$response = $client->mutate('
45+
mutation orderMarkAsPaid($input: OrderMarkAsPaidInput!) {
46+
orderMarkAsPaid(input: $input) {
47+
order {
48+
id
49+
}
50+
userErrors {
51+
field
52+
message
53+
}
54+
}
5355
}
54-
userErrors {
55-
field
56-
message
56+
', [
57+
'input' => [
58+
'id' => 'gid://shopify/Order/123456789',
59+
],
60+
]);
61+
```
62+
63+
## Eloquent Model Integration
64+
65+
Create a service class to manage client instances:
66+
67+
```php
68+
namespace App\Services;
69+
70+
use App\Models\ShopifyShop;
71+
use Luminarix\Shopify\GraphQLClient\Facades\GraphQLClient;
72+
use Luminarix\Shopify\GraphQLClient\Authenticators\ShopifyApp;
73+
use Luminarix\Shopify\GraphQLClient\GraphQLClientMethods;
74+
75+
class ShopifyGraphQLService
76+
{
77+
public function with(ShopifyShop $shop): GraphQLClientMethods
78+
{
79+
$authenticator = new ShopifyApp($shop->domain, $shop->access_token);
80+
81+
return GraphQLClient::factory()->create($authenticator);
5782
}
58-
}
59-
}';
83+
}
84+
```
6085

61-
$variables = [
62-
'input' => [
63-
'id' => 'gid://shopify/<objectName>/10079785100',
64-
],
65-
];
86+
Create a trait for your models:
87+
88+
```php
89+
namespace App\Traits;
90+
91+
use App\Services\ShopifyGraphQLService;
92+
use Luminarix\Shopify\GraphQLClient\GraphQLClientMethods;
6693

67-
$response = $client->mutation($mutation, $variables);
94+
trait InteractsWithShopifyGraphQL
95+
{
96+
public function graphql(): GraphQLClientMethods
97+
{
98+
return app(ShopifyGraphQLService::class)->with($this);
99+
}
100+
}
101+
```
102+
103+
Use on your Shopify shop model:
104+
105+
```php
106+
namespace App\Models;
107+
108+
use App\Traits\InteractsWithShopifyGraphQL;
109+
use Illuminate\Database\Eloquent\Model;
110+
111+
class ShopifyShop extends Model
112+
{
113+
use InteractsWithShopifyGraphQL;
114+
}
115+
```
116+
117+
Now you can call GraphQL directly from your model:
118+
119+
```php
120+
$shop = ShopifyShop::find(1);
121+
122+
$result = $shop->graphql()->query('
123+
query {
124+
shop {
125+
name
126+
}
127+
}
128+
')->toArray();
129+
```
130+
131+
## Response Handling
132+
133+
All query and mutation methods return a `GraphQLClientTransformer`:
134+
135+
```php
136+
$response = $client->query($query);
137+
138+
$response->toArray(); // array
139+
$response->toCollection(); // Illuminate\Support\Collection
140+
$response->toFluent(); // Illuminate\Support\Fluent
141+
$response->toJson(); // string
142+
$response->toDTO(MyDTO::class); // Custom DTO instance
143+
```
144+
145+
The transformer is also equipped with the `Macroable` trait so you can add your own custom methods if needed.
146+
147+
### Getting Response with Extensions
148+
149+
Pass `withExtensions: true` to include cost and rate limit data:
150+
151+
```php
152+
$response = $client->query($query, withExtensions: true);
153+
// Returns: ['data' => [...], 'extensions' => ['cost' => [...]]]
154+
```
155+
156+
## Bulk Operations
157+
158+
For large data exports, use bulk operations:
159+
160+
```php
161+
// Start a bulk operation
162+
$result = $client->createBulkOperation('
163+
{
164+
products {
165+
edges {
166+
node {
167+
id
168+
title
169+
}
170+
}
171+
}
172+
}
173+
');
174+
175+
// Check current bulk operation status
176+
$status = $client->getCurrentBulkOperation();
177+
178+
// Get a specific bulk operation by ID
179+
$operation = $client->getBulkOperation($numericId);
180+
181+
// Cancel running bulk operation
182+
$client->cancelBulkOperation();
183+
```
184+
185+
## Rate Limiting
186+
187+
The package automatically handles Shopify's rate limits:
188+
189+
- Tracks query costs from response extensions
190+
- Waits when throttled before retrying
191+
- Configurable retry attempts
192+
193+
Get current rate limit info:
194+
195+
```php
196+
$info = $client->getRateLimitInfo();
197+
// [
198+
// 'requestedQueryCost' => 10,
199+
// 'actualQueryCost' => 8,
200+
// 'maxAvailableLimit' => 1000,
201+
// 'lastAvailableLimit' => 992,
202+
// 'restoreRate' => 50,
203+
// 'isThrottled' => false,
204+
// ]
205+
```
206+
207+
### Custom Rate Limit Service
208+
209+
Implement `RateLimitable` to customize rate limit handling:
210+
211+
```php
212+
use Luminarix\Shopify\GraphQLClient\Contracts\RateLimitable;
213+
214+
class CustomRateLimitService implements RateLimitable
215+
{
216+
public function getRateLimitInfo(): array { /* ... */ }
217+
public function updateRateLimitInfo(array $data): void { /* ... */ }
218+
public function calculateWaitTime(float $requestedQueryCost): float { /* ... */ }
219+
public function waitIfNecessary(float $requestedQueryCost): void { /* ... */ }
220+
}
221+
222+
$client = GraphQLClient::factory()->create($authenticator, new CustomRateLimitService());
223+
```
224+
225+
## Configuration
226+
227+
```php
228+
// config/shopify-graphql.php
229+
return [
230+
// Shopify API version (format: YYYY-MM)
231+
'api_version' => env('SHOPIFY_API_VERSION', '2025-01'),
232+
233+
// Fail immediately when throttled (vs. waiting and retrying)
234+
'fail_on_throttled' => env('SHOPIFY_FAIL_ON_THROTTLED', true),
235+
236+
// Max retry attempts when throttled
237+
'throttle_max_tries' => env('SHOPIFY_THROTTLE_MAX_TRIES', 5),
238+
];
68239
```
69240

70241
## Testing

0 commit comments

Comments
 (0)