Express Installation Guide
Last researched 2026-08-26
This is the canonical Virent installation guide for Express applications.
Research Notes#
- Express middleware is registered with
app.use()and runs in registration order. - Middleware should call
next()unless it intentionally ends the request-response cycle. - Express has no fixed template system; Human Analytics belongs in the shared template or HTML partial used by the app.
Primary references:
- https://expressjs.com/en/5x/guide/writing-middleware/
Human Analytics#
Add the script to your shared template.
HTML
<!-- views/layout.ejs, views/partials/head.ejs, or your shared template --><head> <meta charset="utf-8" /> <script defer data-write-key="vha_pk_..." src="https://app.virent.app/js/script.js" ></script></head>Best practices:
- Add the script to the common layout used by all public pages.
- If Express serves a React/Vue bundle, edit the static app
index.htmlinstead. - Use the npm SDK only in browser assets for custom events.
- Update Helmet or reverse proxy CSP headers if present.
Verification:
- Render a page and view source for the Virent script.
- Open the page in a browser and check Network requests.
- Refresh Human Analytics status.
Troubleshooting:
- If only some pages track, identify additional layouts.
- If CSP blocks Virent, update Helmet or proxy headers.
- If custom event code runs on the server, move it into browser assets.
AI Analytics#
Set server environment values:
ENV
VIRENT_SITE_ID=site_...VIRENT_INGEST_SECRET=vha_sk_...VIRENT_INGEST_ENDPOINT=https://app.virent.app/v1/ingest/botRegister middleware before page routes.
TS
import type { NextFunction, Request, Response } from "express"const staticAssetPattern = /\.(?:png|jpg|jpeg|gif|svg|ico|webp|css|js|map|txt|xml)$/iconst aiCrawlerPattern = /GPTBot|OAI-SearchBot|ChatGPT-User|ClaudeBot|claudebot|Claude-Web|Claude-User|Claude-SearchBot|PerplexityBot|PerplexityBot\/|Perplexity-User|Perplexity-User\/|Google-CloudVertexBot|DeepSeekBot|DeepSeekSpider|CCBot|Applebot|Bytespider|meta-externalagent|meta-webindexer|meta-externalfetcher|Amazonbot|DuckAssistBot|PetalBot|YouBot/iexport function virentCrawlerAnalytics(req: Request, res: Response, next: NextFunction) { res.on("finish", () => { const userAgent = req.get("user-agent") ?? "" if (staticAssetPattern.test(req.path) || !aiCrawlerPattern.test(userAgent)) { return } void fetch(process.env.VIRENT_INGEST_ENDPOINT!, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": process.env.VIRENT_INGEST_SECRET!, }, body: JSON.stringify({ siteId: process.env.VIRENT_SITE_ID!, url: `${req.protocol}://${req.get("host")}${req.originalUrl}`, host: req.get("host"), path: req.path, method: req.method, referer: req.get("referer") ?? null, userAgent, statusCode: res.statusCode, requestId: crypto.randomUUID(), timestamp: new Date().toISOString(), }), }).catch((error) => { console.error("Virent crawler analytics failed", error) }) }) next()}app.use(virentCrawlerAnalytics)Best practices:
- Register before route handlers.
- Send after
finishso the final response status is available. - Skip static assets and health checks.
- Node 18+ includes
fetch; older runtimes need an HTTP client.
Verification:
SH
curl -A "GPTBot" https://your-domain.com/- Confirm Express logs the request and posts to Virent.
- Refresh AI Analytics status.
Troubleshooting:
- If no events arrive, confirm
app.use(virentCrawlerAnalytics)runs before routes. - If static files are counted, add extension exclusions.
- If
fetchis undefined, upgrade Node or use the app's existing HTTP client.