Node.js / TypeScript
Node.js / TypeScript Examples
Section titled “Node.js / TypeScript Examples”Complete integration examples using Node.js with TypeScript.
npm install node-fetch# or use the built-in fetch in Node.js 18+API Client Class
Section titled “API Client Class”A reusable client for the N’entropy EUDR Integration API:
const BASE_URL = 'https://backend.joinnentropy.com/api/v1/integration';
interface ApiResponse<T> { success: boolean; data: T; error?: string;}
class EUDRClient { private apiKey: string;
constructor(apiKey: string) { this.apiKey = apiKey; }
private async request<T>( method: string, path: string, body?: unknown ): Promise<T> { const res = await fetch(`${BASE_URL}${path}`, { method, headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', }, body: body ? JSON.stringify(body) : undefined, });
if (!res.ok) { const error = await res.json(); throw new Error(`API Error ${res.status}: ${error.error?.message}`); }
const json: ApiResponse<T> = await res.json(); return json.data; }
// Suppliers async upsertSuppliers(suppliers: SupplierInput[]) { return this.request('POST', '/suppliers', { suppliers }); }
async getSuppliers(params?: { page?: number; limit?: number }) { const query = new URLSearchParams(); if (params?.page) query.set('page', String(params.page)); if (params?.limit) query.set('limit', String(params.limit)); return this.request('GET', `/suppliers?${query}`); }
// Products async upsertProducts(products: ProductInput[]) { return this.request('POST', '/products', { products }); }
async getProducts(params?: { page?: number; limit?: number }) { const query = new URLSearchParams(); if (params?.page) query.set('page', String(params.page)); if (params?.limit) query.set('limit', String(params.limit)); return this.request('GET', `/products?${query}`); }
// Batches async upsertBatches(batches: BatchInput[]) { return this.request('POST', '/batches', { batches }); }
// Sync status async getSyncStatus(entityType?: string) { const query = entityType ? `?entityType=${entityType}` : ''; return this.request('GET', `/sync-status${query}`); }
// DDS status async getDDSStatus(batchExternalId: string) { return this.request('GET', `/dds/${batchExternalId}`); }}
// Type definitionsinterface SupplierInput { externalId: string; name: string; country?: string; address?: string; city?: string; email?: string; phone?: string; taxId?: string;}
interface ProductInput { externalId: string; name: string; hsCode?: string; description?: string; supplierExternalId?: string;}
interface BatchInput { externalId: string; productExternalId: string; quantity?: number; unit?: string; batchDate?: string;}Usage Examples
Section titled “Usage Examples”Sync Suppliers from Your System
Section titled “Sync Suppliers from Your System”const client = new EUDRClient('eudr_live_abc123...');
// Fetch suppliers from your databaseconst mySuppliers = await db.query('SELECT * FROM suppliers');
// Transform and syncconst result = await client.upsertSuppliers( mySuppliers.map(s => ({ externalId: s.id, name: s.company_name, country: s.country_code, address: s.street_address, city: s.city, email: s.contact_email, phone: s.phone, taxId: s.vat_number, })));
console.log(`Synced: ${result.created} created, ${result.updated} updated`);Sync Products with Supplier Association
Section titled “Sync Products with Supplier Association”const client = new EUDRClient('eudr_live_abc123...');
const result = await client.upsertProducts([ { externalId: 'PROD-001', name: 'Brazilian Eucalyptus Timber', hsCode: '4407.11', description: 'Sustainably sourced eucalyptus planks', supplierExternalId: 'SUP-100', // Links to previously synced supplier }, { externalId: 'PROD-002', name: 'Indonesian Palm Oil', hsCode: '1511.10', supplierExternalId: 'SUP-200', },]);
console.log(result);Create Batches
Section titled “Create Batches”const client = new EUDRClient('eudr_live_abc123...');
const result = await client.upsertBatches([ { externalId: 'BATCH-2024-001', productExternalId: 'PROD-001', quantity: 5000, unit: 'kg', batchDate: '2024-06-15', },]);Check DDS Status
Section titled “Check DDS Status”const client = new EUDRClient('eudr_live_abc123...');
const ddsStatus = await client.getDDSStatus('BATCH-2024-001');
console.log('DDS Status:', ddsStatus);// {// batch: { externalId: 'BATCH-2024-001', ... },// product: { name: 'Brazilian Eucalyptus Timber', ... },// supplier: { name: 'Amazon Timber Co.', ... },// ddsSubmissions: [{ status: 'submitted', submittedAt: '...' }]// }Webhook Verification
Section titled “Webhook Verification”import { createHmac, timingSafeEqual } from 'crypto';import express from 'express';
const app = express();app.use(express.raw({ type: 'application/json' }));
function verifyWebhook(payload: Buffer, signature: string, secret: string): boolean { const expected = createHmac('sha256', secret) .update(payload) .digest('hex');
const sig = signature.replace('sha256=', ''); return timingSafeEqual(Buffer.from(sig), Buffer.from(expected));}
app.post('/webhooks/eudr', (req, res) => { const signature = req.headers['x-webhook-signature'] as string; const secret = process.env.WEBHOOK_SECRET!;
if (!verifyWebhook(req.body, signature, secret)) { return res.status(401).json({ error: 'Invalid signature' }); }
const event = JSON.parse(req.body.toString());
switch (event.event) { case 'supplier.created': console.log('New supplier synced:', event.data); break; case 'sync.completed': console.log('Sync finished:', event.data); break; case 'dds.status_changed': console.log('DDS status update:', event.data); break; }
res.json({ received: true });});
app.listen(3000);Error Handling
Section titled “Error Handling”async function syncWithRetry(client: EUDRClient, suppliers: SupplierInput[], maxRetries = 3) { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await client.upsertSuppliers(suppliers); } catch (error: any) { if (error.message.includes('429')) { // Rate limited — wait and retry const waitMs = Math.pow(2, attempt) * 1000; console.log(`Rate limited, retrying in ${waitMs}ms...`); await new Promise(r => setTimeout(r, waitMs)); continue; } if (attempt === maxRetries) throw error; } }}