Back to Blog

How to Generate QR Codes in Node.js (3 Methods)

QRStar Team5 min read

QR codes are everywhere: restaurant menus, payment systems, marketing campaigns. If you're building with Node.js, you have several options for generating them.

In this tutorial, you'll learn three approaches, from a simple npm package to a production-ready API.

Method 1: The qrcode Package

The qrcode package is the most popular choice for basic QR code generation. It's free, simple, and works well for most use cases.

Installation

npm install qrcode

Basic Usage

Generate a QR code as a data URL (useful for embedding in HTML):

const QRCode = require('qrcode');

async function generateQR(text) {
  try {
    const dataUrl = await QRCode.toDataURL(text);
    console.log(dataUrl);
    // Returns: data:image/png;base64,iVBORw0KGgo...
  } catch (err) {
    console.error(err);
  }
}

generateQR('https://example.com');

Save to File

const QRCode = require('qrcode');

async function saveQR(text, filepath) {
  try {
    await QRCode.toFile(filepath, text);
    console.log('QR code saved to', filepath);
  } catch (err) {
    console.error(err);
  }
}

saveQR('https://example.com', './qrcode.png');

Customization Options

The package supports colors and error correction:

const QRCode = require('qrcode');

const options = {
  errorCorrectionLevel: 'H', // L, M, Q, H (highest)
  type: 'png',
  width: 512,
  margin: 2,
  color: {
    dark: '#000000',
    light: '#ffffff'
  }
};

async function generateCustomQR(text) {
  const dataUrl = await QRCode.toDataURL(text, options);
  return dataUrl;
}

Pros and Cons

Pros:

  • Free and open source
  • No external dependencies
  • Works offline
  • Good for simple use cases

Cons:

  • No analytics or tracking
  • No URL shortening
  • You handle storage yourself
  • Can't update destination after generation

Method 2: Canvas-Based Approach

For more control over the output, you can use the canvas package with qrcode:

npm install qrcode canvas

This approach gives you pixel-level control:

const QRCode = require('qrcode');
const { createCanvas } = require('canvas');

async function generateWithCanvas(text) {
  const canvas = createCanvas(512, 512);

  await QRCode.toCanvas(canvas, text, {
    width: 512,
    margin: 2,
    color: {
      dark: '#1a1a2e',
      light: '#ffffff'
    }
  });

  // Now you can manipulate the canvas
  const ctx = canvas.getContext('2d');

  // Add a logo in the center (optional)
  // const logo = await loadImage('./logo.png');
  // ctx.drawImage(logo, 206, 206, 100, 100);

  return canvas.toBuffer('image/png');
}

When to Use Canvas

  • You need to add logos or watermarks
  • You want custom shapes or effects
  • You're generating images for specific platforms
  • You need precise control over output quality

Method 3: QRStar API (Production)

For production applications, an API offers advantages that libraries can't match:

  • Analytics: Track scans automatically
  • Dynamic URLs: Change destinations without regenerating
  • CDN delivery: Fast, global image delivery
  • No storage management: We handle it

Basic API Call

async function generateWithAPI(url) {
  const response = await fetch('https://api.qrstar.app/v1/generate', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': process.env.QRSTAR_API_KEY
    },
    body: JSON.stringify({
      data: url,
      size: 512,
      format: 'png'
    })
  });

  const { qr_code_url } = await response.json();
  return qr_code_url;
}

// Usage
const qrUrl = await generateWithAPI('https://example.com');
console.log(qrUrl); // https://cdn.qrstar.app/abc123.png

Dynamic QR Codes with Tracking

The real power is in dynamic codes:

async function generateDynamicQR(redirectUrl) {
  const response = await fetch('https://api.qrstar.app/v1/generate/dynamic', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': process.env.QRSTAR_API_KEY
    },
    body: JSON.stringify({
      redirect_url: redirectUrl,
      expires_at: '2025-12-31T23:59:59Z'
    })
  });

  const data = await response.json();

  return {
    qrCodeUrl: data.qr_code_url,    // The QR code image
    shortUrl: data.short_url,        // Trackable short URL
    analyticsId: data.analytics_id   // For fetching scan data
  };
}

Fetching Analytics

async function getAnalytics(analyticsId) {
  const response = await fetch(
    `https://api.qrstar.app/v1/analytics/${analyticsId}`,
    {
      headers: {
        'X-API-Key': process.env.QRSTAR_API_KEY
      }
    }
  );

  const data = await response.json();

  console.log(`Total scans: ${data.analytics.total_scans}`);
  console.log(`Devices:`, data.analytics.devices);
  console.log(`Scans by day:`, data.analytics.scans_by_day);
}

Which Method Should You Use?

Use the qrcode package when:

  • You're prototyping or learning
  • You don't need analytics
  • Your codes will never change
  • You want zero external dependencies

Use canvas when:

  • You need custom styling or logos
  • You're generating images for specific formats
  • You want pixel-level control

Use an API when:

  • You need scan analytics
  • URLs might change after printing
  • You're building a production app
  • You want CDN-delivered images
  • You don't want to manage storage

Production Considerations

Whichever method you choose, consider these for production:

Error Handling

async function generateQRSafe(text) {
  try {
    const result = await generateQR(text);
    return { success: true, data: result };
  } catch (error) {
    console.error('QR generation failed:', error.message);
    return { success: false, error: error.message };
  }
}

Caching

If you're generating the same codes repeatedly, cache them:

const cache = new Map();

async function generateWithCache(text) {
  if (cache.has(text)) {
    return cache.get(text);
  }

  const qrCode = await generateQR(text);
  cache.set(text, qrCode);

  return qrCode;
}

Rate Limiting

If using an API, respect rate limits:

const pLimit = require('p-limit');

const limit = pLimit(10); // 10 concurrent requests max

async function batchGenerate(urls) {
  const promises = urls.map(url =>
    limit(() => generateWithAPI(url))
  );

  return Promise.all(promises);
}

Summary

MethodBest ForCostAnalytics
qrcode packageLearning, prototypesFreeNo
Canvas approachCustom stylingFreeNo
QRStar APIProduction appsPay-per-useYes

Start with the qrcode package to learn the basics. When you need analytics or dynamic URLs, switch to an API.


Ready to add analytics to your QR codes? Get your free API key and start tracking scans today.

Ready to get started?

Create your free account and start generating QR codes in minutes.