@jazzdev/dpd-local-sdk
v1.0.12
Published
TypeScript SDK for DPD Local shipping API integration - database-agnostic and framework-independent
Downloads
1,184
Maintainers
Readme
DPD Local SDK
TypeScript SDK for integrating DPD Local shipping services into your application. Database-agnostic, framework-independent, and production-ready.
Features
✅ Complete DPD Integration
- Create shipments and generate labels
- Address validation (UK postcodes)
- Real-time tracking
- Multi-parcel support
- Service selection (Next Day, By 12 PM)
✅ Database-Agnostic
- Works with any database (Firestore, MongoDB, PostgreSQL, etc.)
- Adapter pattern for easy integration
- TypeScript-first design
✅ Production-Ready
- Battle-tested in production
- Comprehensive error handling
- Automatic token management
- Request retry logic
- Detailed logging
✅ Developer-Friendly
- Full TypeScript support
- Comprehensive JSDoc comments
- Example implementations included
- Zero runtime dependencies (peer deps only)
Installation
npm install @jazzdev/dpd-local-sdk
# or
yarn add @jazzdev/dpd-local-sdk
# or
pnpm add @jazzdev/dpd-local-sdkQuick Start
1. Create Configuration
import { createDPDConfig } from '@jazzdev/dpd-local-sdk';
const config = createDPDConfig({
credentials: {
accountNumber: process.env.DPD_ACCOUNT_NUMBER!,
username: process.env.DPD_USERNAME!,
password: process.env.DPD_PASSWORD!,
},
business: {
name: 'Your Business Name',
collectionAddress: {
organisation: 'Your Company Ltd',
property: 'Unit 1',
street: '123 Main Street',
locality: '',
town: 'London',
county: 'Greater London',
postcode: 'SW1A 1AA',
countryCode: 'GB',
},
contactName: 'Your Name',
contactPhone: '+441234567890',
contactEmail: '[email protected]',
},
});2. Implement Adapters
The SDK requires two adapters to work with your database and storage:
Database Adapter
import { DatabaseAdapter } from '@jazzdev/dpd-local-sdk';
const myDatabaseAdapter: DatabaseAdapter = {
async getOrder(orderId: string) {
// Your implementation
return await db.orders.findById(orderId);
},
async updateOrder(orderId: string, data: any) {
// Your implementation
await db.orders.update(orderId, data);
},
async getSavedAddresses(userId: string) {
// Your implementation
return await db.addresses.find({ userId });
},
async getSavedAddress(addressId: string) {
// Your implementation
return await db.addresses.findById(addressId);
},
async createSavedAddress(address) {
// Your implementation
const result = await db.addresses.create(address);
return result.id;
},
async updateSavedAddress(addressId: string, data) {
// Your implementation
await db.addresses.update(addressId, data);
},
async deleteSavedAddress(addressId: string) {
// Your implementation
await db.addresses.delete(addressId);
},
async createDPDLog(log) {
// Your implementation
const result = await db.dpdLogs.create(log);
return result.id;
},
async getDPDLogs(filters) {
// Your implementation
return await db.dpdLogs.find(filters);
},
};Storage Adapter
import { StorageAdapter } from '@jazzdev/dpd-local-sdk';
const myStorageAdapter: StorageAdapter = {
async uploadLabel(labelData: string, fileName: string) {
// Upload to your storage (S3, Firebase Storage, etc.)
const url = await storage.upload(labelData, `labels/${fileName}`);
return url;
},
async getLabel(fileName: string) {
// Get label URL from storage
return await storage.getUrl(`labels/${fileName}`);
},
async deleteLabel(fileName: string) {
// Delete label from storage
await storage.delete(`labels/${fileName}`);
},
};3. Create a Shipment
import { createCompleteShipment } from '@jazzdev/dpd-local-sdk';
const result = await createCompleteShipment(
'ORDER123', // Your order ID
{
orderRef: 'ORDER123',
service: '12', // Next Day Delivery
deliveryAddress: {
id: 'addr_123',
userId: 'user_123',
isDefault: true,
property: '10',
street: 'Downing Street',
town: 'London',
postcode: 'SW1A 2AA',
countryCode: 'GB',
contactName: 'John Doe',
contactPhone: '+441234567890',
validated: true,
createdAt: new Date(),
updatedAt: new Date(),
},
totalWeight: 2.5, // kg
numberOfParcels: 1,
customerEmail: '[email protected]',
customerPhone: '+441234567890',
deliveryInstructions: 'Leave with neighbor if not home',
collectionDate: '2024-01-15',
},
config,
myDatabaseAdapter,
myStorageAdapter
);
if (result.success) {
console.log('Shipment created!');
console.log('Shipment ID:', result.shipmentId); // Save this for label regeneration
console.log('Consignment Number:', result.consignmentNumber); // 10-digit reference
console.log('Parcel Number:', result.parcelNumber); // 14-digit tracking number
console.log('Tracking URL:', result.trackingUrl);
console.log('Label URL:', result.labelUrl);
} else {
console.error('Failed:', result.error);
}Understanding DPD Identifiers
When you create a shipment with DPD, you receive three different identifiers:
shipmentId - DPD's internal shipment identifier (numeric)
- Required for label regeneration
- Save this in your database!
consignmentNumber - 10-digit reference number (e.g.,
6504286395)- Used for internal tracking
parcelNumber - 14-digit tracking number (e.g.,
15976504286395)- What customers use to track their delivery
- Used in the tracking URL
// When creating a shipment, save ALL identifiers:
const result = await createCompleteShipment(...);
if (result.success) {
await saveToDatabase({
shipmentId: result.shipmentId, // Save for label regeneration
consignmentNumber: result.consignmentNumber,
parcelNumber: result.parcelNumber, // Give to customer for tracking
trackingUrl: result.trackingUrl,
});
}Regenerating Labels
If you need to regenerate a label (e.g., printer jam), use the shipmentId:
import { generateLabel } from '@jazzdev/dpd-local-sdk';
const result = await generateLabel(credentials, {
shipmentId: '12345678', // Use the shipmentId, not consignment/parcel number
labelFormat: 'zpl' // or 'clp', 'html'
});Testing Label Generation
A test script is included to verify label generation:
# Using npm script
npm run test:label <shipmentId> [format]
# Or directly with tsx
npx tsx test-label-generation.ts <shipmentId> [format]
# Examples
npm run test:label 12345678
npm run test:label 12345678 html
npm run test:label 12345678 zplThe script will:
- Validate your DPD credentials
- Generate the label in the specified format
- Save it to a file (
label-<shipmentId>.<format>) - Display a preview of the label content
API Reference
Configuration
createDPDConfig(options)
Creates a complete DPD module configuration.
Parameters:
options.credentials- DPD API credentialsoptions.business- Your business informationoptions.pricing(optional) - Custom pricing configurationoptions.services(optional) - Service configurationoptions.labels(optional) - Label printer configurationoptions.notifications(optional) - Email/SMS configurationoptions.testMode(optional) - Enable test mode
Returns: DPDModuleConfig
Core Functions
createCompleteShipment(orderId, params, config, dbAdapter, storageAdapter)
Create a complete shipment including label generation and database update.
Parameters:
orderId- Your internal order IDparams- Shipment parameters (address, weight, service, etc.)config- DPD configurationdbAdapter- Database adapterstorageAdapter- Storage adapter
Returns: Promise<CreateShipmentResult>
validateDeliveryAddress(params, credentials)
Validate a UK delivery address using postcodes.io API.
Parameters:
params.postcode- UK postcodeparams.town- Town/city namecredentials- DPD credentials
Returns: Promise<ValidateAddressResult>
testDPDConnection(credentials)
Test connection to DPD API.
Parameters:
credentials- DPD credentials
Returns: Promise<{ success: boolean; message: string }>
Utility Functions
calculateDeliveryFee(subtotal, service, config)
Calculate customer-facing delivery fee.
calculateDPDCost(weight, service, config)
Calculate DPD shipping cost (what DPD charges you).
getNextCollectionDate()
Get next available collection date (excludes Sundays).
getEstimatedDeliveryDate(service, collectionDate?)
Calculate estimated delivery date.
getTrackingUrl(parcelNumber)
Generate DPD tracking URL.
Integration Examples
Next.js App Router
// app/api/shipping/create/route.ts
import { createCompleteShipment } from '@jazzdev/dpd-local-sdk';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(req: NextRequest) {
const { orderId, deliveryAddress, weight } = await req.json();
const result = await createCompleteShipment(
orderId,
{
orderRef: orderId,
service: '12',
deliveryAddress,
totalWeight: weight,
numberOfParcels: 1,
customerEmail: deliveryAddress.contactEmail,
collectionDate: new Date().toISOString().split('T')[0],
},
dpdConfig,
databaseAdapter,
storageAdapter
);
return NextResponse.json(result);
}Express.js
import express from 'express';
import { createCompleteShipment } from '@jazzdev/dpd-local-sdk';
const app = express();
app.post('/api/shipping/create', async (req, res) => {
const { orderId, deliveryAddress, weight } = req.body;
const result = await createCompleteShipment(
orderId,
{
orderRef: orderId,
service: '12',
deliveryAddress,
totalWeight: weight,
numberOfParcels: 1,
customerEmail: deliveryAddress.contactEmail,
collectionDate: new Date().toISOString().split('T')[0],
},
dpdConfig,
databaseAdapter,
storageAdapter
);
res.json(result);
});Environment Variables
# DPD Credentials (Required)
DPD_ACCOUNT_NUMBER=your_account_number
DPD_USERNAME=your_username
DPD_PASSWORD=your_password
# Encryption (Required in production)
DPD_ENCRYPTION_KEY=your_32_byte_hex_key
# Optional
NODE_ENV=productionGenerating Encryption Key
import { generateEncryptionKey } from '@jazzdev/dpd-local-sdk';
const key = generateEncryptionKey();
console.log('DPD_ENCRYPTION_KEY=' + key);Adapter Examples
Complete adapter examples are available in the examples/ directory:
examples/basic-usage.ts- Complete workflow exampleexamples/regenerate-label.ts- Label regeneration exampleexamples/firestore-adapter.ts- Firestore database adapterexamples/firebase-storage-adapter.ts- Firebase Storage adapter
For other databases (MongoDB, PostgreSQL, etc.), implement the DatabaseAdapter interface following the Firestore example as a reference. The adapter pattern is database-agnostic by design.
Error Handling
const result = await createCompleteShipment(...);
if (!result.success) {
console.error("Error Code:", result.errorCode);
console.error("Error Message:", result.error);
// Handle specific errors
switch (result.errorCode) {
case "AUTH_FAILED":
// Invalid credentials
break;
case "INVALID_ADDRESS":
// Address validation failed
break;
case "NETWORK_ERROR":
// Connection issues
break;
default:
// Generic error
}
}TypeScript Support
This SDK is written in TypeScript and provides complete type definitions:
import type {
DPDModuleConfig,
CreateShipmentParams,
CreateShipmentResult,
SavedAddress,
ShippingData,
DatabaseAdapter,
StorageAdapter,
} from '@jazzdev/dpd-local-sdk';Contributing
Contributions are welcome! Please read CONTRIBUTING.md for details.
License
MIT © Taiow Babarinde
Support
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
Changelog
See CHANGELOG.md for release history.
