@tailor-platform/shopify
Version:
Shopify Admin API client with Tailor Platform integration
509 lines (423 loc) • 12.1 kB
Markdown
# @tailor-platform/shopify
A drop-in replacement for [`@shopify/admin-api-client`](https://www.npmjs.com/package/@shopify/admin-api-client) that uses Tailor Platform integration tokens instead of Shopify offline tokens.
## Installation
```bash
npm install @tailor-platform/shopify
# or
yarn add @tailor-platform/shopify
# or
pnpm add @tailor-platform/shopify
```
## Usage
### Basic Usage
```typescript
import { createAdminApiClient, ApiVersion } from '@tailor-platform/shopify';
const client = createAdminApiClient({
storeDomain: 'your-shop.myshopify.com',
apiVersion: ApiVersion.October25, // or ApiVersion.Unstable
accessToken: 'tst_YOUR_INTEGRATION_TOKEN', // Integration token from Tailor Platform
});
// Make GraphQL requests
const { data, errors } = await client.request(
`
query getProduct($id: ID!) {
product(id: $id) {
title
handle
description
}
}
`,
{
variables: {
id: 'gid://shopify/Product/123456789',
},
}
);
```
### Type-Safe Usage
The SDK provides full TypeScript support with automatic type inference:
```typescript
import {
createAdminApiClient,
ApiVersion,
gql,
} from '@tailor-platform/shopify';
// Define your types
interface ProductData {
product: {
id: string;
title: string;
handle: string;
description: string;
};
}
interface ProductVariables {
id: string;
}
const client = createAdminApiClient({
storeDomain: 'your-shop.myshopify.com',
apiVersion: ApiVersion.October25,
accessToken: 'tst_YOUR_INTEGRATION_TOKEN',
});
// Type-safe query with gql template tag
const productQuery = gql<ProductData, ProductVariables>`
query getProduct($id: ID!) {
product(id: $id) {
id
title
handle
description
}
}
`;
// TypeScript automatically infers types from the gql tag
const { data, errors } = await client.request(productQuery, {
variables: {
id: 'gid://shopify/Product/123456789',
},
});
// data is fully typed as ProductData
if (data?.product) {
console.log(data.product.title); // TypeScript knows this is a string
}
// Alternative: Use types explicitly without gql
const { data } = await client.request<ProductData, ProductVariables>(
`query getProduct($id: ID!) { ... }`,
{ variables: { id: 'gid://shopify/Product/123456789' } }
);
```
### Working with Mutations
```typescript
interface CreateProductData {
productCreate: {
product: {
id: string;
title: string;
};
userErrors: Array<{
field: string[];
message: string;
}>;
};
}
interface CreateProductVariables {
input: {
title: string;
descriptionHtml?: string;
vendor?: string;
};
}
const createProductMutation = gql<CreateProductData, CreateProductVariables>`
mutation createProduct($input: ProductInput!) {
productCreate(input: $input) {
product {
id
title
}
userErrors {
field
message
}
}
}
`;
// Type inference works automatically - no need to specify types again
const { data } = await client.request(createProductMutation, {
variables: {
input: {
title: 'New Product',
descriptionHtml: '<p>Description</p>',
},
},
});
// Handle user errors
if (data?.productCreate.userErrors.length > 0) {
console.error('Failed to create product:', data.productCreate.userErrors);
}
```
## API Versions
The SDK exports constants for all available Shopify API versions:
```typescript
import { ApiVersion, LATEST_API_VERSION } from '@tailor-platform/shopify';
// Use a specific version
const client = createAdminApiClient({
storeDomain: 'your-shop.myshopify.com',
apiVersion: ApiVersion.January25,
accessToken: 'tst_YOUR_TOKEN',
});
// Use the latest stable version
const client2 = createAdminApiClient({
storeDomain: 'your-shop.myshopify.com',
apiVersion: LATEST_API_VERSION, // Currently October25
accessToken: 'tst_YOUR_TOKEN',
});
// Use unstable version for testing new features
const client3 = createAdminApiClient({
storeDomain: 'your-shop.myshopify.com',
apiVersion: ApiVersion.Unstable,
accessToken: 'tst_YOUR_TOKEN',
});
```
## Type Safety Features
### Typed GraphQL Documents
The `gql` template tag creates typed documents that carry type information:
```typescript
const query = gql<ResponseType, VariablesType>`...`;
// Types flow automatically to client.request()
```
### Benefits
- **Auto-completion**: Full IntelliSense support for response data
- **Type checking**: Catch errors at compile time, not runtime
- **Type inference**: No need to specify types twice
- **Better DX**: See available fields and their types while coding
## API Compatibility
This SDK provides the same interface as `@shopify/admin-api-client`, making it a drop-in replacement. Simply replace your import statement and use your Tailor Platform integration token instead of a Shopify offline token.
## Token Permissions
Integration tokens support two permission levels for enhanced security:
### Full Access Tokens
- Can execute both GraphQL queries and mutations
- Full read and write access to Shopify Admin API
- **Use for**: Inventory sync, order processing, product management, fulfillment operations
### Read-Only Tokens
- Can only execute GraphQL queries
- Cannot perform mutations or modify data
- Attempts to execute mutations will result in a `403 Forbidden` error
- **Use for**: Analytics dashboards, reporting tools, read-only integrations, data exports
### Best Practices
1. **Principle of Least Privilege**: Always use read-only tokens when write access isn't needed
2. **Security**: Read-only tokens significantly reduce risk if compromised
3. **Compliance**: Easier to audit access when permissions are explicit
4. **Integration Type**: Match token permissions to your integration's actual requirements
### Example: Read-Only Analytics Token
```typescript
import { createAdminApiClient, ApiVersion } from '@tailor-platform/shopify';
// Create a client with a read-only token
const analyticsClient = createAdminApiClient({
storeDomain: 'your-shop.myshopify.com',
apiVersion: ApiVersion.October25,
accessToken: 'tst_YOUR_READONLY_TOKEN', // Read-only token
});
// ✅ This works - queries are allowed
const { data } = await analyticsClient.request(`
query {
orders(first: 10) {
edges {
node {
id
totalPriceSet {
shopMoney {
amount
}
}
}
}
}
}
`);
// ❌ This fails - mutations are blocked
try {
await analyticsClient.request(`
mutation {
orderUpdate(input: { id: "gid://shopify/Order/123" }) {
order { id }
}
}
`);
} catch (error) {
// Error: "This token has read-only access and cannot perform mutations"
console.error(error);
}
```
### Error Handling for Read-Only Tokens
```typescript
import {
createAdminApiClient,
ShopifyApiError,
NetworkError,
} from '@tailor-platform/shopify';
const client = createAdminApiClient({
storeDomain: 'your-shop.myshopify.com',
apiVersion: ApiVersion.October25,
accessToken: 'tst_YOUR_READONLY_TOKEN',
});
try {
const { data } = await client.request(someMutation);
} catch (error) {
if (error instanceof NetworkError && error.statusCode === 403) {
console.error('Permission denied - token may be read-only');
// Handle gracefully: show user message, switch to different token, etc.
} else if (error instanceof ShopifyApiError) {
console.error('Shopify API error:', error.errors);
}
}
```
### Creating Tokens with Permissions
When creating tokens through the Tailor Platform dashboard:
1. Navigate to the **Tokens** page
2. Click **Create new token**
3. Enter a descriptive name
4. Select permission level:
- **Full access**: For integrations that need to modify data
- **Read-only**: For analytics, reporting, or monitoring tools
5. Set an expiration date (recommended for security)
6. Copy and securely store the token (it won't be shown again)
## Response Types
All GraphQL responses follow this structure:
```typescript
interface GraphQLResponse<TData> {
data?: TData;
errors?: GraphQLError[];
extensions?: {
cost?: {
requestedQueryCost: number;
actualQueryCost: number;
throttleStatus: {
maximumAvailable: number;
currentlyAvailable: number;
restoreRate: number;
};
};
[key: string]: unknown;
};
}
interface GraphQLError {
message: string;
extensions?: Record<string, unknown>;
path?: (string | number)[];
locations?: Array<{
line: number;
column: number;
}>;
}
```
## Error Handling
The SDK provides custom error types for better error handling:
```typescript
import {
ShopifyApiError,
NetworkError,
ConfigurationError,
} from '@tailor-platform/shopify';
try {
const { data } = await client.request(query);
} catch (error) {
if (error instanceof ShopifyApiError) {
// Handle Shopify API errors
console.error('Shopify API error:', error.errors);
} else if (error instanceof NetworkError) {
// Handle network errors
console.error('Network error:', error.statusCode);
} else if (error instanceof ConfigurationError) {
// Handle configuration errors
console.error('Configuration error:', error.message);
}
}
```
## Webhook Management
The SDK provides webhook support with Tailor Platform's managed webhook system, offering automatic registration, reliable delivery, and retry logic.
### Creating Webhooks
```typescript
const CREATE_WEBHOOK = gql`
mutation webhookSubscriptionCreate(
$topic: WebhookSubscriptionTopic!
$webhookSubscription: WebhookSubscriptionInput!
) {
webhookSubscriptionCreate(
topic: $topic
webhookSubscription: $webhookSubscription
) {
webhookSubscription {
id
topic
callbackUrl
}
userErrors {
field
message
}
}
}
`;
const { data } = await client.request(CREATE_WEBHOOK, {
variables: {
topic: 'ORDERS_CREATE',
webhookSubscription: {
callbackUrl: 'https://your-app.com/webhooks/orders',
},
},
});
```
### Webhook Topics
The SDK exports webhook topic utilities:
```typescript
import {
getWebhookEventName,
getWebhookTopicEnum,
} from '@tailor-platform/shopify';
// Convert between GraphQL enum and event names
getWebhookEventName('ORDERS_CREATE'); // 'orders/create'
getWebhookTopicEnum('orders/create'); // 'ORDERS_CREATE'
```
### Other Webhook Operations
```typescript
// List webhooks
const LIST_WEBHOOKS = gql`
query webhookSubscriptions($first: Int!) {
webhookSubscriptions(first: $first) {
edges {
node {
id
topic
callbackUrl
}
}
}
}
`;
// Update webhook
const UPDATE_WEBHOOK = gql`
mutation webhookSubscriptionUpdate(
$id: ID!
$webhookSubscription: WebhookSubscriptionInput!
) {
webhookSubscriptionUpdate(
id: $id
webhookSubscription: $webhookSubscription
) {
webhookSubscription {
id
callbackUrl
}
userErrors {
field
message
}
}
}
`;
// Delete webhook
const DELETE_WEBHOOK = gql`
mutation webhookSubscriptionDelete($id: ID!) {
webhookSubscriptionDelete(id: $id) {
deletedWebhookSubscriptionId
userErrors {
field
message
}
}
}
`;
```
### Webhook Reliability
Tailor Platform automatically:
- Registers webhooks with Shopify
- Retries failed deliveries with exponential backoff
- Pauses webhooks after 10 consecutive failures
- Provides monitoring through the dashboard
### Common Webhook Topics
- **Orders**: `ORDERS_CREATE`, `ORDERS_UPDATED`, `ORDERS_PAID`, `ORDERS_FULFILLED`
- **Products**: `PRODUCTS_CREATE`, `PRODUCTS_UPDATE`, `PRODUCTS_DELETE`
- **Customers**: `CUSTOMERS_CREATE`, `CUSTOMERS_UPDATE`, `CUSTOMERS_DELETE`
- **Inventory**: `INVENTORY_ITEMS_CREATE`, `INVENTORY_LEVELS_UPDATE`
For a complete list, see the [Shopify documentation](https://shopify.dev/docs/api/admin-graphql/unstable/enums/WebhookSubscriptionTopic).