Skip to content

Receiving events

Batches arrive as a POST with a text/plain body containing {"events": [...]}. Read the body as text, parse it as JSON, validate it, and store it. Reply 204 No Content: the browser doesn’t read the response.

import express from 'express';
const app = express();
app.post('/api/ux', express.text({ type: '*/*', limit: '100kb' }), async (req, res) => {
try {
const { events } = JSON.parse(req.body);
if (!Array.isArray(events)) return res.sendStatus(400);
await db.insertMany('ux_events', events.slice(0, 100));
res.sendStatus(204);
} catch {
res.sendStatus(400);
}
});

Because the body is text/plain and beacons send no custom headers, a cross-origin endpoint needs no CORS preflight. The browser still sends the request; it just can’t read the response, and pagepal doesn’t need to.

One row per event works well. Suggested columns:

ColumnType
typetext, indexed
tstimestamp
pagetext, indexed
viewtext
dataJSON (everything else)

Then feed rows back to summarize() or query them directly. For example, the top dead clicks this week:

select data->>'element' as element, page, count(*) as clicks
from ux_events
where type = 'dead_click' and ts > now() - interval '7 days'
group by 1, 2
order by clicks desc
limit 10;

The pal in the corner is pagepal itself.