OptimoCMSDocs
Guides

Build a restaurant site

Create a complete restaurant website with menu, bookings, reviews and design tokens using the OptimoCMS SDK, MCP and REST API.

Build a complete restaurant site

In this tutorial you'll build a restaurant website step by step with:

  • Menu page with categories and dishes
  • Online reservations via the booking system
  • Customer reviews
  • Design tokens for branding
  • Publishing to production

Each step shows SDK, MCP and REST examples side by side.


Prerequisites

  • Node.js 18+
  • An OptimoCMS API key (create one in the dashboard under Settings → API)
  • npm install @optimocms/sdk

Step 1 — Create a site

SDK

import { OptimoCMS } from '@optimocms/sdk';

const client = new OptimoCMS({
  apiKey: process.env.OPTIMOCMS_API_KEY!,
});

const site = await client.sites.create({
  name: 'Ristorante Bella Vista',
  subdomain: 'bellavista',
  language: 'nl',
});

console.log(`Site created: ${site.id}`);

MCP (Cursor / Claude)

Create a new site named "Ristorante Bella Vista" with subdomain "bellavista" in Dutch.

The MCP tool create_site is called with:

{
  "name": "Ristorante Bella Vista",
  "subdomain": "bellavista",
  "language": "nl"
}

REST

curl -X POST https://api.optimocms.com/v1/sites \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Ristorante Bella Vista",
    "subdomain": "bellavista",
    "language": "nl"
  }'

Step 2 — Install a template

Choose a restaurant template from the library and install it on your site.

SDK

const templates = await client.templates.list({ category: 'restaurant' });
const template = templates.data[0];

const result = await client.templates.install(site.id, {
  templateId: template.id,
  designTokens: {
    colorPrimary: '#8B0000',
    fontHeading: 'Playfair Display',
    fontBody: 'Inter',
    radiusCard: '12px',
    radiusButton: '8px',
  },
});

console.log(`Template installed, ${result.pagesCreated.length} pages created`);

MCP

Install the restaurant template on site "bellavista" with a dark red primary color (#8B0000) and Playfair Display as heading font.

REST

curl -X POST https://api.optimocms.com/v1/sites/$SITE_ID/templates/install \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "tmpl_restaurant_classic",
    "designTokens": {
      "colorPrimary": "#8B0000",
      "fontHeading": "Playfair Display",
      "fontBody": "Inter",
      "radiusCard": "12px",
      "radiusButton": "8px"
    }
  }'

Step 3 — Populate the menu page

Create a menu page with categories and dishes as content blocks.

SDK

const menuPage = await client.pages.create(site.id, {
  title: 'Menu',
  slug: 'menu',
  status: 'draft',
  blocks: [
    {
      type: 'Hero',
      props: {
        title: 'Our Menu',
        subtitle: 'Fresh Italian dishes, prepared daily',
        backgroundImage: '/images/kitchen.jpg',
      },
    },
    {
      type: 'MenuCategory',
      props: {
        title: "Antipasti",
        items: [
          { name: 'Bruschetta al Pomodoro', price: '€9.50', description: 'Toasted bread with tomato and basil' },
          { name: 'Carpaccio di Manzo', price: '€14.00', description: 'Thinly sliced beef with rocket and parmesan' },
          { name: 'Burrata con Prosciutto', price: '€16.50', description: 'Creamy burrata with parma ham' },
        ],
      },
    },
    {
      type: 'MenuCategory',
      props: {
        title: "Primi Piatti",
        items: [
          { name: 'Spaghetti Carbonara', price: '€16.00', description: 'Classic with guanciale and pecorino' },
          { name: 'Risotto ai Funghi Porcini', price: '€18.50', description: 'Creamy risotto with porcini mushrooms' },
          { name: 'Tagliatelle al Ragù', price: '€17.00', description: 'Fresh pasta with slow-cooked meat sauce' },
        ],
      },
    },
    {
      type: 'MenuCategory',
      props: {
        title: "Dolci",
        items: [
          { name: 'Tiramisù della Casa', price: '€9.00', description: 'Homemade according to grandma\'s recipe' },
          { name: 'Panna Cotta', price: '€8.50', description: 'With seasonal fruit and coulis' },
        ],
      },
    },
  ],
});

console.log(`Menu page created: ${menuPage.id}`);

MCP

Create a menu page on site "bellavista" with categories Antipasti, Primi Piatti and Dolci. Add 2-3 dishes per category with name, price and short description.

REST

curl -X POST https://api.optimocms.com/v1/sites/$SITE_ID/pages \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Menu",
    "slug": "menu",
    "status": "draft",
    "blocks": [
      {
        "type": "Hero",
        "props": { "title": "Our Menu", "subtitle": "Fresh Italian dishes" }
      },
      {
        "type": "MenuCategory",
        "props": {
          "title": "Antipasti",
          "items": [
            { "name": "Bruschetta al Pomodoro", "price": "€9.50" },
            { "name": "Carpaccio di Manzo", "price": "€14.00" }
          ]
        }
      }
    ]
  }'

Step 4 — Enable reservations

Configure the booking system so guests can reserve online.

