takt

Node.js (serveur)

L'entrée @vskstudio/takt-core/server du cœur @vskstudio/takt-core (depuis la 0.9.0) envoie des pages vues et des événements depuis Node, serveur à serveur (S2S). Elle couvre ce que le navigateur ne voit pas : les visiteurs sans JavaScript, les robots, un webhook de paiement ou un job en file.

Le client applique les mêmes règles de payload que le SDK navigateur : nom d’événement, props, revenue et nettoyage d’URL passent par le même code. Aucune dépendance, il s’appuie sur le fetch natif de Node 18+.

pnpm add @vskstudio/takt-core
# ou : npm install @vskstudio/takt-core
# ou : yarn add @vskstudio/takt-core
# ou : bun add @vskstudio/takt-core
N'importe jamais @vskstudio/takt-core/server dans du code envoyé au navigateur : la clé d'API y serait exposée. Côté client, garde init() ou un wrapper de framework.

createServerTakt()

Crée le client une fois, au démarrage du serveur, puis réutilise-le :

import { createServerTakt } from '@vskstudio/takt-core/server'

export const takt = createServerTakt({
  domain: 'exemple.fr',
  apiKey: process.env.TAKT_API_KEY
})
OptionTypeDéfautRôle
domainstringrequisIdentifiant du site, identique au domaine du site lié à la clé d’API. Vide, createServerTakt() lève.
apiKeystringaucuneClé d’API du site portant la permission events:write, envoyée en Authorization: Bearer
endpointstringhttps://taktlytics.com/api/eventURL d’ingestion complète. L’emporte sur scriptOrigin.
scriptOriginstringhttps://taktlytics.comOrigine dont dériver l’endpoint (l’origine suivie de /api/event), par exemple ton proxy first-party
trackQuerybooleanfalseConserve la query string et le hash de url et referrer
queryParamsstring[]aucunAllowlist de paramètres de query à garder
scrubUrl(url: string) => stringaucuneNettoyage d’URL sur mesure, prioritaire sur trackQuery / queryParams
redactRoutesstring[]aucunMotifs de routes sensibles, mêmes règles que le SDK navigateur : un chemin qui correspond part sous la forme du motif (depuis 0.10.0, voir Masquer les routes)
strictbooleanfalseLève sur erreur réseau ou sur toute réponse autre que 202
fetchtypeof fetchfetch globalImplémentation de fetch à utiliser (tests, agent HTTP sur mesure)

endpoint et scriptOrigin se résolvent exactement comme dans createTakt() : endpoint est une URL complète, pas une origine. Le client PHP, lui, accepte aussi l’origine seule et y ajoute /api/event.

pageview() et event()

Les deux méthodes renvoient une Promise<void> :

interface ServerVisitor {
  ip?: string
  userAgent?: string
}

pageview(options?: {
  url?: string
  referrer?: string
  route?: string
  visitor?: ServerVisitor
}): Promise<void>

event(name: string, options?: {
  props?: Record<string, unknown>
  revenue?: { amount: string; currency: string }
  url?: string
  referrer?: string
  route?: string
  visitor?: ServerVisitor
}): Promise<void>

Une page vue rendue côté serveur, avec Express :

import express from 'express'
import { takt } from './takt'

const app = express()

app.get('/tarifs', async (req, res) => {
  await takt.pageview({
    url: `https://exemple.fr${req.originalUrl}`,
    referrer: req.get('referer'),
    visitor: { ip: req.ip, userAgent: req.get('user-agent') }
  })
  res.render('tarifs')
})

Un achat confirmé par un webhook de paiement, hors de toute requête visiteur :

await takt.event('Purchase', {
  props: { plan: 'pro' },
  revenue: { amount: '29.00', currency: 'EUR' },
  url: 'https://exemple.fr/merci'
})

Masquer les routes

Comme dans le navigateur, le chemin de url part en clair : /verify/abc123 transmet le jeton. Depuis @vskstudio/takt-core 0.10.0, deux leviers le remplacent par le motif de la route (voir Masquage de routes).

redactRoutes s’applique à tous les appels du client. Un chemin qui correspond à l’un des motifs part sous la forme du motif, les autres gardent leur chemin réel ; le referrer du même site suit la même règle :

export const takt = createServerTakt({
  domain: 'exemple.fr',
  apiKey: process.env.TAKT_API_KEY,
  redactRoutes: ['/verify/[token]', '/reset/:code']
})

L’option route de pageview() et event() remplace le chemin de cet appel par le gabarit fourni. Ton serveur connaît déjà la route qui a répondu, passe-la telle quelle :

app.get('/factures/:id', async (req, res) => {
  await takt.pageview({
    url: `https://exemple.fr${req.originalUrl}`,
    route: '/factures/:id',
    visitor: { ip: req.ip, userAgent: req.get('user-agent') }
  })
  res.render('facture')
})

L’URL envoyée devient https://exemple.fr/factures/:id. Avec route, un referrer du même site est réduit à l’origine, puisque son gabarit n’est pas connu. Sans route, redactRoutes s’applique.

Ce que le client fait pour toi

  • Authentification : apiKey part en Authorization: Bearer. La clé doit porter la permission events:write et être liée au site dont le domaine correspond exactement à domain, sinon l’ingest répond 401. Sans clé, l’envoi suit le même chemin qu’un navigateur, et il est ignoré en silence si le site exige une clé d’API.
  • Visiteur : visitor.ip part en X-Forwarded-For et visitor.userAgent en User-Agent. Les retours à la ligne (CR/LF) sont retirés des valeurs d’en-tête.
  • URL : sans url, l’événement est rattaché à l’accueil du site (https://exemple.fr/ pour domain: 'exemple.fr'), car l’ingest refuse un événement sans URL absolue. Par défaut, la query string et le hash de url et referrer sont retirés ; trackQuery, queryParams et scrubUrl ajustent ce comportement comme dans le navigateur.
  • Payload : les props sont converties en chaînes et plafonnées (30 clés, clés de 64 caractères, valeurs de 1024 caractères). Un revenue mal formé est abandonné avec un avertissement, l’événement part quand même.
  • Noms : un nom vide ou le nom réservé pageview passé à event() lève toujours, même sans strict. Utilise pageview() pour les pages vues.
  • Erreurs : par défaut, une erreur réseau ou une réponse autre que 202 est avalée, l’analytics ne doit jamais casser ton serveur. Avec strict: true, la promesse est rejetée (pratique en test ou dans un job qui doit réessayer).
L'attribution visiteur est dérivée côté serveur de l'IP et de l'User-Agent transmis. Une requête authentifiée par une clé `events:write` fait foi : l'ingest calcule le visiteur et le pays à partir de l'IP transmise (en-tête `X-Takt-Client-IP`, sinon première entrée de `X-Forwarded-For`), et non de l'IP de ton serveur applicatif. Sans `visitor.ip`, tous les événements d'une même instance sont attribués à ton serveur : transmets l'IP et l'User-Agent de la requête en cours.

Un 202 signifie « accepté », pas « enregistré » : l’ingest filtre encore les robots. Un User-Agent de robot d’IA transmis avec visitor.userAgent alimente le bloc Visibilité IA sans compter comme visite.

Source sur GitHub : github.com/taktlytics/takt-core · package sur npm : @vskstudio/takt-core.