Heim > Web-Frontend > js-Tutorial > [Persönliche Website] So integrieren Sie die Notion-Datenbank in Next

[Persönliche Website] So integrieren Sie die Notion-Datenbank in Next

DDD
Freigeben: 2024-10-12 16:37:02
Original
1077 Leute haben es durchsucht

Um eine Notion-Datenbank in ein Next.js-Projekt zu integrieren, können Sie Notion als Content-Management-System (CMS) verwenden und dessen Inhalte auf Ihrer Website anzeigen. Nachfolgend finden Sie eine einfache Schritt-für-Schritt-Anleitung, die Ihnen bei der Integration der Notion-Datenbank in Next.js hilft.

Grundlegende Vorbereitung

Erhalten Sie den Notion-API-Schlüssel und die Datenbank-ID

  1. Notion-API-Schlüssel abrufen: Gehen Sie zum Notion-Entwicklerportal und erstellen Sie eine neue Integration. Nach der Erstellung erhalten Sie einen API-Schlüssel.
  2. Datenbank-ID abrufen: Navigieren Sie zu der Notion-Datenbank, die Sie integrieren möchten, und kopieren Sie die URL der Datenbankseite. Die Datenbank-ID ist die Zeichenfolge zwischen https://www.notion.so/ und ?v= in der URL.

[Personal Website] How to Integrate Notion Database in Next

还不清楚的可以看上篇文章:【01.【个人网站】如何使用Notion作为数据库进行全栈开发】

Verwendung des offiziellen SDK

Schritt 1: Abhängigkeiten installieren

Zuerst müssen Sie das offizielle SDK von Notion, @notionhq/client, installieren, um mit der Notion-API zu kommunizieren. Sie können es mit npm oder Yarn installieren:

npm install @notionhq/client
# or
yarn add @notionhq/client

Nach dem Login kopieren

Schritt 2: Richten Sie den Notion Client ein

Erstellen Sie eine Datei lib/notion.js im Stammverzeichnis Ihres Next.js-Projekts und konfigurieren Sie den Notion-Client wie folgt:

// lib/notion.js
import { Client } from '@notionhq/client';

const notion = new Client({
  auth: process.env.NOTION_API_KEY,
});

export const getDatabase = async (databaseId) => {
  const response = await notion.databases.query({ database_id: databaseId });
  return response.results;
};

Nach dem Login kopieren

Stellen Sie sicher, dass Sie Ihren Notion-API-Schlüssel in einer Umgebungsvariablen NOTION_API_KEY.(.env.local)
speichern

NOTION_API_KEY=your_secret_api_key
Nach dem Login kopieren

Schritt 3: Datenbankinhalt abrufen

Auf Ihrer Next.js-Seite können Sie getStaticProps oder getServerSideProps verwenden, um den Inhalt aus der Notion-Datenbank abzurufen.

// pages/index.js
import { getDatabase } from '../lib/notion';

export const getStaticProps = async () => {
  const databaseId = process.env.NOTION_DATABASE_ID;
  const posts = await getDatabase(databaseId);

  return {
    props: {
      posts,
    },
    revalidate: 1, // ISR (Incremental Static Regeneration)
  };
};

export default function Home({ posts }) {
  return (
    <div>
      <h1>My Notion Blog</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>
            {post.properties.Name.title[0].plain_text}
          </li>
        ))}
      </ul>
    </div>
  );
}

Nach dem Login kopieren

Stellen Sie sicher, dass Ihre Notion-Datenbank-ID in NOTION_DATABASE_ID als Umgebungsvariable gespeichert ist.

NOTION_DATABASE_ID=your_database_id
Nach dem Login kopieren

Schritt 4: Bereitstellung und Überprüfung

Stellen Sie abschließend Ihr Next.js-Projekt auf Vercel oder einer anderen Plattform bereit und überprüfen Sie, ob Sie erfolgreich Daten aus der Notion-Datenbank abrufen und anzeigen können.

