TypeScript & Node.js Guide¶
PolyXML provides native compiled C/Rust performance directly inside the Node.js event loop via napi-rs. It avoids the CPU bottlenecks of JavaScript-based XML parsers (e.g. xml2js, fast-xml-parser) by executing schema validation and XML parsing in compiled Rust.
📦 Installation¶
PolyXML ships pre-built binaries across Linux, macOS, and Windows via standard npm optional dependencies.
1. Strongly-Typed TypeScript Schemas¶
Define your domain models and corresponding ModelSchema definitions:
import { deserialize, serialize, ModelSchema } from 'polyxml';
// 1. Define TypeScript interface
interface DeviceTelemetry {
deviceId: number;
hostname: string;
uptimeSeconds: number;
isOnline: boolean;
cpuLoad: number;
}
// 2. Define corresponding PolyXML ModelSchema
const telemetrySchema: ModelSchema = {
name: 'DeviceTelemetry',
fields: [
{ name: 'deviceId', xmlName: 'id', kind: 'attribute', scalarType: 'int' },
{ name: 'hostname', xmlName: 'host', kind: 'element', scalarType: 'string' },
{ name: 'uptimeSeconds', xmlName: 'uptime', kind: 'element', scalarType: 'int' },
{ name: 'isOnline', xmlName: 'online', kind: 'element', scalarType: 'bool' },
{ name: 'cpuLoad', xmlName: 'cpu', kind: 'element', scalarType: 'float' },
],
};
const xml = `
<DeviceTelemetry id="88">
<host>edge-gw-01</host>
<uptime>864000</uptime>
<online>true</online>
<cpu>0.42</cpu>
</DeviceTelemetry>
`;
// 3. Deserialize directly into typed JavaScript object
const telem = deserialize(xml, telemetrySchema) as unknown as DeviceTelemetry;
console.log(`Device ${telem.deviceId} (${telem.hostname}): Online=${telem.isOnline}, CPU=${telem.cpuLoad}`);
// 4. Serialize back to formatted XML bytes (Uint8Array)
const xmlBytes = serialize('DeviceTelemetry', telem, telemetrySchema, 2);
console.log(Buffer.from(xmlBytes).toString('utf-8'));
2. Parsing Binary Buffers (Uint8Array / Buffer)¶
PolyXML accepts both JavaScript string and raw Uint8Array / Buffer inputs without converting to UTF-16 strings first:
import * as fs from 'node:fs';
import { deserialize, ModelSchema } from 'polyxml';
const catalogSchema: ModelSchema = {
name: 'Product',
fields: [
{ name: 'sku', xmlName: 'sku', kind: 'attribute', scalarType: 'string' },
{ name: 'title', xmlName: 'title', kind: 'element', scalarType: 'string' },
{ name: 'price', xmlName: 'price', kind: 'element', scalarType: 'float' },
],
};
// Read XML file directly as a Buffer
const buffer: Buffer = fs.readFileSync('product.xml');
// Pass Buffer directly into Rust engine with zero UTF-16 string conversion
const product = deserialize(buffer, catalogSchema);
console.log('Parsed product from buffer:', product);
3. High-Throughput HTTP Service (Express / Fastify)¶
Integrate PolyXML into Express or Fastify request handlers to process high volumes of inbound XML webhooks:
import express, { Request, Response } from 'express';
import { deserialize, serialize, ModelSchema } from 'polyxml';
const orderSchema: ModelSchema = {
name: 'PurchaseOrder',
fields: [
{ name: 'orderId', xmlName: 'id', kind: 'attribute', scalarType: 'int' },
{ name: 'customer', xmlName: 'customer', kind: 'element', scalarType: 'string' },
{ name: 'amount', xmlName: 'amount', kind: 'element', scalarType: 'float' },
],
};
const app = express();
// Use express.raw to receive unparsed XML Buffers
app.post('/api/orders', express.raw({ type: 'application/xml' }), (req: Request, res: Response) => {
try {
const order = deserialize(req.body, orderSchema) as any;
console.log(`Processed Order #${order.orderId} for ${order.customer} ($${order.amount})`);
// Return XML acknowledgment
const ack = { status: 'ACCEPTED', orderId: order.orderId };
const ackSchema: ModelSchema = {
name: 'OrderAck',
fields: [
{ name: 'status', xmlName: 'status', kind: 'element', scalarType: 'string' },
{ name: 'orderId', xmlName: 'orderId', kind: 'element', scalarType: 'int' },
],
};
const ackXml = serialize('OrderAck', ack, ackSchema, 0);
res.setHeader('Content-Type', 'application/xml');
res.send(Buffer.from(ackXml));
} catch (err: any) {
res.status(400).json({ error: 'Malformed XML payload', details: err.message });
}
});
app.listen(3000, () => console.log('XML Server listening on http://localhost:3000'));
4. Error Handling & Validation¶
When parsing malformed XML or invalid scalar values (such as non-numeric characters in an integer element), PolyXML throws informative native errors:
import { deserialize, ModelSchema } from 'polyxml';
const schema: ModelSchema = {
name: 'Data',
fields: [
{ name: 'count', xmlName: 'count', kind: 'element', scalarType: 'int' },
],
};
try {
// Invalid integer scalar "not-a-number"
deserialize('<Data><count>not-a-number</count></Data>', schema);
} catch (err: any) {
console.error('Caught validation error:', err.message);
// Output: Caught validation error: Failed to parse scalar for field count
}
5. Performance Best Practices for Node.js¶
- Avoid Converting Buffers to Strings: If your XML arrives via HTTP or disk as a
Buffer, pass it directly todeserialize(buffer, schema). Convertingbuffer.toString('utf-8')forces V8 to allocate a UTF-16 string on the V8 heap unnecessarily. - Reuse
ModelSchemaObjects: Schema definitions should be instantiated once as top-level constants rather than recreated inside request handlers. - Prefer Compact Serialization for APIs: When sending XML over network APIs, omit the
indentargument (serialize(root, val, schema)) to minimize bandwidth and skip whitespace generation.