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.
This guide covers how to handle SmartMenu API errors gracefully, ensuring a good user experience even when things go wrong.
SmartMenu API returns errors in the standard GraphQL format:
{
"data" : null ,
"errors" : [
{
"message" : "Missing required header: X-Session-ID" ,
"extensions" : {
"code" : "MISSING_HEADER" ,
"field" : "X-Session-ID"
},
"path" : [ "search" ]
}
]
}
Error Codes
Authentication Errors
Code HTTP Status Description User Action MISSING_HEADER400 Required header not provided Fix request headers INVALID_TOKEN401 API key invalid or expired Check API key configuration FORBIDDEN403 Key lacks access to requested chain Contact EveryBite
Validation Errors
Code HTTP Status Description User Action INVALID_INPUT400 Request parameter malformed Check request format CHAIN_NOT_FOUND404 Chain ID doesn’t exist Verify chain ID DISH_NOT_FOUND404 Dish ID doesn’t exist Handle gracefully
Rate Limiting
Code HTTP Status Description User Action RATE_LIMITED429 Too many requests Wait and retry
Server Errors
Code HTTP Status Description User Action INTERNAL_ERROR500 Server error Retry or graceful fallback SERVICE_UNAVAILABLE503 Service temporarily down Retry with backoff
Handling Errors in Code
Basic Error Handler
async function smartMenuQuery ( query , variables ) {
try {
const response = await fetch ( ENDPOINT , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'Authorization' : API_KEY ,
'X-Session-ID' : sessionId ,
},
body: JSON . stringify ({ query , variables })
});
// Handle HTTP errors
if ( ! response . ok ) {
if ( response . status === 429 ) {
const retryAfter = response . headers . get ( 'Retry-After' ) || 5 ;
throw new RateLimitError ( retryAfter );
}
throw new ApiError ( response . status , 'API request failed' );
}
const { data , errors } = await response . json ();
// Handle GraphQL errors
if ( errors && errors . length > 0 ) {
const error = errors [ 0 ];
throw new GraphQLError (
error . message ,
error . extensions ?. code
);
}
return data ;
} catch ( error ) {
handleError ( error );
throw error ;
}
}
Error Classes
class ApiError extends Error {
constructor ( public status : number , message : string ) {
super ( message );
this . name = 'ApiError' ;
}
}
class GraphQLError extends Error {
constructor ( message : string , public code ?: string ) {
super ( message );
this . name = 'GraphQLError' ;
}
}
class RateLimitError extends Error {
constructor ( public retryAfter : number ) {
super ( `Rate limited. Retry after ${ retryAfter } seconds.` );
this . name = 'RateLimitError' ;
}
}
Centralized Error Handler
function handleError ( error : Error ) {
if ( error instanceof RateLimitError ) {
// Show temporary message, auto-retry
showToast ( `Please wait ${ error . retryAfter } seconds...` );
return ;
}
if ( error instanceof GraphQLError ) {
switch ( error . code ) {
case 'DISH_NOT_FOUND' :
showToast ( 'This dish is no longer available' );
break ;
case 'CHAIN_NOT_FOUND' :
showToast ( 'Restaurant not found' );
break ;
case 'INVALID_TOKEN' :
// This shouldn't happen in production
console . error ( 'Invalid API key' );
break ;
default :
showToast ( 'Something went wrong. Please try again.' );
}
return ;
}
// Network or unexpected errors
showToast ( 'Unable to load menu. Please check your connection.' );
}
Graceful Degradation
When SmartMenu API is unavailable, your app should continue to function.
Fallback Pattern
async function getDishDetails ( dishId ) {
try {
// Try SmartMenu API
const data = await smartMenuQuery ( DISH_QUERY , { id: dishId });
return {
... data . dish ,
nutritionAvailable: true
};
} catch ( error ) {
console . error ( 'SmartMenu API error:' , error );
// Fall back to your own data (without nutrition)
const localDish = await getLocalDishData ( dishId );
return {
... localDish ,
nutritionAvailable: false ,
nutrition: null ,
allergens: [],
diets: []
};
}
}
UI Fallback
function DishDetails ({ dish }) {
if ( ! dish . nutritionAvailable ) {
return (
< div className = "dish-details" >
< h2 > { dish . name } </ h2 >
< p > { dish . description } </ p >
< div className = "nutrition-unavailable" >
< InfoIcon />
< span > Nutrition information temporarily unavailable </ span >
</ div >
</ div >
);
}
return (
< div className = "dish-details" >
< h2 > { dish . name } </ h2 >
< p > { dish . description } </ p >
< NutritionPanel nutrition = { dish . nutrition } />
< AllergenBadges allergens = { dish . allergens } />
</ div >
);
}
Retry Logic
Exponential Backoff
async function fetchWithRetry (
fn : () => Promise < any >,
maxRetries = 3 ,
baseDelay = 1000
) {
let lastError : Error ;
for ( let attempt = 0 ; attempt < maxRetries ; attempt ++ ) {
try {
return await fn ();
} catch ( error ) {
lastError = error ;
// Don't retry client errors (4xx)
if ( error instanceof ApiError && error . status < 500 ) {
throw error ;
}
// Don't retry on last attempt
if ( attempt === maxRetries - 1 ) {
throw error ;
}
// Exponential backoff with jitter
const delay = baseDelay * Math . pow ( 2 , attempt ) + Math . random () * 1000 ;
await sleep ( delay );
}
}
throw lastError ;
}
// Usage
const data = await fetchWithRetry (() =>
smartMenuQuery ( SEARCH_QUERY , variables )
);
Rate Limit Handling
async function fetchWithRateLimitRetry ( fn : () => Promise < any >) {
try {
return await fn ();
} catch ( error ) {
if ( error instanceof RateLimitError ) {
// Wait for the specified time
await sleep ( error . retryAfter * 1000 );
// Retry once
return await fn ();
}
throw error ;
}
}
User-Facing Error Messages
Error Code User Message RATE_LIMITED”Please wait a moment and try again” DISH_NOT_FOUND”This dish is no longer available” CHAIN_NOT_FOUND”Restaurant not found” INTERNAL_ERROR”Something went wrong. Please try again.” Network Error ”Unable to connect. Please check your internet connection.”
Logging and Monitoring
function logError ( error : Error , context : object ) {
const errorLog = {
timestamp: new Date (). toISOString (),
name: error . name ,
message: error . message ,
code: error instanceof GraphQLError ? error . code : undefined ,
... context
};
// Send to your logging service
console . error ( 'SmartMenu API Error:' , errorLog );
// Track in analytics
analytics . track ( 'smartmenu_error' , {
error_type: error . name ,
error_code: error instanceof GraphQLError ? error . code : 'unknown' ,
... context
});
}
Best Practices
Always Handle Errors Never let errors bubble up to crash your app. Always catch and handle.
User-Friendly Messages Don’t show technical error messages to users. Translate to helpful guidance.
Fail Gracefully If nutrition isn’t available, the dish should still be orderable.
Log for Debugging Log errors with context for debugging, but don’t expose details to users.