How to Build a Node.js Barcode Reader for Command-Line and Web Applications

The official dynamsoft-capture-vision-for-node npm package wraps the Dynamsoft Capture Vision C++ SDK, so you can decode barcodes in Node.js at native speed. Prebuilt binaries ship for Windows (x86/x64), Linux (x64/arm64), and macOS (x64/Apple Silicon), so installation needs no compiler toolchain. In this guide, we build two working applications on top of it: a command-line barcode reader and a web app with an Express decoding API. On a sample image containing 19 barcodes of different symbologies, the SDK decoded every one of them in about 300 ms.

Node.js barcode reader for command-line and web applications

What you’ll build: Two Node.js barcode reading applications - a command-line tool that prints the format, text, confidence, and location of every barcode in an image or multi-page PDF, and a web app that decodes uploaded images through an Express API and draws bounding boxes around detected barcodes in the browser.

Key Takeaways

  • dynamsoft-capture-vision-for-node is the official Node.js wrapper for the Dynamsoft Capture Vision C++ SDK; it reads barcodes, recognizes label text, and captures documents from Node.js >= 16.
  • One call to CaptureVisionRouter.captureAsync() decodes all barcodes in a jpg, png, bmp, gif, pdf, or tiff file, with the work offloaded to a managed worker-thread pool.
  • Decoding 19 barcodes (Code 128, Code 93, EAN-13, QR Code, Data Matrix, PDF417, Aztec, MaxiCode, and more) from a single 1456x1484 image took about 300 ms in this sample.
  • captureMultiPagesAsync() decodes multi-page PDF and TIFF documents page by page.
  • An Express endpoint can pass uploaded file bytes straight to captureAsync() - no temporary files - and concurrent requests are queued across the worker pool automatically, so PM2 cluster mode is unnecessary for parallelism.
  • Preset templates trade speed for read rate (PT_READ_BARCODES_SPEED_FIRST vs PT_READ_BARCODES_READ_RATE_FIRST), and a custom JSON template can restrict decoding to specific barcode formats such as QR Code and Data Matrix only.

Common Developer Questions

Is there an official Node.js package for reading barcodes with Dynamsoft?

Yes. dynamsoft-capture-vision-for-node is the official Node.js wrapper for Dynamsoft Capture Vision. It supports Node.js >= 16 on Windows (x86, x64), Linux (x64 with glibc >= 2.18, arm64), and macOS (x64, arm64), and installs prebuilt native libraries through npm - no compiler toolchain is required.

How do I read barcodes from an image file in Node.js?

Install dynamsoft-capture-vision-for-node, initialize a license with LicenseManager.initLicense(), then call CaptureVisionRouter.captureAsync('./image.png', EnumPresetTemplate.PT_READ_BARCODES). The decoded results are returned in result.barcodeResultItems, each with formatString, text, confidence, and corner-point location.

Which image and document formats can the Node.js barcode SDK decode?

The capture APIs accept jpg, png, bmp, gif, pdf, and tiff files, either as a file path or as file bytes (Uint8Array). They also accept raw camera frames as DCVImageData (bytes plus width, height, stride, and pixel format). Multi-page PDF and TIFF files are handled by captureMultiPagesAsync().

How do I decode only specific barcode formats like QR Code and Data Matrix in Node.js?

Copy the SDK’s Templates\DBR-PresetTemplates.json, replace the BarcodeFormatIds array in the task-read-barcodes-read-rate task with the formats you want (for example BF_QR_CODE and BF_DATAMATRIX), and load the file with CaptureVisionRouter.initSettings('path/to/the/template/file') before capturing.

Do I need multiple Node.js processes to decode barcodes in parallel on a web server?

No. captureAsync() already runs decoding in a worker-thread pool sized by CaptureVisionRouter.maxWorkerCount (logical processors minus one by default). Requests made while all workers are busy are queued, and you can inspect the backlog through CaptureVisionRouter.waitQueueLength. A single process fully utilizes the CPU, so PM2 cluster mode adds no decoding throughput - though pm2 start server.js remains useful for automatic restarts.

Prerequisites

Step 1: Install the Dynamsoft Capture Vision Node.js SDK