Zusätzliche Tipps

  • Sie können verschiedene Eigenschaften von Notion-Seiten (wie Name, Tags, Datum usw.) auf Ihrer Seite anzeigen.
  • Erwägen Sie die Verwendung von getServerSideProps, um Daten bei jeder Anfrage abzurufen, oder verwenden Sie getStaticProps mit ISR (Inkrementelle statische Regeneration), um die Leistung zu optimieren.

Indem Sie diese Schritte befolgen, können Sie eine Notion-Datenbank erfolgreich in Ihr Next.js-Projekt integrieren und zum Verwalten und Anzeigen von Inhalten verwenden.

Verwendung der Notion-API mit umschlossenen URLs

Schließlich habe ich diese Methode aus Bequemlichkeitsgründen bei der Integration mit den React-Notion-X-Komponenten verwendet.

Stellen Sie sicher, dass Sie Ihre Notion-Datenbank-ID und Ihren Notion-API-Schlüssel zur .env.local-Datei hinzufügen:

NOTION_DATABASE_ID=your_database_id
NOTION_API_KEY=your_secret_api_key
Nach dem Login kopieren

Schritt 1: Installieren Sie die erforderlichen Abhängigkeiten

Installieren Sie zunächst die erforderlichen Abhängigkeiten wie Notion-Types und Notion-Utils und bearbeiten Sie Anfragen an die Notion-API.

npm install notion-types notion-utils got p-map
Nach dem Login kopieren

Schritt 2: Erstellen Sie eine Datei, um Notion-API-Interaktionen zu kapseln

Erstellen Sie in Ihrem Next.js-Projekt eine Datei, zum Beispiel lib/NotionAPI.ts, die Interaktionen mit der Notion-API kapselt. Diese Datei enthält Methoden zum Aufrufen von Notion-API-Endpunkten, um Daten von Seiten und Sammlungen abzurufen.

// lib/NotionAPI.ts
import * as notion from "notion-types";
import got, { OptionsOfJSONResponseBody } from "got";
import {
  getBlockCollectionId,
  getPageContentBlockIds,
  parsePageId,
  uuidToId,
} from "notion-utils";
import pMap from "p-map";

// 定义权限记录接口
export interface SignedUrlRequest {
  permissionRecord: PermissionRecord;
  url: string;
}

export interface PermissionRecord {
  table: string;
  id: notion.ID;
}

export interface SignedUrlResponse {
  signedUrls: string[];
}

// 定义NotionAPI类
export class NotionAPI {
  private readonly _apiBaseUrl: string;
  private readonly _authToken?: string;
  private readonly _activeUser?: string;
  private readonly _userTimeZone: string;

  constructor({
    apiBaseUrl = "<https://www.notion.so/api/v3>",
    authToken,
    activeUser,
    userTimeZone = "America/New_York",
  }: {
    apiBaseUrl?: string;
    authToken?: string;
    userLocale?: string;
    userTimeZone?: string;
    activeUser?: string;
  } = {}) {
    this._apiBaseUrl = apiBaseUrl;
    this._authToken = authToken;
    this._activeUser = activeUser;
    this._userTimeZone = userTimeZone;
  }

