> ## Documentation Index
> Fetch the complete documentation index at: https://docs.everybite.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Data Freshness

> Why we handle caching so you don't have to

## Why You Shouldn't Cache

Menu data is more dynamic than it appears. A dish can sell out mid-service. A location can 86 an ingredient. Nutrition data gets corrected. Seasonal specials appear and disappear. When a guest with a peanut allergy sees stale data that doesn't reflect a recipe change, that's not just a bad experience—it's a safety issue.

EveryBite handles all caching internally. We've invested heavily in a multi-layer caching infrastructure that ensures:

* **Fast response times** optimized for real-time menu browsing
* **Real-time accuracy** for availability and nutrition data
* **Automatic invalidation** when upstream data changes

Your job is simple: call the API, trust the response, display it to the guest.

## Our Caching Architecture

EveryBite is built on AWS using industry best practices for performance, reliability, and security. Our infrastructure spans multiple availability zones with automatic failover, and we leverage AWS's global network to minimize latency for your users regardless of location. We handle caching at multiple layers so you don't have to:

| Layer                   | What It Does                                         |
| ----------------------- | ---------------------------------------------------- |
| **CloudFront**          | Global CDN for DDoS protection and TLS termination   |
| **ElastiCache (Redis)** | Application-level caching of menu and nutrition data |
| **Database**            | Source of truth, updated by Olo/Toast/Admin          |

When menu data changes upstream (Olo webhook, admin update), we invalidate affected caches immediately. You always get fresh data without managing cache invalidation yourself.

## How We Sync Data

EveryBite integrates with various third-party systems to keep menu, nutrition, and allergen data current. Data flows into our platform through three sync methods, each with different freshness characteristics.

### Sync Methods

**Real-time**

Changes sync instantly via webhooks. When the source system updates data, we're notified immediately and propagate the change. No action required—updates flow automatically. This provides the best data freshness for systems that support real-time notifications.

**Scheduled**

Data syncs automatically every hour. We connect to third-party APIs and pull updates on a regular schedule. No action required—freshness is within 1 hour of source changes.

**File Upload**

Data updates when new files are provided. Supports PDFs, spreadsheets, and structured exports from third-party systems. Freshness depends on when files are uploaded.

### Connectors

Each third-party system has a dedicated **connector**—a translation layer that maps between EveryBite's internal data model and the external system's format and API. Connectors handle the specifics of how each integration works while using one of the sync methods above.

| System    | Sync Method              | Freshness                 |
| --------- | ------------------------ | ------------------------- |
| MenuCalc  | Real-time                | Instant                   |
| Trustwell | Scheduled or File Upload | Up to 1 hour or on upload |
| PDF Menus | File Upload              | On upload                 |

<Info>
  For real-time connectors like MenuCalc, changes to recipes, nutrition, allergens, or ingredients are reflected in the API within seconds. For scheduled connectors, changes appear within 1 hour.
</Info>

## What This Means for You

### Don't Cache API Responses

```typescript theme={null}
// ❌ Don't do this
const cachedMenu = localStorage.getItem('menu');
if (cachedMenu && !isExpired(cachedMenu)) {
  return JSON.parse(cachedMenu);
}
const menu = await fetchMenu();
localStorage.setItem('menu', JSON.stringify(menu));

// ✅ Do this instead
const menu = await fetchMenu();
```

### Don't Add Cache Headers

```typescript theme={null}
// ❌ Don't do this
fetch(url, {
  headers: {
    'Cache-Control': 'max-age=300'
  }
});

// ✅ Just call the API
fetch(url);
```

### Don't Batch Requests to Reduce Calls

```typescript theme={null}
// ❌ Don't do this
const allDishes = await fetchAllDishes();
const dish = allDishes.find(d => d.id === dishId);

// ✅ Fetch what you need when you need it
const dish = await fetchDish(dishId);
```

## When Fresh Data Matters Most

### Allergen Information

A recipe change that adds a new allergen must be reflected immediately. Our system propagates allergen updates within seconds of an admin change.

<Steps>
  <Step title="Recipe Updated">
    Admin updates a recipe in the portal, adding dairy as an ingredient.
  </Step>

  <Step title="Cache Invalidated">
    EveryBite immediately invalidates all cached data for that dish.
  </Step>

  <Step title="Fresh Data Served">
    Next API request for that dish returns updated allergen information showing dairy.
  </Step>

  <Step title="Warning Displayed">
    Your app shows the dairy allergen warning to guests with dairy restrictions.
  </Step>
</Steps>

### Real-Time Availability

When a dish sells out, the update propagates through webhooks:

1. POS marks dish unavailable
2. Olo/Toast sends webhook to EveryBite
3. We invalidate caches and update availability
4. Next API call reflects the change

<Info>
  Availability changes typically propagate within **5 seconds** of the POS update.
</Info>

### Nutrition Corrections

When nutrition data is corrected (calorie count was wrong, serving size updated), caches are invalidated immediately. Guests making health-conscious decisions always see accurate information.

## Performance Without Client Caching

You might worry that not caching will hurt performance. It won't. Here's why:

| Concern           | Our Solution                                      |
| ----------------- | ------------------------------------------------- |
| Latency           | Edge caching at 200+ global locations             |
| Repeated requests | Application-level caching with smart invalidation |
| High traffic      | Auto-scaling infrastructure handles spikes        |
| Bandwidth         | Efficient GraphQL—request only fields you need    |

## The One Exception: Filter Options

The `filterOptions` query returns available diets, allergens, and nutrient ranges for a chain. This data changes rarely (when a chain adds/removes menu items). You may cache this response briefly:

```typescript theme={null}
// Filter options can be cached for a few minutes
const FILTER_OPTIONS_TTL = 5 * 60 * 1000; // 5 minutes

async function getFilterOptions() {
  const cached = filterOptionsCache.get(chainId);
  if (cached && Date.now() - cached.timestamp < FILTER_OPTIONS_TTL) {
    return cached.data;
  }

  const data = await fetchFilterOptions();
  filterOptionsCache.set(chainId, { data, timestamp: Date.now() });
  return data;
}
```

<Warning>
  Only cache `filterOptions`. Never cache search results, dish details, or any data that includes nutrition, allergens, or availability.
</Warning>

## Best Practices

<CardGroup cols={2}>
  <Card title="Trust the API" icon="shield-check">
    Our caching is optimized for your use case. Let us handle freshness.
  </Card>

  <Card title="Fetch on Demand" icon="bolt">
    Request data when you need it. Don't prefetch and store.
  </Card>

  <Card title="Use GraphQL Efficiently" icon="code">
    Request only the fields you need—less data transferred, faster responses.
  </Card>

  <Card title="Handle Errors Gracefully" icon="triangle-exclamation">
    If a request fails, retry rather than falling back to stale cached data.
  </Card>
</CardGroup>

## Summary

| Data Type      | Cache Locally? | Why                                            |
| -------------- | -------------- | ---------------------------------------------- |
| Search results | No             | Match status depends on real-time availability |
| Dish details   | No             | Nutrition and allergens must be current        |
| Availability   | No             | Changes in real-time                           |
| Filter options | 5 min max      | Changes rarely, safe to cache briefly          |

Let us handle the complexity of caching. You focus on building a great guest experience.
