Skip to content

tRPC v12 isn’t on npm yet. The @trpc12/* packages on this site can’t be installed until it’s released. Use v11 today

tRPC

ExperimentalMore adapters might change or go away in any release. Stability

Firebase

Call procedures from Firebase callable functions and map their errors to HttpsError.

A callable function gets JSON data and Firebase's auth, and reports errors as HttpsError. Call the procedure with call() and map the error code:

import {
  HttpsError,
  onCall,
  type FunctionsErrorCode,
} from 'firebase-functions/v2/https';
import { call, isTrpcError } from '@trpc12/trpc/server';

const firebaseCode: Record<string, FunctionsErrorCode> = {
  BAD_REQUEST: 'invalid-argument',
  UNPROCESSABLE_CONTENT: 'invalid-argument',
  UNAUTHORIZED: 'unauthenticated',
  FORBIDDEN: 'permission-denied',
  NOT_FOUND: 'not-found',
  CONFLICT: 'already-exists',
  PRECONDITION_FAILED: 'failed-precondition',
  TOO_MANY_REQUESTS: 'resource-exhausted',
  TIMEOUT: 'deadline-exceeded',
  CLIENT_CLOSED_REQUEST: 'cancelled',
  NOT_IMPLEMENTED: 'unimplemented',
  SERVICE_UNAVAILABLE: 'unavailable',
};

export const createPost = onCall(async (request) => {
  try {
    return await call(appRouter.post.create, request.data, {
      ctx: { uid: request.auth?.uid ?? null },
    });
  } catch (error) {
    const code = isTrpcError(error) ? firebaseCode[error.code] : undefined;
    // Anything else reaches the client as Firebase's masked `internal`.
    if (!isTrpcError(error) || code === undefined) throw error;
    throw new HttpsError(code, error.message, error.data);
  }
});

The input is validated by the procedure as usual. Firebase's wire format is plain JSON, so outputs lose Dates, Maps and the other types the tRPC serializer keeps; return strings or numbers for those.

Firebase hosting can also serve handler.fetch through an HTTPS function (onRequest) behind Express (Express), which keeps the whole protocol.