Store templates

Templates live in your database; labelviewer stores nothing. The Editor gives your code everything a row needs on every save (saveHandler or the save event, see Embed the editor), and load() takes the same fields back.

Schema#

A table like this covers everything the editor gives you:

CREATE TABLE label_template (
    id          BIGINT PRIMARY KEY,           -- or a UUID
    name        VARCHAR(200)  NOT NULL,
    zpl         TEXT          NOT NULL,       -- the template, with {{placeholders}}; up to 1 MiB
    sample_data JSON          NULL,           -- example record(s) for the designer's preview (JSONB / NVARCHAR(MAX))
    dpmm        SMALLINT      NOT NULL DEFAULT 8,      -- 6, 8, 12 or 24 (152, 203, 300, 600 dpi)
    width_in    DECIMAL(5,2)  NOT NULL DEFAULT 4,      -- label size in inches
    height_in   DECIMAL(5,2)  NOT NULL DEFAULT 6,
    version     INTEGER       NOT NULL DEFAULT 1,      -- bumped on every save
    updated_at  TIMESTAMP     NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ColumnFrom the editor's saveInto load()
zplvaluezpl
sample_datasampleData (undefined when there is none: store NULL)sampleData
dpmmsettings.dpmmdpmm
width_in, height_insettings.widthIn, settings.heightInwidth, height
nameyour own form (the editor reports fileName)fileName (optional)

Size and density belong to the template. The same ZPL prints differently at 203 and 300 dpi; store dpmm and the size with it, pass them to load(), and use them again when you print or render it.

Sample data is for designing. Keep it small and free of real customer data: it is shown to everyone who opens the template. Production data comes from your orders at print time.

Concurrent edits: optimistic locking#

Send the version you loaded with every save and update with

UPDATE label_template
SET zpl = ?, sample_data = ?, dpmm = ?, width_in = ?, height_in = ?, version = version + 1, updated_at = CURRENT_TIMESTAMP
WHERE id = ? AND version = ?

Zero rows updated means someone else saved first: answer 409, and let your saveHandler throw. The editor then shows "Save failed: <your message>" and keeps the user's changes, so nothing is lost.

Versioning#

If printed labels must be reproducible (audits, reprints, customer disputes), keep every saved version:

CREATE TABLE label_template_version (
    template_id BIGINT        NOT NULL REFERENCES label_template (id),
    version     INTEGER       NOT NULL,
    zpl         TEXT          NOT NULL,
    dpmm        SMALLINT      NOT NULL,
    width_in    DECIMAL(5,2)  NOT NULL,
    height_in   DECIMAL(5,2)  NOT NULL,
    saved_at    TIMESTAMP     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    saved_by    VARCHAR(200)  NULL,
    PRIMARY KEY (template_id, version)
);
  • Insert a row in the same transaction as each successful update.
  • Record the template_id and version a print job used, so a reprint uses the same template even after later edits.
  • To restore an old version, load() it into the editor and let the user save it as the next version.

Variables are a contract#

The paths a template uses ({{order.id}}, {{shipTo.name|John Doe}}) must exist in the data your backend sends at print time, or have a default. The editor's Variables tab lists them. If several teams own the data, document the fields each template may use, and check new versions with a merge against a real record before you switch production to them (Print from your backend).

Backups#

Your database is the only place templates exist: include these tables in its backups. labelviewer itself has nothing to back up (Install › Backups).