cd..blog

Full-Stack Fusion: React, Fastify, & Firebase with TypeScript

const published = "Feb 19, 2026, 10:36 PM";const readTime = 5 min;
reactfastifyfirebasetypescriptfullstack
Discover how to build robust, scalable web applications by seamlessly integrating React, Fastify, and Firebase using TypeScript.

Full-Stack Fusion: React, Fastify, & Firebase with TypeScript

Building modern web applications demands a stack that offers speed, scalability, and a superior developer experience. This post demonstrates how to integrate React for dynamic UIs, Fastify for a high-performance backend, and Firebase for backend services, all powered by TypeScript.

The Integrated Architecture

This architecture leverages React for the frontend, providing a component-based UI. Fastify serves as the API layer, handling business logic and secure interactions. Firebase provides authentication, real-time database (Firestore), and other essential services, minimizing infrastructure overhead.

Fastify Backend: High-Performance API

Fastify is a fast and low-overhead web framework for Node.js, ideal for building robust APIs. We'll use it to expose secure endpoints and interact with Firebase Admin SDK.

First, set up a basic Fastify server with TypeScript and install @fastify/cors to handle cross-origin requests from your React app. Install firebase-admin to interact with Firebase services securely from the backend.

// src/server.ts
import Fastify from 'fastify';
import cors from '@fastify/cors';
import admin from 'firebase-admin';
import * as dotenv from 'dotenv';

dotenv.config();

// Initialize Firebase Admin SDK (replace with your service account key path or config)
// Ensure FIREBASE_SERVICE_ACCOUNT_KEY is a stringified JSON object in your .env
const serviceAccount = JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT_KEY || '{}');
admin.initializeApp({
  credential: admin.credential.cert(serviceAccount)
});

const fastify = Fastify({ logger: true });

fastify.register(cors, {
  origin: 'http://localhost:3000' // Your React app's origin
});

interface AuthenticatedRequest extends Fastify.FastifyRequest {
  user?: admin.auth.DecodedIdToken;
}

// Pre-handler hook for authentication
fastify.addHook('preHandler', async (request: AuthenticatedRequest, reply) => {
  try {
    const authHeader = request.headers.authorization;
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      throw new Error('No authorization token provided.');
    }
    const idToken = authHeader.split('Bearer ')[1];
    const decodedToken = await admin.auth().verifyIdToken(idToken);
    request.user = decodedToken;
  } catch (error: any) {
    reply.code(401).send({ message: error.message || 'Unauthorized' });
    throw new Error('Unauthorized'); // Stop further processing
  }
});

fastify.get('/api/secure-data', async (request: AuthenticatedRequest, reply) => {
  // Access authenticated user via request.user
  const userId = request.user?.uid;
  const userEmail = request.user?.email;

  // Example: Fetch data from Firestore
  const docRef = admin.firestore().collection('users').doc(userId);
  const doc = await docRef.get();

  if (doc.exists) {
    return { message: `Hello, ${userEmail}! Here's your data:`, data: doc.data() };
  } else {
    // Create a new user document if it doesn't exist
    await docRef.set({ email: userEmail, createdAt: admin.firestore.FieldValue.serverTimestamp() });
    return { message: `Welcome, ${userEmail}! New user data created.`, data: { email: userEmail } };
  }
});

const start = async () => {
  try {
    await fastify.listen({ port: 3001 });
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

start();

This Fastify server includes a pre-handler hook to verify Firebase ID tokens, securing your API endpoints. It then uses the Firebase Admin SDK to interact with Firestore, demonstrating how to fetch or create user-specific data.

React Frontend: Dynamic User Interface

React builds interactive user interfaces efficiently. We'll use the Firebase Client SDK for user authentication and fetch to communicate with our Fastify backend.

Install firebase and react-firebase-hooks (optional, but useful for auth state).

// src/firebaseConfig.ts
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';

const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_AUTH_DOMAIN",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_STORAGE_BUCKET",
  messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
  appId: "YOUR_APP_ID"
};

const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
// src/App.tsx
import React, { useState, useEffect } from 'react';
import { signInWithPopup, GoogleAuthProvider, signOut, User } from 'firebase/auth';
import { auth } from './firebaseConfig';

function App() {
  const [user, setUser] = useState<User | null>(null);
  const [data, setData] = useState<any | null>(null);
  const [loading, setLoading] = useState<boolean>(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged((currentUser) => {
      setUser(currentUser);
      setData(null); // Clear data on auth state change
    });
    return () => unsubscribe();
  }, []);

  const handleGoogleSignIn = async () => {
    const provider = new GoogleAuthProvider();
    try {
      await signInWithPopup(auth, provider);
    } catch (err: any) {
      console.error(err);
      setError(err.message);
    }
  };

  const handleSignOut = async () => {
    try {
      await signOut(auth);
    } catch (err: any) {
      console.error(err);
      setError(err.message);
    }
  };

  const fetchSecureData = async () => {
    if (!user) return;
    setLoading(true);
    setError(null);
    try {
      const idToken = await user.getIdToken();
      const response = await fetch('http://localhost:3001/api/secure-data', {
        headers: {
          Authorization: `Bearer ${idToken}`,
        },
      });
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      const result = await response.json();
      setData(result);
    } catch (err: any) {
      console.error('Error fetching secure data:', err);
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div style={{ padding: '20px', fontFamily: 'sans-serif' }}>
      <h1>React + Fastify + Firebase App</h1>
      {!user ? (
        <button onClick={handleGoogleSignIn}>Sign in with Google</button>
      ) : (
        <div>
          <p>Welcome, {user.displayName || user.email}!</p>
          <button onClick={handleSignOut}>Sign Out</button>
          <button onClick={fetchSecureData} disabled={loading} style={{ marginLeft: '10px' }}>
            {loading ? 'Loading...' : 'Fetch Secure Data'}
          </button>
          {error && <p style={{ color: 'red' }}>Error: {error}</p>}
          {data && (
            <div style={{ marginTop: '20px', border: '1px solid #ccc', padding: '10px' }}>
              <h3>Secure Data from Fastify:</h3>
              <pre>{JSON.stringify(data, null, 2)}</pre>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

export default App;

This React component handles user authentication via Firebase's client SDK. Once authenticated, it retrieves the user's ID token and sends it to the Fastify backend to access a protected endpoint. The backend then verifies this token and returns user-specific data.

Firebase: Authentication & Data Persistence

Firebase provides scalable backend services without managing servers. Firebase Authentication handles user sign-up/sign-in, while Firestore offers a flexible, NoSQL cloud database.

  • Authentication: The React frontend uses firebase/auth for client-side authentication flows (e.g., Google Sign-In). The Fastify backend uses firebase-admin to verify the authenticity of user tokens, ensuring only legitimate requests access protected resources.
  • Firestore: Both client and server can interact with Firestore. The Fastify backend, with its elevated privileges via firebase-admin, can perform server-side data operations (e.g., creating/updating user profiles, sensitive data access). The React frontend can also directly access Firestore for less sensitive, user-specific data, often secured by Firestore Security Rules.

Conclusion

By integrating React, Fastify, and Firebase with TypeScript, you create a powerful, type-safe, and scalable full-stack application. React delivers a rich user experience, Fastify ensures a performant and secure API layer, and Firebase handles the heavy lifting of authentication and data management, allowing developers to focus on core application logic. This stack provides a robust foundation for modern web development, ready for complex features and deployment to platforms like Vercel (for React) and Cloud Run (for Fastify).