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

distru-sdk

v1.0.2

Published

Official TypeScript/JavaScript SDK for the Distru API - Cannabis supply chain management

Readme

Distru TypeScript/JavaScript SDK

Official TypeScript/JavaScript SDK for the Distru API - Cannabis supply chain management platform.

npm version License: MIT

Installation

npm install distru-sdk
# or
yarn add distru-sdk
# or
pnpm add distru-sdk

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

  1. Log into your Distru account
  2. Navigate to SettingsAPI Keys
  3. Click Create API Key
  4. Copy the generated token
  5. 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 available

Rate 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-After headers
// 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 install

Running Tests

# Run all tests
npm test

# Run tests in watch mode
npm run test:watch

# Run with coverage
npm run test:coverage

Building

# Build TypeScript to JavaScript
npm run build

# Clean build artifacts
npm run clean

Code Quality

# Type check
npx tsc --noEmit

# Run tests
npm test

Examples

See the examples/ directory for complete examples:

Support

License

MIT License - see LICENSE file for details.

Changelog

See CHANGELOG.md for version history and changes.