Quick Start
protected async onSession(session: AppSession, sessionId: string, userId: string) {
// Store data
await session.simpleStorage.set('theme', 'dark');
// Retrieve data
const theme = await session.simpleStorage.get('theme');
// Check if exists
const hasTheme = await session.simpleStorage.hasKey('theme');
}
Key Features
- Automatic sync - Data syncs between your server and MentraOS Cloud
- User-isolated - Each user has separate storage
- App-scoped - Other apps can’t access your data
- Local caching - Fast reads after first fetch
- Simple API - Works like localStorage
Basic Operations
Store Data
await session.simpleStorage.set('username', 'john_doe');
await session.simpleStorage.set('score', '1000');
await session.simpleStorage.set('lastVisit', Date.now().toString());
Retrieve Data
const username = await session.simpleStorage.get('username');
if (username) {
session.layouts.showTextWall(`Welcome back, ${username}!`);
}
Check Existence
if (await session.simpleStorage.hasKey('onboardingComplete')) {
// User has completed onboarding
} else {
// Show onboarding
}
Delete Data
await session.simpleStorage.delete('temporaryData');
Clear All
await session.simpleStorage.clear();
Storing Complex Data
Storage is key-value pairs with string values. Use JSON for objects:
// Store object
const userData = { name: 'John', age: 30, premium: true };
await session.simpleStorage.set('user', JSON.stringify(userData));
// Retrieve object
const userJson = await session.simpleStorage.get('user');
const user = userJson ? JSON.parse(userJson) : null;
// Store array
const favorites = ['app1', 'app2', 'app3'];
await session.simpleStorage.set('favorites', JSON.stringify(favorites));
Batch Operations
Get All Keys
const keys = await session.simpleStorage.keys();
// ['theme', 'username', 'score']
Get All Data
const allData = await session.simpleStorage.getAllData();
// { theme: 'dark', username: 'john_doe', score: '1000' }
Set Multiple Items
await session.simpleStorage.setMultiple({
'setting1': 'value1',
'setting2': 'value2',
'setting3': 'value3'
});
Count Items
const count = await session.simpleStorage.size();
session.logger.info(`Stored ${count} items`);
Common Patterns
User Preferences
class PreferencesManager {
constructor(private storage: SimpleStorage) {}
async getTheme(): Promise<'light' | 'dark'> {
return (await this.storage.get('theme') as 'light' | 'dark') || 'light';
}
async setTheme(theme: 'light' | 'dark') {
await this.storage.set('theme', theme);
}
async getLanguage(): Promise<string> {
return await this.storage.get('language') || 'en';
}
}
First-Time User Detection
protected async onSession(session: AppSession, sessionId: string, userId: string) {
const isFirstTime = !(await session.simpleStorage.hasKey('hasUsedApp'));
if (isFirstTime) {
// Show tutorial
session.layouts.showTextWall('Welcome! Let me show you around...');
await session.simpleStorage.set('hasUsedApp', 'true');
} else {
// Returning user
session.layouts.showTextWall('Welcome back!');
}
}
Saving Session State
protected async onSession(session: AppSession, sessionId: string, userId: string) {
// Restore previous state
const lastPage = await session.simpleStorage.get('currentPage') || 'home';
const scrollPos = await session.simpleStorage.get('scrollPosition') || '0';
this.navigateToPage(session, lastPage, parseInt(scrollPos));
// Save state when user navigates
session.events.onButtonPress(async (data) => {
if (data.button === 'forward') {
await session.simpleStorage.set('currentPage', 'next');
}
});
}
protected async onStop(sessionId: string, userId: string, reason: string) {
// State is already saved from event handlers
}
Usage Counter
protected async onSession(session: AppSession, sessionId: string, userId: string) {
// Increment usage count
const countStr = await session.simpleStorage.get('usageCount') || '0';
const count = parseInt(countStr) + 1;
await session.simpleStorage.set('usageCount', count.toString());
session.logger.info(`User has opened app ${count} times`);
// Show milestone
if (count === 10) {
session.layouts.showTextWall('🎉 You've used this app 10 times!');
}
}
Feature Flags
protected async onSession(session: AppSession, sessionId: string, userId: string) {
// Track which features user has tried
const featuresJson = await session.simpleStorage.get('featuresUsed');
const features = featuresJson ? JSON.parse(featuresJson) : [];
if (!features.includes('voice_commands')) {
// First time using voice - show hint
session.layouts.showTextWall('Try saying "help" for voice commands');
features.push('voice_commands');
await session.simpleStorage.set('featuresUsed', JSON.stringify(features));
}
}
Simple Storage caches data locally for fast access:
// First call fetches from cloud
const theme = await storage.get('theme'); // Network request
// Subsequent calls use cache
const theme2 = await storage.get('theme'); // Instant
// Set operations update cache immediately
await storage.set('theme', 'light'); // Cache updated + cloud sync
Cache is per-session. New sessions fetch fresh data from cloud.
Best Practices
✅ Do This
// Use descriptive keys
await storage.set('user.theme', 'dark');
await storage.set('app.lastSyncTime', Date.now().toString());
// Provide defaults
const theme = await storage.get('theme') || 'light';
// Handle JSON errors
try {
const data = JSON.parse(await storage.get('config') || '{}');
} catch (e) {
session.logger.error('Invalid JSON in storage');
}
// Store environment variables for API keys elsewhere
const apiKey = process.env.EXTERNAL_API_KEY; // ✅
❌ Avoid This
// Vague key names
await storage.set('data', 'value');
// No default values
const theme = await storage.get('theme'); // Could be undefined
// Storing large data (>1MB per value)
await storage.set('bigFile', hugeString); // Use external storage
// Storing secrets in user storage
await storage.set('apiKey', 'secret'); // Security risk
Limitations
| Aspect | Limit | Recommendation |
|---|
| Value size | ~1MB | Store large files externally |
| Total storage | ~10MB per user | Keep data minimal |
| Keys | Unlimited | Use namespaced keys |
| API calls | No hard limit | Batch when possible |
Error Handling
try {
await session.simpleStorage.set('key', 'value');
} catch (error) {
session.logger.error('Storage error:', error);
// Fallback: use in-memory storage
}
// Get returns undefined on error
const value = await session.simpleStorage.get('key');
if (value === undefined) {
// Either doesn't exist or error occurred
session.logger.warn('No value found for key');
}
Complete Example
class NotesApp extends AppServer {
protected async onSession(session: AppSession, sessionId: string, userId: string) {
// Load saved notes
const notesJson = await session.simpleStorage.get('notes');
const notes = notesJson ? JSON.parse(notesJson) : [];
session.layouts.showTextWall(`You have ${notes.length} notes`);
// Listen for voice input to create notes
session.events.onTranscription(async (data) => {
if (data.isFinal && data.text.startsWith('note:')) {
const noteText = data.text.replace('note:', '').trim();
// Add note
notes.push({
text: noteText,
timestamp: Date.now()
});
// Save
await session.simpleStorage.set('notes', JSON.stringify(notes));
session.layouts.showTextWall('Note saved!');
}
});
}
}
API Reference
| Method | Description |
|---|
get(key) | Get value by key |
set(key, value) | Store key-value pair |
hasKey(key) | Check if key exists |
delete(key) | Remove key |
clear() | Delete all data |
keys() | Get all keys |
size() | Count items |
getAllData() | Get all key-value pairs |
setMultiple(data) | Store multiple pairs at once |
Simple Storage is ideal for preferences and app state. For user authentication data or external API keys, use environment variables or secure credential storage.