Frontend and Node.js integration
These examples cover both browser (fetch API) and Node.js (axios) environments.
No setup required - modern browsers support the fetch API natively.
npm install axios
Never expose your API key in frontend JavaScript! Use a backend proxy to make API calls from the 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));
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));
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));
Safe pattern for browser: call your backend, which calls the API:
// 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);
});
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');
});
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));
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;
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');
});
Always use a backend proxy server to make API calls. Store API keys in environment variables.
Modern JavaScript async/await syntax is much cleaner than promise chains.
Create custom error classes for different error types to handle them appropriately.
Centralize authentication and error handling using axios interceptors.
Network errors and 5xx errors should be retried with exponential backoff.