npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pixpilot/supabase-functions-client

v0.2.0

Published

A client library for Supabase Functions, providing a simple and efficient way to interact with serverless functions deployed on the Supabase platform.

Readme

@pixpilot/supabase-functions-client

A lightweight, type-safe wrapper around the Supabase Edge Functions client with structured error handling, automatic error message extraction, and chainable response helpers.


Installation

pnpm add @pixpilot/supabase-functions-client @supabase/supabase-js

Quick Start

import { FunctionsClient } from '@pixpilot/supabase-functions-client';
import { createClient } from '@supabase/supabase-js';

const supabase = createClient('https://your-project.supabase.co', 'your-anon-key');

// Wrap the Supabase client once
const functionsClient = new FunctionsClient(supabase);

API

FunctionsClient

class FunctionsClient {
  constructor(supabaseClient: SupabaseClient);

  invoke<RequestBody = void, ResponseData = any>(
    functionName: string,
    options?: FunctionInvokeOptions<RequestBody>,
  ): Promise<ResponseData>;

  handleInvoke<T>(
    invokeCall: () => Promise<T>,
    onSuccess: (data: T) => Promise<void> | void,
    onError: (error: EdgeFunctionError) => Promise<void> | void,
    context?: string,
  ): Promise<void>;

  /** @deprecated Use handleInvoke instead. */
  handleResponse<T>(
    result: FunctionsResponse<T>,
    onSuccess: (data: T) => Promise<void> | void,
    onError: (error: string, code?: string, details?: unknown) => Promise<void> | void,
    context?: string,
  ): Promise<void>;
}

Usage Patterns

1. invoke – Direct Try/Catch (Recommended)

invoke either returns data on success or throws an EdgeFunctionError (or EdgeFunctionNoDataError) on failure.

import {
  EdgeFunctionError,
  EdgeFunctionNoDataError,
  FunctionsClient,
} from '@pixpilot/supabase-functions-client';

interface ProcessJobRequest {
  jobContent: string;
  jobUrl: string;
}

interface ProcessJobResponse {
  jobId: string;
  status: string;
}

try {
  /*
   * RequestBody type is inferred, ResponseData is explicit.
   * Because ProcessJobRequest has required properties, `body` is required
   * in the second argument – TypeScript will enforce this.
   */
  const job = await functionsClient.invoke<ProcessJobRequest, ProcessJobResponse>(
    'process-job',
    { body: { jobContent: htmlContent, jobUrl: url } },
  );

  console.log('Job created:', job.jobId);
} catch (error) {
  if (error instanceof EdgeFunctionNoDataError) {
    console.error('Function returned no data for:', error.functionName);
  } else if (error instanceof EdgeFunctionError) {
    console.error('Function failed:', error.message);
    if (error.code) console.error('Error code:', error.code);
    if (error.details) console.error('Details:', error.details);
  } else {
    throw error; // Re-throw unexpected errors
  }
}

Void body (no request body)

When RequestBody is void or omitted, the options argument is optional and body is not allowed:

interface HealthCheckResponse {
  status: 'ok' | 'degraded';
  timestamp: string;
}

// No second argument needed
const health = await functionsClient.invoke<void, HealthCheckResponse>('health-check');
console.log('Status:', health.status);

All-optional body

When every property of RequestBody is optional, the options argument is also optional:

interface SearchRequest {
  query?: string;
  limit?: number;
}

interface SearchResponse {
  results: string[];
}

// Can omit options entirely
const results = await functionsClient.invoke<SearchRequest, SearchResponse>('search');

// Or pass a partial body
const filtered = await functionsClient.invoke<SearchRequest, SearchResponse>('search', {
  body: { query: 'typescript', limit: 10 },
});

2. handleInvoke – Callback Pattern

Use handleInvoke when you want clean success/error callbacks without a try/catch block. Uncaught non-EdgeFunctionError exceptions are re-thrown.

await functionsClient.handleInvoke(
  // The function call
  () =>
    functionsClient.invoke<ProcessJobRequest, ProcessJobResponse>('process-job', {
      body: { jobContent: htmlContent, jobUrl: url },
    }),

  // Success handler
  (job) => {
    console.log('Job created:', job.jobId);
    showSuccessNotification(job);
  },

  // Error handler – receives an EdgeFunctionError
  (error) => {
    console.error('Function failed:', error.message);
    if (error.code === 'QUOTA_EXCEEDED') {
      showQuotaWarning();
    } else {
      showErrorNotification(error.message);
    }
  },

  // Optional context label – included in log output
  'Process Job',
);

