@kody/shopify
README.md
223 lines · 8.2 KB · Markdown@kody/shopify
Intent
Give agents a fork-ready Shopify Admin API toolkit — GraphQL-first shop,
product, order, customer, inventory, collection, fulfillment, draft-order,
webhook, and metafield helpers — without a Kody Shopify OAuth integration.
Transport is one compact authenticated client against
https://{shop}.myshopify.com/admin. Use ./graphql or ./request for any
unwrapped Admin path.
This listing is meant to be forked. The live @kody/shopify package does not
talk to a shared store. After you fork, save your own shop credentials and
call the helpers in your account.
When To Use
- Read a shop profile, catalog, orders, or customers from Admin GraphQL
- Preview or apply catalog, inventory, fulfillment, draft-order, webhook, or metafield writes (
dryRundefaults on) - Call an unwrapped Admin GraphQL operation or legacy REST resource
- Start a private Shopify workflow by forking this package instead of scaffolding from zero
Do not use this package to invent a platform Shopify OAuth app. Auth is secret-backed on purpose.
Required setup
You need a shop handle plus one credential lane. There is no saved Kody
shopify integration.
1. Shop handle
Pass shop: "your-store" (the *.myshopify.com subdomain) on each call.
After you fork, you can persist a default with ./config.
Approve outbound host {your-store}.myshopify.com in the account secrets UI.
Admin API traffic is shop-hosted; there is no single shared API hostname.
2a. Preferred — Dev Dashboard client credentials
Shopify's current custom-app path issues a Client ID and Client secret. You exchange those for a 24-hour Admin access token. This package does that per invoke and never stores the access token.
- Create and install a Dev Dashboard app on your shop. Docs: https://shopify.dev/docs/apps/build/dev-dashboard/create-apps-using-dev-dashboard
- Copy Client ID and Client secret from Settings.
- Save them as user secrets (do not paste values into chat):
- https://kody.codes/account/secrets/new?name=shopifyClientId&description=Shopify%20Dev%20Dashboard%20client%20id&scope=user
- https://kody.codes/account/secrets/new?name=shopifyClientSecret&description=Shopify%20Dev%20Dashboard%20client%20secret&allowedHosts=&scope=user
Then approve host {your-store}.myshopify.com on those secrets.
Token exchange docs: https://shopify.dev/docs/apps/build/dev-dashboard/get-api-access-tokens
2b. Legacy — Admin API access token
Some older custom apps still show a static Admin API access token (shpat_…).
Save that as shopifyAdminAccessToken. If this secret exists, it wins over
client credentials.
Access scopes
Grant only what you will call. Typical starting set:
read_products/write_productsread_orders/write_orders(protected customer data may need extra Shopify approval)read_customers/write_customersread_inventory/write_inventoryread_locationsread_fulfillments/write_fulfillmentsread_draft_orders/write_draft_ordersread_contentor the webhook topics you subscribe to
Default API version is 2026-07. Override per call with apiVersion or
store a fork-local default.
Exports
| Export | Description |
|---|---|
. | Package overview and domain map |
./graphql | shopifyGraphql — Admin GraphQL escape hatch |
./request | shopifyRequest — Admin REST escape hatch |
./shop | getShop |
./products | listProducts, getProduct, createProduct, updateProduct |
./orders | listOrders, getOrder |
./customers | listCustomers, getCustomer, createCustomer |
./inventory | listLocations, listInventoryLevels, adjustInventory |
./collections | listCollections, getCollection |
./fulfillments | listFulfillmentOrders, createFulfillment |
./draft-orders | listDraftOrders, getDraftOrder, createDraftOrder |
./webhooks | listWebhooks, createWebhook, deleteWebhook |
./metafields | listMetafields, setMetafield |
./config | getConfig, setDefaultShop, setDefaultApiVersion, clearConfig |
./smoke-test | Local helper checks plus optional live shop read |
./types | Shared TypeScript types |
Default exports: ./graphql → graphql; ./request → shopifyRequest;
./shop → getShop; ./products → listProducts; ./orders → listOrders;
./customers → listCustomers; ./inventory → listLocations;
./collections → listCollections; ./fulfillments → listFulfillmentOrders;
./draft-orders → listDraftOrders; ./webhooks → listWebhooks;
./metafields → listMetafields; ./config → getConfig;
./smoke-test → smokeTest.
Writes default to dry-run. Pass dryRun: false only after the user
confirms the exact shop and mutation.
Example
import { getShop } from 'kody:@kody/shopify/shop'
import { listProducts } from 'kody:@kody/shopify/products'
import { listOrders } from 'kody:@kody/shopify/orders'
const shop = await getShop({ shop: 'acme' })
const products = await listProducts({
shop: 'acme',
query: 'status:active',
first: 10,
})
const unpaid = await listOrders({
shop: 'acme',
query: 'financial_status:pending',
first: 10,
})Unwrapped GraphQL:
import { shopifyGraphql } from 'kody:@kody/shopify/graphql'
const data = await shopifyGraphql({
shop: 'acme',
query: `query ($query: String) {
products(first: 5, query: $query) {
nodes { id title handle }
}
}`,
variables: { query: 'vendor:acme' },
})Preview a write, then apply only after confirmation:
import { createProduct } from 'kody:@kody/shopify/products'
const preview = await createProduct({
shop: 'acme',
title: 'Hiking backpack',
status: 'DRAFT',
})
// => { dryRun: true, wouldCall: { mutation: 'productCreate', product: { ... } } }
const created = await createProduct({
shop: 'acme',
title: 'Hiking backpack',
status: 'DRAFT',
dryRun: false,
})Forking
Community listings are snapshots. Fork this listing into your account, review
the source, publish your copy, then save secrets. Do not treat the live
@kody/shopify packageStorage() as yours — platform packages share the
Kody-scope bucket.
See CONTRIBUTING.md for the clone-edit-publish loop and how to add another domain module.
One-click listing URL after publish: https://kody.codes/@kody/shopify
Smoke test
./smoke-test always runs local shop/id helper checks. When shop and
credentials are present it also reads shop { name }.
import smokeTest from 'kody:@kody/shopify/smoke-test'
export default async function main() {
return await smokeTest({ shop: 'acme' })
}Notes
- GraphQL is the supported Admin surface. REST remains as
./requestbecause some older resources are still documented there. - Requests stay on
{shop}.myshopify.com/admin/…. Arbitrary hosts are rejected. - Client-credentials tokens are requested per invoke and never written to
packageStorage(). - Rate limits: GraphQL uses calculated query cost; REST is 40 req/min (higher
on Shopify Plus). The client retries
429/5xxand honorsRetry-After. - Helpers project slim objects. Use
./graphqlwhen you need extra fields. - Numeric ids are accepted and normalized to
gid://shopify/{Type}/{id}. - This package is not affiliated with or endorsed by Shopify Inc.
Branding
The community icon is The Shopping Bag,
Shopify's official brandmark, taken from the public brand-assets kit
(shopify-shopping-bag.zip → shopify_glyph.svg). Paths are unmodified.
Shopify® and The Shopping Bag are trademarks of Shopify Inc.
Docs
- Admin GraphQL API
- Admin REST API (legacy)
- Client credentials
- Access scopes
- API versioning
- Auth notes in this repo
- Domain map and extension tips