saas-base
v0.0.2
Published
Shared core logic (Firebase, Auth, State) for SaaS apps.
Maintainers
Readme
saas-back (Core Services Package) This is the core backend-as-a-service package for building SaaS apps. It provides a set of shared utilities to be used across all applications (pro-editor, design-studio, televerse).
This package MUST NOT contain app-specific business logic.
Its core responsibilities are: Initializing Firebase (Auth, Firestore, Storage)Providing a global Zustand store for authenticationExporting a React hook to listen to auth state changes
- Installation
Install the package and its required peer dependencies into your Expo application.# Install the package (use your scoped name if you published one)
npm install saas-back
Install its peer dependencies
npm install firebase zustand
- Quick Start: Connecting Your AppThis package requires the consuming application (e.g., pro-editor) to provide the Firebase API keys.Step
2a: Set Environment Variables
In the root of your pro-editor project, create a .env file. Expo requires the EXPO_PUBLIC_ prefix to read variables on the client.
pro-editor/.env
EXPO_PUBLIC_FIREBASE_API_KEY="AIzaSy...YOUR_API_KEY"
EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN="your-project-id.firebaseapp.com"
EXPO_PUBLIC_FIREBASE_PROJECT_ID="your-project-id"
EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET="your-project-id.appspot.com"
EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID="1234567890"
EXPO_PUBLIC_FIREBASE_APP_ID="1:1234567890:web:abcdef123456"Step 2b: Initialize in Your Root LayoutIn your Expo app's root layout (app/_layout.tsx), you must import and call the initializeFirebaseCore function once. This configures the package for the rest of your app's lifecycle.This file also shows the complete implementation of protected routes using the package's auth listener.// pro-editor/app/_layout.tsx
import {
initializeFirebaseCore,
FirebaseCoreConfig,
useAuthStateListener,
useAuthStore
} from 'saas-back';
import { Slot, useRouter, useSegments } from 'expo-router';
import { useEffect } from 'react';
import { View, ActivityIndicator } from 'react-native';
// --- 1. Load Config & Initialize ---
// This code runs ONCE when the app module is loaded
const firebaseConfig: FirebaseCoreConfig = {
apiKey: process.env.EXPO_PUBLIC_FIREBASE_API_KEY!,
authDomain: process.env.EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN!,
projectId: process.env.EXPO_PUBLIC_FIREBASE_PROJECT_ID!,
storageBucket: process.env.EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET!,
messagingSenderId: process.env.EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID!,
appId: process.env.EXPO_PUBLIC_FIREBASE_APP_ID!,
};
// Call the init function to configure the package
initializeFirebaseCore(firebaseConfig);
// --- End of Initialization ---/**
* This is the main layout for the entire app.
* It sets up the auth listener and handles protected routes.
*/
export default function RootLayout() {
// --- 2. Set up Auth Listener & Protected Routes ---
// This hook now works because 'auth' is initialized.
// It subscribes to Firebase and updates the Zustand store.
useAuthStateListener();
// Get auth state from the Zustand store
const { user, isLoading } = useAuthStore();
const segments = useSegments();
const router = useRouter();
useEffect(() => {
if (isLoading) {
// Wait until the auth state is confirmed
return;
}
const inAuthGroup = segments[0] === '(auth)';
if (!user && !inAuthGroup) {
// If user is NOT logged in and is NOT on an auth screen,
// redirect them to the sign-in page.
router.replace('/(auth)/sign-in');
} else if (user && inAuthGroup) {
// If user IS logged in but is still on an auth screen (e.g. sign-in),
// redirect them to the app's home screen.
router.replace('/(app)/home');
}
}, [user, isLoading, segments, router]); // Re-run effect when state changes
// Show a loading indicator while the initial auth check is running
if (isLoading) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ActivityIndicator size="large" color="#0000ff" />
</View>
);
}
// Once loaded, render the current child route (e.g., sign-in or home)
return <Slot />;
}- Available UtilitiesYour app is now set up. Here is how to use the package utilities.Accessing Auth StateTo get the user's profile or check their auth status in any component:
import { useAuthStore } from 'saas-back';
function ProfileHeader() {
// Select the 'user' object from the store
const { user } = useAuthStore();
if (!user) {
return <Text>Not logged in</Text>;
}
return <Text>Welcome, {user.email}</Text>;
}Using Firebase Services
The auth, firestore, and storage instances are now configured and can be imported directly for use in your app's screens or API files.// Example: A sign-in function
import { auth, firestore } from 'saas-back';
import { signInWithEmailAndPassword } from 'firebase/auth';
import { doc, getDoc } from 'firebase/firestore';
export async function handleSignIn(email, password) {
try {
const userCredential = await signInWithEmailAndPassword(auth, email, password);
// Example: Fetch user profile from Firestore
const userDoc = await getDoc(doc(firestore, 'users', userCredential.user.uid));
if (userDoc.exists()) {
console.log("User profile:", userDoc.data());
}
} catch (error) {
console.error("Sign-in failed:", error);
}
}- For Package Maintainers (How to Update)To publish a new version of this saas-back package:Make your code changes in the saas-back/src directory.Increment the "version" number in saas-back/package.json (e.g., 0.0.1 -> 0.0.2).Run the build command: npm run buildPublish to the registry: npm publishIn your app (pro-editor, etc.), run
npm install saas-back@latestto pull in the new version.
