@aiwa/firekit
v1.3.0
Published
AIWA's managed Firebase SDK for multi-tenant applications
Downloads
577
Maintainers
Readme
Firekit 🔥
AIWA's managed Firebase SDK for multi-tenant applications. Zero configuration, complete isolation.
Features
- ✅ Zero Config - Automatic Firebase initialization from AIWA backend
- 🔒 Multi-Tenant Isolation - Each app is completely isolated via collection paths
- 🔐 Built-in Auth - Email/password authentication with custom claims
- ⚡ Real-time Updates - Live data subscriptions
- 🎯 Type-Safe - Full TypeScript support
- 🚀 Edge-Ready - Works with Vercel Edge Functions
- 🔐 Secure - Firebase credentials managed server-side, never exposed to users
How It Works
- Your app calls
createDatabase({ projectId }) - Firekit validates projectId with AIWA backend
- Backend returns userId, appId, and Firebase credentials
- Firekit initializes with returned credentials
- All data isolated to
/users/{userId}/apps/{appId}/data/
Security: Firebase credentials stay on AIWA's servers. User apps never see them.
Installation
npm install @aiwa/firekit
# or
pnpm add @aiwa/firekit
# or
yarn add @aiwa/firekitEnvironment Variables
Add to your .env.local:
NEXT_PUBLIC_PROJECT_ID=your-project-idThat's it. Firebase credentials are automatically retrieved from AIWA's secure backend.
Quick Start
import { createDatabase, createAuth } from '@aiwa/firekit'
// Initialize database (multi-tenant isolated)
const db = await createDatabase({
projectId: process.env.NEXT_PUBLIC_PROJECT_ID!,
})
// Initialize auth
const auth = await createAuth({
projectId: process.env.NEXT_PUBLIC_PROJECT_ID!,
})Database Operations
Create
const post = await db.create('posts', {
title: 'Hello World',
content: 'My first post',
published: true,
})Read
// Get single document
const post = await db.read('posts', 'post-id')
// Get all documents
const posts = await db.getAll('posts')
// Query with filters
const publishedPosts = await db.query('posts', {
where: [{ field: 'published', operator: '==', value: true }],
orderBy: { field: 'createdAt', direction: 'desc' },
limit: 10,
})Update
await db.update('posts', 'post-id', {
title: 'Updated Title',
})Delete
await db.delete('posts', 'post-id')Real-time Subscriptions
const unsubscribe = db.subscribe('posts', (posts) => {
console.log('Posts updated:', posts)
})
// Clean up when done
unsubscribe()Authentication
Sign Up
const { user, token } = await auth.signUp(
'[email protected]',
'password123',
'John Doe', // optional display name
)Sign In
const { user, token } = await auth.signIn('[email protected]', 'password123')Sign Out
await auth.signOut()Auth State Listener
const unsubscribe = auth.onAuthStateChanged((user) => {
if (user) {
console.log('Signed in:', user.email)
} else {
console.log('Signed out')
}
})Password Reset
await auth.resetPassword('[email protected]')React Example
'use client'
import { createDatabase, createAuth } from 'firekit'
import { useEffect, useState } from 'react'
const db = createDatabase({
userId: process.env.NEXT_PUBLIC_AIWA_USER_ID!,
appId: process.env.NEXT_PUBLIC_AIWA_APP_ID!,
})
const auth = createAuth({
appId: process.env.NEXT_PUBLIC_AIWA_APP_ID!,
})
export default function TodoApp() {
const [user, setUser] = useState(null)
const [todos, setTodos] = useState([])
// Auth listener
useEffect(() => {
return auth.onAuthStateChanged(setUser)
}, [])
// Real-time todos subscription
useEffect(() => {
if (!user) return
return db.subscribe(
'todos',
setTodos,
{
where: [{ field: 'userId', operator: '==', value: user.uid }],
orderBy: { field: '_createdAt', direction: 'desc' },
},
)
}, [user])
const addTodo = async (title: string) => {
await db.create('todos', {
title,
completed: false,
userId: user.uid,
})
}
const toggleTodo = async (id: string, completed: boolean) => {
await db.update('todos', id, { completed })
}
if (!user) return <SignInForm />
return <TodoList todos={todos} onAdd={addTodo} onToggle={toggleTodo} />
}Multi-Tenant Isolation
Firekit automatically isolates data using collection paths:
/users/{userId}/apps/{appId}/data/{collection}/{docId}Example:
- User A's app:
/users/user-a/apps/app-123/data/posts/post-1 - User B's app:
/users/user-b/apps/app-456/data/posts/post-1
Complete isolation - User A's app users cannot access User B's app data.
Security Rules
Firekit enforces security via Firebase Security Rules with custom claims:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId}/apps/{appId}/data/{collection}/{document=**} {
// AIWA user (app owner) has full access
allow read, write: if request.auth.token.userId == userId;
// App end-users have access based on appId
allow read, write: if request.auth.token.appId == appId
&& request.auth != null;
}
}
}Error Handling
import { FirekitDatabaseError, FirekitAuthError } from 'firekit'
try {
await db.create('posts', { title: 'Hello' })
} catch (error) {
if (error instanceof FirekitDatabaseError) {
console.error('Database error:', error.message, error.code)
}
}
try {
await auth.signIn('email', 'password')
} catch (error) {
if (error instanceof FirekitAuthError) {
console.error('Auth error:', error.message, error.code)
}
}API Reference
Database Methods
create(collection, data)- Create documentset(collection, id, data, merge?)- Set document with IDread(collection, id)- Read single documentquery(collection, options?)- Query documentsgetAll(collection)- Get all documentsupdate(collection, id, data)- Update documentdelete(collection, id)- Delete documentsubscribe(collection, callback, options?)- Real-time subscriptionsubscribeToDoc(collection, id, callback)- Single document subscriptionbatchCreate(collection, items)- Batch createcount(collection, options?)- Count documents
Auth Methods
signUp(email, password, displayName?)- Register usersignIn(email, password)- Sign in usersignOut()- Sign outresetPassword(email)- Send reset emailupdateProfile(data)- Update profileupdateEmail(email)- Update emailupdatePassword(password)- Update passwordonAuthStateChanged(callback)- Listen to auth changesgetIdToken(forceRefresh?)- Get ID tokenisAuthenticated()- Check auth status
License
Apache-2.0
