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 @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
}) | Option | Type | Défaut | Rôle |
|---|---|---|---|
domain | string | requis | Identifiant du site, identique au domaine du site lié à la clé d’API. Vide, createServerTakt() lève. |
apiKey | string | aucune | Clé d’API du site portant la permission events:write, envoyée en Authorization: Bearer |
endpoint | string | https://taktlytics.com/api/event | URL d’ingestion complète. L’emporte sur scriptOrigin. |
scriptOrigin | string | https://taktlytics.com | Origine dont dériver l’endpoint (l’origine suivie de /api/event), par exemple ton proxy first-party |
trackQuery | boolean | false | Conserve la query string et le hash de url et referrer |
queryParams | string[] | aucun | Allowlist de paramètres de query à garder |
scrubUrl | (url: string) => string | aucune | Nettoyage d’URL sur mesure, prioritaire sur trackQuery / queryParams |
redactRoutes | string[] | aucun | Motifs 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) |
strict | boolean | false | Lève sur erreur réseau ou sur toute réponse autre que 202 |
fetch | typeof fetch | fetch global | Implé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 :
apiKeypart enAuthorization: Bearer. La clé doit porter la permissionevents:writeet être liée au site dont le domaine correspond exactement àdomain, sinon l’ingest répond401. 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.ippart enX-Forwarded-Foretvisitor.userAgentenUser-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/pourdomain: 'exemple.fr'), car l’ingest refuse un événement sans URL absolue. Par défaut, la query string et le hash deurletreferrersont retirés ;trackQuery,queryParamsetscrubUrlajustent ce comportement comme dans le navigateur. - Payload : les
propssont converties en chaînes et plafonnées (30 clés, clés de 64 caractères, valeurs de 1024 caractères). Unrevenuemal formé est abandonné avec un avertissement, l’événement part quand même. - Noms : un nom vide ou le nom réservé
pageviewpassé àevent()lève toujours, même sansstrict. Utilisepageview()pour les pages vues. - Erreurs : par défaut, une erreur réseau ou une réponse autre que
202est avalée, l’analytics ne doit jamais casser ton serveur. Avecstrict: true, la promesse est rejetée (pratique en test ou dans un job qui doit réessayer).
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.
@vskstudio/takt-core.