v2.0.0Stable
TypeScript SDK
Full TypeScript support for Node.js and browsers. Includes React hooks, streaming support, and first-class Next.js integration.
Node.js 18+
Modern Browsers
React Native
Installation
npmbash
npm install @justkalm/sdkpnpmbash
pnpm add @justkalm/sdkTypeScript Built-in
The SDK is written in TypeScript and includes full type definitions. No need to install @types packages.
Quick Start
quickstart.tstypescript
import { JustKalm } from '@justkalm/sdk';
// Initialize the client
const client = new JustKalm({
apiKey: process.env.JUSTKALM_API_KEY!,
environment: 'production', // or 'sandbox'
});
// Create a product
const product = await client.products.create({
name: 'Organic Cotton T-Shirt',
brand: 'EcoWear',
category: 'apparel',
materials: {
organic_cotton: 0.95,
elastane: 0.05,
},
certifications: ['GOTS', 'Fair Trade'],
});
console.log(`Created product: ${product.id}`);
// Get valuation with health scoring
const valuation = await client.valuations.create(product.id, {
includeHealth: true,
includeSustainability: true,
});
console.log(`Health Score: ${valuation.healthScore}/100`);
console.log(`Value: $${valuation.adjustedValue.toFixed(2)}`);React Hooks
The SDK includes React hooks for seamless frontend integration with built-in loading states, error handling, and caching.
Setup Provider
// app/providers.tsx
'use client';
import { JustKalmProvider } from '@justkalm/sdk/react';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<JustKalmProvider
apiKey={process.env.NEXT_PUBLIC_JUSTKALM_API_KEY!}
options={{
refetchOnWindowFocus: true,
staleTime: 5 * 60 * 1000, // 5 minutes
}}
>
{children}
</JustKalmProvider>
);
}useProduct Hook
import { useProduct, useValuation } from '@justkalm/sdk/react';
function ProductCard({ productId }: { productId: string }) {
const {
data: product,
isLoading,
error
} = useProduct(productId);
const {
data: valuation
} = useValuation(productId, {
includeHealth: true,
});
if (isLoading) return <Skeleton />;
if (error) return <Error message={error.message} />;
return (
<div className="p-4 border rounded-lg">
<h2>{product.name}</h2>
<p>Brand: {product.brand}</p>
{valuation && (
<>
<HealthScore score={valuation.healthScore} />
<Price value={valuation.adjustedValue} />
</>
)}
</div>
);
}Mutations
import { useCreateProduct } from '@justkalm/sdk/react';
function CreateProductForm() {
const {
mutate: createProduct,
isPending,
error
} = useCreateProduct();
const handleSubmit = async (data: FormData) => {
createProduct({
name: data.get('name') as string,
brand: data.get('brand') as string,
category: 'apparel',
}, {
onSuccess: (product) => {
toast.success(`Created ${product.name}`);
router.push(`/products/${product.id}`);
},
onError: (error) => {
toast.error(error.message);
},
});
};
return (
<form onSubmit={handleSubmit}>
{/* form fields */}
<button type="submit" disabled={isPending}>
{isPending ? 'Creating...' : 'Create Product'}
</button>
</form>
);
}Products API
// Create a product
const product = await client.products.create({
name: 'Premium Leather Handbag',
brand: 'LuxuryBrand',
category: 'accessories',
materials: {
full_grain_leather: 0.85,
brass: 0.10,
cotton_lining: 0.05,
},
condition: 'excellent',
originalPrice: 1200,
metadata: {
color: 'cognac',
year: 2023,
},
});
// List products with filtering
const { data: products, pagination } = await client.products.list({
limit: 50,
category: 'apparel',
brand: 'EcoWear',
minHealthScore: 70,
});
// Iterate with pagination
for await (const product of client.products.iterate()) {
console.log(product.name);
}
// Get, update, delete
const product = await client.products.get('prod_abc123');
const updated = await client.products.update('prod_abc123', {
condition: 'good'
});
await client.products.delete('prod_abc123');Streaming
Stream real-time updates for long-running operations like batch processing or AI insights.
// Stream valuation generation
const stream = await client.valuations.createStream(productId, {
includeHealth: true,
includeInsights: true,
});
for await (const chunk of stream) {
switch (chunk.type) {
case 'health_score':
console.log(`Health: ${chunk.data.score}/100`);
updateUI({ healthScore: chunk.data.score });
break;
case 'sustainability_score':
console.log(`Sustainability: ${chunk.data.score}/100`);
break;
case 'insight':
console.log(`Insight: ${chunk.data.text}`);
break;
case 'complete':
console.log('Valuation complete');
break;
}
}
// React hook for streaming
function ValuationStream({ productId }: { productId: string }) {
const {
chunks,
isStreaming,
error
} = useValuationStream(productId);
return (
<div>
{chunks.map((chunk, i) => (
<StreamChunk key={i} chunk={chunk} />
))}
{isStreaming && <Spinner />}
</div>
);
}Error Handling
import {
JustKalmError,
AuthenticationError,
RateLimitError,
ValidationError,
NotFoundError,
isJustKalmError,
} from '@justkalm/sdk';
try {
const product = await client.products.get('prod_invalid');
} catch (error) {
if (error instanceof NotFoundError) {
console.log(`Not found: ${error.message}`);
} else if (error instanceof RateLimitError) {
console.log(`Rate limited. Retry after: ${error.retryAfter}s`);
} else if (error instanceof ValidationError) {
console.log(`Validation failed:`);
error.errors.forEach(e => {
console.log(` ${e.field}: ${e.message}`);
});
} else if (error instanceof AuthenticationError) {
console.log(`Auth error: ${error.message}`);
} else if (isJustKalmError(error)) {
// Any SDK error
console.log(`SDK Error: ${error.message}`);
} else {
// Unknown error
throw error;
}
}
// Type-safe error checking
function handleError(error: unknown) {
if (isJustKalmError(error)) {
// TypeScript knows this is JustKalmError
console.log(error.code, error.message);
}
}Next.js Integration
// app/products/[id]/page.tsx
import { getJustKalmClient } from '@justkalm/sdk/server';
export default async function ProductPage({
params
}: {
params: { id: string }
}) {
const client = getJustKalmClient();
const [product, valuation] = await Promise.all([
client.products.get(params.id),
client.valuations.get(params.id),
]);
return (
<div>
<h1>{product.name}</h1>
<HealthScoreBadge score={valuation.healthScore} />
<ValuationDetails valuation={valuation} />
</div>
);
}
// With caching
export const revalidate = 60; // Revalidate every 60 seconds