Customer Example: E-commerce Integration

See how a Nordic e-commerce/affiliate site integrated Besökskollen and got data-driven "Popular Products" in 30 minutes.

Results

  • Integration took ~30 minutes
  • No external dependencies - just Next.js + Besökskollen
  • Fallback logic based on confidence levels
  • ISR caching for best performance

Background

A Nordic e-commerce site in electronics accessories wanted to show "Popular Products" based on actual clicks, not manually selected products.

Previously, they used a static list that was manually updated every other week. With Besökskollen, they now get real-time data on which products visitors actually click on.

Step-by-step

StepTimeWhat
12 minRegistered event properties in the dashboard
21 minCreated API key
31 minSaved credentials in .env.local
410 minImplemented va() tracking on affiliate links
515 minBuilt API client with fallback
65 minIntegrated with product lists

1. Register event properties

First, they registered which properties they wanted to save. In Settings → Event Properties they clicked "Add all" to get the most common ones:

  • product_id - Unique product ID
  • category_slug - Product category
  • partner_slug - Affiliate partner
  • price - Price at click time

2-3. Create API key and save credentials

In Settings → API Keys they created a key and saved it in .env.local:

# .env.local
BESOKSKOLLEN_API_KEY=bk_xxxxxxxxxxxx
BESOKSKOLLEN_SITE_ID=din-sajt-id

4. Implement va() tracking

They added tracking to their affiliate link component. When a user clicks, an event is sent with relevant properties:

// components/AffiliateLinkButton.tsx
'use client';

interface Props {
  productId: string;
  categorySlug: string;
  partnerSlug: string;
  price: number;
  href: string;
  children: React.ReactNode;
}

export function AffiliateLinkButton({
  productId, categorySlug, partnerSlug, price, href, children
}: Props) {
  const handleClick = () => {
    // Send event to analytics
    if (typeof window !== 'undefined' && window.va) {
      window.va('event', 'affiliate_click', {
        product_id: productId,
        category_slug: categorySlug,
        partner_slug: partnerSlug,
        price: price
      });
    }
  };

  return (
    <a
      href={href}
      onClick={handleClick}
      target="_blank"
      rel="noopener noreferrer"
      className="btn-primary"
    >
      {children}
    </a>
  );
}

Tip: Add TypeScript types for window.va in a types/analytics.d.ts file.

5. Build API client with fallback

The key to a robust integration is handling the case when there's too little data. They built a client that automatically falls back to an alternative data source:

// lib/besokskollen.ts
const API_KEY = process.env.BESOKSKOLLEN_API_KEY;
const SITE_ID = process.env.BESOKSKOLLEN_SITE_ID;
const BASE_URL = 'https://besokskollen.se/api/v1';

interface PopularProductsResponse {
  data: Array<{
    product_id: string;
    product_slug?: string;
    clicks: number;
  }>;
  meta: {
    confidence: 'high' | 'medium' | 'low';
    total_clicks: number;
  };
}

export async function getPopularProductIds(limit = 10) {
  try {
    const res = await fetch(
      `${BASE_URL}/popular/products?site_id=${SITE_ID}&days=30&limit=${limit}`,
      {
        headers: { 'Authorization': `Bearer ${API_KEY}` },
        next: { revalidate: 1800 } // 30 min ISR
      }
    );

    if (!res.ok) throw new Error('API error');

    const data: PopularProductsResponse = await res.json();

    return {
      ids: data.data.map(p => p.product_id),
      confidence: data.meta.confidence
    };
  } catch (error) {
    console.error('Besökskollen error:', error);
    return { ids: [], confidence: 'low' as const };
  }
}

6. Integrate with product lists

Finally, they used the API client in their product list with smart fallback:

// lib/queries.ts
import { getPopularProductIds } from './besokskollen';

export async function getPopularProducts(limit = 12) {
  // Try to fetch from analytics
  const { ids, confidence } = await getPopularProductIds(limit);

  if (confidence !== 'low' && ids.length > 0) {
    // Fetch products from your database based on IDs
    const products = await db.query.products.findMany({
      where: inArray(products.id, ids),
      limit
    });

    // Sort in the same order as analytics returned
    return products.sort((a, b) =>
      ids.indexOf(a.id) - ids.indexOf(b.id)
    );
  }

  // Fallback: Most recently updated products
  return db.query.products.findMany({
    orderBy: desc(products.updatedAt),
    limit
  });
}

Confidence Levels

  • high - 100+ events, 10+ products → Show "Popular Products"
  • medium - 20+ events → Show with warning or mix
  • low - Little data → Use fallback

Final Result

After the integration, the site now has:

  • Data-driven product lists - "Popular Products" based on actual clicks
  • Robust fallback - Works even with little data
  • Minimal overhead - ISR caching provides fast page loading
  • Insights in the dashboard - Can see which products are trending in the Properties tab

"The integration was surprisingly simple. The RFC documentation was clear, the API is standard REST with Bearer token, and the confidence level makes fallback logic trivial."

— Developer, Nordic e-commerce site

Want to do the same?