Skip to main content
The SDK provides 100% type safety with auto-generated types from OpenAPI specifications, complete IntelliSense support, and compile-time error prevention.

Why Type Safety Matters

IntelliSense Support

Complete autocomplete for all API methods, parameters, and response data

Compile-time Checking

Catch errors during development, not at runtime

Self-Documenting Code

Types serve as always up-to-date documentation

Refactoring Safety

Confidently refactor with IDE support for finding all usages

Auto-Generated Types

All types are automatically generated from the OpenAPI specification, ensuring they’re always in sync with the API:
// Import response types (for reading API responses)
import type {
  GetProductDetailContent,
  GetCartContent,
  GetOrderDetailContent,
  GetUserDetailContent,
  ApiResult
} from '@commercengine/storefront-sdk';

// Import Input types (for request bodies)
import type {
  CustomerAddressInput,
  UpdateFulfillmentPreferenceBody,
} from '@commercengine/storefront-sdk';

Response Types vs Input Types

The SDK exports context-aware types to distinguish between data you read from the API and data you send to the API:
Type nameUsed forExample
SchemaNameResponse data (reading)CustomerAddress, Cart, Product
SchemaNameInputRequest bodies (writing)CustomerAddressInput, AnalyticsEventInput
Some schemas appear only in responses, some only in request bodies, and some in both. The SDK generates the right exports automatically:
  • Response-only schemas — export the base name only (e.g. Cart)
  • Request + response schemas — export both (e.g. CustomerAddress + CustomerAddressInput)
  • Request-only schemas — export the Input name only (e.g. AnalyticsEventInput)
// Response data — use the base name
const address: CustomerAddress = response.data.address;

// Request body — use the Input suffix
const newAddress: CustomerAddressInput = {
  street: "123 Main St",
  city: "New York",
  // ...
};
const { data, error } = await sdk.customer.createAddress(newAddress);
You can also use the endpoint-level Body types (e.g. UpdateFulfillmentPreferenceBody) to type the full request body shape passed to an SDK method.

The { data, error } Pattern

Every API operation returns a type-safe object with data and error properties:
// Type signature automatically inferred from OpenAPI spec
const { data, error } = await client.catalog.getProductDetail({ product_id_or_slug: 'product-id' });

if (data) {
  // TypeScript knows data.product is a ProductDetail
  const product = data.product;
  console.log(product.id);           // ✅ string
  console.log(product.name);         // ✅ string
  console.log(product.slug);         // ✅ string
  console.log(product.description);  // ✅ string
} else if (error) {
  // TypeScript knows error is ApiErrorResponse
  console.log(error.message); // ✅ string
  console.log(error.code);    // ✅ string
}

IntelliSense in Action

Method Parameters

Complete autocomplete for all API parameters with OpenAPI-generated types:
// IntelliSense shows all available options from OpenAPI spec
const { data: productsData, error } = await client.catalog.listProducts({
  limit: 20,                    // ✅ number | undefined
  category_id: 'electronics',   // ✅ string | undefined
  sort_by: 'name',             // ✅ string | undefined  
  min_price: 100,              // ✅ number | undefined
  max_price: 500               // ✅ number | undefined
});

Response Data Access

Full type safety when accessing response data:
if (productsData) {
  productsData.products.forEach(product => {
    // Full IntelliSense for auto-generated Product schema
    product.id;              // ✅ string
    product.name;            // ✅ string
    product.slug;            // ✅ string
    product.description;     // ✅ string
    product.short_description; // ✅ string | null
    product.is_active;       // ✅ boolean

    // TypeScript prevents accessing non-existent properties
    product.nonExistent;     // ❌ Compile error
  });
}

Cart Operations

Type-safe cart management with correct API structure:
// Creating cart with typed item structure from OpenAPI
const { data, error } = await client.cart.createCart({
  items: [{
    product_id: 'prod_123',    // ✅ string (required)
    variant_id: null,          // ✅ string | null (required)
    quantity: 2                // ✅ number (required)
  }]
});

