Building a User Preferences System
This tutorial demonstrates how to build a user preferences system using the Memory API to store and manage user preferences and settings.
Prerequisites
Before you begin, you'll need:
- An API key for the Memory API
- Basic understanding of async/await and REST APIs
- Node.js installed
- Express.js for the web server
Implementation
1. Project Setup
Create a new project and set up your environment:
mkdir user-preferences
cd user-preferences
npm init -y
npm install express dotenv node-fetch
npm install @papr/memory
Create a .env
file:
PAPR_MEMORY_API_KEY=your_api_key_here
2. Creating the Preferences Manager
Create src/preferences-manager.ts
:
import Papr from '@papr/memory';
import dotenv from 'dotenv';
dotenv.config();
export class PreferencesManager {
private client: any;
constructor() {
this.client = new Papr({
apiKey: process.env.PAPR_MEMORY_API_KEY!
});
}
async savePreference(userId: string, key: string, value: any, isExternalId: boolean = false) {
try {
const response = await this.client.memory.add({
content: JSON.stringify({ key, value }),
type: 'user_preference',
metadata: {
...(isExternalId ? { external_user_id: userId } : { userId }),
preferenceKey: key,
timestamp: new Date().toISOString()
}
});
return response;
} catch (error) {
throw new Error(`Failed to save preference: ${error.message}`);
}
}
async getPreference(userId: string, key: string, isExternalId: boolean = false) {
try {
const results = await this.client.memory.search({
query: '',
metadata: {
type: 'user_preference',
...(isExternalId ? { external_user_id: userId } : { userId }),
preferenceKey: key
},
rank_results: false,
max_memories: 1
});
if (results.data.memories.length === 0) {
return null;
}
const preference = JSON.parse(results.data.memories[0].content);
return preference.value;
} catch (error) {
throw new Error(`Failed to get preference: ${error.message}`);
}
}
async getAllPreferences(userId: string, isExternalId: boolean = false) {
try {
const results = await this.client.memory.search({
query: '',
metadata: {
type: 'user_preference',
...(isExternalId ? { external_user_id: userId } : { userId })
},
rank_results: false
});
const preferences: Record<string, any> = {};
for (const memory of results.data.memories) {
const preference = JSON.parse(memory.content);
preferences[preference.key] = preference.value;
}
return preferences;
} catch (error) {
throw new Error(`Failed to get preferences: ${error.message}`);
}
}
async deletePreference(userId: string, key: string, isExternalId: boolean = false) {
try {
const results = await this.client.memory.search({
query: '',
metadata: {
type: 'user_preference',
...(isExternalId ? { external_user_id: userId } : { userId }),
preferenceKey: key
},
rank_results: false
});
for (const memory of results.data.memories) {
await this.client.memory.delete(memory.id);
}
} catch (error) {
throw new Error(`Failed to delete preference: ${error.message}`);
}
}
}
3. Setting up the API Server
Create src/server.ts
:
import express from 'express';
import { PreferencesManager } from './preferences-manager';
const app = express();
app.use(express.json());
const preferencesManager = new PreferencesManager();
// Helper function to determine if user ID is external
function isExternalId(req: express.Request): boolean {
return req.query.external === 'true';
}
// Save a preference
app.post('/users/:userId/preferences/:key', async (req, res) => {
try {
const { userId, key } = req.params;
const { value } = req.body;
const external = isExternalId(req);
const result = await preferencesManager.savePreference(userId, key, value, external);
res.json(result);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Get a specific preference
app.get('/users/:userId/preferences/:key', async (req, res) => {
try {
const { userId, key } = req.params;
const external = isExternalId(req);
const value = await preferencesManager.getPreference(userId, key, external);
if (value === null) {
res.status(404).json({ error: 'Preference not found' });
} else {
res.json({ value });
}
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Get all preferences
app.get('/users/:userId/preferences', async (req, res) => {
try {
const { userId } = req.params;
const external = isExternalId(req);
const preferences = await preferencesManager.getAllPreferences(userId, external);
res.json(preferences);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Delete a preference
app.delete('/users/:userId/preferences/:key', async (req, res) => {
try {
const { userId, key } = req.params;
const external = isExternalId(req);
await preferencesManager.deletePreference(userId, key, external);
res.json({ message: 'Preference deleted successfully' });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Usage Example
Here's how to use the user preferences system:
Using Internal User IDs
# Set a preference (theme = dark)
curl -X POST http://localhost:3000/users/user123/preferences/theme \
-H "Content-Type: application/json" \
-d '{"value": "dark"}'
# Get a specific preference
curl http://localhost:3000/users/user123/preferences/theme
# Get all preferences for a user
curl http://localhost:3000/users/user123/preferences
# Delete a preference
curl -X DELETE http://localhost:3000/users/user123/preferences/theme
Using External User IDs
# Set a preference using external user ID
curl -X POST "http://localhost:3000/users/external_user_abc/preferences/theme?external=true" \
-H "Content-Type: application/json" \
-d '{"value": "light"}'
# Get a specific preference with external user ID
curl "http://localhost:3000/users/external_user_abc/preferences/theme?external=true"
# Get all preferences for an external user ID
curl "http://localhost:3000/users/external_user_abc/preferences?external=true"
# Delete a preference with external user ID
curl -X DELETE "http://localhost:3000/users/external_user_abc/preferences/theme?external=true"
Key Features
- Persistent user preferences using the Memory API
- Simple CRUD operations for managing preferences
- Fast retrieval of user settings
- Support for both internal and external user IDs
- Automatic user creation when using external IDs
- No need to create users explicitly when using external user IDs
Best Practices
Security
- Validate and sanitize user input
- Implement proper authentication
- Protect sensitive user data
Performance
- Cache frequently accessed preferences
- Consider user privacy and data protection
- Implement rate limiting for API requests
Conclusion
This tutorial demonstrated how to build a user preferences system using the Memory API. You can extend this system to handle more complex preferences, versioning, or multi-tenant applications.
The complete code for this tutorial is available in our examples repository.
Resources
- API Reference for detailed endpoint information
- Memory Management Guide