Create a project folder and install the SDK together with its AI model package, which the read-rate-first barcode template uses:

npm i dynamsoft-capture-vision-for-node@3.2.5002 -E
npm i dynamsoft-capture-vision-for-node-model@3.2.5001 -E

The npm installer automatically pulls the prebuilt native library matching your OS and CPU architecture. To deploy on a different platform than your development machine, force-install the matching resource package, for example:

npm i dynamsoft-capture-vision-for-node-lib-linux-arm64@<version> -f -E

Step 2: Build a Command-Line Barcode Reader

Create index.js. The CLI takes an image path, decodes every barcode with captureAsync(), and prints the results. Multi-page PDF and TIFF files are decoded page by page with captureMultiPagesAsync():

const path = require('node:path');
const fs = require('node:fs');
const { LicenseManager, CaptureVisionRouter, EnumPresetTemplate } = require('dynamsoft-capture-vision-for-node');

// Replace with your own license key - see the Prerequisites section.
const LICENSE_KEY = process.env.DCV_LICENSE || 'LICENSE-KEY';
LicenseManager.initLicense(LICENSE_KEY);

function printResult(result) {
  const items = result.barcodeResultItems || [];
  if (items.length === 0) {
    console.log('No barcode found.');
    return;
  }
  console.log(`Decoded ${items.length} barcode(s):`);
  for (const item of items) {
    console.log('-'.repeat(48));
    console.log(`Format     : ${item.formatString}`);
    console.log(`Text       : ${item.text}`);
    console.log(`Confidence : ${item.confidence}`);
    const points = item.location.points.map(p => `(${p.x}, ${p.y})`).join(' ');
    console.log(`Location   : ${points}`);
  }
}

(async () => {
  const imagePath = path.resolve(process.argv[2]);
  const startTime = Date.now();
  const ext = path.extname(imagePath).toLowerCase();

  if (ext === '.pdf' || ext === '.tif' || ext === '.tiff') {
    const results = await CaptureVisionRouter.captureMultiPagesAsync(imagePath, EnumPresetTemplate.PT_READ_BARCODES);
    for (const result of results) {
      printResult(result);
    }
  } else {
    const result = await CaptureVisionRouter.captureAsync(imagePath, EnumPresetTemplate.PT_READ_BARCODES);
    printResult(result);
  }
  console.log(`\nElapsed time: ${Date.now() - startTime} ms`);

  // Terminate workers so the process can exit.
  await CaptureVisionRouter.terminateIdleWorkers();
})();

Run it against an image - for example the AllSupportedBarcodeTypes.png test image, which contains 19 barcodes of different symbologies:

node index.js ./AllSupportedBarcodeTypes.png

Node.js command-line barcode reader output

The CLI decoded all 19 barcodes in 299 ms on a Windows desktop, printing each barcode’s format, text, confidence score, and corner coordinates. The complete sample adds argument validation, a --template flag, and usage instructions - see the Source Code section.

Step 3: Pick a Preset Template for Your Workload

The second parameter of the capture APIs is a template name. Four barcode-related presets ship with the SDK:

Template Purpose
PT_READ_BARCODES Balanced default for general barcode reading
PT_READ_BARCODES_SPEED_FIRST Prioritizes decoding speed
PT_READ_BARCODES_READ_RATE_FIRST Prioritizes read rate on difficult images; requires the AI model package
PT_READ_SINGLE_BARCODE Optimized for images containing exactly one barcode

Pass a different preset per call:

const result = await CaptureVisionRouter.captureAsync('./photo.jpg', EnumPresetTemplate.PT_READ_BARCODES_SPEED_FIRST);

Step 4: Build a Web Barcode Reader with Express

For the web application, create an Express server that accepts image uploads and decodes them in memory. Uploaded bytes go directly to captureAsync(), so no files touch disk:

const express = require('express');
const multer = require('multer');
const { LicenseManager, CaptureVisionRouter, EnumPresetTemplate } = require('dynamsoft-capture-vision-for-node');

LicenseManager.initLicense(process.env.DCV_LICENSE || 'LICENSE-KEY');

const app = express();
const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 10 * 1024 * 1024 } });

app.use(express.static('public'));

