# Fumadocs Core (the core library of Fumadocs): Orama Cloud
URL: /zh-TW/docs/headless/search/orama-cloud
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/headless/search/orama-cloud.zh-TW.mdx

整合 Orama Cloud

首先，請在 Orama Cloud 上建立帳號。

## REST API [#rest-api]

使用 REST API 整合時，您的文件網站需要自行上傳索引。

1. 在 Orama Cloud 主控台建立一個以 **REST API** 作為資料來源的新專案。

   將您的認證資訊儲存在 `.env.local` 中，例如 (在 Vite 框架上 `NEXT_PUBLIC_` 改為 `VITE_`，在 Astro 上則為 `PUBLIC_`) ：

   ```dotenv
   NEXT_PUBLIC_ORAMA_DATASOURCE_ID="Rest API data source ID"
   NEXT_PUBLIC_ORAMA_PROJECT_ID="project ID"
   NEXT_PUBLIC_ORAMA_API_KEY="public API key"

   ORAMA_PRIVATE_API_KEY="private API key"
   ```

2. 預先算繪 `static.json` 路由，以便在建置時匯出搜尋索引，並在每次建置後透過指令碼將索引同步至 Orama Cloud：

   ```ts tab="Next.js" title="app/static.json/route.ts"
   import { source } from '@/lib/source';
   import { toDocuments } from 'fumadocs-core/search/orama-cloud';

   export const revalidate = false;

   export async function GET() {
     return Response.json(await toDocuments(source));
   }

   ```

   ```ts tab="Next.js" title="scripts/sync-content.ts"
   import * as fs from 'node:fs/promises';
   import { type OramaDocument as DocumentRecord, sync } from 'fumadocs-core/search/orama-cloud';
   import { OramaCloud } from '@orama/core';

   // Next.js writes it under `route-cache` when built with an adapter, like on Vercel
   const [file] = await Array.fromAsync(fs.glob('.next/server/{app,route-cache/*/*/$}/static.json.body'));
   const records = JSON.parse(await fs.readFile(file, 'utf-8')) as DocumentRecord[];

   const orama = new OramaCloud({
     projectId: process.env.NEXT_PUBLIC_ORAMA_PROJECT_ID!,
     apiKey: process.env.ORAMA_PRIVATE_API_KEY!,
   });

   await sync(orama, {
     index: process.env.NEXT_PUBLIC_ORAMA_DATASOURCE_ID!,
     documents: records,
   });

   console.log(`search updated: ${records.length} records`);

   ```

   ```json tab="Next.js" title="package.json"
   {
     "scripts": {
       "build": "next build && node --env-file-if-exists=.env.local scripts/sync-content.ts"
     }
   }
   ```

   ```ts tab="React Router" title="app/routes/static.json.ts"
   import { source } from '@/lib/source';
   import { toDocuments } from 'fumadocs-core/search/orama-cloud';

   export async function loader() {
     return Response.json(await toDocuments(source));
   }

   ```

   ```ts tab="React Router" title="app/routes.ts"
   import { index, route, type RouteConfig } from '@react-router/dev/routes';

   export default [
     index('routes/home.tsx'),
     route('static.json', 'routes/static.json.ts'), // [!code ++:1]
   ] satisfies RouteConfig;

   ```

   ```ts tab="React Router" title="scripts/sync-content.ts"
   import * as fs from 'node:fs/promises';
   import { type OramaDocument as DocumentRecord, sync } from 'fumadocs-core/search/orama-cloud';
   import { OramaCloud } from '@orama/core';

   const records = JSON.parse(await fs.readFile('build/client/static.json', 'utf-8')) as DocumentRecord[];

   const orama = new OramaCloud({
     projectId: process.env.VITE_ORAMA_PROJECT_ID!,
     apiKey: process.env.ORAMA_PRIVATE_API_KEY!,
   });

   await sync(orama, {
     index: process.env.VITE_ORAMA_DATASOURCE_ID!,
     documents: records,
   });

   console.log(`search updated: ${records.length} records`);

   ```

   ```json tab="React Router" title="package.json"
   {
     "scripts": {
       "build": "react-router build && node --env-file-if-exists=.env.local scripts/sync-content.ts"
     }
   }
   ```

   ```ts tab="TanStack Start" title="src/routes/static[.]json.ts"
   import { createFileRoute } from '@tanstack/react-router';
   import { source } from '@/lib/source';
   import { toDocuments } from 'fumadocs-core/search/orama-cloud';

   export const Route = createFileRoute('/static.json')({
     server: {
       handlers: {
         GET: async () => Response.json(await toDocuments(source)),
       },
     },
   });

   ```

   ```ts tab="TanStack Start" title="vite.config.ts"
   import { tanstackStart } from '@tanstack/react-start/plugin/vite';
   import { nitro } from 'nitro/vite';
   import { defineConfig } from 'vite';

   export default defineConfig({
     plugins: [
       tanstackStart({
         prerender: {
           enabled: true,
         },
         pages: [ // [!code ++:3]
           { path: '/static.json' }
         ],
       }),
       nitro({
         preset: 'vercel',
       }),
     ],
   });

   ```

   ```ts tab="TanStack Start" title="scripts/sync-content.ts"
   import * as fs from 'node:fs/promises';
   import { type OramaDocument as DocumentRecord, sync } from 'fumadocs-core/search/orama-cloud';
   import { OramaCloud } from '@orama/core';

   const records = JSON.parse(await fs.readFile('.vercel/output/static/static.json', 'utf-8')) as DocumentRecord[];

   const orama = new OramaCloud({
     projectId: process.env.VITE_ORAMA_PROJECT_ID!,
     apiKey: process.env.ORAMA_PRIVATE_API_KEY!,
   });

   await sync(orama, {
     index: process.env.VITE_ORAMA_DATASOURCE_ID!,
     documents: records,
   });

   console.log(`search updated: ${records.length} records`);

   ```

   ```json tab="TanStack Start" title="package.json"
   {
     "scripts": {
       "build": "vite build && node --env-file-if-exists=.env.local scripts/sync-content.ts"
     }
   }
   ```

   ```ts tab="Waku" title="src/pages/_api/static.json.ts"
   import { source } from '@/lib/source';
   import { toDocuments } from 'fumadocs-core/search/orama-cloud';

   export async function GET() {
     return Response.json(await toDocuments(source));
   }

   export async function getConfig() {
     return {
       render: 'static' as const,
     } as const;
   }

   ```

   ```ts tab="Waku" title="scripts/sync-content.ts"
   import * as fs from 'node:fs/promises';
   import { type OramaDocument as DocumentRecord, sync } from 'fumadocs-core/search/orama-cloud';
   import { OramaCloud } from '@orama/core';

   const records = JSON.parse(await fs.readFile('dist/public/static.json', 'utf-8')) as DocumentRecord[];

   const orama = new OramaCloud({
     projectId: process.env.VITE_ORAMA_PROJECT_ID!,
     apiKey: process.env.ORAMA_PRIVATE_API_KEY!,
   });

   await sync(orama, {
     index: process.env.VITE_ORAMA_DATASOURCE_ID!,
     documents: records,
   });

   console.log(`search updated: ${records.length} records`);

   ```

   ```json tab="Waku" title="package.json"
   {
     "scripts": {
       "build": "waku build && node --env-file-if-exists=.env.local scripts/sync-content.ts"
     }
   }
   ```

   ```ts tab="Astro" title="src/pages/static.json.ts"
   import type { APIRoute } from 'astro';
   import { source } from '@/lib/source';
   import { toDocuments } from 'fumadocs-core/search/orama-cloud';

   export const prerender = true;

   export const GET: APIRoute = async () => Response.json(await toDocuments(source));

   ```

   ```ts tab="Astro" title="scripts/sync-content.ts"
   import * as fs from 'node:fs/promises';
   import { type OramaDocument as DocumentRecord, sync } from 'fumadocs-core/search/orama-cloud';
   import { OramaCloud } from '@orama/core';

   const records = JSON.parse(await fs.readFile('dist/static.json', 'utf-8')) as DocumentRecord[];

   const orama = new OramaCloud({
     projectId: process.env.PUBLIC_ORAMA_PROJECT_ID!,
     apiKey: process.env.ORAMA_PRIVATE_API_KEY!,
   });

   await sync(orama, {
     index: process.env.PUBLIC_ORAMA_DATASOURCE_ID!,
     documents: records,
   });

   console.log(`search updated: ${records.length} records`);

   ```

   ```json tab="Astro" title="package.json"
   {
     "scripts": {
       "build": "astro build && node --env-file-if-exists=.env.local scripts/sync-content.ts"
     }
   }
   ```

   `build` 指令碼會在每次建置時進行同步，因此請在您的部署平台上設定相同的變數。

### 搜尋用戶端 [#search-client]

若要在用戶端搜尋文件，可以考慮以下方式：

* 使用 [Fumadocs UI 搜尋對話方塊](/docs/next/search/orama-cloud)。

* 使用 Fumadocs 內建的 hook 自訂搜尋 UI：

  ```ts
  import { useOramaCloudSearch } from 'fumadocs-core/search/client';
  import { OramaCloud } from '@orama/core';

  const orama = new OramaCloud({
    projectId: process.env.NEXT_PUBLIC_ORAMA_PROJECT_ID,
    apiKey: process.env.NEXT_PUBLIC_ORAMA_API_KEY,
  });

  const params = {
    // 選用的搜尋參數
  };

  const { search, onSearchChange, isLoading, data } = useOramaCloudSearch({
    client: orama,
    params,
  });
  ```

* 直接使用 Orama 提供的搜尋用戶端。

## 網路爬蟲 [#web-crawler]

1. 在主控台中建立 Crawler 資料來源，並套用「Documentation」預設範本，完成正確設定。
2. 從主控台複製認證資訊。

### 搜尋用戶端 [#search-client-1]

設定方式與 REST API 整合相同，但請務必將 `index` 設為 `crawler`。

```ts
import { useOramaCloudSearch } from 'fumadocs-core/search/client';
import { OramaCloud } from '@orama/core';

const orama = new OramaCloud({
  projectId: '<project id>',
  apiKey: '<read only api key>',
});

const params = {
  // 選用的搜尋參數
};

const { search, onSearchChange, isLoading, data } = useOramaCloudSearch({
  index: 'crawler',
  client: orama,
  params,
});
```

Fumadocs UI 也是如此。