Skip to main content
On this page

    Integrating External APIs with Vitto Hooks

    Aris Ripandi Updated vitto api hooks tutorial data-fetching

    Learn how to fetch data from REST APIs, headless CMS platforms, and databases at build time using Vitto's hooks system.

    Static sites don’t mean static data. Vitto hooks can fetch from any API at build time, making external data part of your compiled site.

    Fetching from a REST API

    ts
    // hooks/products.ts
    import { defineHooks } from 'vitto';
    
    export default defineHooks('products', async () => {
      const res = await fetch('https://api.example.com/products');
      if (!res.ok) return [];
      return res.json();
    });

    Connecting to a Headless CMS

    Strapi

    ts
    export default defineHooks('pages', async () => {
      const res = await fetch(`${process.env.STRAPI_URL}/api/pages?populate=*`, {
        headers: { Authorization: `Bearer ${process.env.STRAPI_TOKEN}` },
      });
      const { data } = await res.json();
      return data.map((item) => ({
        slug: item.attributes.slug,
        title: item.attributes.title,
        content: item.attributes.content,
      }));
    });

    Sanity

    ts
    import { createClient } from '@sanity/client';
    
    const client = createClient({
      projectId: process.env.SANITY_PROJECT_ID,
      dataset: 'production',
      useCdn: true,
    });
    
    export default defineHooks('posts', async () => {
      return await client.fetch(`*[_type == "post"]{slug, title, body}`);
    });

    Reading from the File System

    ts
    import fs from 'node:fs/promises';
    import path from 'node:path';
    
    export default defineHooks('data', async () => {
      const filePath = path.join(process.cwd(), 'data/products.json');
      const content = await fs.readFile(filePath, 'utf-8');
      return JSON.parse(content);
    });

    Combining Multiple Sources

    ts
    export default defineHooks('site', async () => {
      const [posts, products, settings] = await Promise.all([
        fetchPosts(),
        fetchProducts(),
        fetchSettings(),
      ]);
      return { posts, products, settings };
    });

    Error Handling

    Always handle errors gracefully:

    ts
    export default defineHooks('posts', async () => {
      try {
        const res = await fetch('https://api.example.com/posts');
        return await res.json();
      } catch (error) {
        console.error('Failed to fetch posts:', error);
        return []; // Return empty array on failure
      }
    });

    Warning

    API keys and tokens used in hooks are only available at build time. Never expose secrets in client-side code.

    Performance Tips

    ts
    const CACHE_DURATION = 60_000; // 1 minute
    let cache = null;
    let cacheTime = 0;
    
    export default defineHooks('data', async () => {
      if (Date.now() - cacheTime < CACHE_DURATION) return cache;
      cache = await fetchExpensiveData();
      cacheTime = Date.now();
      return cache;
    });