Package Exports
- basebase-js
- basebase-js/auth
Readme
BaseBase JavaScript SDK
An SDK for BaseBase server interactions, patterned after the Firebase/Firestore SDK. Makes it easy to build web applications that use BaseBase as a data store and user authentication service.
⚠️ Early Development
This SDK is currently in early development (0.1.x). The API may change between versions. Please use with caution in production environments.
🚀 Features
- No initialization required - Set environment variable and start using immediately
- Firebase-like API - Drop-in replacement for Firebase/Firestore
- Phone verification authentication - SMS-based auth with JWT tokens
- Real-time data operations - CRUD operations with collections and documents
- Advanced querying - where, orderBy, limit constraints with server-side structured queries
- TypeScript support - Full type safety and IntelliSense
- Cookie & localStorage management - Automatic token persistence
- Cross-platform - Works in browsers and Node.js environments
📦 Installation
npm install basebase-jsOr with yarn:
yarn add basebase-js🛠 Quick Start
1. Authentication First
import { requestCode, verifyCode } from "basebase-js";
// Request SMS verification code
const response = await requestCode("john_doe", "+1234567890");
// Verify the code and get JWT token
const authResult = await verifyCode(
"+1234567890",
"123456",
"bb_your_api_key_here"
);
console.log("User:", authResult.user);
console.log("Token:", authResult.token);
// Token is automatically stored - now you can use the database!2. Start Using Database - No Setup Required!
Browser (after authentication):
import { db, doc, getDoc, collection, getDocs, addDoc } from "basebase-js";
// Get a single document - no setup needed after auth!
const userRef = doc(db, "myproject/users/user123");
const userSnap = await getDoc(userRef);
if (userSnap.exists) {
console.log("User data:", userSnap.data());
}
// Get all documents in a collection
const usersRef = collection(db, "myproject/users");
const snapshot = await getDocs(usersRef);
snapshot.forEach((doc) => {
console.log(doc.id, doc.data());
});
// Add a new document
const newUserRef = await addDoc(collection(db, "myproject/users"), {
name: "Jane Doe",
email: "jane@example.com",
});
console.log("New user ID:", newUserRef.id);Server/Node.js (with token):
import { getDatabase, doc, getDoc, collection, addDoc } from "basebase-js";
// Create database instance with JWT token
const db = getDatabase("your_jwt_token_here");
// Use exactly the same API as browser
const userRef = doc(db, "myproject/users/user123");
const userSnap = await getDoc(userRef);
if (userSnap.exists) {
console.log("User data:", userSnap.data());
}3. Document Operations
import {
db,
doc,
collection,
getDoc,
getDocs,
addDoc,
setDoc,
updateDoc,
deleteDoc,
} from "basebase-js";
// Get a single document
const userRef = doc(db, "myproject/users/user123");
const userSnap = await getDoc(userRef);
if (userSnap.exists) {
console.log("User data:", userSnap.data());
}
// Get all documents in a collection
const usersRef = collection(db, "myproject/users");
const snapshot = await getDocs(usersRef);
snapshot.forEach((doc) => {
console.log(doc.id, doc.data());
});
// Add document with auto-generated ID
const newUserRef = await addDoc(collection(db, "myproject/users"), {
name: "Jane Doe",
email: "jane@example.com",
});
// Set document with specific ID (URL-safe string under 24 characters)
const userId = "ben_wen";
await setDoc(doc(db, `users/${userId}`), {
name: "John Doe",
email: "john@example.com",
});
// Update specific fields
await updateDoc(doc(db, "myproject/users/user123"), {
age: 31,
lastLogin: new Date().toISOString(),
});
// Delete document
await deleteDoc(doc(db, "myproject/users/user123"));4. Path Structure & Naming Rules
Project and Document IDs: BaseBase allows flexible naming for projects and documents using URL-safe strings up to 24 characters long:
- ✅ Allowed characters:
a-z,A-Z,0-9,_,- - ✅ Examples:
test_project,user-123,myapp2024,ben_wen - ❌ Not allowed: spaces, special characters like
/,@,., etc. - ❌ Must be 24 characters or less
Path Structure: All paths are relative to the specified project. This eliminates ambiguity:
- ✅
"users"= collection named "users" - ✅
"users/ben_wen"= document "ben_wen" in "users" collection - ✅
"users/ben_wen/posts"= "posts" subcollection under document "ben_wen"
To work with a different project, use the optional projectName parameter:
// Work with documents in another project
const otherUserRef = doc(db, "otherproject/users/user123");
const otherUserData = await getDoc(otherUserRef);
// Add to another project's collection
await addDoc(collection(db, "users", "otherProject"), {
name: "Cross-project user",
});
// Set document with same ID across projects (powerful for data consistency)
const userId = "user_ben";
// Set base user data in main project
await setDoc(doc(db, `users/${userId}`), {
name: "John Doe",
email: "john@example.com",
phone: "+1234567890",
});
// Set app-specific data in another project using same ID
await setDoc(doc(db, `users/${userId}`, "news_app"), {
sources: ["techcrunch", "ars-technica"],
preferences: { theme: "dark", notifications: true },
});5. Querying Data
import {
db,
query,
where,
orderBy,
limit,
getDocs,
collection,
} from "basebase-js";
// Advanced queries
const q = query(
collection(db, "myproject/users"),
where("age", ">=", 18),
where("status", "==", "active"),
orderBy("name", "asc"),
limit(10)
);
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.data());
});📚 API Reference
Authentication
requestCode(username, phone, baseUrl?)
Request an SMS verification code. Username must be alphanumeric characters only with no spaces.
const response = await requestCode("john_doe", "+1234567890");verifyCode(phone, code, projectApiKey, baseUrl?)
Verify SMS code and get authentication token.
const result = await verifyCode("+1234567890", "123456", "bb_your_api_key");getAuthState()
Get current authentication state.
const authState = getAuthState();
console.log("Authenticated:", authState.isAuthenticated);
console.log("User:", authState.user);signOut()
Sign out the current user.
signOut();onAuthStateChanged(callback)
Listen for authentication state changes.
const unsubscribe = onAuthStateChanged((authState) => {
if (authState.isAuthenticated) {
console.log("User signed in:", authState.user);
} else {
console.log("User signed out");
}
});
// Later, stop listening
unsubscribe();Document Operations
doc(db, path, projectName?)
Create a document reference. Paths are always relative to the specified project.
// Document in your project
const docRef = doc(db, "myproject/users/user123");
// Document in another project
const otherDocRef = doc(db, "otherproject/users/user123");collection(db, path, projectName?)
Create a collection reference. Paths are always relative to the specified project.
// Collection in your project
const collectionRef = collection(db, "myproject/users");
// Collection in another project
const otherCollectionRef = collection(db, "users", "otherProject");getDoc(docRef)
Get a document snapshot.
const docRef = doc(db, "myproject/users/user123");
const snapshot = await getDoc(docRef);
if (snapshot.exists) {
console.log("User data:", snapshot.data());
}getDocs(collectionRef)
Get all documents in a collection.
const collectionRef = collection(db, "myproject/users");
const snapshot = await getDocs(collectionRef);
snapshot.forEach((doc) => {
console.log(doc.id, doc.data());
});addDoc(collectionRef, data)
Add a new document with auto-generated ID.
const docRef = await addDoc(collection(db, "myproject/users"), {
name: "Jane Doe",
email: "jane@example.com",
});
console.log("New document ID:", docRef.id);setDoc(docRef, data, options?)
Set a document with a specific ID, optionally merging with existing data.
const userId = "john_doe";
const userRef = doc(db, `users/${userId}`);
await setDoc(userRef, {
name: "John Doe",
email: "john@example.com",
});
// Merge with existing data
await setDoc(userRef, { age: 30 }, { merge: true });updateDoc(docRef, data)
Update specific fields in a document.
const docRef = doc(db, "myproject/users/user123");
await updateDoc(docRef, {
age: 31,
lastLogin: new Date().toISOString(),
});deleteDoc(docRef)
Delete a document.
const docRef = doc(db, "myproject/users/user123");
await deleteDoc(docRef);Querying
query(collectionRef, ...constraints)
Create a query with constraints.
const q = query(
collection(db, "myproject/users"),
where("age", ">=", 18),
orderBy("name"),
limit(10)
);where(field, operator, value)
Create a where constraint.
Supported operators: ==, !=, <, <=, >, >=, array-contains, in, not-in, array-contains-any, matches
where("status", "==", "active");
where("age", ">=", 18);
where("tags", "array-contains", "javascript");
where("category", "in", ["tech", "science"]);orderBy(field, direction?)
Create an orderBy constraint.
orderBy("createdAt", "desc");
orderBy("name"); // defaults to 'asc'limit(count)
Limit the number of results.
limit(10);runStructuredQuery(collectionRef, ...constraints)
NEW: Execute queries using server-side structured query processing (recommended for better performance).
This function uses the POST :runQuery endpoint with structured query support, providing server-side filtering and sorting instead of client-side processing.
import {
db,
collection,
where,
orderBy,
limit,
runStructuredQuery,
} from "basebase-js";
// Server-side structured query with filtering, ordering, and limiting
const usersRef = collection(db, "myproject/users");
const results = await runStructuredQuery(
usersRef,
where("age", ">=", 18), // Server-side filtering
where("status", "==", "active"), // Multiple filters supported
orderBy("name", "asc"), // Server-side sorting
limit(10) // Limit results
);
// Process results
results.forEach((doc) => {
console.log("User:", doc.data());
});Advantages of runStructuredQuery:
- ✅ Server-side processing - Better performance for large datasets
- ✅ Native database filtering - More efficient than client-side filtering
- ✅ Reduced network traffic - Only matching documents are returned
- ✅ Same API - Uses the same
where,orderBy,limitconstraints
Supported operators: ==, !=, <, <=, >, >=, array-contains, in, not-in, array-contains-any, matches
Example with multiple filters:
// Find active adult users, sorted by creation date, limit 20
const results = await runStructuredQuery(
collection(db, "myproject/users"),
where("age", ">=", 21),
where("status", "==", "active"),
where("role", "in", ["admin", "moderator"]),
orderBy("createdAt", "desc"),
limit(20)
);Custom server configuration: For custom BaseBase server instances, use explicit database configuration:
import { getDatabase, collection, runStructuredQuery } from "basebase-js";
// Create database instance with custom server
const db = getDatabase({
token: "your_jwt_token",
baseUrl: "http://localhost:8000",
});
const results = await runStructuredQuery(
collection(db, "myproject/users"),
where("age", ">=", 18),
orderBy("name"),
limit(10)
);🧪 Testing
A simple test webapp is included at test.html to demonstrate SDK functionality:
Build the SDK:
npm install npm run build
Open the test webapp: Open
test.htmlin your browser (requires internet connection for js-cookie CDN). The webapp includes:- Phone verification authentication flow
- Document operations on a "test" collection
- Real-time UI updates
Test workflow:
- Set your API key in the test interface
- Request SMS verification code
- Verify code to authenticate
- Add/view/update/delete documents
- Test sign out functionality
Note: The test webapp uses the UMD build (
dist/index.umd.js) for browser compatibility and loads js-cookie from CDN. It defaults tohttps://api.basebase.us. Adjust the configuration for your BaseBase server instance.
🔧 Configuration
Browser Applications
Authentication required first:
import { requestCode, verifyCode, doc, getDoc, setDoc } from "basebase-js";
// 1. Authenticate first
await verifyCode("+1234567890", "123456", "bb_your_api_key");
// 2. Then use database - no setup required
const userRef = doc(db, "myproject/users/user123");
const snapshot = await getDoc(userRef);Server/Node.js Applications
For server environments, use the getDatabase function with a JWT token:
import { getDatabase, doc, getDoc } from "basebase-js";
// Create a database instance with your JWT token
const db = getDatabase("user_jwt_token_here");
// Now use normally
const userRef = doc(db, "myproject/users/user123");
const snapshot = await getDoc(userRef);TypeScript Configuration
The SDK is written in TypeScript and provides full type safety:
import { BasebaseDocumentData, DocumentSnapshot } from "basebase-js";
interface User extends BasebaseDocumentData {
name: string;
email: string;
age: number;
}
const userSnap: DocumentSnapshot = await getDoc(
doc(db, "myproject/users/user123")
);
const userData = userSnap.data() as User;🔒 Security
API Key Management
- Store your API key securely
- Never expose API keys in client-side code in production
- Use environment variables for configuration
Authentication Flow
- User provides phone number and username (alphanumeric only)
- SMS verification code is sent
- User enters code to get JWT token
- Token is stored in cookies and localStorage
- Token is automatically included in API requests
🚀 Migration from Firebase
BaseBase SDK is designed to be API-compatible with Firebase/Firestore. Here's how to migrate:
Before (Firebase)
import { initializeApp } from "firebase/app";
import {
getFirestore,
collection,
doc,
getDoc,
getDocs,
} from "firebase/firestore";
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const userRef = doc(db, "myproject/users/user123");
const userSnap = await getDoc(userRef);After (BaseBase)
import { verifyCode, db, doc, getDoc } from "basebase-js";
// Authenticate first
await verifyCode("+1234567890", "123456", "bb_your_api_key");
// Then use database - no initialization needed!
const userRef = doc(db, "myproject/users/user123");
const userSnap = await getDoc(userRef);Key Differences
- Authentication required first - BaseBase uses phone verification
- No initialization required - BaseBase exports a ready-to-use
dbobject after auth - Path format: BaseBase uses slash-separated paths instead of separate arguments (e.g.,
"users/user123"vs"users", "user123")
📱 Browser Support
- Chrome 60+
- Firefox 55+
- Safari 12+
- Edge 79+
🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details.
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🆘 Support
🔄 Changelog
See CHANGELOG.md for a list of changes and version history.