@headless-commerce/sdk
v0.2.0
Published
TypeScript SDK for Headless Commerce API
Downloads
32
Maintainers
Readme
@headless-commerce/sdk
Headless Commerce API를 위한 공식 TypeScript SDK입니다.
Features
- Type-safe: 모든 API 요청/응답에 대한 완전한 TypeScript 타입
- Zero dependencies:
fetchAPI만 사용 (Node.js 18+, Deno, Bun, 브라우저) - Dual format: ESM + CJS 동시 지원
- Auto-pagination:
for await기반 자동 페이지네이션 - Retry & Timeout: 429/5xx 자동 재시도 + 요청 타임아웃
- Stripe-like DX:
client.products.list()패턴의 직관적인 API - Idempotency: 중복 요청 방지를 위한
Idempotency-Key지원 - Debug logging: 요청/응답 디버깅 로그 + 커스텀 로거
- Webhook verification: HMAC-SHA256 기반 웹훅 서명 검증
Installation
npm install @headless-commerce/sdk
# or
pnpm add @headless-commerce/sdk
# or
yarn add @headless-commerce/sdkSetup
API Keys
Headless Commerce는 두 종류의 API 키를 사용합니다:
| 키 종류 | Prefix | 용도 |
|---------|--------|------|
| Publishable Key | pk_ | 프론트엔드 (Storefront) — 브라우저에 노출 가능 |
| Secret Key | sk_ | 백엔드 (Admin) — 서버에서만 사용. 절대 프론트엔드에 노출하지 마세요 |
Base URL
// 프로덕션 (기본값)
const client = new HeadlessCommerce({ apiKey: 'pk_xxx' });
// → https://api.headlesscommerce.io/v1
// 로컬 개발
const client = new HeadlessCommerce({
apiKey: 'pk_test_xxx',
baseUrl: 'http://localhost:3000/v1',
});Quick Start
Storefront (프론트엔드) — 타입 안전 팩토리
import { createStorefrontClient } from '@headless-commerce/sdk';
// StorefrontClient 타입 — admin 메서드가 autocomplete에 노출되지 않음
const client = createStorefrontClient({
apiKey: 'pk_test_xxx', // Publishable key
baseUrl: 'http://localhost:3000/v1',
});
// 상품 목록 조회
const products = await client.products.list({ limit: 10 });
console.log(products.data);
// 장바구니 생성 → 상품 추가
const cart = await client.carts.create();
await client.carts.addItem(cart.id, {
variant_id: 'var_xxx',
quantity: 2,
});
// 체크아웃
const order = await client.checkout.create(cart.id, {
email: '[email protected]',
shipping_address: {
name: 'Hong Gildong',
line1: '123 Main St',
city: 'Seoul',
postal_code: '06000',
country: 'KR',
},
});Admin (백엔드) — 풀 액세스
import { createAdminClient } from '@headless-commerce/sdk';
const admin = createAdminClient({
apiKey: 'sk_test_xxx', // Secret key
});
// 상품 생성
const product = await admin.products.adminCreate({
name: 'New T-Shirt',
slug: 'new-t-shirt',
type: 'physical',
status: 'active',
});
// 주문 관리
const orders = await admin.orders.adminList({ status: 'pending' });
await admin.orders.adminConfirm('ord_xxx');Customer Authentication
서버에서 고객 토큰을 발급하고 클라이언트에서 사용합니다.
// Server: 토큰 발급
const admin = new HeadlessCommerce({ apiKey: 'sk_test_xxx' });
const { token } = await admin.customers.adminCreateToken('cus_xxx');
// Client: 토큰 설정
const client = new HeadlessCommerce({ apiKey: 'pk_test_xxx' });
client.setCustomerToken(token);
// 인증된 요청
const me = await client.customers.me();
const addresses = await client.customers.listAddresses();Auto-Pagination
for await로 전체 데이터를 자동 순회합니다.
// 리소스에 내장된 자동 페이지네이션
for await (const product of client.products.listAutoPaginate({ limit: 50 })) {
console.log(product.name);
}
// 또는 autoPaginate 헬퍼 직접 사용
import { autoPaginate } from '@headless-commerce/sdk';
for await (const order of autoPaginate(
(params) => client.orders.list(params),
{ limit: 20 },
)) {
console.log(order.number);
}Retry & Timeout
const client = new HeadlessCommerce({
apiKey: 'pk_test_xxx',
maxRetries: 3, // 429, 5xx 에러 시 최대 3회 재시도 (기본값: 2)
timeout: 30000, // 요청 타임아웃 30초 (기본값: 없음)
});재시도는 지수 백오프(exponential backoff)를 사용하며, 429 Too Many Requests와 5xx 서버 에러에 대해서만 재시도합니다.
Idempotency
결제, 주문 생성 등 중복 방지가 필요한 요청에 idempotencyKey를 전달합니다.
await client.checkout.create(cart.id, checkoutInput, {
idempotencyKey: 'unique-checkout-key-123',
});
await admin.orders.adminCreate(orderInput, {
idempotencyKey: `order-${Date.now()}`,
});Debug Logging
// 기본 콘솔 로깅
const client = new HeadlessCommerce({
apiKey: 'pk_test_xxx',
debug: true,
});
// 출력: [HeadlessCommerce] GET https://.../ 200 45ms
// 커스텀 로거 (Datadog, Sentry 등 연동)
const client = new HeadlessCommerce({
apiKey: 'pk_test_xxx',
logger: (entry) => {
myLogger.info('api_request', {
method: entry.method,
url: entry.url,
status: entry.status,
duration: entry.duration,
});
},
});Webhook Verification
웹훅 수신 시 서명을 검증하여 요청의 진위를 확인합니다.
import { verifyWebhookSignature } from '@headless-commerce/sdk';
app.post('/webhooks', async (req, res) => {
const signature = req.headers['x-webhook-signature'] as string;
const isValid = await verifyWebhookSignature(
req.body, // raw body string
signature,
WEBHOOK_SECRET,
);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(req.body);
// event.type: 'order.created', 'payment.completed', ...
});Error Handling
import { HeadlessCommerce, HeadlessCommerceError } from '@headless-commerce/sdk';
try {
await client.products.get('invalid_id');
} catch (err) {
if (err instanceof HeadlessCommerceError) {
console.error(err.message); // "Product not found"
console.error(err.code); // "RESOURCE_NOT_FOUND"
console.error(err.status); // 404
console.error(err.details); // { resource: "Product", id: "invalid_id" }
// Boolean helpers로 에러 유형 분기
if (err.isNotFound) {
// 404 — 리소스 없음
} else if (err.isValidationError) {
// 400/422 — 입력값 오류
} else if (err.isAuthError) {
// 401 — 인증 실패
} else if (err.isRateLimitError) {
// 429 — 속도 제한
}
// 네트워크/타임아웃 에러는 .cause로 원인 추적
if (err.code === 'NETWORK_ERROR' || err.code === 'TIMEOUT') {
console.error('Original error:', err.cause);
}
}
}Error Codes
API Error Codes
| Code | HTTP | 설명 |
|------|------|------|
| MISSING_API_KEY | 401 | API 키 누락 |
| INVALID_API_KEY | 401 | 유효하지 않은 API 키 |
| INVALID_CUSTOMER_TOKEN | 401 | 유효하지 않은 고객 토큰 |
| VALIDATION_ERROR | 400 | 요청 데이터 검증 실패 |
| INVALID_CURRENCY | 400 | 지원하지 않는 통화 코드 |
| RESOURCE_NOT_FOUND | 404 | 리소스를 찾을 수 없음 |
| INSUFFICIENT_STOCK | 409 | 재고 부족 |
| INVALID_STATE_TRANSITION | 409 | 유효하지 않은 상태 전환 (예: cancelled → confirmed) |
| CART_NOT_ACTIVE | 409 | 비활성 장바구니에 대한 조작 시도 |
| DISCOUNT_EXPIRED | 409 | 만료된 할인 코드 |
| RATE_LIMIT_EXCEEDED | 429 | 요청 속도 제한 초과 |
| INTERNAL_ERROR | 500 | 서버 내부 오류 |
SDK Error Codes
| Code | 설명 |
|------|------|
| NETWORK_ERROR | 네트워크 연결 실패 (.cause에 원본 에러 포함) |
| TIMEOUT | 요청 타임아웃 초과 (.cause에 AbortError 포함) |
| UNKNOWN | 서버에서 JSON이 아닌 응답을 반환한 경우 |
API Reference
Storefront Resources
| Resource | Methods |
|----------|---------|
| client.products | list(), get(), getBySlug(), listAutoPaginate() |
| client.categories | list(), getBySlug() |
| client.collections | list(), get(), getBySlug(), listAutoPaginate() |
| client.carts | create(), get(), getBySession(), update(), addItem(), updateItem(), removeItem(), applyDiscount(), removeDiscount(), merge() |
| client.checkout | create() |
| client.orders | list(), get(), lookup(), listAutoPaginate() |
| client.customers | me(), updateMe(), listAddresses(), createAddress(), updateAddress(), deleteAddress() |
| client.shipping | list() |
Admin Resources
| Resource | Methods |
|----------|---------|
| client.products | adminList(), adminGet(), adminCreate(), adminUpdate(), adminDelete(), adminAddImage(), adminRemoveImage(), adminListBundleItems(), adminAddBundleItem(), adminRemoveBundleItem(), adminListAutoPaginate() |
| client.categories | adminList(), adminCreate(), adminUpdate(), adminDelete() |
| client.collections | adminList(), adminCreate(), adminUpdate(), adminDelete(), adminListAutoPaginate() |
| client.orders | adminList(), adminGet(), adminCreate(), adminUpdate(), adminConfirm(), adminProcess(), adminComplete(), adminCancel(), adminListAutoPaginate() |
| client.customers | adminList(), adminGet(), adminCreate(), adminUpdate(), adminDelete(), adminCreateToken(), adminListAutoPaginate() |
| client.variants | adminList(), adminCreate(), adminUpdate(), adminDelete() |
| client.inventory | adminList(), adminGet(), adminUpdateSettings(), adminAdjust(), adminListAutoPaginate() |
| client.payments | adminCreate(), adminComplete() |
| client.fulfillments | adminCreate(), adminUpdate(), adminShip(), adminDeliver() |
| client.discounts | adminList(), adminGet(), adminCreate(), adminUpdate(), adminDelete(), adminListAutoPaginate() |
| client.shippingMethods | adminList(), adminGet(), adminCreate(), adminUpdate(), adminDelete() |
| client.apiKeys | adminList(), adminCreate(), adminDelete(), adminRotate() |
| client.organization | adminGet(), adminUpdate(), adminListMembers(), adminAddMember(), adminUpdateMember(), adminRemoveMember() |
| client.store | adminGet(), adminUpdate() |
| client.webhooks | adminList(), adminGet(), adminCreate(), adminUpdate(), adminDelete(), adminTest() |
Type Exports
모든 타입을 re-export합니다.
import type {
Product, Variant, Category, Collection,
Cart, CartItem, Order, OrderLine,
Customer, CustomerAddress,
ShippingMethod, Discount,
PaginatedResponse, Money,
} from '@headless-commerce/sdk';Requirements
- Node.js 18+ (native
fetch필요) - TypeScript 5.0+ (권장)
License
MIT
