Home Projects Portfolio Dashboard Export PDF Log in

Adding a Simple Visit Counter API with NestJS and Supabase

Understanding user engagement is crucial for any project, even a preview site like vive-tu-mente-preview. While full-fledged analytics platforms can be overkill for simple needs, a lightweight visit counter provides valuable insights into content popularity. This post details the implementation of a basic visit counter API to track page views, leveraging NestJS for the API logic and Supabase for persistent storage.

The Challenge: Tracking Engagement Simply

For the vive-tu-mente-preview project, we needed a straightforward way to monitor how often pages were accessed. The goal was to understand which sections garnered the most interest without introducing complex client-side tracking libraries or heavy analytics infrastructure. A simple, server-side visit counter offered the perfect balance of insight and minimal overhead.

The Solution: A Dedicated Visit Counter API

We implemented a new API endpoint specifically for incrementing visit counts. This approach keeps the tracking logic isolated, allowing client applications to simply make a request when a page is viewed. This not only simplifies client-side code but also provides a more reliable count by reducing susceptibility to ad-blockers or JavaScript errors.

Implementation Details

The visit counter API was built using NestJS, a powerful and flexible framework for building scalable server-side applications with TypeScript. Supabase was chosen as the backend database, offering an easy-to-use PostgreSQL instance with excellent integration capabilities.

At a high level, the flow involves:

  1. A client application (e.g., a web page) makes a request to the API when a page loads.
  2. The NestJS controller receives the request, extracts relevant page identification.
  3. A dedicated service handles the business logic, interacting with the Supabase database to increment the visit count for that specific page.

Database Schema (Supabase)

We started with a simple page_visits table in Supabase:

CREATE TABLE page_visits (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  page_path TEXT UNIQUE NOT NULL,
  visit_count INTEGER DEFAULT 0 NOT NULL,
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Function to update 'updated_at' on row update
CREATE OR REPLACE FUNCTION update_updated_at_column() RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$
LANGUAGE plpgsql;

-- Trigger to call the function
CREATE TRIGGER update_page_visits_updated_at BEFORE UPDATE ON page_visits FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

This schema allows us to store unique page paths and their respective visit counts. The updated_at column helps in monitoring when a count was last updated.

NestJS Service for Incrementing Visits

Within NestJS, a service class encapsulates the logic for interacting with Supabase to manage visit counts. Leveraging Dependency Injection, this service can be easily consumed by controllers.

import { Injectable } from '@nestjs/common';
import { createClient, SupabaseClient } from '@supabase/supabase-js';

@Injectable()
export class VisitsService {
  private supabase: SupabaseClient;

  constructor() {
    // Initialize Supabase client (replace with actual environment variables)
    this.supabase = createClient(
      process.env.SUPABASE_URL || 'your-supabase-url',
      process.env.SUPABASE_ANON_KEY || 'your-supabase-anon-key'
    );
  }

  async incrementVisit(pagePath: string): Promise<number> {
    // Upsert logic: insert if pagePath doesn't exist, otherwise update count
    const { data, error } = await this.supabase
      .from('page_visits')
      .upsert(
        { page_path: pagePath, visit_count: 1 },
        { onConflict: 'page_path', ignoreDuplicates: false }
      )
      .select('visit_count')
      .single();

    if (error && error.code === '23505') { // Unique violation means record exists
        const { data: updatedData, error: updateError } = await this.supabase
            .rpc('increment_page_visit', { p_page_path: pagePath }); // Call a custom SQL function for atomic increment
        if (updateError) throw updateError;
        return updatedData;
    } else if (error) {
        throw error;
    }
    return data.visit_count;
  }
}

// Example of a Supabase RPC function for atomic increment
// CREATE OR REPLACE FUNCTION increment_page_visit(p_page_path TEXT)
// RETURNS INTEGER AS $$
//   UPDATE page_visits
//   SET visit_count = visit_count + 1, updated_at = NOW()
//   WHERE page_path = p_page_path
//   RETURNING visit_count;
// $$ LANGUAGE plpgsql;

The incrementVisit method uses Supabase's upsert functionality. For atomic increments in a concurrent environment, it's best to rely on a custom PostgreSQL function (RPC call) if the onConflict update logic becomes complex, ensuring data integrity.

Key Takeaways

Implementing a simple visit counter provides valuable, low-cost insights into user engagement without the overhead of complex analytics platforms. By combining NestJS for a robust API layer and Supabase for a scalable and managed database, we can quickly deploy essential tracking features. This pattern is adaptable for various simple data collection needs, ensuring that understanding your project's usage is always within reach.


Generated with Gitvlg.com

Adding a Simple Visit Counter API with NestJS and Supabase
SOFIA DESIREE BARTOLI

SOFIA DESIREE BARTOLI

Author

Share: