Skip to content

Context Utilities

skitsanos edited this page Jul 20, 2025 · 4 revisions

Context Utilities: Your API Superpowers

What if building APIs felt like using superpowers instead of fighting frameworks? 🦸‍♂️

Traditional backend development means writing the same boilerplate over and over: database connections, authentication, validation, caching, job queues...

Foxx Builder eliminates this pain with powerful context utilities - pre-built, battle-tested functions that handle the hard stuff so you can focus on your business logic.

The Magic of module.context

Every route in your Foxx Builder API has access to a rich set of utilities via module.context. Think of it as your API's Swiss Army knife:

// In any route handler
const { db, auth, config, jobs, utils } = module.context;

Why This Changes Everything

Traditional approach:

// Express.js - you build everything from scratch
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const redis = require('redis');
const mongodb = require('mongodb');

// 50+ lines of setup code...
// Database connection management
// JWT middleware
// Caching layer
// Error handling
// Validation logic

Foxx Builder approach:

// routes/users/post.js
module.exports = {
  handler: (req, res) => {
    const { db, auth } = module.context;
    
    // That's it. Everything just works.
    const user = db.insert('users', req.body);
    const token = auth.encode({ userId: user._key });
    
    return { user, token };
  }
};

🗄️ Database Operations: Zero-Config Database Power

Skip the ORM complexity. Get type-safe, optimized database operations out of the box:

Basic CRUD Operations

const { db } = module.context;

// Create
const newUser = await db.insert('users', {
  username: 'johndoe',
  email: '[email protected]',
  role: 'user'
});

// Read with filters
const activeUsers = await db.find('users', 
  { status: 'active' }, 
  { limit: 20, sort: { createdAt: -1 } }
);

// Update
const updated = await db.update('users', userId, {
  lastLogin: new Date().toISOString()
});

// Delete
await db.remove('users', userId);

Advanced Database Features

// Batch operations for performance
const users = await db.insertMany('users', [user1, user2, user3]);

// Caching built-in
const cachedData = await db.find('posts', {}, {
  useCache: true,
  cacheTtl: 300000  // 5 minutes
});

// Raw AQL for complex queries
const analytics = await db.query(`
  FOR post IN posts
    COLLECT author = post.authorId WITH COUNT INTO postCount
    SORT postCount DESC
    LIMIT 10
    RETURN { author, postCount }
`);

🤖 Vector Search: AI-Powered Search

Build AI features without complex infrastructure:

const { db } = module.context;

// Find similar products using AI embeddings
const similarProducts = await db.vector.cosineSimilarity(
  'products',           // collection
  'embedding',          // vector field
  userSearchVector,     // query vector
  { 
    limit: 10,
    minScore: 0.8,
    filter: { category: 'electronics' }
  }
);

// Recommendation engine in 3 lines
const recommendations = await db.vector.l2Distance(
  'recommendations',
  'userPreferenceVector',
  currentUserVector,
  { limit: 5 }
);

🔐 Authentication: Enterprise-Grade Security

JWT authentication that actually works properly:

Token Management

const { auth } = module.context;

// Create tokens with custom claims
const accessToken = auth.encode({
  userId: user._key,
  roles: ['user', 'premium'],
  permissions: ['read:profile', 'write:posts']
});

// Refresh token system
const refreshToken = auth.createRefreshToken(user._key);

// Validate and extract data
const payload = auth.decode(token);
const userId = auth.validateToken(token);

// Check expiration
if (auth.isExpired(token)) {
  // Handle expired token
}

Automatic Route Protection

// Create middleware once, use everywhere
const authMiddleware = auth.createMiddleware({
  exempt: ['/login', '/signup', '/health'],
  onSuccess: (req, res) => {
    // Automatically attach user data
    const user = module.context.db.get('users', req.userId);
    req.user = user;
  }
});

// Apply to all routes automatically
module.context.use(authMiddleware);

Route-Level Authorization

// routes/admin/users/get.js
module.exports = {
  name: 'List All Users',
  requiresAuth: true,        // Built-in auth check
  requiresRole: 'admin',     // Role-based access
  
  handler: (req, res) => {
    // req.user is automatically available
    const users = module.context.db.find('users', {});
    return { 
      users, 
      requestedBy: req.user.username 
    };
  }
};

⚙️ Configuration: Type-Safe App Settings

Manage configuration without the headaches:

const { config } = module.context;

// Type-safe configuration access
const apiKey = config.getString('stripe.apiKey');
const maxUploadSize = config.getNumber('upload.maxSize', 10485760);
const enableAI = config.getBoolean('features.aiSearch', false);
const emailSettings = config.getJSON('email.smtp', {});

// Feature flags made easy
if (config.isEnabled('experimental.newDashboard')) {
  // Show new dashboard
}

// Environment-aware configuration
const dbConfig = config.getSection('database');

📋 Job Queue: Background Processing

Handle long-running tasks without blocking requests:

const { jobs } = module.context;

// Send email in background
const emailJobId = jobs.run('sendEmail', {
  to: '[email protected]',
  template: 'welcome',
  data: { username: 'johndoe' }
}, {
  delay: 5000,      // 5 second delay
  maxFailures: 3    // Retry up to 3 times
});

// Process images asynchronously  
const imageJobId = jobs.run('processImages', {
  userId: user._key,
  images: uploadedFiles
});

// Check job status
const jobStatus = jobs.getStatus(emailJobId);

🔄 Transactions: Data Consistency

Ensure data integrity with atomic operations:

const { transaction } = module.context;

// Transfer credits between users atomically
const result = transaction.execute(
  function(params) {
    const users = db._collection('users');
    const logs = db._collection('transaction_logs');
    
    // Deduct from sender
    const sender = users.document(params.senderId);
    if (sender.credits < params.amount) {
      throw new Error('Insufficient credits');
    }
    
    users.update(params.senderId, { 
      credits: sender.credits - params.amount 
    });
    
    // Add to receiver
    const receiver = users.document(params.receiverId);
    users.update(params.receiverId, { 
      credits: receiver.credits + params.amount 
    });
    
    // Log transaction
    logs.save({
      type: 'credit_transfer',
      senderId: params.senderId,
      receiverId: params.receiverId,
      amount: params.amount,
      timestamp: Date.now()
    });
    
    return { success: true };
  },
  { write: ['users', 'transaction_logs'] },
  { senderId, receiverId, amount: 100 }
);

🛠️ Utilities: Common Tasks Made Simple

Everyday utilities that save you time:

const { utils } = module.context;

// Email validation
const isValidEmail = utils.isEmail('[email protected]'); // true

// Build database filters from user input
const filters = [
  { key: 'status', op: '=', value: 'active' },
  { key: 'age', op: '>', value: 18 },
  { key: 'city', op: 'LIKE', value: 'New%' }
];

const filterExpression = utils.filter(filters, 'user');
// Use in AQL: FOR user IN users ${filterExpression} RETURN user

// Advanced query building with custom conditions
const customQuery = utils.filterBuilder(filters, 'user');

🏗️ Real-World Example: Complete User System

Here's how all these utilities work together in a real endpoint:

// routes/users/post.js - User registration
const joi = require('joi');

module.exports = {
  name: 'Register New User',
  body: {
    type: 'object',
    properties: {
      username: { type: 'string', minLength: 3 },
      email: { type: 'string', format: 'email' },
      password: { type: 'string', minLength: 8 }
    },
    required: ['username', 'email', 'password']
  },
  
  handler: async (req, res) => {
    const { db, auth, jobs, utils, config } = module.context;
    const { username, email, password } = req.body;
    
    // Validate email format
    if (!utils.isEmail(email)) {
      res.throw(400, 'Invalid email format');
    }
    
    // Check if user exists
    const existingUser = await db.find('users', { email });
    if (existingUser.length > 0) {
      res.throw(409, 'User already exists');
    }
    
    // Create user with encrypted password
    const hashedPassword = crypto.sha384(password);
    const newUser = await db.insert('users', {
      username,
      email,
      password: hashedPassword,
      status: 'pending',
      createdAt: new Date().toISOString()
    });
    
    // Generate auth token
    const token = auth.encode({
      userId: newUser._key,
      roles: ['user']
    });
    
    // Send welcome email in background
    if (config.getBoolean('email.enabled', true)) {
      jobs.run('sendWelcomeEmail', {
        userId: newUser._key,
        email,
        username
      });
    }
    
    // Return user data (without password)
    return {
      success: true,
      user: {
        id: newUser._key,
        username,
        email,
        status: 'pending'
      },
      token
    };
  }
};

Performance Benefits

Database Co-location

Since your API runs inside ArangoDB, context utilities have zero network latency:

// This executes instantly - no network round-trips
const users = await db.find('users', { active: true });
const posts = await db.find('posts', { authorId: users[0]._key });
const comments = await db.find('comments', { postId: posts[0]._key });

Automatic Optimizations

  • Connection pooling: Handled automatically
  • Query caching: Built into database operations
  • Memory efficiency: Shared contexts across requests
  • Transaction management: Optimized for ArangoDB

Migration from Traditional Frameworks

Before (Express.js):

// 200+ lines of setup
const express = require('express');
const mongoose = require('mongoose');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const redis = require('redis');
const nodemailer = require('nodemailer');

// Database connection
mongoose.connect(process.env.DB_URL);

// Redis for caching
const redisClient = redis.createClient();

// JWT middleware
const authMiddleware = (req, res, next) => {
  // 30+ lines of auth logic
};

// Email service
const emailService = nodemailer.createTransport({
  // Configuration...
});

// Route handler
app.post('/users', authMiddleware, async (req, res) => {
  // Manual validation
  // Manual password hashing
  // Manual database operations
  // Manual error handling
  // Manual response formatting
});

After (Foxx Builder):

// routes/users/post.js
module.exports = {
  requiresAuth: true,
  body: { /* validation schema */ },
  
  handler: (req, res) => {
    const { db, auth, jobs } = module.context;
    
    // Everything just works
    const user = db.insert('users', userData);
    const token = auth.encode({ userId: user._key });
    jobs.run('sendWelcome', { email: userData.email });
    
    return { user, token };
  }
};

Why Developers Love Context Utilities

"I went from 200 lines of boilerplate to 20 lines of business logic. The context utilities handle everything I used to spend hours on." - Sarah, Full-stack Developer

"The database utilities alone saved me weeks. No ORM complexity, just intuitive operations that work." - Mike, Backend Developer

"Authentication that actually works out of the box? This is what I've been waiting for." - Alex, Startup CTO

Getting Started

Context utilities are available in every route automatically:

// Any route file
module.exports = {
  handler: (req, res) => {
    const { db, auth, config, jobs, utils } = module.context;
    
    // Build amazing APIs without the boilerplate
  }
};

Ready to explore more advanced patterns? Check out API Routes to see how context utilities integrate with file-based routing.

Clone this wiki locally