Overview

Crave’s API supports multiple integration patterns depending on your technical requirements, timeline, and existing infrastructure. This guide outlines the different approaches available.

API Capabilities

Core Restaurant Operations

The Crave API handles standard restaurant ordering operations:
  • Menu Management: Product catalog with categories, modifiers, pricing, and availability tracking
  • Cart Processing: Session-based cart management with tax calculations and validation
  • Payment Processing: Stripe integration for payment intent creation and confirmation
  • Order Management: Order creation, status tracking, and fulfillment workflow
  • Multi-Location Support: Location-specific menus, hours, and configuration
  • Customer Management: User authentication, profiles, and order history

API Architecture

REST-based: Standard HTTP methods (GET, POST, PUT, DELETE) with JSON payloads Authentication: API key-based authentication with rate limiting (200 requests per 10 minutes) Base URL: https://api.cravejs.com/api/v1 Response Format: Consistent JSON structure with error handling

Technical Requirements

Subscription Model: Paid subscription required for API access
  • Storefront APIs: Available with standard subscription
  • Admin APIs: Enterprise tier subscription required
Environment: Production-hosted service (no local installation available) Integration Methods: Direct API calls, server-side proxy, or starter template

Implementation Considerations

Security: API keys must be handled server-side to prevent exposure in client-side code Rate Limiting: 200 requests per 10 minutes per IP address with automatic backoff Error Handling: Standard HTTP status codes with detailed error responses Caching: Recommended for menu data and location information to reduce API calls Payment Flow: Requires Stripe integration for payment processing

Integration Approaches

1. Custom Frontend with Direct API Integration

Build your own frontend application that calls Crave’s API directly. Architecture:
Your Frontend → Crave API → Backend Services
Implementation:
// Direct API calls from your frontend
const menu = await fetch(`https://api.cravejs.com/api/v1/locations/${locationId}/menus`, {
  headers: {
    'X-API-Key': process.env.CRAVEUP_API_KEY,
    'Content-Type': 'application/json'
  }
});
Best For:
  • Custom user experiences
  • Existing React/Vue/Angular applications
  • Teams with strong frontend capabilities
  • Projects requiring unique workflows
Considerations:
  • API key must be handled server-side
  • Rate limiting applies per IP address
  • Error handling is your responsibility
  • Payment processing requires Stripe integration

2. Server-Side Proxy Pattern

Create a backend service that proxies requests to Crave’s API. Architecture:
Your Frontend → Your Backend → Crave API
Implementation:
// Your backend API route
export default async function handler(req, res) {
  const response = await fetch(`https://api.cravejs.com/api/v1/locations/${locationId}/menus`, {
    headers: {
      'X-API-Key': process.env.CRAVEUP_API_KEY,
      'Content-Type': 'application/json'
    }
  });
  
  const data = await response.json();
  res.json(data);
}
Best For:
  • Additional business logic layer
  • Data transformation requirements
  • Caching and performance optimization
  • Security-sensitive applications
Considerations:
  • Additional server infrastructure required
  • More complex deployment
  • Better security (API keys on server)
  • Enables caching and data transformation

3. Hybrid Approach with Starter Template

Use Crave’s starter template as a foundation and customize it. Architecture:
Crave Starter Template → Your Customizations → Crave API
Implementation:
# Clone the starter template
git clone https://github.com/craveup/storefront-template
cd storefront-template

# Install dependencies
npm install

# Configure environment
cp .env.example .env.local
# Add your API keys

# Customize components
# Modify components/, pages/, styles/
Best For:
  • Rapid development
  • Standard ordering experiences
  • Teams new to Crave
  • MVP and proof-of-concept projects
Considerations:
  • Pre-built components and patterns
  • Faster time to market
  • Limited customization initially
  • Next.js based (React required)

Technical Implementation Details

Authentication Patterns

Client-Side (Not Recommended):
// ❌ Don't do this - exposes API key
const response = await fetch('/api/v1/locations/123', {
  headers: {
    'X-API-Key': 'your_api_key' // Exposed in browser
  }
});
Server-Side (Recommended):
// ✅ Do this - API key on server
// pages/api/menu.js
export default async function handler(req, res) {
  const response = await fetch(`https://api.cravejs.com/api/v1/locations/${req.query.locationId}/menus`, {
    headers: {
      'X-API-Key': process.env.CRAVEUP_API_KEY // Secure on server
    }
  });
  
  const data = await response.json();
  res.json(data);
}

Error Handling Patterns

Basic Error Handling:
async function fetchMenu(locationId) {
  try {
    const response = await fetch(`/api/v1/locations/${locationId}/menus`, {
      headers: { 'X-API-Key': apiKey }
    });
    
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    
    return await response.json();
  } catch (error) {
    console.error('Menu fetch failed:', error);
    throw error;
  }
}
Advanced Error Handling:
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url, options);
      
      if (response.status === 429) {
        // Rate limited - wait and retry
        const retryAfter = response.headers.get('Retry-After') || 60;
        await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
        continue;
      }
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }
      
      return await response.json();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
}

Caching Patterns

Memory Cache:
const cache = new Map();