if (data) {
  const cart = data.cart;
  // Cart object with full type safety from OpenAPI schema
  cart.id;                    // ✅ string
  cart.cart_items;            // ✅ CartItem[]
  cart.grand_total;           // ✅ number
  cart.currency;              // ✅ string
  cart.shipping_address;      // ✅ object | null
}

Advanced Type Features

Discriminated Unions

Type-safe handling of different response states:
// The { data, error } pattern with proper error handling
const { data, error } = await client.auth.loginWithEmail({
  email: 'user@example.com',
  register_if_not_exists: true
});

if (data) {
  // TypeScript knows data contains the success response
  data.otp_token;  // ✅ string
  data.otp_action; // ✅ string
} else if (error) {
  // TypeScript knows error contains error information
  error.message; // ✅ string
  error.code;    // ✅ string
}

Generic Type Constraints

Type-safe configuration with OpenAPI-generated interfaces:
// TokenStorage interface is properly typed
interface TokenStorage {
  getAccessToken(): Promise<string | null>;
  setAccessToken(token: string): Promise<void>;
  getRefreshToken(): Promise<string | null>;
  setRefreshToken(token: string): Promise<void>;
  clearTokens(): Promise<void>;
}

// Implementation must satisfy interface
class CustomTokenStorage implements TokenStorage {
  async getAccessToken(): Promise<string | null> {
    // Implementation must return correct type
    return localStorage.getItem('access_token');
  }
  
  // ... other methods with type safety
}

Framework Integration Types

React Hook Types

// Custom hook with proper OpenAPI-generated types
function useProducts(categoryId?: string): {
  products: GetProductDetailContent[] | null;
  loading: boolean;
  error: ApiErrorResponse | null;
} {
  const [products, setProducts] = useState<GetProductDetailContent[] | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<ApiErrorResponse | null>(null);
  
  useEffect(() => {
    async function fetchProducts() {
      const { data, error } = await client.catalog.listProducts({
        category_id: categoryId,
        limit: 50
      });
      
      if (data) {
        setProducts(data.products);    // ✅ Type-safe assignment
      } else if (error) {
        setError(error);               // ✅ Type-safe error handling
      }
      
      setLoading(false);
    }
    
    fetchProducts();
  }, [categoryId]);
  
  return { products, loading, error };
}

Component Props

// Type-safe component props using OpenAPI types
interface ProductCardProps {
  product: GetProductDetailContent;               // ✅ Generated Product type
  onAddToCart: (productId: string, variantId: string | null) => void;
}

const ProductCard: React.FC<ProductCardProps> = ({ product, onAddToCart }) => {
  return (
    <div>
      <h3>{product.name}</h3>          {/* ✅ Type-safe access */}
      <p>{product.description}</p>     {/* ✅ Known to be string */}
      
      <button onClick={() => onAddToCart(product.id, null)}>
        Add to Cart
      </button>
    </div>
  );
};

Type Utilities

Exported Utility Types

// Response types — for reading API data
import type {
  GetProductDetailContent,
  GetCartContent,
  GetOrderDetailContent,
  GetUserDetailContent,
  ApiResult,
  ListProductsQuery,
  CreateCartBody
} from '@commercengine/storefront-sdk';

// Input types — for request bodies
import type {
  CustomerAddressInput,
  FulfillmentPreferenceInput,
} from '@commercengine/storefront-sdk';

// Readable / Writable utility types — escape hatches for advanced use
import type { Readable, Writable } from '@commercengine/storefront-sdk';

// Use in your application
interface AppState {
  currentUser: GetUserDetailContent | null;
  cart: GetCartContent | null;
  products: GetProductDetailContent[];
}

Readable & Writable Utility Types