  // 获取页面内容
  public async getPage(
    pageId: string,
    {
      concurrency = 3,
      fetchMissingBlocks = true,
      fetchCollections = true,
      signFileUrls = true,
      chunkLimit = 100,
      chunkNumber = 0,
      gotOptions,
    }: {
      concurrency?: number;
      fetchMissingBlocks?: boolean;
      fetchCollections?: boolean;
      signFileUrls?: boolean;
      chunkLimit?: number;
      chunkNumber?: number;
      gotOptions?: OptionsOfJSONResponseBody;
    } = {}
  ): Promise<notion.ExtendedRecordMap> {
    const page = await this.getPageRaw(pageId, {
      chunkLimit,
      chunkNumber,
      gotOptions,
    });

    const recordMap = page?.recordMap as notion.ExtendedRecordMap;

    if (!recordMap?.block) {
      throw new Error(`Notion page not found "${uuidToId(pageId)}"`);
    }

    recordMap.collection = recordMap.collection ?? {};
    recordMap.collection_view = recordMap.collection_view ?? {};
    recordMap.notion_user = recordMap.notion_user ?? {};
    recordMap.collection_query = {};
    recordMap.signed_urls = {};

    if (fetchMissingBlocks) {
      while (true) {
        const pendingBlockIds = getPageContentBlockIds(recordMap).filter(
          (id) => !recordMap.block[id]
        );

        if (!pendingBlockIds.length) {
          break;
        }

        const newBlocks = await this.getBlocks(
          pendingBlockIds,
          gotOptions
        ).then((res) => res.recordMap.block);

        recordMap.block = { ...recordMap.block, ...newBlocks };
      }
    }

    const contentBlockIds = getPageContentBlockIds(recordMap);

    if (fetchCollections) {
      const allCollectionInstances: Array<{
        collectionId: string;
        collectionViewId: string;
      }> = contentBlockIds.flatMap((blockId) => {
        const block = recordMap.block[blockId].value;
        const collectionId =
          block &&
          (block.type === "collection_view" ||
            block.type === "collection_view_page") &&
          getBlockCollectionId(block, recordMap);

        if (collectionId) {
          return block.view_ids?.map((collectionViewId) => ({
            collectionId,
            collectionViewId,
          }));
        } else {
          return [];
        }
      });

      await pMap(
        allCollectionInstances,
        async (collectionInstance) => {
          const { collectionId, collectionViewId } = collectionInstance;
          const collectionView =
            recordMap.collection_view[collectionViewId]?.value;

          try {
            const collectionData = await this.getCollectionData(
              collectionId,
              collectionViewId,
              collectionView,
              {
                gotOptions,
              }
            );

            recordMap.block = {
              ...recordMap.block,
              ...collectionData.recordMap.block,
            };

            recordMap.collection = {
              ...recordMap.collection,
              ...collectionData.recordMap.collection,
            };

            recordMap.collection_view = {
              ...recordMap.collection_view,
              ...collectionData.recordMap.collection_view,
            };

            recordMap.notion_user = {
              ...recordMap.notion_user,
              ...collectionData.recordMap.notion_user,
            };

            recordMap.collection_query![collectionId] = {
              ...recordMap.collection_query![collectionId],
              [collectionViewId]: (collectionData.result as any)
                ?.reducerResults,
            };
          } catch (err: any) {
            console.warn(
              "NotionAPI collectionQuery error",
              pageId,
              err.message
            );
          }
        },
        {
          concurrency,
        }
      );
    }

    if (signFileUrls) {
      await this.addSignedUrls({ recordMap, contentBlockIds, gotOptions });
    }

    return recordMap;
  }

  public async addSignedUrls({
    recordMap,
    contentBlockIds,
    gotOptions = {},
  }: {
    recordMap: notion.ExtendedRecordMap;
    contentBlockIds?: string[];
    gotOptions?: OptionsOfJSONResponseBody;
  }) {
    recordMap.signed_urls = {};

    if (!contentBlockIds) {
      contentBlockIds = getPageContentBlockIds(recordMap);
    }

    const allFileInstances = contentBlockIds.flatMap((blockId) => {
      const block = recordMap.block[blockId]?.value;

      if (
        block &&
        (block.type === "pdf" ||
          block.type === "audio" ||
          (block.type === "image" && block.file_ids?.length) ||
          block.type === "video" ||
          block.type === "file" ||
          block.type === "page")
      ) {
        const source =
          block.type === "page"
            ? block.format?.page_cover
            : block.properties?.source?.[0]?.[0];

        if (source) {
          if (!source.includes("secure.notion-static.com")) {
            return [];
          }

          return {
            permissionRecord: {
              table: "block",
              id: block.id,
            },
            url: source,
          };
        }
      }

      return [];
    });

    if (allFileInstances.length > 0) {
      try {
        const { signedUrls } = await this.getSignedFileUrls(
          allFileInstances,
          gotOptions
        );

        if (signedUrls.length === allFileInstances.length) {
          for (let i = 0; i < allFileInstances.length; ++i) {
            const file = allFileInstances[i];
            const signedUrl = signedUrls[i];

            recordMap.signed_urls[file.permissionRecord.id] = signedUrl;
          }
        }
      } catch (err) {
        console.warn("NotionAPI getSignedfileUrls error", err);
      }
    }
  }

