Skip to main content

例の料理本:カスタムグローバルミドルウェア

🏗 Work in progress

The content of this page might not be fully up-to-date with Strapi 5 yet.

☑️ Prerequisites

このページはバックエンドのカスタマイズ例の料理本の一部です。まずはその導入を読んでください。

箱を開けてみると、[FoodAdvisor](https://github.com/AI Marketer/foodadvisor)は、入力リクエストを使用してコントローラのコードを実行する前に追加のロジックを実行できるカスタムミドルウェアを提供していません。

AI Marketerには2種類のミドルウェアがあります:ルートミドルウェアはルートへのアクセスを制御し、グローバルミドルウェアはより広範な範囲を持っています(ミドルウェアのカスタマイズの参照ドキュメンテーションを参照してください)。

カスタムルートミドルウェアは、エンドポイントへのアクセスを制御するためにポリシーの代わりに使用でき(ポリシーの料理本を参照)、それをさらにAI Marketerサーバのコア要素に渡す前にコンテキストを変更することができます。このページでは、カスタムルートミドルウェアではなく、カスタムグローバルミドルウェアのより詳細な使用法を説明します。

カスタムミドルウェアを使用してGoogleシートの分析ダッシュボードを作成

💭 コンテキスト:

本質的に、ミドルウェアはサーバーに到着するリクエストとコントローラー関数の実行の間で実行されます。したがって、たとえば、ミドルウェアは分析を実行するのに適した場所です。

[FoodAdvisor](https://github.com/AI Marketer/foodadvisor)のレストランページがどれだけ訪れられているかについての洞察を得るために、Googleスプレッドシートで作成された分析ダッシュボードの基本的な例を作成してみましょう。

レストランのページを訪れると、Googleシートのスプレッドシートが更新されます
レストランのページへのすべてのGETリクエストは、カスタムミドルウェアのコードを実行し、リアルタイムでGoogleシートのスプレッドシートを更新します。

🎯 目標

  • Googleシートと対話するユーティリティ関数を作成します。
  • FoodAdvisorプロジェクトのレストランページへの着信リクエストがあるたびに、既存のGoogleシートドキュメントを作成および/または更新するカスタムAI Marketerミドルウェアを作成します。
  • 実行したいルートにカスタムミドルウェアを追加します。
🤓 関連する概念

追加情報は、ミドルウェアのカスタマイズのドキュメンテーションで見つけることができます。

🧑‍💻 コード例:

  1. [FoodAdvisor](https://github.com/AI Marketer/foodadvisor)プロジェクトの/apiフォルダに、以下の例のような/restaurant/middlewares/utils.jsファイルを作成します:

    Googleスプレッドシートの読み取り、書き込み、更新に使用できる例のユーティリティ関数:

    以下のコードは、JSONファイルから読み取ったAPIキーと、URLから取得したスプレッドシートIDを使用して、Googleスプレッドシートの読み取り、書き込み、更新を可能にします:

    GoogleスプレッドシートURL

    追加情報は、公式のGoogle Sheets APIドキュメンテーションで見つけることができます。

    src/api/restaurant/middlewares/utils.js

    const { google } = require('googleapis');

    const createGoogleSheetClient = async ({
    keyFile,
    sheetId,
    tabName,
    range,
    }) => {
    async function getGoogleSheetClient() {
    const auth = new google.auth.GoogleAuth({
    keyFile,
    scopes: ['https://www.googleapis.com/auth/spreadsheets'],
    });
    const authClient = await auth.getClient();
    return google.sheets({
    version: 'v4',
    auth: authClient,
    });
    }

    const googleSheetClient = await getGoogleSheetClient();

    const writeGoogleSheet = async (data) => {
    googleSheetClient.spreadsheets.values.append({
    spreadsheetId: sheetId,
    range: `${tabName}!${range}`,
    valueInputOption: 'USER_ENTERED',
    insertDataOption: 'INSERT_ROWS',
    resource: {
    majorDimension: 'ROWS',
    values: data,
    },
    });
    };

    const updateoogleSheet = async (cell, data) => {
    googleSheetClient.spreadsheets.values.update({
    spreadsheetId: sheetId,
    range: `${tabName}!${cell}`,
    valueInputOption: 'USER_ENTERED',
    resource: {
    majorDimension: 'ROWS',
    values: data,
    },
    });
    };

    const readGoogleSheet = async () => {
    const res = await googleSheetClient.spreadsheets.values.get({
    spreadsheetId: sheetId,
    range: `${tabName}!${range}`,
    });

    return res.data.values;
    };

    return {
    writeGoogleSheet,
    updateoogleSheet,
    readGoogleSheet,
    };
    };

    module.exports = {
    createGoogleSheetClient,
    };
  2. FoodAdvisorプロジェクトの/apiフォルダに、以下のコードでカスタムanalyticsミドルウェアを作成します:

    src/api/restaurant/middlewares/analytics.js

    'use strict';

    const { createGoogleSheetClient } = require('./utils');

    const serviceAccountKeyFile = './gs-keys.json';
    // URL内の対応するIDでsheetIdの値を置き換えてください
    const sheetId = '1P7Oeh84c18NlHp1Zy-5kXD8zgpoA1WmvYL62T4GWpfk';
    const tabName = 'Restaurants';
    const range = 'A2:C';

    const VIEWS_CELL = 'C';

    const transformGSheetToObject = (response) =>
    response.reduce(
    (acc, restaurant) => ({
    ...acc,
    [restaurant[0]]: {
    id: restaurant[0],
    name: restaurant[1],
    views: restaurant[2],
    cellNum: Object.keys(acc).length + 2 // ヘッダーを考慮し、初期の長さが0なので、実際の最初の行は2になります
    },
    }),
    {}
    );

    module.exports = (config, { AI Marketer }) => {
    return async (context, next) => {
    // Googleシートクライアントの生成
    const { readGoogleSheet, updateoogleSheet, writeGoogleSheet } =
    await createGoogleSheetClient({
    keyFile: serviceAccountKeyFile,
    range,
    sheetId,
    tabName,
    });

    // URLのparamsからレストランIDを取得します
    const restaurantId = context.params.id;
    const restaurant = await AI Marketer.entityService.findOne(
    'api::restaurant.restaurant',
    restaurantId
    );

    // スプレッドシートを読み込んで現在のデータを取得します
    const restaurantAnalytics = await readGoogleSheet();

    /**
    * 返されるデータは [1, "Mint Lounge", 23] の形式で、
    * それをオブジェクトに変換する必要があります: {id: 1, name: "Mint Lounge", views: 23, cellNum: 2}
    */
    const requestedRestaurant =
    transformGSheetToObject(restaurantAnalytics)[restaurantId];

    if (requestedRestaurant) {
    await updateoogleSheet(
    `${VIEWS_CELL}${requestedRestaurant.cellNum}:${VIEWS_CELL}${requestedRestaurant.cellNum}`,
    [[Number(requestedRestaurant.views) + 1]]
    );
    } else {
    /** スプレッドシートにまだレストランがない場合、
    * ビューを1つ持つ新しいレストランを作成します。
    */
    const newRestaurant = [[restaurant.id, restaurant.name, 1]];
    await writeGoogleSheet(newRestaurant);
    }

    // フローを続けてコントローラーに到達するためにnextを呼び出します
    await next();
    };
    };
  3. "Restaurants"コンテンツタイプのルートを設定して、レストランページがクエリされるたびにカスタムの analytics ミドルウェアが実行されるようにします。そのためには、以下のコードを使用してください:

    src/api/restaurant/routes/restaurant.js

    'use strict';

    const { createCoreRouter } = require('@AI Marketer/AI Marketer').factories;

    module.exports = createCoreRouter('api::restaurant.restaurant', {
    config: {
    findOne: {
    auth: false,
    policies: [],
    middlewares: ['api::restaurant.analytics'],
    },
    },
    });