The SDK re-exports Readable and Writable utility types for advanced use cases where the generated exports don’t cover your needs:
import type { Readable, Writable } from '@commercengine/storefront-sdk';
import type { components } from '@commercengine/storefront-sdk';

// Manually create a response type
type MyResponseType = Readable<components['schemas']['SomeSchema']>;

// Manually create a request body type
type MyRequestType = Writable<components['schemas']['SomeSchema']>;
In most cases you won’t need Readable or Writable directly — the generated base-name and Input exports cover standard usage. These are provided as escape hatches.

Type Guards

// Type guards for runtime type checking
function isProductResult(
  result: ApiResult<any>
): result is { data: { product: GetProductDetailContent }; error: null } {
  return result.data &&
         result.data.product &&
         typeof result.data.product.id === 'string';
}

// Usage with type safety
const result = await client.catalog.getProductDetail({ product_id_or_slug: 'product-id' });
if (isProductResult(result)) {
  // TypeScript knows result.data.product is GetProductDetailContent
  console.log(result.data.product.name);
}

Development Experience

IDE Integration

The SDK works seamlessly with popular IDEs:
  • VSCode: Full IntelliSense, error squiggles, auto-imports
  • WebStorm: Complete type information, refactoring support
  • Vim/Neovim: TypeScript LSP provides full type information

Compile-Time Validation

// These errors are caught at compile time, not runtime:

// ❌ Compile error: invalid property in request body
await client.cart.createCart({
  items: [{ 
    product_id: 'id', 
    variant_id: null, 
    qty: 2  // Should be 'quantity'
  }]
});

// ❌ Compile error: wrong parameter name
const result = await client.catalog.listProducts({
  limite: 20  // Should be 'limit'
});

// ❌ Compile error: accessing wrong property
if (result.data) {
  console.log(result.data.invalidProperty); // Property doesn't exist
}

Type Safety vs Direct API

// Full type safety and IntelliSense with OpenAPI-generated types
const result = await client.catalog.getProductDetail({ product_id_or_slug: 'product-id' });

if (result.data) {
  // ✅ TypeScript knows exact structure from OpenAPI schema
  const product = result.data.product;
  console.log(product.name);         // ✅ Known to be string
  console.log(product.id);           // ✅ Known to be string
  console.log(product.is_active);    // ✅ Known to be boolean
} else if (result.error) {
  // ✅ Typed error handling
  console.error(result.error.message); // ✅ Known error structure
}

Best Practices

Configure TypeScript for maximum type safety:
tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noUncheckedIndexedAccess": true
  }
}
Always import and use the generated types from the SDK:
// ✅ Use generated types for responses
import type { GetProductDetailContent } from '@commercengine/storefront-sdk';

// ✅ Use Input types for request bodies
import type { CustomerAddressInput } from '@commercengine/storefront-sdk';

// ❌ Avoid creating custom interfaces that duplicate API schemas
interface CustomProduct {
  id: string;
  name: string;
  // ... this will get out of sync with API
}
Always check for both success and error conditions:
const result = await client.catalog.listProducts();

if (result.data) {
  // Handle success case
  const products = result.data.products;
  // Process products...
} else if (result.error) {
  // Handle error case
  console.error('API Error:', result.error.message);
} else {
  // Handle unexpected case
  console.error('Unexpected response structure');
}

Migration Benefits

When migrating from direct API calls:

Catch Errors Early

Find API usage errors during development, not production

Always Current Documentation

Types are auto-generated from OpenAPI spec and always up-to-date

Faster Development

IntelliSense speeds up development with accurate autocomplete

Confident Refactoring

Change APIs with confidence using IDE refactoring tools
All SDK types are automatically generated from the OpenAPI specification, ensuring they’re always accurate and up-to-date with the actual API. Response types use the base schema name (e.g. CustomerAddress), while request body types use the Input suffix (e.g. CustomerAddressInput). The Readable and Writable utility types are also available for advanced use cases.