
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@shopkit/data-layer
Advanced tools
Enterprise data abstraction layer providing unified API for e-commerce platforms including Shopify and custom backends
A comprehensive, production-ready data layer package for e-commerce applications, providing unified APIs for both Shopify and custom backend integrations.
IResponse format across all operationsnpm install @shopkit/data-layer
# or
yarn add @shopkit/data-layer
# or
pnpm add @shopkit/data-layer
import { createCommerceClient, IClientConfig } from '@shopkit/data-layer';
// Shopify Client Configuration
const shopifyConfig: IClientConfig = {
type: "shopify",
config: {
domain: "your-store.myshopify.com",
storefrontAccessToken: "your-storefront-access-token",
apiVersion: "2025-04" as const,
},
logging: {
enabled: true,
level: "info",
prettyPrint: true,
name: "Shopify Data Layer"
}
};
// Custom Backend Client Configuration
const customConfig: IClientConfig = {
type: "custom",
config: {
merchantId: "your-merchant-id",
storeId: "your-store-id",
services: {
product: {
apiBaseUrl: "https://api.yourstore.com/products",
},
order: {
apiBaseUrl: "https://api.yourstore.com/orders",
},
cart: {
apiBaseUrl: "https://api.yourstore.com/cart",
},
},
},
logging: {
enabled: true,
level: "info",
prettyPrint: true,
name: "Custom Backend Data Layer"
}
};
// Create the client
const commerceClient = createCommerceClient(shopifyConfig);
// or
const commerceClient = createCommerceClient(customConfig);
// Get a product
const product = await client.getProduct({ handle: "product-handle" });
if (product.success) {
console.log(product.data);
} else {
console.error(product.error?.message);
}
// Shopify Configuration
interface IShopifyConfig {
domain: string;
storefrontAccessToken: string;
apiVersion: string;
}
// Custom Backend Configuration
interface ICustomConfig {
merchantId: string;
storeId: string;
services: {
product: { apiBaseUrl: string };
order: { apiBaseUrl: string };
cart: { apiBaseUrl: string };
};
}
// Client Configuration Union Type
type IClientConfig =
| { type: "shopify"; config: IShopifyConfig; logging?: LoggerConfig }
| { type: "custom"; config: ICustomConfig; logging?: LoggerConfig };
interface LoggerConfig {
enabled: boolean;
level: "trace" | "debug" | "info" | "warn" | "error" | "fatal";
prettyPrint: boolean;
name: string;
redact?: string[];
}
All API methods support optional configuration:
interface IOptionsRequest {
query?: string; // Custom GraphQL query
variables?: Record<string, any>; // Custom variables
headers?: Record<string, string>; // Custom headers
params?: Record<string, string>; // URL parameters
}
All methods follow this consistent pattern:
methodName(params: ISpecificRequest, options?: IOptionsRequest): Promise<IResponse>
interface IResponse<T = any> {
success: boolean;
message: string;
data?: T;
error?: {
message: string;
details?: any;
};
meta?: IResponseMeta;
}
// Get single product by ID or handle
getProduct(params: ISingleProductRequest, options?: IOptionsRequest): Promise<IResponse>
interface ISingleProductRequest {
id?: string;
handle?: string;
}
// Get multiple products with filtering and pagination
getProducts(params: IProductQueryRequest, options?: IOptionsRequest): Promise<IResponse>
interface IProductQueryRequest {
first?: number;
last?: number;
after?: string;
before?: string;
query?: string;
sortKey?: string;
reverse?: boolean;
filters?: Array<{
available?: boolean;
price?: { min?: number; max?: number };
productType?: string;
vendor?: string;
tag?: string;
}>;
}
// Get multiple products by handles
getProductsByHandles(params: IProductsByHandlesRequest, options?: IOptionsRequest): Promise<IResponse>
interface IProductsByHandlesRequest {
handles: string[];
}
// Get single collection by ID or handle
getCollection(params: ISingleCollectionRequest, options?: IOptionsRequest): Promise<IResponse>
interface ISingleCollectionRequest {
id?: string;
handle?: string;
productLimit?: number;
productSortKey?: string;
productReverse?: boolean;
filters?: Array<{
available?: boolean;
price?: { min?: number; max?: number };
productType?: string;
vendor?: string;
tag?: string;
}>;
}
// Get multiple collections with filtering and pagination
getCollections(params: ICollectionQueryRequest, options?: IOptionsRequest): Promise<IResponse>
interface ICollectionQueryRequest {
first?: number;
last?: number;
after?: string;
before?: string;
query?: string;
sortKey?: string;
reverse?: boolean;
}
// Get multiple collections by handles with individual query parameters
getCollectionsByHandles(params: ICollectionsByhandlesRequest, options?: IOptionsRequest): Promise<IResponse>
interface ICollectionsByhandlesRequest {
handles: string[];
first?: number;
last?: number;
after?: string;
before?: string;
query?: string;
reverse?: boolean;
sortKey?: string;
productLimit?: number;
productSortKey?: string;
productReverse?: boolean;
filters?: Array<{
available?: boolean;
price?: { min?: number; max?: number };
productType?: string;
vendor?: string;
tag?: string;
}>;
}
// Create a new cart
createCart(options?: IOptionsRequest): Promise<IResponse>
// Get cart by ID
getCart(params: ISingleCartRequest, options?: IOptionsRequest): Promise<IResponse>
interface ISingleCartRequest {
id: string;
}
// Update cart
updateCart(params: IUpdateCartRequest, options?: IOptionsRequest): Promise<IResponse>
interface IUpdateCartRequest {
id: string;
items: ICartItemRequest;
}
interface ICartItemRequest {
productId: string;
variantId: string;
quantity: number;
sellingPlanId?: string;
attributes?: Array<{
key: string;
value: string;
}>;
}
// Add items to cart
addToCart(params: IAddToCartRequest, options?: IOptionsRequest): Promise<IResponse>
interface IAddToCartRequest {
id: string;
items: ICartItemRequest[];
}
// Remove items from cart
removeFromCart(params: IRemoveFromCartRequest, options?: IOptionsRequest): Promise<IResponse>
interface IRemoveFromCartRequest {
id: string;
itemIds: string[];
}
// Get single order by ID (Admin only)
getOrder(params: ISingleOrderRequest, options?: IOptionsRequest): Promise<IResponse>
interface ISingleOrderRequest {
id: string;
}
// Get multiple orders with filtering (Admin only)
getOrders(params: IOrderQueryRequest, options?: IOptionsRequest): Promise<IResponse>
interface IOrderQueryRequest {
first?: number;
last?: number;
after?: string;
before?: string;
query?: string;
sortKey?: string;
reverse?: boolean;
}
// Create new order (Admin only)
createOrder(params: IOrderRequest, options?: IOptionsRequest): Promise<IResponse>
interface IOrderRequest {
lineItems: Array<{
variantId: string;
quantity: number;
}>;
customer?: {
email: string;
firstName?: string;
lastName?: string;
};
shippingAddress?: {
address1: string;
city: string;
province: string;
country: string;
zip: string;
};
}
// Update existing order (Admin only)
updateOrder(params: IUpdateOrderRequest, options?: IOptionsRequest): Promise<IResponse>
interface IUpdateOrderRequest {
id: string;
data: Partial<IOrderRequest>;
}
// Get customer order history
getOrderHistory(params: IOrderHistoryRequest, options?: IOptionsRequest): Promise<IResponse>
interface IOrderHistoryRequest {
customerAccessToken: string;
page?: number;
limit?: number;
}
// Get specific customer order
getOrderById(params: IOrderByIdRequest, options?: IOptionsRequest): Promise<IResponse>
interface IOrderByIdRequest {
customerAccessToken: string;
orderId: string;
}
// Get single customer by ID (Admin only)
getCustomer(params: ISingleCustomerRequest, options?: IOptionsRequest): Promise<IResponse>
interface ISingleCustomerRequest {
id: string;
}
// Get multiple customers (Admin only)
getCustomers(params: ICustomerQueryRequest, options?: IOptionsRequest): Promise<IResponse>
// Create new customer
createCustomer(params: ICustomerRequest, options?: IOptionsRequest): Promise<IResponse>
interface ICustomerRequest {
email: string;
firstName?: string;
lastName?: string;
phone?: string;
password?: string;
acceptsMarketing?: boolean;
}
// Update existing customer
updateCustomer(params: IUpdateCustomerRequest, options?: IOptionsRequest): Promise<IResponse>
interface IUpdateCustomerRequest {
id: string;
data: Partial<ICustomerRequest>;
}
// Get customer profile (Shopify only)
getCustomerProfile(params: ICustomerProfileRequest, options?: IOptionsRequest): Promise<IResponse>
interface ICustomerProfileRequest {
customerAccessToken: string;
}
// Create checkout
createCheckout(params: ICreateCheckoutRequest, options?: IOptionsRequest): Promise<IResponse>
interface ICreateCheckoutRequest {
lineItems?: Array<{
variantId: string;
quantity: number;
}>;
email?: string;
shippingAddress?: {
address1: string;
city: string;
province: string;
country: string;
zip: string;
};
}
// Get single article
getArticle(params?: IGetArticleRequest, options?: IOptionsRequest): Promise<IResponse>
interface IGetArticleRequest {
blogHandle?: string;
articleHandle?: string;
first?: number;
}
// Get blog articles
getBlogArticles(params: IBlogArticleRequest, options?: IOptionsRequest): Promise<IResponse>
interface IBlogArticleRequest {
blogHandle: string;
first?: number;
after?: string;
}
// Get trending articles
getTrendingArticles(params: ITrendingArticleRequest, options?: IOptionsRequest): Promise<IResponse>
interface ITrendingArticleRequest {
first?: number;
sortBy?: string;
}
// Get article by handle
getArticleByHandle(params: IArticleByHandleRequest, options?: IOptionsRequest): Promise<IResponse>
interface IArticleByHandleRequest {
blogHandle: string;
articleHandle: string;
}
// Custom GraphQL request (Shopify only)
graphqlRequest(params: IGraphqlRequest): Promise<IResponse>
interface IGraphqlRequest {
query: string;
variables?: Record<string, any>;
}
// Custom HTTP fetch request
fetchRequest(params: IFetchRequest): Promise<IResponse>
interface IFetchRequest {
url: string;
method?: string;
headers?: Record<string, string>;
body?: string;
signal?: AbortSignal;
[key: string]: any;
}
// Get single product
const product = await client.getProduct({ handle: "product-handle" });
// Get single product with custom headers
const product = await client.getProduct(
{ handle: "product-handle" },
{ headers: { "X-Custom-Header": "value" } }
);
// Get multiple products
const products = await client.getProducts({
first: 20,
query: "shirt",
sortKey: "TITLE"
});
// Get products by handles
const products = await client.getProductsByHandles({
handles: ["handle1", "handle2"]
});
// Get single collection
const collection = await client.getCollection({ handle: "collection-handle" });
// Get multiple collections
const collections = await client.getCollections({ first: 10 });
// Get collections by handles with shared parameters
const collections = await client.getCollectionsByHandles({
handles: ["featured-products", "sale-items", "new-arrivals"],
first: 10,
productLimit: 20,
filters: [{ available: true }]
});
// Create cart
const cart = await client.createCart();
// Get cart
const cart = await client.getCart({ id: "cart-id" });
// Add items to cart
const updatedCart = await client.addToCart({
id: "cart-id",
items: [
{
productId: "product-id",
variantId: "variant-id",
quantity: 1
}
]
});
// Update cart
const updatedCart = await client.updateCart({
id: "cart-id",
data: {
items: [
{
productId: "product-id",
variantId: "variant-id",
quantity: 2
}
]
}
});
// Remove items from cart
const updatedCart = await client.removeFromCart({
id: "cart-id",
itemIds: ["line-id"]
});
// Get order history
const orders = await client.getOrderHistory({
customerAccessToken: "customer-access-token",
page: 1,
limit: 10
});
// Get specific order
const order = await client.getOrderById({
customerAccessToken: "customer-access-token",
orderId: "order-id"
});
// app/products/[handle]/page.tsx
import { createCommerceClient } from '@shopkit/data-layer';
export default async function ProductPage({ params }: { params: { handle: string } }) {
const client = createCommerceClient(shopifyConfig);
const response = await client.getProduct({ handle: params.handle });
if (!response.success) {
return <div>Error: {response.error?.message}</div>;
}
const product = response.data;
return (
<div>
<h1>{product.title}</h1>
<p>{product.description}</p>
<div>Price: {product.priceRange.minVariantPrice.amount} {product.priceRange.minVariantPrice.currencyCode}</div>
</div>
);
}
// app/api/products/route.ts
import { NextRequest } from 'next/server';
import { createCommerceClient } from '@shopkit/data-layer';
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const client = createCommerceClient(shopifyConfig);
const response = await client.getProducts({
first: 10,
query: searchParams.get('query') || undefined
});
return Response.json(response);
}
'use client';
import { useState, useEffect } from 'react';
import { IResponse } from '@shopkit/data-layer';
export function useProducts(query?: string) {
const [response, setResponse] = useState<IResponse | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchProducts() {
try {
const res = await fetch(`/api/products?query=${query || ''}`);
const data = await res.json();
setResponse(data);
} catch (error) {
setResponse({
success: false,
message: 'Failed to fetch products',
error: { message: error instanceof Error ? error.message : 'Unknown error' }
});
} finally {
setLoading(false);
}
}
fetchProducts();
}, [query]);
return { response, loading };
}
| Method | Custom Adapter | Shopify Adapter | Notes |
|---|---|---|---|
| Product Operations | |||
getProduct | ✅ Implemented | ✅ Implemented | Core product retrieval |
getProducts | ✅ Implemented | ✅ Implemented | Product listing with filters |
getProductsByHandles | ✅ Implemented | ✅ Implemented | Batch product retrieval |
| Collection Operations | |||
getCollection | ✅ Implemented | ✅ Implemented | Single collection retrieval |
getCollections | ✅ Implemented | ✅ Implemented | Collection listing |
getCollectionsByHandles | ✅ Implemented | ✅ Implemented | Batch collection retrieval |
| Order Operations | |||
getOrder | ❌ (Error thrown) | ❌ (NotImplemented) | Single order retrieval |
getOrders | ❌ (Error thrown) | ❌ (NotImplemented) | Order listing |
createOrder | ❌ (Error thrown) | ❌ (NotImplemented) | Order creation |
updateOrder | ❌ (Error thrown) | ❌ (NotImplemented) | Order updates |
getOrderHistory | ✅ Implemented | ✅ Implemented | Customer order history |
getOrderById | ✅ Implemented | ✅ Implemented | Customer specific order |
| Customer Operations | |||
getCustomer | ❌ (Error thrown) | ❌ (NotImplemented) | Single customer retrieval |
getCustomers | ❌ (Error thrown) | ❌ (NotImplemented) | Customer listing |
createCustomer | ❌ (Error thrown) | ❌ (NotImplemented) | Customer creation |
updateCustomer | ❌ (Error thrown) | ❌ (NotImplemented) | Customer updates |
getCustomerProfile | ❌ (Not available) | ✅ Implemented | Shopify-specific method |
| Cart Operations | |||
getCart | ✅ Implemented | ✅ Implemented | Cart retrieval |
createCart | ✅ Implemented | ✅ Implemented | Cart creation |
updateCart | ✅ Implemented | ✅ Implemented | Cart updates |
addToCart | ✅ Implemented | ✅ Implemented | Add items to cart |
removeFromCart | ✅ Implemented | ✅ Implemented | Remove items from cart |
| Checkout Operations | |||
createCheckout | ✅ Implemented | ❌ (NotImplemented) | Checkout creation |
| Article/Blog Operations | |||
getArticle | ❌ (NotImplemented) | ✅ Implemented | Single article retrieval |
getBlogArticles | ❌ (NotImplemented) | ✅ Implemented | Blog article listing |
getTrendingArticles | ❌ (NotImplemented) | ✅ Implemented | Trending articles |
getArticleByHandle | ❌ (NotImplemented) | ✅ Implemented | Article by handle |
| Utility Operations | |||
graphqlRequest | ❌ (NotImplemented) | ✅ Implemented | GraphQL utility |
fetchRequest | ✅ Inherited | ✅ Inherited | HTTP fetch utility |
The data layer provides comprehensive error handling:
const response = await client.getProduct({ handle: "invalid-handle" });
if (!response.success) {
console.error("Error:", response.error?.message);
// Handle specific error types
if (response.error?.details?.name === "NotFoundError") {
// Handle product not found
}
}
response.success before accessing dataFor complete examples and usage patterns, see the data layer example app.
The example application includes several user experience improvements:
The example app provides improved textarea formatting for complex JSON payloads:
Example Add to Cart Payload Format:
[
{
"productId": "gid://shopify/Product/123",
"variantId": "gid://shopify/ProductVariant/123",
"quantity": 1,
"sellingPlanId": "selling-plan-id",
"attributes": [
{
"key": "gift-wrap",
"value": "true"
}
]
}
]
Example Create Checkout Payload Format:
{
"cart_token": "cart-token-here",
"checkout_id": "checkout-id-here",
"attributes": {
"email": "customer@example.com",
"note": "Special instructions",
"phone": "+1234567890",
"custom_field": "custom_value"
}
}
These improvements provide:
MIT License - see the LICENSE file for details.
FAQs
Enterprise data abstraction layer providing unified API for e-commerce platforms including Shopify and custom backends
We found that @shopkit/data-layer demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.