Home Projects Portfolio Dashboard Export PDF Log in

Integrating Supabase into Your NestJS Application

The vive-tu-mente-preview project is enhancing its backend capabilities. A recent initiative involved the integration of Supabase, a powerful open-source Firebase alternative, to manage data and potentially authentication, providing a robust and scalable solution for the application's needs. This post details the process of adding a base Supabase module to a NestJS application.

The Problem

Modern applications often require robust backend services for data storage, authentication, and real-time capabilities. Setting up and managing a full-fledged backend, including databases, authentication systems, and APIs, can be a time-consuming and complex endeavor for development teams.

The Approach

To streamline our backend development and leverage powerful ready-to-use services, we opted for Supabase. Integrating it into our existing NestJS application required a structured approach, carefully utilizing NestJS's modularity and powerful dependency injection system to create a reusable and configurable Supabase module.

Phase 1: Setting up the Supabase Client

The first step involved installing the official Supabase JavaScript client and initializing it with our project's specific URL and anon key. This client acts as the primary interface for interacting with Supabase services like the database, authentication, and storage.

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

const supabaseUrl = process.env.SUPABASE_URL || 'https://example.com';
const supabaseAnonKey = process.env.SUPABASE_ANON_KEY || 'your-anon-key';

const supabase: SupabaseClient = createClient(supabaseUrl, supabaseAnonKey);

// You can now use 'supabase' for database queries, auth, etc.
// For example: await supabase.from('items').select('*');

Phase 2: Integrating with a NestJS Module

To make the Supabase client globally available throughout the NestJS application and ensure it benefits from NestJS's dependency injection container, we wrapped its initialization within a custom SupabaseModule. This allows for a clean, modular setup and easy configuration.

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

export const SUPABASE_CLIENT = 'SUPABASE_CLIENT';

@Module({})
export class SupabaseModule {
  static forRoot(): DynamicModule {
    const supabaseClientProvider: Provider = {
      provide: SUPABASE_CLIENT,
      useFactory: (): SupabaseClient => {
        const supabaseUrl = process.env.SUPABASE_URL || '';
        const supabaseAnonKey = process.env.SUPABASE_ANON_KEY || '';
        if (!supabaseUrl || !supabaseAnonKey) {
          throw new Error('Supabase environment variables are not set.');
        }
        return createClient(supabaseUrl, supabaseAnonKey);
      },
    };

    return {
      module: SupabaseModule,
      providers: [supabaseClientProvider],
      exports: [supabaseClientProvider],
      global: true,
    };
  }
}

This forRoot method ensures that the Supabase client is instantiated once and made available as a global provider. The SUPABASE_CLIENT token is used for injection.

Phase 3: Using the Supabase Client

Once the SupabaseModule is imported into the root AppModule using SupabaseModule.forRoot(), the SupabaseClient can be easily injected into any service or controller that needs to interact with Supabase. This demonstrates the power of Dependency Injection in managing external service integrations.

import { Injectable, Inject } from '@nestjs/common';
import { SupabaseClient } from '@supabase/supabase-js';
import { SUPABASE_CLIENT } from '../supabase/supabase.module';

@Injectable()
export class ItemService {
  constructor(
    @Inject(SUPABASE_CLIENT) private readonly supabase: SupabaseClient,
  ) {}

  async getItems() {
    const { data, error } = await this.supabase.from('items').select('*');
    if (error) {
      throw new Error(error.message);
    }
    return data;
  }

  async createItem(item: any) {
    const { data, error } = await this.supabase.from('items').insert([item]);
    if (error) {
      throw new Error(error.message);
    }
    return data;
  }
}

Key Insight

By encapsulating the Supabase client within a dedicated NestJS module and leveraging NestJS's powerful dependency injection system, we achieved a clean separation of concerns. This modular approach not only simplified configuration but also ensured that the Supabase client could be seamlessly injected and utilized throughout the application, making external service integration robust and maintainable. This pattern is highly recommended for integrating any third-party service into a NestJS project.


Generated with Gitvlg.com

Integrating Supabase into Your NestJS Application
SOFIA DESIREE BARTOLI

SOFIA DESIREE BARTOLI

Author

Share: