How to Build a Web Barcode Reader with Python, FastAPI and HTML5

FastAPI is a modern, async-ready Python web framework for building APIs. It wraps low-level ASGI (Asynchronous Server Gateway Interface) interfaces and is built on top of Starlette and Pydantic. The combination of FastAPI and Uvicorn is ideal for creating high-performance web applications. In this article, you’ll learn how to build a web-based barcode reader using FastAPI, Uvicorn, Dynamsoft Barcode Reader SDK and HTML5.

Web Barcode Reader Demo

Prerequisites

Python Web Framework Comparison: FastAPI vs Flask vs Django

Before diving into the code, let’s briefly compare FastAPI with other popular Python web frameworks: Flask and Django.

Feature / Aspect FastAPI Flask Django
Performance (raw speed) High
Built on ASGI + Starlette
~2–3x faster than Flask
Medium
WSGI-based
Low to Medium
WSGI-based
Async Support Native async/await from the ground up ❌ Not native, needs workarounds ❌ Partial (via channels or async views)
Auto-generated Docs Swagger & ReDoc built-in ❌ None by default ❌ None by default
Deployment ASGI (e.g., Uvicorn, Hypercorn) WSGI (e.g., Gunicorn) WSGI (e.g., Gunicorn)

Project Structure

The project structure is as follows:

Project
├── templates
│   └── index.html
├── static
│   ├── script.js
│   └── style.css
└── backend.py

Explanation

  • templates: Contains HTML templates.
  • static: Contains JavaScript and CSS files.
  • backend.py: The main FastAPI application file that handles the backend logic.

The Features to Implement

The web barcode reader application is capable of:

  • Upload images containing barcodes.
  • Detect and decode barcodes using the Dynamsoft Capture Vision Bundle.
  • Display barcode details, including type and content.
  • Visualize barcode locations on the uploaded image.

Steps to Build the Web Barcode Reader

In the following sections, we will walk through the steps to build the web barcode reader application using FastAPI and HTML5.

Step 1: Install Dependencies

Install the required packages using pip:

fastapi uvicorn dynamsoft-capture-vision-bundle python-multipart

Step 2: Set Up the FastAPI Application

  1. Import required libraries:

     from fastapi import FastAPI, File, UploadFile
     from fastapi.responses import JSONResponse
     from fastapi.staticfiles import StaticFiles
     from fastapi.middleware.cors import CORSMiddleware
     from fastapi.templating import Jinja2Templates
     from fastapi.requests import Request
     from dynamsoft_capture_vision_bundle import *
    
  2. Create the FastAPI application and configure CORS and static files:

     app = FastAPI()
     app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"],)
     app.mount("/static", StaticFiles(directory="static"), name="static")
     templates = Jinja2Templates(directory="templates")
    
  3. Initialize the Dynamsoft Barcode Reader SDK:

     license_key = "YOUR_LICENSE_KEY"
     LicenseManager.init_license(license_key)
     cvr_instance = CaptureVisionRouter()
    
    
  4. Define the FastAPI routes:

     @app.get("/")
     async def upload_page(request: Request):
         return templates.TemplateResponse("index.html", {"request": request})
        
     @app.post("/scan")
     async def scan_barcode(file: UploadFile = File(...)):
         try:
             image_data = await file.read()
             result = cvr_instance.capture(image_data, EnumPresetTemplate.PT_READ_BARCODES.value)
        
             if result.get_error_code() != EnumErrorCode.EC_OK:
                 return JSONResponse({"success": False, "error": "No barcode detected"}, status_code=400)
        
             items = result.get_items()
             return_items = []
             for item in items:
                 location = item.get_location()
                 return_items.append({
                     "format": item.get_format_string(),
                     "text": item.get_text(),
                     "location": {
                         "x1": location.points[0].x, "y1": location.points[0].y,
                         "x2": location.points[1].x, "y2": location.points[1].y,
                         "x3": location.points[2].x, "y3": location.points[2].y,
                         "x4": location.points[3].x, "y4": location.points[3].y,
                     }
                 })
        
             return {"success": True, "count": len(items), "items": return_items}
         except Exception as e:
             return JSONResponse({"success": False, "error": str(e)}, status_code=500)
    

    Explanation

    • The / route serves the HTML page for uploading images.
    • The /scan route handles the image upload and barcode scanning. It reads the uploaded image, processes it using the Dynamsoft Barcode Reader SDK, and returns the barcode results in JSON format.
  5. Import uvicorn and run the FastAPI application in the main block:

     if __name__ == "__main__":
         import uvicorn
         uvicorn.run(app, host="0.0.0.0", port=8000)
    

