Last updated

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