UNPKG

@tailor-platform/shopify

Version:

Shopify Admin API client with Tailor Platform integration

509 lines (423 loc) 12.1 kB
# @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).