npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@shopkit/data-layer

v1.1.0

Published

Enterprise data abstraction layer providing unified API for e-commerce platforms including Shopify and custom backends

Readme

@shopkit/data-layer

A comprehensive, production-ready data layer package for e-commerce applications, providing unified APIs for both Shopify and custom backend integrations.

Table of Contents

Features

  • 🌐 Multi-Platform Support: Seamlessly works with both Shopify and custom backend APIs
  • ⚙️ Configurable Architecture: Zero environment variable dependencies - fully configurable through parameters
  • 🔷 TypeScript First: Built with TypeScript for complete type safety
  • 📦 Modular Design: Tree-shakable imports - use only what you need
  • 🔧 Flexible Options: Support for custom queries and variables in all API methods
  • 📋 Consistent Response Format: Standardized IResponse format across all operations
  • 🪵 Advanced Logging: Built-in logging with configurable levels and formatting
  • 🎯 Standardized Interface: All methods follow consistent signature patterns

Installation

npm install @shopkit/data-layer
# or
yarn add @shopkit/data-layer
# or
pnpm add @shopkit/data-layer

Quick Start

1. Create a Commerce Client

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);

2. Basic Usage

// Get a product
const product = await client.getProduct({ handle: "product-handle" });

if (product.success) {
  console.log(product.data);
} else {
  console.error(product.error?.message);
}

Configuration

Client Configuration Interfaces

// 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 };

Logging Configuration

interface LoggerConfig {
  enabled: boolean;
  level: "trace" | "debug" | "info" | "warn" | "error" | "fatal";
  prettyPrint: boolean;
  name: string;
  redact?: string[];
}

Request Options

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
}

API Reference

Method Signature Pattern

All methods follow this consistent pattern:

methodName(params: ISpecificRequest, options?: IOptionsRequest): Promise<IResponse>

Standardized Response Format

interface IResponse<T = any> {
  success: boolean;
  message: string;
  data?: T;
  error?: {
    message: string;
    details?: any;
  };
  meta?: IResponseMeta;
}

Product Operations

// 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[];
}

Collection Operations

// 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;
  }>;
}

Cart Operations

// 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[];
}

Order Operations

// 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;
}

Customer Operations

// 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;
}

Checkout Operations

// 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;
  };
}

Blog/Article Operations (Shopify only)

// 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;
}

Utility Operations

// 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;
}

Usage Examples

Product Operations

// 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"]
});

Collection Operations

// 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 }]
});

Cart Operations

// 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"]
});

Order Operations

// 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"
});

Framework Integration Examples

Next.js App Router (Server-Side)

// 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>
  );
}

API Route Usage

// 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);
}

Client-Side Usage

'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 };
}

Platform Support

Shopify Adapter

  • Products: Full support for product operations
  • Collections: Full support for collection operations
  • Cart: Full cart management
  • Orders: Customer order history and details
  • Blog: Article and blog operations
  • GraphQL: Custom GraphQL queries
  • Customer: Customer profile access

Custom Backend Adapter

  • Products: Full support for product operations
  • Collections: Full support for collection operations
  • Cart: Full cart management
  • Orders: Customer order history and details
  • Checkout: Basic checkout creation
  • Blog: Not implemented
  • GraphQL: Not available

Method Support Matrix

| 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 |

Error Handling

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
  }
}

Best Practices

  1. Always check response.success before accessing data
  2. Use TypeScript for full type safety and better developer experience
  3. Enable logging during development for better debugging
  4. Handle errors gracefully with proper fallbacks
  5. Use environment variables for sensitive configuration data
  6. Implement proper caching for frequently accessed data
  7. Use custom queries sparingly and only when standard methods are insufficient
  8. Follow the standardized method signature pattern for consistency

Examples

For complete examples and usage patterns, see the data layer example app.

Data Layer Example App Features

The example application includes several user experience improvements:

Enhanced JSON Input Fields

The example app provides improved textarea formatting for complex JSON payloads:

  • Add to Cart: Multi-line textarea with formatted JSON structure for items array
  • Update Cart: Enhanced textarea for cart item modifications with proper formatting
  • Create Checkout: Improved textarea for checkout payload with correct interface structure

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": "[email protected]",
    "note": "Special instructions",
    "phone": "+1234567890",
    "custom_field": "custom_value"
  }
}

These improvements provide:

  • Better Readability: Multi-line JSON with proper indentation
  • Larger Input Areas: Textarea fields instead of single-line inputs
  • Complete Examples: Shows all available fields and proper structure
  • Error Prevention: Properly formatted examples reduce JSON syntax errors

License

MIT License - see the LICENSE file for details.