3. handleResponse – Legacy Callback Pattern (deprecated)

This method takes a FunctionsResponse object (the old {data, error} shape) and dispatches to one of two callbacks. Prefer handleInvoke for new code.

const result: FunctionsResponse<ProcessJobResponse> = {
  data: { jobId: '123', status: 'pending' },
  error: null,
};

await functionsClient.handleResponse(
  result,
  // onSuccess
  (job) => {
    console.log('Job created:', job.jobId);
  },
  // onError
  (errorMessage, errorCode, details) => {
    console.error('Function failed:', errorMessage);
    if (errorCode) console.error('Error code:', errorCode);
  },
);

Error Classes

EdgeFunctionError

The base error class for all Edge Function failures.

class EdgeFunctionError extends Error {
  /** Name of the Edge Function that failed */
  readonly functionName: string;

  /** Optional machine-readable error code from the function response */
  readonly code?: string;

  /** Additional structured details from the function response */
  readonly details?: unknown;
}

EdgeFunctionNoDataError

Extends EdgeFunctionError. Thrown when the function call succeeds (HTTP 2xx) but returns no data.

class EdgeFunctionNoDataError extends EdgeFunctionError {
  // message: 'No data received from Edge Function.'
}

instanceof Checks

import {
  EdgeFunctionError,
  EdgeFunctionNoDataError,
} from '@pixpilot/supabase-functions-client';

try {
  const data = await functionsClient.invoke('my-function');
} catch (error) {
  if (error instanceof EdgeFunctionNoDataError) {
    // Successful HTTP call but empty body
    console.log('No data from:', error.functionName);
  } else if (error instanceof EdgeFunctionError) {
    // HTTP error OR network error
    console.log(`${error.functionName} failed: ${error.message}`);
    if (error.code) console.log('Code:', error.code);
  }
}

Automatic Error Message Extraction

When an Edge Function returns a non-2xx response, the client automatically parses the JSON body and extracts the human-readable message. The resolution order is:

  1. body.error field
  2. body.message field
  3. HTTP error message (fallback)

Edge Functions should return a structured error body:

// From your Edge Function (Deno / Node):
return new Response(
  JSON.stringify({
    error: 'User not found', // → EdgeFunctionError.message
    code: 'USER_NOT_FOUND', // → EdgeFunctionError.code
    details: { userId: 123 }, // → EdgeFunctionError.details
  }),
  { status: 404, headers: { 'Content-Type': 'application/json' } },
);

Nested ApiResponse Unwrapping

If your Edge Function wraps data in a { data, message } envelope, the client automatically unwraps it:

// Edge Function returns:
// { data: { jobId: '123' }, message: 'Success' }

// invoke() returns the inner data directly:
const job = await functionsClient.invoke<void, ProcessJobResponse>('process-job');
// job === { jobId: '123' }

Exported Types

| Type | Description | | ------------------------------------ | ---------------------------------------------------------------------- | | EdgeFunctionErrorResponse | Shape of the error JSON returned by Edge Functions | | FunctionsResponse<T> | Legacy { data, error, code, details } response envelope | | FunctionInvokeOptions<RequestBody> | Options passed to invoke(), body requirement adapts to RequestBody | | InvokeOptions<RequestBody> | Determines whether options are required or optional | | RequiredKeys<T> | Utility: extracts required keys from a type | | HasRequiredProperties<T> | Utility: true if T has at least one required key |


Migration from Direct supabase.functions.invoke

Before:

const result = await supabase.functions.invoke<ProcessJobResponse>('process-job', {
  body: { jobContent, jobUrl },
});

if (result.error) {
  if (result.error instanceof FunctionsHttpError) {
    // Manual JSON parsing, error code extraction...
  }
  return null;
}

return result.data ?? undefined;

After:

try {
  return await functionsClient.invoke<ProcessJobRequest, ProcessJobResponse>(
    'process-job',
    { body: { jobContent, jobUrl } },
  );
} catch (error) {
  if (error instanceof EdgeFunctionError) {
    console.error('Failed:', error.message, error.code);
  }
  return null;
}

License

MIT