  public async getPageRaw(
    pageId: string,
    {
      gotOptions,
      chunkLimit = 100,
      chunkNumber = 0,
    }: {
      chunkLimit?: number;
      chunkNumber?: number;
      gotOptions?: OptionsOfJSONResponseBody;
    } = {}
  ): Promise<notion.PageChunk> {
    const parsedPageId = parsePageId(pageId);

    if (!parsedPageId) {
      throw new Error(`invalid notion pageId "${pageId}"`);
    }

    const body = {
      pageId: parsedPageId,
      limit: chunkLimit,
      chunkNumber: chunkNumber,
      cursor: { stack: [] },
      verticalColumns: false,
    };

    return this.fetch<notion.PageChunk>({
      endpoint: "loadPageChunk",
      body,
      gotOptions,
    });
  }

  public async getCollectionData(
    collectionId: string,
    collectionViewId: string,
    collectionView?: any,
    {
      limit = 9999,
      searchQuery = "",
      userTimeZone = this._userTimeZone,
      loadContentCover = true,
      gotOptions,
    }: {
      limit?: number;
      searchQuery?: string;
      userTimeZone?: string;
      loadContentCover?: boolean;
      gotOptions?: OptionsOfJSONResponseBody;
    } = {}
  ) {
    const type = collectionView?.type;

    const isBoardType = type === "board";
    const groupBy = isBoardType
      ? collectionView?.format?.board_columns_by
      : collectionView?.format?.collection_group_by;

    let filters = [];
    if (collectionView?.format?.property_filters) {
      filters = collectionView.format?.property_filters.map(
        (filterObj

: any) => ({
          property: filterObj?.property,
          filter: {
            operator: "and",
            filters: filterObj?.filter?.filters,
          },
        })
      );
    }

    const body = {
      collection: {
        id: collectionId,
      },
      collectionView: {
        id: collectionViewId,
      },
      loader: {
        type: "reducer",
        reducers: {
          collection_group_results: {
            type: "results",
            limit,
            loadContentCover,
          },
        },
        userTimeZone,
        limit,
        loadContentCover,
        searchQuery,
        userLocale: "en",
        ...(filters.length > 0 ? { filters } : {}),
        ...(groupBy
          ? {
              groupBy,
            }
          : {}),
      },
    };

    return this.fetch<notion.CollectionInstance>({
      endpoint: "queryCollection",
      body,
      gotOptions,
    });
  }

  private async fetch<R>({
    endpoint,
    body,
    gotOptions,
  }: {
    endpoint: string;
    body: unknown;
    gotOptions?: OptionsOfJSONResponseBody;
  }) {
    const url = `${this._apiBaseUrl}/${endpoint}`;
    const json = true;
    const method = "POST";

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
    };

    if (this._authToken) {
      headers.cookie = `token_v2=${this._authToken}`;
    }

    if (this._activeUser) {
      headers["x-notion-active-user-header"] = this._activeUser;
    }

    try {
      const res = await got.post(url, {
        ...gotOptions,
        json,
        method,
        body,
        headers,
      });

      return res.body as R;
    } catch (err) {
      console.error(`NotionAPI error: ${err.message}`);
      throw err;
    }
  }

  private async getSignedFileUrls(
    urls: SignedUrlRequest[],
    gotOptions?: OptionsOfJSONResponseBody
  ): Promise<SignedUrlResponse> {
    return this.fetch<SignedUrlResponse>({
      endpoint: "getSignedFileUrls",
      body: { urls },
      gotOptions,
    });
  }

  private async getBlocks(
    blockIds: string[],
    gotOptions?: OptionsOfJSONResponseBody
  ): Promise<notion.PageChunk> {
    return this.fetch<notion.PageChunk>({
      endpoint: "syncRecordValues",
      body: {
        requests: blockIds.map((blockId) => ({
          id: blockId,
          table: "block",
          version: -1,
        })),
      },
      gotOptions,
    });
  }
}

Nach dem Login kopieren

Schritt 3: Verwenden Sie die gekapselte API in Next.js-Seiten

Um Notion-Datenbankinhalte in Ihren Next.js-Seiten abzurufen, können Sie die gekapselte NotionAPI-Klasse verwenden und die abgerufenen Daten zum Rendern an React-Notion-X-Komponenten übergeben.

// pages/[pageId].tsx
import { GetServerSideProps } from 'next';
import { NotionAPI } from '../lib/NotionAPI';
import { NotionRenderer } from 'react-notion-x';
import 'react-notion-x/src/styles.css';

export const getServerSideProps: GetServerSideProps = async (context) => {
  const { pageId } = context.params;
  const notion = new NotionAPI();
  const recordMap = await notion.getPage(pageId as string);

  return {
    props: {
      recordMap,
    },
  };
};

const NotionPage = ({ recordMap }) => {
  return <NotionRenderer recordMap={recordMap} fullPage={true} darkMode={false} />;
};

export default NotionPage;

Nach dem Login kopieren

Schritt 4: Routing in Next.js konfigurieren

Um sicherzustellen, dass die Datei [pageId].tsx verschiedene Notion-Seiten dynamisch rendern kann, müssen Sie dynamisches Routing in Next.js einrichten. Dadurch können Sie den pageId-Parameter abgleichen und den entsprechenden Notion-Seiteninhalt abrufen.

So können Sie dynamische Routen konfigurieren:

  1. Erstellen Sie eine dynamische Routendatei im Seitenverzeichnis:

Erstellen Sie in Ihrem Seitenverzeichnis eine neue Datei mit dem Namen [pageId].tsx:

pages/[pageId].tsx
Nach dem Login kopieren
  1. Dynamisches Seiten-Rendering implementieren:

Rufen Sie in der Datei [pageId].tsx den Notion-Inhalt basierend auf der pageId von der URL ab und rendern Sie ihn dynamisch.

Beispielcode:

import { GetStaticProps, GetStaticPaths } from 'next';
import { NotionRenderer } from 'react-notion-x';
import { getNotionPageData } from '../lib/NotionAPI';
import 'react-notion-x/src/styles.css';

export default function NotionPage({ pageData }) {
  return (
    <div>
      <NotionRenderer recordMap={pageData} fullPage={true} darkMode={false} />
    </div>
  );
}

export const getStaticProps: GetStaticProps = async ({ params }) => {
  const { pageId } = params!;
  const pageData = await getNotionPageData(pageId as string);

  return {
    props: {
      pageData,
    },
    revalidate: 10, // Revalidate content every 10 seconds (ISR)
  };
};

export const getStaticPaths: GetStaticPaths = async () => {
  return {
    paths: [], // We’ll use fallback to handle dynamic routes
    fallback: 'blocking', // Generate pages on the fly if not pre-rendered
  };
};
Nach dem Login kopieren
  1. Explanation:
  2. getStaticPaths: Since your content is dynamic, we return an empty array for paths and set fallback: 'blocking' to generate pages on demand.
  3. getStaticProps: Fetches the Notion page content based on the pageId passed in the URL.

Summary

By following the steps above, you can now encapsulate Notion API requests and render Notion pages dynamically in your Next.js project using react-notion-x. This setup allows you to efficiently integrate Notion as a CMS while ensuring scalability and maintainability in your Next.js application.

Das obige ist der detaillierte Inhalt von[Persönliche Website] So integrieren Sie die Notion-Datenbank in Next. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage