JavaScript Examples

Frontend and Node.js integration

Setup

These examples cover both browser (fetch API) and Node.js (axios) environments.

Browser Setup

No setup required - modern browsers support the fetch API natively.

Node.js Setup

npm install axios

⚠️ Security Warning

Never expose your API key in frontend JavaScript! Use a backend proxy to make API calls from the browser.

1. Simple GET Request (Browser)

Fetch account information using the native fetch API:

// IMPORTANT: This should be called from your backend, not directly from browser
const apiKey = 'c2p_live_your_api_key_here';
const baseUrl = 'https://app1.connect2print.com/api/v1';

async function getAccount() {
    try {
        const response = await fetch(`${baseUrl}/account`, {
            method: 'GET',
            headers: {
                'Authorization': `Bearer ${apiKey}`,
                'Accept': 'application/json'
            }
        });

        if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }

        const data = await response.json();

        if (data.success) {
            console.log('Account:', data.data.company_name);
            console.log('Email:', data.data.email);
            return data.data;
        }

    } catch (error) {
        console.error('Error fetching account:', error);
        throw error;
    }
}

// Usage
getAccount()
    .then(account => console.log('Success:', account))
    .catch(error => console.error('Failed:', error));

2. List Orders with Pagination (Node.js)

Fetch all orders using axios with pagination:

const axios = require('axios');

const apiKey = 'c2p_live_your_api_key_here';
const baseUrl = 'https://app1.connect2print.com/api/v1';

// Create axios instance with default config
const api = axios.create({
    baseURL: baseUrl,
    headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Accept': 'application/json'
    }
});

async function fetchAllOrders() {
    let page = 1;
    const perPage = 50;
    const allOrders = [];
    let hasMore = true;

    while (hasMore) {
        try {
            const response = await api.get('/orders', {
                params: { page, per_page: perPage }
            });

            if (response.data.success) {
                allOrders.push(...response.data.data);
                hasMore = response.data.pagination.has_more;
                page++;

                console.log(`Fetched page ${page - 1}, total: ${allOrders.length}`);

                // Rate limit protection
                if (hasMore) {
                    await new Promise(resolve => setTimeout(resolve, 100));
                }
            } else {
                throw new Error(response.data.error.message);
            }

        } catch (error) {
            if (error.response) {
                console.error(`HTTP ${error.response.status}:`, error.response.data);
            } else {
                console.error('Error:', error.message);
            }
            throw error;
        }
    }

    return allOrders;
}

// Usage
fetchAllOrders()
    .then(orders => console.log(`Total orders: ${orders.length}`))
    .catch(error => console.error('Failed:', error));

3. Create a Customer (Node.js)

Create a new customer with validation:

const axios = require('axios');

const apiKey = 'c2p_live_your_api_key_here';
const baseUrl = 'https://app1.connect2print.com/api/v1';

async function createCustomer(customerData) {
    try {
        const response = await axios.post(`${baseUrl}/customers`, customerData, {
            headers: {
                'Authorization': `Bearer ${apiKey}`,
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            }
        });

        if (response.status === 201) {
            console.log('Customer created successfully!');
            console.log('Customer ID:', response.data.data.id);
            return response.data.data;
        }

    } catch (error) {
        if (error.response) {
            const status = error.response.status;
            const data = error.response.data;

            if (status === 400) {
                console.error('Validation errors:');
                if (data.error.details) {
                    Object.entries(data.error.details).forEach(([field, errors]) => {
                        console.error(`  ${field}:`, errors.join(', '));
                    });
                }
            } else {
                console.error(`HTTP ${status}:`, data.error.message);
            }
        } else {
            console.error('Error:', error.message);
        }
        throw error;
    }
}

// Usage
const newCustomer = {
    email: 'john.doe@example.com',
    company_name: 'Acme Corporation',
    contact_name: 'John Doe',
    phone: '+45 12 34 56 78',
    billing_address: {
        street_address: 'Main Street 123',
        city: 'Copenhagen',
        postal_code: '1000',
        country: 'DK'
    }
};

createCustomer(newCustomer)
    .then(customer => console.log('Created:', customer))
    .catch(error => console.error('Failed:', error));

4. Update an Order via Backend Proxy

Safe pattern for browser: call your backend, which calls the API:

Frontend (Browser)

// Frontend code - calls YOUR backend API
async function updateOrder(orderId, updates) {
    try {
        const response = await fetch(`/api/orders/${orderId}`, {
            method: 'PATCH',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(updates)
        });

        if (!response.ok) {
            const error = await response.json();
            throw new Error(error.message || `HTTP ${response.status}`);
        }

        const data = await response.json();
        console.log('Order updated:', data);
        return data;

    } catch (error) {
        console.error('Update failed:', error);
        // Show error to user
        showErrorMessage(error.message);
        throw error;
    }
}

// Usage
updateOrder(123, {
    status: 'processing',
    internal_notes: 'Started printing'
})
.then(order => {
    console.log('Updated to status:', order.status);
    updateUI(order);
});

Backend Proxy (Node.js/Express)

const express = require('express');
const axios = require('axios');

const app = express();
app.use(express.json());

const apiKey = process.env.CONNECT2PRINT_API_KEY;
const baseUrl = 'https://app1.connect2print.com/api/v1';

// Proxy endpoint
app.patch('/api/orders/:id', async (req, res) => {
    try {
        const { id } = req.params;
        const updates = req.body;

        const response = await axios.patch(
            `${baseUrl}/orders/${id}`,
            updates,
            {
                headers: {
                    'Authorization': `Bearer ${apiKey}`,
                    'Content-Type': 'application/json'
                }
            }
        );

        res.json(response.data.data);

    } catch (error) {
        if (error.response) {
            res.status(error.response.status).json({
                message: error.response.data.error?.message || 'API Error'
            });
        } else {
            res.status(500).json({ message: 'Server error' });
        }
    }
});

app.listen(3000, () => {
    console.log('Proxy server running on port 3000');
});

5. Upload a File (Node.js)

Upload a production file using form-data:

const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');

const apiKey = 'c2p_live_your_api_key_here';
const baseUrl = 'https://app1.connect2print.com/api/v1';

async function uploadFile(filePath, orderId) {
    try {
        // Create form data
        const form = new FormData();
        form.append('file', fs.createReadStream(filePath));
        form.append('related_type', 'order');
        form.append('related_id', orderId);

        // Upload
        const response = await axios.post(
            `${baseUrl}/files/upload`,
            form,
            {
                headers: {
                    'Authorization': `Bearer ${apiKey}`,
                    ...form.getHeaders()
                },
                maxContentLength: Infinity,
                maxBodyLength: Infinity
            }
        );

        if (response.status === 201) {
            console.log('File uploaded successfully!');
            console.log('File ID:', response.data.data.id);
            console.log('Filename:', response.data.data.filename);
            console.log('Size:', Math.round(response.data.data.file_size / 1024), 'KB');
            return response.data.data;
        }

    } catch (error) {
        if (error.response) {
            console.error(`HTTP ${error.response.status}:`, error.response.data);
        } else {
            console.error('Error:', error.message);
        }
        throw error;
    }
}

// Usage
uploadFile('./documents/brochure.pdf', 123)
    .then(file => console.log('Uploaded:', file))
    .catch(error => console.error('Failed:', error));

6. Reusable API Client Class

Create a comprehensive API client for Node.js:

const axios = require('axios');

class Connect2PrintAPI {
    constructor(apiKey, options = {}) {
        this.apiKey = apiKey;
        this.baseUrl = options.baseUrl || 'https://app1.connect2print.com/api/v1';

        // Create axios instance
        this.client = axios.create({
            baseURL: this.baseUrl,
            headers: {
                'Authorization': `Bearer ${this.apiKey}`,
                'Accept': 'application/json'
            },
            timeout: options.timeout || 30000
        });

        // Add response interceptor for error handling
        this.client.interceptors.response.use(
            response => response,
            error => this.handleError(error)
        );
    }

    async handleError(error) {
        if (error.response) {
            const { status, data } = error.response;

            switch (status) {
                case 400:
                    throw new ValidationError(data.error.message, data.error.details);
                case 401:
                    throw new AuthenticationError('Invalid API key');
                case 403:
                    throw new AuthorizationError('Insufficient permissions');
                case 404:
                    throw new NotFoundError('Resource not found');
                case 429:
                    const retryAfter = data.retry_after || 60;
                    throw new RateLimitError(`Rate limit exceeded. Retry after ${retryAfter}s`);
                case 500:
                case 502:
                case 503:
                    throw new ServerError(`Server error (${status}). Please try again later`);
                default:
                    throw new APIError(data.error?.message || `HTTP ${status}`, status);
            }
        }

        throw new NetworkError(error.message);
    }

    // Orders
    async getOrders(params = {}) {
        const response = await this.client.get('/orders', { params });
        return response.data;
    }

    async getOrder(id) {
        const response = await this.client.get(`/orders/${id}`);
        return response.data.data;
    }

    async createOrder(orderData) {
        const response = await this.client.post('/orders', orderData);
        return response.data.data;
    }

    async updateOrder(id, updates) {
        const response = await this.client.patch(`/orders/${id}`, updates);
        return response.data.data;
    }

    // Customers
    async getCustomers(params = {}) {
        const response = await this.client.get('/customers', { params });
        return response.data;
    }

    async getCustomer(id) {
        const response = await this.client.get(`/customers/${id}`);
        return response.data.data;
    }

    async createCustomer(customerData) {
        const response = await this.client.post('/customers', customerData);
        return response.data.data;
    }

    // Pagination helper
    async *paginateAll(endpoint, params = {}) {
        let page = 1;
        let hasMore = true;

        while (hasMore) {
            const response = await this.client.get(endpoint, {
                params: { ...params, page, per_page: 100 }
            });

            for (const item of response.data.data) {
                yield item;
            }

            hasMore = response.data.pagination.has_more;
            page++;

            // Rate limit protection
            if (hasMore) {
                await new Promise(resolve => setTimeout(resolve, 100));
            }
        }
    }
}

// Custom error classes
class APIError extends Error {
    constructor(message, status) {
        super(message);
        this.name = 'APIError';
        this.status = status;
    }
}

class ValidationError extends APIError {
    constructor(message, details) {
        super(message, 400);
        this.name = 'ValidationError';
        this.details = details;
    }
}

class AuthenticationError extends APIError {
    constructor(message) {
        super(message, 401);
        this.name = 'AuthenticationError';
    }
}

class AuthorizationError extends APIError {
    constructor(message) {
        super(message, 403);
        this.name = 'AuthorizationError';
    }
}

class NotFoundError extends APIError {
    constructor(message) {
        super(message, 404);
        this.name = 'NotFoundError';
    }
}

class RateLimitError extends APIError {
    constructor(message) {
        super(message, 429);
        this.name = 'RateLimitError';
    }
}

class ServerError extends APIError {
    constructor(message) {
        super(message, 500);
        this.name = 'ServerError';
    }
}

class NetworkError extends Error {
    constructor(message) {
        super(message);
        this.name = 'NetworkError';
    }
}

// Usage example
async function example() {
    const api = new Connect2PrintAPI(process.env.CONNECT2PRINT_API_KEY);

    try {
        // Fetch a single order
        const order = await api.getOrder(123);
        console.log('Order:', order);

        // Create a customer
        const customer = await api.createCustomer({
            email: 'test@example.com',
            company_name: 'Test Company'
        });
        console.log('Customer ID:', customer.id);

        // Fetch all orders using pagination
        console.log('Fetching all orders...');
        for await (const order of api.paginateAll('/orders')) {
            console.log(`Order ${order.id}: ${order.total}`);
        }

    } catch (error) {
        if (error instanceof ValidationError) {
            console.error('Validation failed:', error.details);
        } else if (error instanceof RateLimitError) {
            console.error('Rate limited:', error.message);
        } else {
            console.error('Error:', error.message);
        }
    }
}

module.exports = Connect2PrintAPI;

7. Webhook Handler (Express)

Receive and validate webhook notifications in Node.js:

const express = require('express');
const crypto = require('crypto');

const app = express();

// IMPORTANT: Use raw body for signature verification
app.use(express.json({
    verify: (req, res, buf) => {
        req.rawBody = buf.toString('utf8');
    }
}));

const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

// Webhook endpoint
app.post('/webhooks/connect2print', (req, res) => {
    // Get signature from headers
    const signature = req.headers['x-webhook-signature'];

    if (!signature) {
        return res.status(401).json({ error: 'Missing signature' });
    }

    // Verify signature
    const expectedSignature = crypto
        .createHmac('sha256', WEBHOOK_SECRET)
        .update(req.rawBody)
        .digest('hex');

    if (signature !== expectedSignature) {
        return res.status(401).json({ error: 'Invalid signature' });
    }

    // Process webhook
    const webhook = req.body;

    console.log('Webhook received:', webhook.event);

    // Handle different events
    switch (webhook.event) {
        case 'order.created':
            handleOrderCreated(webhook.data);
            break;

        case 'order.completed':
            handleOrderCompleted(webhook.data);
            break;

        case 'invoice.paid':
            handleInvoicePaid(webhook.data);
            break;

        case 'shipment.delivered':
            handleShipmentDelivered(webhook.data);
            break;

        default:
            console.log('Unknown event:', webhook.event);
    }

    // Always return 200 OK
    res.json({ success: true });
});

// Event handlers
function handleOrderCreated(order) {
    console.log(`New order #${order.id} from ${order.customer.company_name}`);
    // Send notification, update database, etc.
}

function handleOrderCompleted(order) {
    console.log(`Order #${order.id} completed`);
    // Trigger fulfillment process
}

function handleInvoicePaid(invoice) {
    console.log(`Invoice #${invoice.id} paid`);
    // Update accounting system
}

function handleShipmentDelivered(shipment) {
    console.log(`Shipment #${shipment.id} delivered`);
    // Send delivery confirmation
}

app.listen(3000, () => {
    console.log('Webhook server running on port 3000');
});

Best Practices

1. Never expose API keys in frontend code

Always use a backend proxy server to make API calls. Store API keys in environment variables.

2. Use async/await for cleaner code

Modern JavaScript async/await syntax is much cleaner than promise chains.

3. Implement proper error handling

Create custom error classes for different error types to handle them appropriately.

4. Use axios interceptors

Centralize authentication and error handling using axios interceptors.

5. Implement retry logic for transient errors

Network errors and 5xx errors should be retried with exponential backoff.