⚡ Node.jsQR / Barcodes

QR Code Validation in Node.js

Validate and classify QR code content in Node.js — detect URLs, vCards, Wi-Fi credentials, email addresses, phone numbers, and SMS payloads. Handle safe redirect logic and batch scan multiple codes in parallel.

Also available in Python

1. QR code content types

A QR code is just an encoded string. The payload determines how it should be handled:

typePayload exampleAction
urlhttps://example.com/product/123Validate URL, safe redirect
vcardBEGIN:VCARD\nFN:Jane Doe\n…Parse and save contact
wifiWIFI:T:WPA;S:MyNetwork;P:pass;;Auto-connect to Wi-Fi
emailmailto:hello@example.comOpen email client
phonetel:+447911123456Initiate call
smssms:+447911123456?body=HelloOpen SMS app
textAny plain text stringDisplay text

2. Validate QR code content

Endpoint: GET /v0/qr-code?value=…

{
  "valid": true,
  "type": "url",
  "content": "https://example.com/product/12345",
  "url": "https://example.com/product/12345"
}
{
  "valid": true,
  "type": "wifi",
  "content": "WIFI:T:WPA;S:MyNetwork;P:secret;;",
  "ssid": "MyNetwork",
  "security": "WPA"
}
  1. Check valid — well-formed QR payload
  2. Switch on type to determine appropriate action
  3. Use content as the raw parsed value

3. Handling URL payloads safely

⚠️Never blindly redirect users to a URL extracted from a QR code. QR codes are a common phishing vector — a malicious code can encode a lookalike domain or a data URI. Always validate the URL before acting on it.
import { createClient } from '@isvalid-dev/sdk'
const iv = createClient({ apiKey: process.env.ISVALID_API_KEY! })

async function safeQrRedirect(scannedPayload: string): Promise<string> {
  const qr = await iv.qrCode(scannedPayload)

  if (!qr.valid || qr.type !== 'url') {
    throw new Error('QR code does not contain a URL')
  }

  const url = new URL(qr.url!)

  // Block non-HTTPS and known dangerous schemes
  if (url.protocol !== 'https:') {
    throw new Error(`Blocked: non-HTTPS URL (${url.protocol})`)
  }

  // Optionally validate the domain
  const domain = await iv.domain(url.hostname)
  if (!domain.valid) {
    throw new Error(`Blocked: invalid domain (${url.hostname})`)
  }

  return qr.url!
}

4. Batch scanning with Promise.all

When processing multiple QR codes from a document scan or inventory system, validate them concurrently.

import { createClient } from '@isvalid-dev/sdk'

const iv = createClient({ apiKey: process.env.ISVALID_API_KEY! })

async function processQrCode(rawContent: string) {
  const result = await iv.qrCode(rawContent)

  if (!result.valid) {
    throw new Error(`Invalid QR code content: ${rawContent}`)
  }

  switch (result.type) {
    case 'url':
      // Validate the embedded URL before redirecting
      if (!result.url?.startsWith('https://')) {
        throw new Error('QR code contains a non-HTTPS URL — refusing redirect')
      }
      return { action: 'redirect', target: result.url }

    case 'vcard':
      return { action: 'save_contact', data: result.content }

    case 'wifi':
      return { action: 'connect_wifi', data: result.content }

    case 'email':
    case 'phone':
    case 'sms':
      return { action: result.type, data: result.content }

    default:
      return { action: 'display_text', data: result.content }
  }
}

// Batch scan — validate multiple QR code payloads in parallel
async function batchScan(payloads: string[]) {
  const results = await Promise.allSettled(payloads.map(p => iv.qrCode(p)))
  return payloads.map((payload, i) => ({
    payload,
    result: results[i].status === 'fulfilled' ? results[i].value : null,
    error: results[i].status === 'rejected' ? results[i].reason?.message : null,
  }))
}

// Example
const action = await processQrCode('https://example.com/product/12345')
console.log(action)  // { action: 'redirect', target: 'https://example.com/product/12345' }

5. Edge cases

Data URI in URL payload

⚠️A QR code can encode data:text/html,<script>…</script>. Always block non-http/https schemes before processing.
const qr = await iv.qrCode(payload)
if (qr.type === 'url' && qr.url) {
  const url = new URL(qr.url)
  const ALLOWED_SCHEMES = ['https:', 'http:']
  if (!ALLOWED_SCHEMES.includes(url.protocol)) {
    throw new Error(`Scheme not allowed: ${url.protocol}`)
  }
}

Empty or whitespace payload

// QR decoders sometimes return empty string on failed scan
const payload = scannedString.trim()
if (!payload) throw new Error('Empty QR code payload — scan may have failed')

const result = await iv.qrCode(payload)

6. Summary checklist

Validate QR payload before acting on it
Switch on type to dispatch correct action
Block non-HTTPS URLs from QR redirects
Validate embedded domain for URL payloads
Reject data: and javascript: URI schemes
Handle empty payload from failed scans
Run batch scans with Promise.allSettled
Return 422 with reason on invalid payloads

See also

Ready to integrate?

Free tier — 1,000 requests/month. No credit card required.

Get your API key →