You’re going to build a small Node.js app that turns a SKU, tracking number, or asset ID into a scannable Code 128 or Code 39 barcode image, using one POST request to the Barcode Generator API. The full working project is on GitHub: apiverve/barcode-generator-node-tutorial — clone it if you’d rather read code than prose.
Generate a barcode with one API request
Here’s the whole thing in curl. Swap in your key and run it:
curl -X POST https://api.apiverve.com/v1/barcodegenerator \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{"data": "51001544700", "type": "code128", "displayValue": true}'
And what comes back (the signed URL is trimmed — it’s long):
{
"status": "ok",
"error": null,
"data": {
"imageName": "cc85680f-a1d0-4627-89ff-7d7e53663a2b.png",
"format": ".png",
"type": "CODE128",
"expires": 1766010012598,
"downloadURL": "https://storage.googleapis.com/apiverve/APIData/barcodegenerator/cc85680f-....png?GoogleAccessId=...&Expires=1766010012&Signature=..."
}
}
You don’t get image bytes. You get a PNG hosted at downloadURL, plus expires — a millisecond timestamp roughly 24 hours out. Remember that second field. It matters later.
Three request parameters do all the work:
| Parameter | Required | What it does |
|---|---|---|
data | Yes | The string to encode |
type | Yes | code128 or code39 |
displayValue | No | Print the value under the bars (defaults to true) |
There are also lineColor and backgroundColor parameters, but those need a paid plan. Everything in this tutorial works on the free plan, and honestly, black-on-white is what scanners like best anyway.
Step 1: Set up the project
No key yet? Get a free API key first. Then either clone the repo:
git clone https://github.com/apiverve/barcode-generator-node-tutorial.git
cd barcode-generator-node-tutorial
npm install
Or start from scratch. The only dependency is Express — Node 18+ ships fetch, so there’s no HTTP client to install:
mkdir barcode-generator && cd barcode-generator
npm init -y
npm install express
Step 2: Configure the server
Create server.js. Keep the API key on the server and read it from an environment variable — the browser should never see it.
const express = require('express');
const path = require('path');
const API_KEY = process.env.API_KEY || 'your-api-key-here';
const API_URL = 'https://api.apiverve.com/v1/barcodegenerator';
const PORT = process.env.PORT || 3000;
const app = express();
app.use(express.json());
app.use(express.static('public'));
The repo keeps a placeholder default so the server boots without a key, then refuses to call the API until you set one. Nicer than a cryptic 401 on your first click.
Step 3: Call the Barcode Generator API
This route is the core of the app. It takes data, type, and displayValue from the frontend, forwards them to APIVerve, and hands back just the image URL.
app.post('/api/generate', async (req, res) => {
const { data, type = 'code128', displayValue = true } = req.body;
if (!data) {
return res.status(400).json({ error: 'Data is required' });
}
if (API_KEY === 'your-api-key-here') {
return res.status(500).json({
error: 'API key not configured. Set API_KEY environment variable or edit server.js'
});
}
try {
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': API_KEY
},
body: JSON.stringify({ data, type, displayValue })
});
const result = await response.json();
if (result.status === 'ok') {
res.json({
success: true,
data,
type,
imageUrl: result.data.downloadURL
});
} else {
res.status(400).json({ error: result.error || 'Generation failed' });
}
} catch (err) {
console.error('API Error:', err);
res.status(500).json({ error: 'Failed to generate barcode' });
}
});
app.listen(PORT, () => {
console.log(`Barcode Generator running at http://localhost:${PORT}`);
});
A few things worth pointing out:
- Check
result.status, not just the HTTP code. Every APIVerve response uses the same envelope:statusis"ok"or"error", and on failuredataisnullanderrorholds the message. Branching onstatusmeans you never try to readresult.data.downloadURLoff a null. - Pass
result.errorthrough. When a request fails, the API’s own message is far more useful than “something went wrong.” The repo surfaces it straight to the UI. - Defaults live in two places. The route defaults
typetocode128because the API requirestype— skip it and the request fails.displayValuedefaults totrueon both sides.
More on the response envelope and status codes in the error handling docs.
Step 4: Wire up the frontend
The repo’s public/index.html is a single page with a text input, a type dropdown, and a checkbox. The part that matters is the fetch to your own server:
async function generate() {
const data = document.getElementById('data').value.trim();
const type = document.getElementById('type').value;
const displayValue = document.getElementById('displayValue').checked;
const response = await fetch('/api/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ data, type, displayValue })
});
const result = await response.json();
if (result.error) {
showError(result.error);
} else {
document.getElementById('barcode-image').src = result.imageUrl;
document.getElementById('download-link').href = result.imageUrl;
}
}
The image URL goes straight into an <img> tag and a download link. No base64 decoding, no canvas drawing.
Step 5: Run it
API_KEY=your-real-key npm start
(On Windows PowerShell: $env:API_KEY="your-real-key"; npm start.) Open http://localhost:3000, type SKU-12345678, and generate.
Handling Code 39’s character limits
Code 128 takes any ASCII, so it rarely fails on input. Code 39 is pickier: uppercase A-Z, digits, space, and - . $ / + %. Send it a lowercase SKU and generation fails — you’ll get status: "error" back instead of an image.
Don’t spend a request finding that out. Check before you call:
const CODE39_PATTERN = /^[A-Z0-9 \-.$/+%]+$/;
function validateForType(data, type) {
if (type === 'code39' && !CODE39_PATTERN.test(data)) {
return 'Code 39 only supports A-Z, 0-9, space, and - . $ / + %';
}
return null;
}
Drop that into the route right after the !data check and return a 400 when it gives you a message. You could uppercase the input automatically instead, but then the barcode doesn’t match what’s in your database — and that mismatch is exactly the kind of bug that surfaces in a warehouse, not in testing.
Save the image before the URL expires
This is the gotcha. downloadURL is a signed link that stops working around 24 hours after generation (that’s the expires field). Fine for a preview. Terrible for an inventory record you’ll reprint in six months.
If a barcode ends up on a label, in a PDF, or in your database, download the PNG and store it yourself:
const fs = require('fs/promises');
async function saveBarcode(sku) {
const response = await fetch('https://api.apiverve.com/v1/barcodegenerator', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-api-key': process.env.API_KEY },
body: JSON.stringify({ data: sku, type: 'code128', displayValue: true })
});
const result = await response.json();
if (result.status !== 'ok') throw new Error(result.error);
const image = await fetch(result.data.downloadURL);
await fs.writeFile(`${sku}.png`, Buffer.from(await image.arrayBuffer()));
}
async function main() {
for (const sku of ['SKU-1001', 'SKU-1002', 'SKU-1003']) {
await saveBarcode(sku);
}
}
main().catch(console.error);
The loop goes one at a time on purpose — for a few hundred labels that’s plenty fast and keeps you well clear of rate limits. Each call costs credits, so generate once per SKU and reuse the file rather than regenerating every time a label prints.
Code 128 or Code 39, and what breaks on real scanners
Default to Code 128. It encodes full ASCII and packs more data per inch, so the same SKU prints physically shorter than in Code 39. Shorter barcodes fit smaller labels and read more reliably at an angle.
Use Code 39 only when someone makes you. Some government and automotive supply chains still specify it. If there’s no spec forcing it, there’s no reason to pick it.
Keep displayValue on. Scanners fail. Labels get scuffed. When that happens, a human needs to read the number and type it in.
Test on the real printer and the real scanner. A barcode that scans off your monitor can fail on glossy label stock or in a dim warehouse aisle. Leave blank space (a quiet zone) on both sides of the bars — cropping the image tight to the edges is the fastest way to get a label that looks fine and never scans.
Need more than an ID? Linear barcodes are for short identifiers. If you want to encode a URL or structured data, use the QR Code Generator instead — the QR code API guide covers when that’s the better call. For printing full label sheets, pair saved barcode images with HTML to PDF.
Next steps
- Clone the complete project: github.com/apiverve/barcode-generator-node-tutorial
- Read the parameters and try requests on the Barcode Generator API page
- New to APIVerve? The quickstart covers keys and auth in a couple of minutes
The same key works for every API on the platform. Get a free API key and generate your first barcode.