distru-sdk
v1.0.2
Published
Official TypeScript/JavaScript SDK for the Distru API - Cannabis supply chain management
Maintainers
Readme
Distru TypeScript/JavaScript SDK
Official TypeScript/JavaScript SDK for the Distru API - Cannabis supply chain management platform.
Installation
npm install distru-sdk
# or
yarn add distru-sdk
# or
pnpm add distru-sdkQuick Start
import { DistruClient } from 'distru-sdk';
// Initialize the client with your API token
const client = new DistruClient({ apiToken: 'your_api_token_here' });
// List products
const products = await client.products.list();
for await (const product of products.autoPaginate()) {
console.log(`${product.name} - $${product.sale_price}`);
}
// Create an order
const order = await client.orders.create({
company_relationship_id: 123,
order_date: '2025-10-06T12:00:00Z',
order_items: [
{
product_id: 'prod-uuid-123',
quantity: 10,
unit_price: '15.00'
}
]
});
console.log(`Order created: ${order.order_number}`);Authentication
The Distru API uses JWT Bearer token authentication. To get an API token:
- Log into your Distru account
- Navigate to Settings → API Keys
- Click Create API Key
- Copy the generated token
- Store it securely (never commit to source control!)
// Initialize client
const client = new DistruClient({ apiToken: 'your_api_token_here' });
// Optional: Configure timeout and retries
const client = new DistruClient({
apiToken: 'your_api_token_here',
timeout: 60000, // Request timeout in milliseconds (default: 30000)
maxRetries: 5 // Maximum retry attempts (default: 3)
});Usage
Products
// List all products
const products = await client.products.list();
// Auto-paginate through all results
for await (const product of products.autoPaginate()) {
console.log(product.name);
}
// Search products
const products = await client.products.list({ search: 'Blue Dream' });
// Get a specific product
const product = await client.products.get('prod-uuid-123');
// Create a product
const product = await client.products.create({
name: 'Blue Dream 1g',
sku: 'BD-1G',
unitTypeId: 1,
inventoryTrackingMethod: 'BATCH',
salePrice: '15.00',
wholesalePrice: '10.00'
});
// Update a product
const product = await client.products.update('prod-uuid-123', {
salePrice: '17.00',
wholesalePrice: '11.00'
});
// Delete a product
await client.products.delete('prod-uuid-123');Orders
// List orders
const orders = await client.orders.list();
// Filter orders
const orders = await client.orders.list({
status: 'Submitted',
company_relationship_id: 123,
order_date_start: '2025-01-01',
order_date_end: '2025-12-31'
});
// Get a specific order
const order = await client.orders.get('order-uuid-123');
// Create an order
const order = await client.orders.create({
company_relationship_id: 123,
order_date: '2025-10-06T12:00:00Z',
due_date: '2025-10-20T12:00:00Z',
order_items: [
{
product_id: 'prod-uuid-1',
quantity: 10,
unit_price: '15.00'
},
{
product_id: 'prod-uuid-2',
quantity: 5,
unit_price: '25.00'
}
]
});Invoices
// List invoices
const invoices = await client.invoices.list();
// Filter invoices
const invoices = await client.invoices.list({
status: 'Not Paid',
company_relationship_id: 123
});
// Get a specific invoice
const invoice = await client.invoices.get(456);
// Create an invoice
const invoice = await client.invoices.create({
order_id: 'order-uuid-123',
invoice_date: '2025-10-06T12:00:00Z',
due_date: '2025-10-20T12:00:00Z',
invoice_items: [
{
order_item_id: 'order-item-uuid-1',
quantity: 10
}
]
});
// Add a payment to an invoice
const payment = await client.invoices.addPayment(456, {
amount: '150.00',
payment_date: '2025-10-06T12:00:00Z',
payment_method_id: 1
});Inventory
// Get current inventory
const inventory = await client.inventory.list();
// Include cost information
const inventory = await client.inventory.list({ include_costs: true });
// Filter by location
const inventory = await client.inventory.list({ location_id: 1 });
// Filter by product
const inventory = await client.inventory.list({ product_id: 'prod-uuid-123' });Companies (Customers/Vendors)
// List all companies
const companies = await client.companies.list();
// Search companies
const companies = await client.companies.list({ search: 'Acme' });
// Filter by state
const companies = await client.companies.list({ us_state: 'CA' });
// Get a specific company
const company = await client.companies.get(123);Pagination
All list endpoints return paginated results. The SDK provides helper methods for easy pagination:
Auto-Pagination
// Automatically fetch all pages
const products = await client.products.list();
for await (const product of products.autoPaginate()) {
console.log(product.name);
}Manual Pagination
// Iterate page by page
const response = await client.products.list();
for await (const page of response.iterPages()) {
console.log(`Processing page with ${page.length} items`);
for (const product of page) {
console.log(product.name);
}
}Page-by-Page
// Fetch specific pages
const page1 = await client.products.list({ page: 1, limit: 100 });
const page2 = await client.products.list({ page: 2, limit: 100 });Error Handling
The SDK provides specific exception classes for different error types:
import {
DistruAPIError,
AuthenticationError,
AuthorizationError,
NotFoundError,
ValidationError,
RateLimitError,
ServerError,
NetworkError,
TimeoutError,
} from 'distru-sdk';
try {
const order = await client.orders.get('invalid-uuid');
} catch (error) {
if (error instanceof NotFoundError) {
console.error(`Order not found: ${error.message}`);
} else if (error instanceof AuthenticationError) {
console.error(`Invalid API token: ${error.message}`);
} else if (error instanceof RateLimitError) {
console.error(`Rate limited, retry after ${error.retryAfter} seconds`);
} else if (error instanceof ValidationError) {
console.error(`Validation error: ${error.message}`);
console.error(`Details: ${JSON.stringify(error.details)}`);
} else if (error instanceof ServerError) {
console.error(`Server error: ${error.message}`);
} else if (error instanceof NetworkError) {
console.error(`Network error: ${error.message}`);
} else if (error instanceof TimeoutError) {
console.error(`Request timed out: ${error.message}`);
} else if (error instanceof DistruAPIError) {
console.error(`API error: ${error.message}`);
}
}Webhooks
Handle webhook events from Distru:
import { WebhookHandler } from 'distru-sdk';
// Create webhook handler
const handler = new WebhookHandler();
// Register event handlers
handler.on('ORDER', (event) => {
if (event.isCreated()) {
console.log(`New order: ${event.afterChanges.order_number}`);
} else if (event.isUpdated()) {
console.log(`Order updated: ${event.afterChanges.order_number}`);
console.log(`Changed fields: ${event.getChangedFields().join(', ')}`);
} else if (event.isDeleted()) {
console.log(`Order deleted: ${event.beforeChanges.order_number}`);
}
});
handler.on('INVOICE', (event) => {
console.log(`Invoice event: ${event.type} - ${event.action}`);
});
// Handle all events
handler.onAny((event) => {
console.log(`Event received: ${event.type}`);
});
// In your web framework (Express, Fastify, Koa, etc.)
import express from 'express';
const app = express();
app.post('/webhooks', express.json(), async (req, res) => {
try {
await handler.process(req.body);
res.json({ status: 'ok' });
} catch (error) {
res.status(400).json({ error: error.message });
}
});Data Consistency
The Distru API uses eventual consistency. Changes may take up to 1 second to propagate to GET endpoints.
Best Practices:
- Use the returned data from create/update operations immediately
- For critical operations, add a small delay before fetching updated data
- Implement idempotency using unique identifiers
// Create an order
const order = await client.orders.create({...});
// The response contains the created order immediately
console.log(order.order_number); // Available immediately
// If you need to fetch it again, wait briefly
await new Promise(resolve => setTimeout(resolve, 1500));
const fetchedOrder = await client.orders.get(order.id); // Now guaranteed to be availableRate Limiting
The SDK automatically handles rate limiting with exponential backoff:
- Automatic retry on 429 (rate limit) and 5xx errors
- Exponential backoff: 1s, 2s, 4s, 8s, 10s (max)
- Maximum 3 retries by default (configurable)
- Respects
Retry-Afterheaders
// Configure retry behavior
const client = new DistruClient({
apiToken: 'your_token',
maxRetries: 5, // Increase max retries
timeout: 60000 // Increase timeout (milliseconds)
});TypeScript Support
The SDK is written in TypeScript and provides full type definitions:
import { DistruClient, Product, CreateProductData } from 'distru-sdk';
const client = new DistruClient({ apiToken: 'your_token' });
// TypeScript will provide auto-completion and type checking
const productData: CreateProductData = {
name: 'Blue Dream 1g',
sku: 'BD-1G',
unitTypeId: 1,
inventoryTrackingMethod: 'BATCH',
salePrice: '15.00'
};
const product: Product = await client.products.create(productData);Requirements
- Node.js 14.0.0 or higher
- TypeScript 5.0.0 or higher (for TypeScript projects)
Development
Setup
# Clone repository
git clone https://github.com/DistruApp/distru-api-sdk.git
cd distru-api-sdk/typescript
# Install dependencies
npm installRunning Tests
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run with coverage
npm run test:coverageBuilding
# Build TypeScript to JavaScript
npm run build
# Clean build artifacts
npm run cleanCode Quality
# Type check
npx tsc --noEmit
# Run tests
npm testExamples
See the examples/ directory for complete examples:
- basic_usage.ts - Basic SDK usage
- pagination_example.ts - Pagination patterns
- error_handling.ts - Error handling strategies
- webhook_handler.ts - Webhook integration
Support
- Documentation: GitHub Repository
- Issues: GitHub Issues
- Email: [email protected]
License
MIT License - see LICENSE file for details.
Changelog
See CHANGELOG.md for version history and changes.