Step 3: Create the Frontend

The index.html file includes a file input for uploading images and triggering the barcode scanning, an img element to preview the uploaded image, a canvas element for drawing the barcode locations, and a section to display the results.

<!DOCTYPE html>
<html>

<head>
    <title>Barcode Scanner</title>
    <link rel="stylesheet" href="/static/style.css">
</head>

<body>
    <div class="container">
        <h1>Upload Barcode Image</h1>
        <input type="file" id="fileInput" accept="image/*" capture="camera">
        <div id="imageview">
            <img id="preview">
            <canvas id="overlay"></canvas>
        </div>
        <div class="progress" id="progress">Processing...</div>
        <div id="result"></div>
    </div>


    <script src="/static/script.js"></script>
</body>

</html>

The style.css file contains the CSS styles for the web page, including styles for the container, image preview and canvas overlay.

.container {
    max-width: 600px;
    margin: 2rem auto;
    padding: 2rem;
}

#imageview {
    position: relative;
    display: block;
    width: 100%;
    max-width: 80%;
}

#imageview img {
    width: 100%;
    height: auto;
    object-fit: contain;
    display: block;
}

#overlay {
    position: absolute;
    top: 0;
    left: 0;
    pointer-events: none;
    width: 100%;
    height: 100%;
}

The script.js file contains the JavaScript code that handles image uploads, receives barcode results, and draws the barcode locations on the canvas.

const fileInput = document.getElementById('fileInput');
const preview = document.getElementById('preview');
const overlay = document.getElementById('overlay');
const progress = document.getElementById('progress');
const resultDiv = document.getElementById('result');

fileInput.addEventListener('change', async (e) => {
    const file = e.target.files[0];
    if (!file) return;

    const imageURL = URL.createObjectURL(file);
    preview.src = imageURL;

    preview.onload = async () => {
        overlay.width = preview.naturalWidth;
        overlay.height = preview.naturalHeight;

        const formData = new FormData();
        formData.append('file', file);

        progress.style.display = 'block';
        resultDiv.innerHTML = '';
        const ctx = overlay.getContext('2d');
        ctx.clearRect(0, 0, overlay.width, overlay.height);

        try {
            const response = await fetch('/scan', {
                method: 'POST',
                body: formData
            });

            const data = await response.json();
            if (data.success) {
                let resultHTML = `<h3>Found ${data.count} barcode(s)</h3><div class="results-container">`;

                data.items.forEach((item, index) => {
                    const loc = item.location;

                    const x1 = loc.x1;
                    const y1 = loc.y1;
                    const x2 = loc.x2;
                    const y2 = loc.y2;
                    const x3 = loc.x3;
                    const y3 = loc.y3;
                    const x4 = loc.x4;
                    const y4 = loc.y4;

                    ctx.beginPath();
                    ctx.moveTo(x1, y1);
                    ctx.lineTo(x2, y2);
                    ctx.lineTo(x3, y3);
                    ctx.lineTo(x4, y4);
                    ctx.closePath();
                    ctx.lineWidth = 2;
                    ctx.strokeStyle = 'red';
                    ctx.stroke();

                    ctx.font = '16px Arial';
                    ctx.fillStyle = 'blue';
                    ctx.fillText(item.text, x1 + 5, y1 - 5);

                    resultHTML += `
                        <div class="barcode-result">
                            <h4>Barcode #${index + 1}</h4>
                            <p><strong>Type:</strong> ${item.format}</p>
                            <p><strong>Content:</strong> ${item.text}</p>
                        </div>
                    `;
                });

                resultHTML += `</div>`;
                resultDiv.innerHTML = resultHTML;
            } else {
                resultDiv.innerHTML = `Error: ${data.error}`;
            }
        } catch (err) {
            resultDiv.innerHTML = 'Request failed';
        } finally {
            progress.style.display = 'none';
        }
    };
});

Step 4: Run the Web Barcode Reader Application

  1. In your terminal, navigate to the project directory and run:

     python backend.py
    
  2. Open your browser and visit http://localhost:8000.

    web barcode reader

Source Code

https://github.com/yushulx/python-barcode-qrcode-sdk/tree/main/examples/official/fastapi