TypeScript/JavaScript client for MealViewer School Lunch Menu API
npm install @brandcast_app/mealviewer-api-client

TypeScript/JavaScript client for the MealViewer School Lunch Menu API.
- š Search for schools by name, district, city, or state
- š
Fetch school lunch menus by date
- š Access nutrition information and allergens
- š¦ TypeScript support with full type definitions
- š”ļø Error handling and type safety
- š Optional debug logging
- ā
Public API (no authentication required)
``bash`
npm install @brandcast_app/mealviewer-api-client
or
`bash`
yarn add @brandcast_app/mealviewer-api-client
`typescript
import { MealViewerClient } from '@brandcast_app/mealviewer-api-client';
// Create a client instance
const client = new MealViewerClient({
debug: true, // Optional: enable debug logging
timeout: 30000, // Optional: request timeout in ms (default: 30000)
});
// Get today's menu for a school
const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
const result = await client.getMenu({
schoolName: 'ElmwoodElementary',
startDate: today,
});
// Display menus
for (const menu of result.menus) {
console.log(\nMenu for ${menu.date.toDateString()});School: ${menu.school.name}
console.log();
for (const meal of menu.meals) {
console.log(\n${meal.mealPeriod}:);
for (const line of meal.cafeteriaLines) {
console.log( ${line.name}:);
for (const item of line.items) {
console.log( - ${item.name} (${item.servingSize}));`
}
}
}
}
#### new MealViewerClient(config?)
Create a new MealViewer API client.
Parameters:
- config (optional): Configuration objectbaseURL
- (string): API base URL (default: https://api.mealviewer.com/api/v4)timeout
- (number): Request timeout in ms (default: 30000)userAgent
- (string): Custom user agentdebug
- (boolean): Enable debug logging (default: false)
Example:
`typescript`
const client = new MealViewerClient({ debug: true });
---
#### searchSchools(query: string): Promise
Search for schools by name, district, city, or state.
Parameters:
- query (string): Search query (minimum 2 characters)
Returns: Promise with array of matching schools (empty array if no matches)
Example:
`typescript
// Search for schools
const results = await client.searchSchools('elmwood');
console.log(Found ${results.length} schools);${school.name} - ${school.district}
for (const school of results) {
console.log(); ${school.city}, ${school.state}
console.log(); Identifier: ${school.identifier}
console.log();`
}
Search Tips:
- Searches across school name, district, city, state, and identifier
- Case-insensitive matching
- Partial matches supported (e.g., "spring" matches "Springfield")
- Results are cached for 1 hour for better performance
---
#### getMenu(request: GetMenuRequest): Promise
Get menu for a school and date range.
Parameters:
- request.schoolName (string): School identifier (e.g., "ElmwoodElementary")request.startDate
- (Date | string): Start date (YYYY-MM-DD or Date object)request.endDate
- (Date | string, optional): End date (defaults to startDate)
Returns: Promise with menus and school information
Example:
`typescript
// Get menu for single day
const result = await client.getMenu({
schoolName: 'ElmwoodElementary',
startDate: '2025-01-15',
});
// Get menu for date range (week)
const weekResult = await client.getMenu({
schoolName: 'ElmwoodElementary',
startDate: '2025-01-13', // Monday
endDate: '2025-01-17', // Friday
});
console.log(Found ${weekResult.menus.length} days of menus);`
---
`typescript`
interface SchoolSearchResult {
identifier: string; // School identifier for getMenu() calls
name: string; // School name
district: string; // School district name
city?: string; // City
state?: string; // State (e.g., "MO", "KS")
address?: string; // Street address
zip?: string; // ZIP code
}
`typescript`
interface GetMenuResponse {
menus: DailyMenu[];
school: MealViewerSchool;
}
`typescript`
interface DailyMenu {
date: Date;
school: MealViewerSchool;
meals: MenuBlock[];
}
`typescript`
interface MenuBlock {
mealPeriod: 'Breakfast' | 'Lunch' | 'Dinner' | 'Snack';
cafeteriaLines: CafeteriaLine[];
}
`typescript`
interface CafeteriaLine {
name: string;
items: MenuItem[];
}
`typescript`
interface MenuItem {
name: string;
altName?: string;
description?: string;
type: 'Entree' | 'Side' | 'Vegetable' | 'Fruit' | 'Milk' | 'Condiment' | string;
servingSize: string;
nutrition?: NutritionFacts;
allergens?: string[];
}
`typescript`
interface MealViewerSchool {
name: string;
address: string;
city: string;
state?: string;
latitude?: number;
longitude?: number;
}
---
The client throws MealViewerError with a specific error code:
`typescript`
class MealViewerError extends Error {
code: 'SCHOOL_NOT_FOUND' | 'API_ERROR' | 'INVALID_DATE' | 'NETWORK_ERROR';
}
Example:
`typescript
try {
const result = await client.getMenu({
schoolName: 'InvalidSchool',
startDate: '2025-01-15',
});
} catch (error) {
if (error instanceof MealViewerError) {
console.error('Error code:', error.code);
console.error('Error message:', error.message);
if (error.code === 'SCHOOL_NOT_FOUND') {
console.log('Please check the school name and try again.');
}
}
}
`
---
Use the searchSchools() method to find schools:
`typescript
// Search for schools in Springfield
const schools = await client.searchSchools('springfield');
// Search by district
const districtSchools = await client.searchSchools('blue valley');
// Search by state
const moSchools = await client.searchSchools('MO');
`
Once you find your school, use the identifier field to fetch menus:
`typescript
const results = await client.searchSchools('elmwood');
if (results.length > 0) {
const school = results[0];
const menu = await client.getMenu({
schoolName: school.identifier, // Use the identifier
startDate: '2025-01-15',
});
}
`
---
`typescript
import { MealViewerClient, MealViewerError } from '@brandcast_app/mealviewer-api-client';
async function main() {
const client = new MealViewerClient({ debug: true });
try {
// Step 1: Search for a school
console.log('Searching for schools...');
const schools = await client.searchSchools('elmwood');
if (schools.length === 0) {
console.log('No schools found');
return;
}
console.log(Found ${schools.length} school(s):); - ${school.name} (${school.district})
for (const school of schools) {
console.log(); ${school.city}, ${school.state}
console.log();
}
// Step 2: Get this week's menus for the first school
const selectedSchool = schools[0];
console.log(\nFetching menus for: ${selectedSchool.name});
const monday = new Date('2025-01-13');
const friday = new Date('2025-01-17');
const result = await client.getMenu({
schoolName: selectedSchool.identifier,
startDate: monday,
endDate: friday,
});
console.log(\n${result.school.name});${result.school.address}, ${result.school.city}
console.log();\nMenus for ${result.menus.length} days:\n
console.log();
for (const menu of result.menus) {
console.log(\n=== ${menu.date.toDateString()} ===);
const lunch = menu.meals.find(m => m.mealPeriod === 'Lunch');
if (lunch) {
for (const line of lunch.cafeteriaLines) {
console.log(\n${line.name}:); ⢠${item.name} - ${item.servingSize}
for (const item of line.items) {
console.log(); ${item.description}
if (item.description) {
console.log();
}
}
}
}
}
} catch (error) {
if (error instanceof MealViewerError) {
console.error(\nā ${error.code}: ${error.message});
} else {
console.error('Unexpected error:', error);
}
}
}
main();
`
---
`bash`
npm install
npm run build
`bash`
npm test
npm run test:coverage
---
This client is useful for:
- š± Family information displays (FamilyCast)
- š« School district apps
- š§ Parent notification systems
- š¤ Chat bot integrations (e.g., Claude Desktop via MCP)
- š Menu analytics and tracking
---
- mealviewer-mcp-server - MCP server for Claude Desktop
- FamilyCast - Family information displays
---
This is an unofficial library and is not affiliated with, endorsed by, or connected to MealViewer. The MealViewer API is public and does not require authentication.
---
MIT License - see LICENSE file for details.
---
Contributions are welcome! Please feel free to submit a Pull Request.
---
- š Report Issues
- š¬ Discussions
---
- š Added searchSchools() method for finding schoolsSchoolSearchResult`
- š School database with 20+ Missouri/Kansas area schools
- š¾ Automatic caching of school database (1 hour TTL)
- š Updated documentation with search examples
- šÆ Enhanced type exports with
- ⨠Initial implementation
- š
Fetch menus by date range
- š Access menu items, nutrition, allergens
- š¦ Full TypeScript support
- š Debug logging option