SDK

const bookingPage = await client.pages.create(site.id, {
  title: 'Reservations',
  slug: 'reservations',
  status: 'published',
  blocks: [
    {
      type: 'Hero',
      props: { title: 'Reserve a table', subtitle: 'Choose your preferred date and time' },
    },
    {
      type: 'BookingWidget',
      props: { serviceId: 'dinner', showStaff: false, showBranch: false },
    },
  ],
});

// Fetch available slots
const slots = await client.booking.getSlots(site.id, {
  serviceId: 'dinner',
  date: '2026-06-15',
});

console.log(`${slots.slots.filter(s => s.available).length} available time slots`);

// Create a test reservation
const booking = await client.booking.create(site.id, {
  serviceId: 'dinner',
  date: '2026-06-15',
  startTime: '19:00',
  customer: {
    name: 'Jan de Vries',
    email: 'jan@example.com',
    phone: '+31612345678',
  },
});

console.log(`Reservation confirmed: ${booking.id} (${booking.status})`);

MCP

Enable reservations on site "bellavista". Create a reservation page with a booking widget for dinner service.

REST

# Fetch slots
curl https://api.optimocms.com/v1/sites/$SITE_ID/booking/slots?serviceId=dinner&date=2026-06-15 \
  -H "Authorization: Bearer $API_KEY"

# Create reservation
curl -X POST https://api.optimocms.com/v1/sites/$SITE_ID/booking \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "serviceId": "dinner",
    "date": "2026-06-15",
    "startTime": "19:00",
    "customer": {
      "name": "Jan de Vries",
      "email": "jan@example.com"
    }
  }'

Step 5 — Set design tokens

Customise the branding with design tokens — these apply across all pages.

SDK

const tokens = await client.designTokens.update(site.id, {
  colorPrimary: '#8B0000',
  colorBackground: '#FFF8F0',
  colorSurface: '#FFFFFF',
  colorText: '#1A1A1A',
  colorTextMuted: '#6B7280',
  fontHeading: 'Playfair Display',
  fontBody: 'Inter',
  radiusCard: '12px',
  radiusButton: '8px',
  radiusInput: '6px',
});

console.log(`Design tokens updated: ${tokens.updatedAt}`);

MCP

Update the design tokens of site "bellavista": primary colour dark red #8B0000, background warm white #FFF8F0, heading font Playfair Display, body font Inter.

REST

curl -X PUT https://api.optimocms.com/v1/sites/$SITE_ID/design-tokens \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "colorPrimary": "#8B0000",
    "colorBackground": "#FFF8F0",
    "colorSurface": "#FFFFFF",
    "colorText": "#1A1A1A",
    "fontHeading": "Playfair Display",
    "fontBody": "Inter",
    "radiusCard": "12px",
    "radiusButton": "8px"
  }'

Step 6 — Publish

Publish the site to production so visitors can reach it.

SDK

const publishResult = await client.sites.publish(site.id);

if (publishResult.status === 'queued' || publishResult.status === 'deploying') {
  console.log(`Site is being published! Deploy ID: ${publishResult.deployId}`);
  console.log(`Live at: https://bellavista.optimocms.com`);
}

MCP

Publish site "bellavista" to production.

REST

curl -X POST https://api.optimocms.com/v1/sites/$SITE_ID/publish \
  -H "Authorization: Bearer $API_KEY"

Response:

{
  "siteId": "site_abc123",
  "status": "deploying",
  "deployId": "dpl_xyz789"
}

Complete script

Here's the full script combining all steps:

import { OptimoCMS } from '@optimocms/sdk';

const client = new OptimoCMS({
  apiKey: process.env.OPTIMOCMS_API_KEY!,
});

async function buildRestaurantSite() {
  const site = await client.sites.create({
    name: 'Ristorante Bella Vista',
    subdomain: 'bellavista',
    language: 'nl',
  });

  const templates = await client.templates.list({ category: 'restaurant' });
  await client.templates.install(site.id, {
    templateId: templates.data[0].id,
  });

  await client.designTokens.update(site.id, {
    colorPrimary: '#8B0000',
    colorBackground: '#FFF8F0',
    fontHeading: 'Playfair Display',
    fontBody: 'Inter',
    radiusCard: '12px',
    radiusButton: '8px',
  });

  await client.pages.create(site.id, {
    title: 'Menu',
    slug: 'menu',
    status: 'published',
    blocks: [
      { type: 'Hero', props: { title: 'Our Menu' } },
      { type: 'MenuCategory', props: { title: 'Antipasti', items: [
        { name: 'Bruschetta', price: '€9.50' },
        { name: 'Carpaccio', price: '€14.00' },
      ]}},
    ],
  });

  await client.pages.create(site.id, {
    title: 'Reservations',
    slug: 'reservations',
    status: 'published',
    blocks: [
      { type: 'BookingWidget', props: { serviceId: 'dinner' } },
    ],
  });

  const result = await client.sites.publish(site.id);
  console.log(`✓ Site live at https://bellavista.optimocms.com (${result.status})`);
}

buildRestaurantSite().catch(console.error);

Next steps

On this page