api-graphql
Work with Shopify GraphQL APIs including Admin API and Storefront API. Use this skill for querying and mutating Shopify data, managing products, orders, customers, handling pagination, working with metafields, and understanding rate limits. Covers authentication, queries, mutations, and webhooks.
Works with
---
name: api-graphql
description: Work with Shopify GraphQL APIs including Admin API and Storefront API. Use this skill for querying and mutating Shopify data, managing products, orders, customers, handling pagination, working with metafields, and understanding rate limits. Covers authentication, queries, mutations, and webhooks.
license: MIT
---
# Shopify GraphQL APIs
## When to use this skill
Use this skill when:
- Querying Shopify data (products, orders, customers)
- Creating or updating resources via mutations
- Building integrations with Shopify stores
- Working with metafields and metaobjects
- Handling pagination in API responses
- Managing API authentication and rate limits
## API Types
### Admin API
- Full store access (backend only)
- Requires authentication (OAuth or API key)
- Used by apps and integrations
### Storefront API
- Public storefront access
- Uses public access token
- Safe for frontend/client-side
## Getting Started
### Admin API Authentication
```javascript
// Using @shopify/shopify-api
import { shopifyApi, LATEST_API_VERSION } from "@shopify/shopify-api";
const shopify = shopifyApi({
apiKey: process.env.SHOPIFY_API_KEY,
apiSecretKey: process.env.SHOPIFY_API_SECRET,
scopes: ["read_products", "write_products"],
hostName: "your-app.com",
apiVersion: LATEST_API_VERSION,
});
```
### Making Requests
```javascript
// In a Remix app route
import { authenticate } from "../shopify.server";
export async function loader({ request }) {
const { admin } = await authenticate.admin(request);
const response = await admin.graphql(`
query {
products(first: 10) {
nodes {
id
title
}
}
}
`);
return response.json();
}
```
## Common Queries
### Products
```graphql
# Get products with variants
query GetProducts($first: Int!, $after: String) {
products(first: $first, after: $after) {
pageInfo {
hasNextPage
endCursor
}
nodes {
id
title
handle
description
status
vendor
productType
tags
featuredImage {
url
altText
}
variants(first: 10) {
nodes {
id
title
sku
price
compareAtPrice
inventoryQuantity
selectedOptions {
name
value
}
}
}
priceRange {
minVariantPrice {
amount
currencyCode
}
maxVariantPrice {
amount
currencyCode
}
}
}
}
}
```
```graphql
# Get single product by ID
query GetProduct($id: ID!) {
product(id: $id) {
id
title
description
variants(first: 100) {
nodes {
id
title
price
}
}
}
}
# Get product by handle
query GetProductByHandle($handle: String!) {
productByHandle(handle: $handle) {
id
title
}
}
```
### Orders
```graphql
query GetOrders($first: Int!, $query: String) {
orders(first: $first, query: $query) {
nodes {
id
name
email
createdAt
displayFinancialStatus
displayFulfillmentStatus
totalPriceSet {
shopMoney {
amount
currencyCode
}
}
customer {
id
firstName
lastName
email
}
lineItems(first: 50) {
nodes {
id
title
quantity
variant {
id
sku
}
originalTotalSet {
shopMoney {
amount
}
}
}
}
shippingAddress {
address1
city
province
country
zip
}
}
}
}
```
### Customers
```graphql
query GetCustomers($first: Int!, $query: String) {
customers(first: $first, query: $query) {
nodes {
id
firstName
lastName
email
phone
ordersCount
totalSpent {
amount
currencyCode
}
addresses(first: 5) {
address1
city
province
country
zip
}
tags
createdAt
}
}
}
```
### Collections
```graphql
query GetCollection($handle: String!) {
collectionByHandle(handle: $handle) {
id
title
description
products(first: 20) {
nodes {
id
title
featuredImage {
url
}
}
}
}
}
```
### Inventory
```graphql
query GetInventory($locationId: ID!) {
location(id: $locationId) {
inventoryLevels(first: 100) {
nodes {
id
available
item {
id
variant {
id
displayName
sku
}
}
}
}
}
}
```
## Common Mutations
### Create Product
```graphql
mutation CreateProduct($input: ProductInput!) {
productCreate(input: $input) {
product {
id
title
handle
}
userErrors {
field
message
}
}
}
```
Variables:
```json
{
"input": {
"title": "New Product",
"descriptionHtml": "<p>Product description</p>",
"vendor": "My Store",
"productType": "Apparel",
"tags": ["new", "featured"],
"variants": [
{
"price": "29.99",
"sku": "NEW-001",
"inventoryManagement": "SHOPIFY",
"inventoryPolicy": "DENY"
}
]
}
}
```
### Update Product
```graphql
mutation UpdateProduct($input: ProductInput!) {
productUpdate(input: $input) {
product {
id
title
}
userErrors {
field
message
}
}
}
```
Variables:
```json
{
"input": {
"id": "gid://shopify/Product/123456",
"title": "Updated Title",
"tags": ["sale", "featured"]
}
}
```
### Create Order
```graphql
mutation CreateDraftOrder($input: DraftOrderInput!) {
draftOrderCreate(input: $input) {
draftOrder {
id
invoiceUrl
}
userErrors {
field
message
}
}
}
```
### Update Inventory
```graphql
mutation AdjustInventory($input: InventoryAdjustQuantityInput!) {
inventoryAdjustQuantity(input: $input) {
inventoryLevel {
available
}
userErrors {
field
message
}
}
}
```
Variables:
```json
{
"input": {
"inventoryLevelId": "gid://shopify/InventoryLevel/123456",
"availableDelta": 10
}
}
```
## Metafields
### Read Metafields
```graphql
# Product metafields
query GetProductMetafields($id: ID!) {
product(id: $id) {
metafields(first: 20) {
nodes {
id
namespace
key
value
type
}
}
# Specific metafield
careInstructions: metafield(namespace: "custom", key: "care_instructions") {
value
}
}
}
```
### Write Metafields
```graphql
mutation SetMetafields($metafields: [MetafieldsSetInput!]!) {
metafieldsSet(metafields: $metafields) {
metafields {
id
key
value
}
userErrors {
field
message
}
}
}
```
Variables:
```json
{
"metafields": [
{
"ownerId": "gid://shopify/Product/123456",
"namespace": "custom",
"key": "care_instructions",
"value": "Machine wash cold",
"type": "single_line_text_field"
},
{
"ownerId": "gid://shopify/Product/123456",
"namespace": "custom",
"key": "dimensions",
"value": "{\"width\": 10, \"height\": 20, \"depth\": 5}",
"type": "json"
}
]
}
```
### Metafield Types
| Type | Description | Example Value |
| ------------------------ | ----------- | ------------------------ |
| `single_line_text_field` | Short text | `"Hello"` |
| `multi_line_text_field` | Long text | `"Line 1\nLine 2"` |
| `number_integer` | Integer | `"42"` |
| `number_decimal` | Decimal | `"19.99"` |
| `boolean` | True/False | `"true"` |
| `date` | Date | `"2025-01-15"` |
| `json` | JSON object | `"{\"key\": \"value\"}"` |
| `url` | URL | `"https://example.com"` |
| `color` | Color hex | `"#FF0000"` |
## Pagination
### Cursor-Based Pagination
```javascript
async function getAllProducts(admin) {
let products = [];
let hasNextPage = true;
let cursor = null;
while (hasNextPage) {
const response = await admin.graphql(
`
query GetProducts($first: Int!, $after: String) {
products(first: $first, after: $after) {
pageInfo {
hasNextPage
endCursor
}
nodes {
id
title
}
}
}
`,
{
variables: { first: 50, after: cursor },
},
);
const data = await response.json();
products = [...products, ...data.data.products.nodes];
hasNextPage = data.data.products.pageInfo.hasNextPage;
cursor = data.data.products.pageInfo.endCursor;
}
return products;
}
```
## Rate Limits
### Understanding Cost
```graphql
query {
products(first: 100) {
nodes {
id
title
}
}
}
```
Response includes:
```json
{
"extensions": {
"cost": {
"requestedQueryCost": 102,
"actualQueryCost": 52,
"throttleStatus": {
"maximumAvailable": 2000,
"currentlyAvailable": 1948,
"restoreRate": 100
}
}
}
}
```
### Handling Rate Limits
```javascript
async function queryWithRetry(admin, query, variables, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await admin.graphql(query, { variables });
const data = await response.json();
if (data.errors?.some((e) => e.extensions?.code === "THROTTLED")) {
const waitTime = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, waitTime));
continue;
}
return data;
} catch (error) {
if (attempt === maxRetries - 1) throw error;
}
}
}
```
## Storefront API
### Authentication
```javascript
const storefrontClient = new StorefrontApiClient({
privateAccessToken: process.env.STOREFRONT_ACCESS_TOKEN,
storeDomain: "your-store.myshopify.com",
apiVersion: "2025-01",
});
```
### Product Query (Storefront)
```graphql
query GetProduct($handle: String!) {
product(handle: $handle) {
id
title
description
images(first: 5) {
nodes {
url
altText
}
}
variants(first: 100) {
nodes {
id
title
price {
amount
currencyCode
}
availableForSale
}
}
}
}
```
### Cart Operations (Storefront)
```graphql
# Create cart
mutation CreateCart($lines: [CartLineInput!]!) {
cartCreate(input: { lines: $lines }) {
cart {
id
checkoutUrl
lines(first: 10) {
nodes {
id
quantity
merchandise {
... on ProductVariant {
title
}
}
}
}
}
}
}
# Add to cart
mutation AddToCart($cartId: ID!, $lines: [CartLineInput!]!) {
cartLinesAdd(cartId: $cartId, lines: $lines) {
cart {
id
lines(first: 10) {
nodes {
id
quantity
}
}
}
}
}
```
## Webhooks
### Subscribe to Webhooks
```graphql
mutation WebhookSubscriptionCreate(
$topic: WebhookSubscriptionTopic!
$webhookSubscription: WebhookSubscriptionInput!
) {
webhookSubscriptionCreate(
topic: $topic
webhookSubscription: $webhookSubscription
) {
webhookSubscription {
id
topic
endpoint {
... on WebhookHttpEndpoint {
callbackUrl
}
}
}
userErrors {
field
message
}
}
}
```
Variables:
```json
{
"topic": "PRODUCTS_CREATE",
"webhookSubscription": {
"callbackUrl": "https://your-app.com/webhooks",
"format": "JSON"
}
}
```
### Webhook Topics
| Topic | Description |
| ------------------------- | ----------------- |
| `PRODUCTS_CREATE` | Product created |
| `PRODUCTS_UPDATE` | Product updated |
| `PRODUCTS_DELETE` | Product deleted |
| `ORDERS_CREATE` | Order placed |
| `ORDERS_UPDATED` | Order modified |
| `ORDERS_PAID` | Order paid |
| `CUSTOMERS_CREATE` | Customer created |
| `INVENTORY_LEVELS_UPDATE` | Inventory changed |
## Bulk Operations
### Bulk Query
```graphql
mutation BulkProducts {
bulkOperationRunQuery(
query: """
{
products {
edges {
node {
id
title
variants {
edges {
node {
id
sku
price
}
}
}
}
}
}
}
"""
) {
bulkOperation {
id
status
}
userErrors {
field
message
}
}
}
```
### Poll Bulk Operation
```graphql
query BulkOperationStatus {
currentBulkOperation {
id
status
objectCount
url
}
}
```
## Best Practices
1. **Request only needed fields** - Reduces cost and response size
2. **Use fragments** - Reuse common field selections
3. **Handle errors** - Check for userErrors in mutations
4. **Implement pagination** - Don't request all records at once
5. **Monitor rate limits** - Use throttle status in responses
6. **Use bulk operations** - For large data exports
## Resources
- [Admin API Reference](https://shopify.dev/docs/api/admin-graphql)
- [Storefront API Reference](https://shopify.dev/docs/api/storefront)
- [GraphQL Basics](https://shopify.dev/docs/apps/build/graphql)
- [Rate Limits](https://shopify.dev/docs/api/usage/rate-limits)
- [Webhooks Reference](https://shopify.dev/docs/api/admin-graphql/latest/enums/WebhookSubscriptionTopic)
- [GraphiQL Explorer](https://shopify.dev/docs/apps/build/graphql/basics/graphiql)
For app integration, see the [app-development](../app-development/SKILL.md) skill.More API Design skills
lark-event
larksuite/cli
Lark/Feishu real-time event listening / subscribing / consuming: stream events as NDJSON via `lark-cli event consume <EventKey>` (covers IM messages/reactions/chat changes, Approval status changes, Task updates, VC meeting started/joined/ended, Minutes generated, Whiteboard updated, etc.). Use for Lark bots, real-time message processing, long-running subscribers, streaming webhook/push handlers. Supports `--max-events` / `--timeout` bounded runs and a stderr ready-marker contract — designed for AI agents running as subprocesses.
lark-contact
larksuite/cli
飞书 / Lark 通讯录:按姓名 / 邮箱解析成 open_id,或按 open_id 反查姓名 / 部门 / 邮箱 / 联系方式 / 个人状态 / 签名,以及按关键词搜索当前用户可见的机器人 / 智能体(agent)。当用户提到一个名字要下一步发消息 / 排日程,或拿到 open_id 想查具体信息时使用。不负责部门树遍历、按部门列员工、组织架构图,这类需求走原生 OpenAPI。
lark-openapi-explorer
larksuite/cli
飞书/Lark 原生 OpenAPI 探索:从官方文档库中挖掘未经 CLI 封装的原生 OpenAPI 接口。当用户的需求无法被现有 lark-* skill 或 lark-cli 已注册命令满足,需要查找并调用原生飞书 OpenAPI 时使用。