async function getCachedMenu(locationId) {
  const cacheKey = `menu_${locationId}`;
  
  if (cache.has(cacheKey)) {
    return cache.get(cacheKey);
  }
  
  const menu = await fetchMenu(locationId);
  cache.set(cacheKey, menu);
  
  // Cache for 5 minutes
  setTimeout(() => cache.delete(cacheKey), 5 * 60 * 1000);
  
  return menu;
}
Redis Cache:
const redis = require('redis');
const client = redis.createClient();

async function getCachedMenu(locationId) {
  const cacheKey = `menu_${locationId}`;
  
  // Try cache first
  const cached = await client.get(cacheKey);
  if (cached) {
    return JSON.parse(cached);
  }
  
  // Fetch from API
  const menu = await fetchMenu(locationId);
  
  // Cache for 5 minutes
  await client.setex(cacheKey, 300, JSON.stringify(menu));
  
  return menu;
}

Technology Stack Considerations

Frontend Frameworks

React/Next.js:
// Recommended for starter template users
import { useState, useEffect } from 'react';

export default function Menu({ locationId }) {
  const [menu, setMenu] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    fetchMenu(locationId).then(setMenu).finally(() => setLoading(false));
  }, [locationId]);
  
  if (loading) return <div>Loading...</div>;
  return <div>{/* Render menu */}</div>;
}
Vue.js:
// Custom integration approach
export default {
  data() {
    return {
      menu: null,
      loading: true
    };
  },
  async mounted() {
    try {
      this.menu = await this.$http.get(`/api/menu?locationId=${this.locationId}`);
    } finally {
      this.loading = false;
    }
  }
};
Angular:
// Service-based approach
@Injectable()
export class MenuService {
  constructor(private http: HttpClient) {}
  
  getMenu(locationId: string): Observable<Menu> {
    return this.http.get<Menu>(`/api/menu?locationId=${locationId}`);
  }
}

Backend Options

Node.js/Express:
app.get('/api/menu', async (req, res) => {
  const { locationId } = req.query;
  
  const menu = await fetch(`https://api.cravejs.com/api/v1/locations/${locationId}/menus`, {
    headers: { 'X-API-Key': process.env.CRAVEUP_API_KEY }
  }).then(r => r.json());
  
  res.json(menu);
});
Python/FastAPI:
@app.get("/api/menu")
async def get_menu(location_id: str):
    response = await httpx.get(
        f"https://api.cravejs.com/api/v1/locations/{location_id}/menus",
        headers={"X-API-Key": os.getenv("CRAVEUP_API_KEY")}
    )
    return response.json()

Security Considerations

API Key Management

Environment Variables:
# .env.local
CRAVEUP_API_KEY=your_api_key_here
CRAVEUP_API_BASE_URL=https://api.cravejs.com
Key Rotation:
// Support multiple keys for rotation
const apiKeys = [
  process.env.CRAVEUP_API_KEY_PRIMARY,
  process.env.CRAVEUP_API_KEY_SECONDARY
];

async function fetchWithKeyRotation(url, options) {
  for (const key of apiKeys) {
    try {
      return await fetch(url, {
        ...options,
        headers: {
          ...options.headers,
          'X-API-Key': key
        }
      });
    } catch (error) {
      if (error.status === 401) continue;
      throw error;
    }
  }
  throw new Error('All API keys failed');
}

CORS Configuration

// For direct API calls from frontend
const corsOptions = {
  origin: process.env.FRONTEND_URL,
  credentials: true
};

Performance Optimization

Request Batching

// Batch multiple requests
async function loadStorefront(locationId) {
  const [location, menu, hours] = await Promise.all([
    fetch(`/api/v1/locations/${locationId}`),
    fetch(`/api/v1/locations/${locationId}/menus`),
    fetch(`/api/v1/locations/${locationId}/time-intervals`)
  ]);
  
  return {
    location: await location.json(),
    menu: await menu.json(),
    hours: await hours.json()
  };
}

Progressive Loading

// Load critical data first
async function loadStorefrontProgressive(locationId) {
  // Load location details immediately
  const location = await fetch(`/api/v1/locations/${locationId}`);
  
  // Load menu in background
  const menuPromise = fetch(`/api/v1/locations/${locationId}/menus`);
  
  return {
    location: await location.json(),
    menuPromise
  };
}

Choosing Your Approach

Decision Matrix

FactorDirect APIServer ProxyStarter Template
Development SpeedMediumSlowFast
CustomizationHighHighMedium
SecurityMediumHighMedium
PerformanceMediumHighMedium
ComplexityMediumHighLow

Recommendations

Use Direct API Integration when:
  • You have existing frontend infrastructure
  • You need maximum customization
  • Your team has strong API integration experience
  • You can handle server-side proxy setup
Use Server Proxy when:
  • You need additional business logic
  • You require caching and performance optimization
  • Security is a primary concern
  • You have backend development resources
Use Starter Template when:
  • You need to launch quickly
  • You’re building a standard ordering experience
  • Your team is new to Crave
  • You’re creating an MVP or proof of concept

Next Steps

  1. Authentication Guide - Set up API keys
  2. Quick Start Tutorial - Build your first integration
  3. API Reference - Detailed endpoint documentation
  4. Stripe Integration - Payment processing setup