app.post('/api/decode', upload.single('image'), async (req, res) => {
  const templateName = EnumPresetTemplate[req.body.template] || EnumPresetTemplate.PT_READ_BARCODES;
  const startTime = Date.now();
  // dataTransferType 'copy' keeps the uploaded buffer accessible after decoding.
  const result = await CaptureVisionRouter.captureAsync(req.file.buffer, {
    templateName,
    dataTransferType: 'copy'
  });
  const barcodes = (result.barcodeResultItems || []).map(item => ({
    format: item.formatString,
    text: item.text,
    confidence: item.confidence,
    location: item.location.points.map(p => ({ x: p.x, y: p.y }))
  }));
  res.json({ count: barcodes.length, elapsedTime: Date.now() - startTime, barcodes });
});

app.listen(2020, () => console.log('Barcode reader web app running at http://localhost:2020'));

The front-end page lets users drop an image onto a drop zone, posts it to /api/decode, then redraws the image on a canvas with green bounding boxes and format labels over every detected barcode. Because decoding runs in the SDK’s worker-thread pool, concurrent uploads never block the Express event loop.

Start the server and open the page:

npm start

The demo below shows the full flow: load the sample image, click Decode, and get all 19 barcodes back with bounding boxes in about 120 ms of server-side processing:

Node.js web barcode reader with bounding boxes

You can also call the API directly - handy for integration tests or headless pipelines:

curl -F "image=@barcode.png" -F "template=PT_READ_BARCODES" http://localhost:2020/api/decode
{
  "count": 1,
  "elapsedTime": 96,
  "queueLength": 0,
  "barcodes": [
    {
      "format": "QR_CODE",
      "text": "https://www.dynamsoft.com",
      "confidence": 100,
      "location": [{ "x": 120, "y": 48 }, { "x": 420, "y": 48 }, { "x": 420, "y": 348 }, { "x": 120, "y": 348 }]
    }
  ]
}

Step 5: Restrict Decoding to Specific Barcode Formats

When your workflow only expects certain symbologies, restricting formats improves both speed and accuracy. Copy the SDK’s Templates\DBR-PresetTemplates.json and edit the BarcodeFormatIds array of the task you use. For QR Code and Data Matrix only:

  "Name": "task-read-barcodes-read-rate",
  "ExpectedBarcodesCount": 0,
  "BarcodeFormatIds": [
-  "BF_DEFAULT"
+  "BF_QR_CODE",
+  "BF_DATAMATRIX"
  ],

Load the customized template once at startup:

CaptureVisionRouter.initSettings('path/to/the/template/file');

Common Issues & Edge Cases

  • The Node.js process does not exit after decoding: captureAsync() keeps its worker threads alive for reuse. Call await CaptureVisionRouter.terminateIdleWorkers() when your CLI or script is done - skip it in a long-running web server until shutdown.
  • License errors on startup: LicenseManager.initLicense() throws when the key is missing, expired, or invalid. Request a fresh trial license and prefer passing it through an environment variable (DCV_LICENSE) instead of hard-coding it.
  • PT_READ_BARCODES_READ_RATE_FIRST fails or underperforms without the model package: barcode tasks other than the speed-first preset use AI models. Make sure dynamsoft-capture-vision-for-node-model is installed at the version listed in the SDK’s peerDependencies.
  • Uploaded buffers become empty after decoding: by default, captureAsync() transfers byte buffers into the worker, which neuters them on the main thread. Pass dataTransferType: 'copy' when the buffer is needed later, as the web sample does before sending its response.
  • Slow first decode in a fresh process: the first capture pays for worker startup and model loading. For latency-sensitive services, issue one warm-up capture at startup before accepting traffic.

Conclusion

With dynamsoft-capture-vision-for-node, a production-grade Node.js barcode reader comes down to three steps: install two npm packages, initialize a license, and call captureAsync(). The command-line sample covers batch and document workflows, while the Express sample shows how the built-in worker pool turns a single Node.js process into a concurrent decoding service with a JSON API.

Source Code

Disclaimer:

The wrappers and sample code on Dynamsoft Codepool are community editions, shared as-is and not fully tested. Dynamsoft is happy to provide technical support for users exploring these solutions but makes no guarantees.