1 | |
2 |
|
3 | |
4 | * Update Product Variant |
5 | * Update a variant's price, compare-at price, SKU, barcode, weight or option values. Only the fields you fill in are changed. option_values maps option names to new values (e.g. {"Size": "XL"}). |
6 | */ |
7 | export async function main( |
8 | auth: RT.Shopify, |
9 | product_id: string, |
10 | variant_id: string, |
11 | price: string | undefined, |
12 | compare_at_price: string | undefined, |
13 | sku: string | undefined, |
14 | barcode: string | undefined, |
15 | weight: number | undefined, |
16 | weight_unit: "KILOGRAMS" | "GRAMS" | "POUNDS" | "OUNCES" | undefined, |
17 | inventory_policy: "DENY" | "CONTINUE" | undefined, |
18 | option_values: { [key: string]: string } | undefined |
19 | ) { |
20 | const productId = product_id.startsWith("gid://") |
21 | ? product_id |
22 | : `gid://shopify/Product/${product_id}` |
23 | const id = variant_id.startsWith("gid://") |
24 | ? variant_id |
25 | : `gid://shopify/ProductVariant/${variant_id}` |
26 | const inventoryItem = { |
27 | sku: sku || undefined, |
28 | measurement: |
29 | weight != null |
30 | ? { weight: { value: weight, unit: weight_unit || "KILOGRAMS" } } |
31 | : undefined, |
32 | } |
33 | const options = Object.entries(option_values ?? {}) |
34 |
|
35 | const response = await fetch( |
36 | `https://${auth.store_name}.myshopify.com/admin/api/2026-10/graphql.json`, |
37 | { |
38 | method: "POST", |
39 | headers: { |
40 | "X-Shopify-Access-Token": auth.token, |
41 | "Content-Type": "application/json", |
42 | Accept: "application/json", |
43 | }, |
44 | body: JSON.stringify({ |
45 | query: `mutation ($productId: ID!, $variants: [ProductVariantsBulkInput!]!) { productVariantsBulkUpdate(productId: $productId, variants: $variants) { productVariants { id legacyResourceId title sku price compareAtPrice inventoryPolicy updatedAt selectedOptions { name value } barcodes(first: 5) { nodes { value type } } inventoryItem { id measurement { weight { value unit } } } } userErrors { field message code } } }`, |
46 | variables: { |
47 | productId, |
48 | variants: [ |
49 | { |
50 | id, |
51 | price: price || undefined, |
52 | compareAtPrice: compare_at_price || undefined, |
53 | barcodes: barcode ? [{ value: barcode }] : undefined, |
54 | inventoryPolicy: inventory_policy || undefined, |
55 | inventoryItem: |
56 | inventoryItem.sku || inventoryItem.measurement |
57 | ? inventoryItem |
58 | : undefined, |
59 | optionValues: options.length |
60 | ? options.map(([optionName, name]) => ({ optionName, name })) |
61 | : undefined, |
62 | }, |
63 | ], |
64 | }, |
65 | }), |
66 | } |
67 | ) |
68 |
|
69 | if (!response.ok) { |
70 | throw new Error(`${response.status} ${await response.text()}`) |
71 | } |
72 |
|
73 | const { data, errors } = await response.json() |
74 | if (errors) { |
75 | throw new Error(JSON.stringify(errors)) |
76 | } |
77 | if (data.productVariantsBulkUpdate.userErrors.length) { |
78 | throw new Error(JSON.stringify(data.productVariantsBulkUpdate.userErrors)) |
79 | } |
80 |
|
81 | return data.productVariantsBulkUpdate |
82 | } |
83 |
|