> ## Documentation Index
> Fetch the complete documentation index at: https://developers.hubspot.jp/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# サーバーレス関数を使ってみる

> HubSpotのCMS上で初めてのサーバーレス関数を作成し、レスポンスを受け取ります。

'HubSpotのCMS上で初めてのサーバーレス関数を作成し、レスポンスを受け取ります。';

<ProductTier tiers={['cms-enterprise']} />

サーバーレス関数を使用すると、サーバーサイドでHubSpotを介してJavaScriptを実行することができ、JavaScriptがクライアントに公開されるのを防ぐことができます。これは、APIキーやその他の資格情報などのセンシティブ情報を含むAPIリクエストを行う場合に特に重要です。関数が呼び出されると、HubSpotのバックエンドで関数が実行されます。つまり、CMSウェブサイトへのリクエストを行うために、バックエンドサーバーを用意する必要はありません。HubSpotのサーバーレス関数は[NodeJS](https://nodejs.org/en/about/)ランタイムを使用します。

このガイドでは、サードパーティーのサービスから見積もりを取得し、ウェブサイトページ上に表示する、シンプルなサーバーレス関数を作成する方法について説明します。サーバーレス関数をビルドしてアカウントにデプロイするには、HubSpot開発者プロジェクトを使用します。

<Alert type="warning" titleText="注：">
  以前ドキュメントで紹介したサーバーレス関数をデザインマネージャーにアップロードする方法は
  現在もサポートされていますが、開発者プロジェクトの使用をお勧めします。
  開発者プロジェクトには認証を容易にするための非公開アプリが含まれており、
  サードパーティーの依存関係も使用できます。このガイドの内容は、
  最新の方法を使用するように更新されています。デザインマネージャーベースのサーバーレス関数のリファレンス情報については、
  [リファレンス
  セクション](/cms/reference/serverless-functions/serverless-functions-in-projects)をご参照ください。
</Alert>

サーバーレス関数の概要については、[サーバーレス関数の概要](https://developers.hubspot.com/docs)をご確認ください。また、[HubSpot DevelopersのYouTubeチャンネル](https://www.youtube.com/watch?v=N0arXQOyfXo)では、CMSプロジェクトでのサーバーレス関数の使用方法について説明しています。

## 前提条件

このチュートリアルを開始する前に、以下のものが必要です。

* **Content Hub** Enterpriseのアカウント、または[CMS開発者サンドボックスアカウント](https://app.hubspot.com/signup-hubspot/cms-developers)。
* [HubSpot CLI](/developer-tooling/local-development/hubspot-cli/reference#install-the-cli)（最新バージョンを推奨）をインストールし、使用しているアカウントで認証する必要があります。どのアカウントが認証されているかを確認するには、`hs accounts list`を実行します。詳しくは[ローカル開発を開始する](/developer-tooling/local-development/hubspot-cli/install-the-cli)をご参照ください。

## 1. プロジェクトをローカルで作成する

まず、プロジェクトをローカルで作成し、ビルドしてアカウントにデプロイできるようにします。

* ターミナルで、`cd`コマンドを使用してプロジェクトを保存するディレクトリーに移動します。

```shell theme={null}
cd Documents/Dev/serverless-function-project
```

* `hs project create`を実行して新しいプロジェクトを作成します。

```shell theme={null}
hs project create
```

* ターミナルのプロンプトに従ってプロジェクトを作成します。テンプレートには\*\*空のプロジェクトを作成（テンプレートなし）\*\*を選択します。

![テンプレートなしで空のプロジェクトを作成するためのターミナルプロンプトオプション](https://www.hubspot.jp/hubfs/Knowledge_Base_2023-24-25/terminal-select-empty-project.png)

* `cd`コマンドを使用して、新しいプロジェクトディレクトリーに移動します。プロジェクトに「my new project」という名前を付けた場合は以下のようになります。

```shell theme={null}
cd my-new-project
```

## 2. プロジェクトにサーバーレス関数を追加する

プロジェクトが作成されたら、[VS Code](https://developers.hubspot.com/docs)などの任意のエディターで開きます。空の`src`ディレクトリーと`hsproject.json`構成ファイルと共に、プロジェクトディレクトリーがHubSpotで自動的に作成されます。プロジェクトにサーバーレス関数を追加するには、`src`ディレクトリー内に`app`ディレクトリーを作成し、その中に以下のディレクトリーとファイルを追加します。

* `app/app.json`：アプリ構成ファイル。
* `app/app.functions`：サーバーレス関数ディレクトリー。`.functions`で終わる任意の名前を付けることができます。
* `app/app.functions/function.js`：関数が呼び出されたときに実行されるJavaScriptコード。
* `app/app.functions/serverless.json`：サーバーレス関数の構成ファイル。
* `app/app.functions/package.json`：必要な依存関係が含まれています。

![上記のファイルとフォルダーをプロジェクトディレクトリーに追加した後のプロジェクト構造](https://www.hubspot.jp/hubfs/Knowledge_Base_2023-24-25/project-serverless-functions-structure.png)

* 以下のサンプルコードをローカルの`app.json`ファイル、`function.js`ファイル、`serverless.json`ファイル、`package.json`ファイルにそれぞれコピーします。各`.json`コードブロックの下には、フィールド定義を含むテーブルもあります。

<Tabs defaultSelected="0">
  <Tab tabId="0" title="app.json">
    ```json theme={null}
    {
      "name": "Serverless function app",
      "description": "This app runs a serverless function to fetch a quote using the Zen Quotes API.",
      "scopes": ["crm.objects.contacts.read", "crm.objects.contacts.write"],
      "uid": "serverless-function-app",
      "public": false
    }
    ```

    | フィールド         | 型   | 説明                                                                                                                                            |
    | ------------- | --- | --------------------------------------------------------------------------------------------------------------------------------------------- |
    | `name`        | 文字列 | HubSpotのUIに表示されるアプリの名前。                                                                                                                       |
    | `description` | 文字列 | HubSpotのUIに表示されるアプリの説明。                                                                                                                       |
    | `scopes`      | 配列  | 非公開アプリのアクセストークンを使用してリクエストを認証するためにアプリがアクセスできるスコープ。上記の2つのスコープは、最低限必要なスコープです。このチュートリアルでは、リクエストに非公開アプリアクセストークンを使用する必要がないため、追加のスコープは追加しません。        |
    | `uid`         | 文字列 | アプリ固有のID。任意の文字列を指定できますが、アプリを識別できる意味のある文字列である必要があります。HubSpotではこのIDでアプリが識別されるため、ログなどの履歴データやステートフルデータを削除することなく、アプリの`name`をローカルまたはHubSpotで変更できます。 |
    | `public`      | 文字列 | 非公開アプリの場合は`false`に設定します。                                                                                                                      |
  </Tab>

  <Tab tabId="1" title="function.js">
    この関数は、[Zen Quotes API](https://zenquotes.io/)からランダムに見積もりを取得します。

    ```js theme={null}
    const axios = require("axios");

    exports.main = async context => {
      try {
        // Make GET request to the ZenQuotes API
        const response = await axios.get("https://zenquotes.io/api/random");

        // Extract the quote data (first item in the array)
        const quoteData = response.data[0];

        // Log the quote and author to console
        console.log(`"${quoteData.q}" — ${quoteData.a}`);

        // Return a properly formatted response with status code and body
        return {
          statusCode: 200,
          body: quoteData,
          headers: {
            "Content-Type": "application/json",
          },
        };
      } catch (error) {
        // Handle any errors that occur during the request
        console.error("Error fetching quote:", error.message);

        // Return an error response
        return {
          statusCode: 500,
          body: { error: "Failed to fetch quote" },
          headers: {
            "Content-Type": "application/json",
          },
        };
      }
    };
    ```
  </Tab>

  <Tab tabId="2" title="serverless.json">
    ```js theme={null}
    {
      "appFunctions": {
         "quote-function": {
          "file": "function.js",
          "secrets": [],
          "endpoint": {
            "path": "fetch-quote",
            "method": ["GET"]
           }
        }
      }
    }

    ```

    | フィールド            | 型      | 説明                                                                                                                           |
    | ---------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------- |
    | `quote-function` | オブジェクト | サーバーレス関数の構成の詳細を格納するオブジェクト。このオブジェクトには任意の名前を付けることができますが、`serverless.json`ファイルの`appFunctions`フィールドで参照する関連フィールド名と一致する必要があります。    |
    | `file`           | 文字列    | 実行するサーバーレス関数コードが含まれているJavaScriptファイルの名前。                                                                                     |
    | `secrets`        | 配列     | 関数がリクエストの認証に使用するシークレットの名前を含む配列。このチュートリアルでは、シークレットは必要ありません。                                                                   |
    | `endpoint`       | オブジェクト | 関数を呼び出すためにヒットできるエンドポイントに関する詳細を含むオブジェクト。`path`フィールドは`/hs/serverless/<path>`エンドポイントパスの最後の値を定義し、`method`フィールドはリクエストのメソッドを定義します。 |
  </Tab>

  <Tab tabId="3" title="package.json">
    ```json theme={null}
    {
      "name": "example-serverless-function",
      "version": "0.1.0",
      "author": "HubSpot",
      "license": "MIT",
      "dependencies": {
        "@hubspot/api-client": "^7.0.1",
        "axios": "^0.27.2"
      }
    }
    ```

    | フィールド          | 型      | 説明                     |
    | -------------- | ------ | ---------------------- |
    | `dependencies` | オブジェクト | サーバーレス関数に関する依存関係が含まれる。 |
  </Tab>
</Tabs>

* 上記のコードを追加した後、変更を保存します。

## 3. HubSpotにプロジェクトをアップロードする

変更を保存したら、プロジェクトをHubSpotにアップロードして、アプリとサーバーレス関数をビルドしてデプロイできます。

* ターミナルで`hs project upload`を実行します。

```shell theme={null}
hs project upload
```

* アカウントにプロジェクトを作成することを確認します。初回作成後に再度確認する必要はありません。ターミナルには、ビルドとデプロイのステップの進行状況が表示されます。
* アップロードが完了したら、`hs project open`を実行してプロジェクトをHubSpotで表示します。

```shell theme={null}
hs project open
```

HubSpotでは、プロジェクトの詳細の表示、ログやサーバーレス関数ログのビルドとデプロイ、プロジェクトやアプリの管理などを行うことができます。詳しくは[OpenAIのAPIプラットフォームでのプロジェクト管理](https://developers.hubspot.com/docs#view-the-project-in-hubspot)についてをご確認ください。

## 4. 関数のテストを行う

サーバーレス関数がデプロイされたら、公開URLをクリックして呼び出すことができます。開発者プロジェクトを使用して作成されたサーバーレス関数の公開URL構造は、`https://<domain>/hs/serverless/<endpoint-path-from-config>`のようになります。

* `<domain>`：アカウントに接続されている任意のドメインを使用できます。例えば、website.comとsubdomain.brand.comがアカウントに接続されている場合は、`https://website.com/hs/serverless/<path>`または`https://subdomain.brand.com/hs/serverless/<path>`を使用して関数を呼び出すことができます。
* `<endpoint-path-from-config>`：`serverless.json`の`path`フィールドの値。

このチュートリアルで提供されているサンプルコードに基づくと、関数を呼び出すための公開URLは`https://<domain>/hs/serverless/fetch-quote`になります。

<Alert type="info">
  関数を呼び出すURLのエンドポイントパスは、
  アプリまたはプロジェクトのスコープではなくグローバルです。複数のアプリまたはプロジェクトに同一のエンドポイントパスがある場合は、
  最後にデプロイされたエンドポイント関数が 優先されます。
</Alert>

関数の出力を表示するには、ブラウザーで該当URLに移動し、ドメインをHubSpotがホスティングするドメインのいずれかに置き換えます。カスタムドメインを接続していない場合は、HubSpotが提供するデフォルトのドメインである`<hubId>.hs-sites.com`（例：`123456.hs-sites.com`）を使用できます。ブラウザーには、Zen Quotes APIによって返されたデータが表示されます。

![ブラウザーに表示されるZen Quotes APIによって返されたデータ](https://www.hubspot.jp/hubfs/Knowledge_Base_2023-24-25/zen-quotes-api-return.png)

## 5. ウェブサイトページから関数を呼び出す

サーバーレス関数を作成し、この関数でデータが返されることを確認したら、この関数をページに実装して、返されるデータを現実に即したコンテキストで確認しましょう。このチュートリアルでは、関数を実行して応答データを表示するためのカスタムJavaScriptとHTMLを含むページテンプレートを作成します。

まず、新規ページテンプレートを作成します。

* ローカル環境で、ページテンプレートを格納する新しいディレクトリーを作成します。このチュートリアルでは、`hs project create`によって作成されたプロジェクトディレクトリーの外にこのディレクトリーを作成します。

* ターミナルで、`cd`コマンドを使用してそのディレクトリーに移動します。

```shell theme={null}
cd Documents/Dev/serverless-page
```

* `hs create template "serverless-template"`を実行して、`serverless-template`という名前の新しいテンプレートを作成します。

```shell theme={null}
hs create template "serverless-template"
```

* テンプレートタイプとして**ページ**を選択します。
* 新しく作成したページテンプレートのファイルをエディターで開き、ボイラープレートコードを以下のコードに置き換えます。関数エンドポイントURL（`fetch('http://<domain>/hs/serverless/fetch-quote')`）の`<domain>`は、HubSpotアカウントに接続されているドメインに置き換えてください。

```html theme={null}
<!--
    templateType: page
    label: Serverless function example template
    isAvailableForNewContent: true
-->
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>{{ content.html_title }}</title>
    <meta name="description" content="{{ content.meta_description }}" />
    {{ standard_header_includes }}
  </head>
  <body>
    {% module "page_template_logo" path="@hubspot/logo" label="Logo" %}
    <!-- Button to fetch quote via serverless function -->
    <div class="serverless-container">
      <h2>Get quote</h2>
      <p class="subtitle">Click the button to fetch and display a quote.</p>
      <button id="data-button" type="button">Get quote!</button>
      <div id="data-container">
        <p class="data"></p>
      </div>
    </div>
    <!-- End button section -->

    {{ standard_footer_includes }}

    <!-- JavaScript that invokes the serverless function
     and adds returned data into the "data" text element -->
    <script>
      const dataButton = document.getElementById("data-button");
      const dataContainer = document.getElementById("data-container");

      dataButton.addEventListener("click", function (e) {
        console.log("Button clicked!");

        // Show loading state
        dataContainer.innerText = "Loading...";
        // Replace <domain> with your own domain
        fetch("http://<domain>/hs/serverless/fetch-quote")
          .then(response => {
            if (!response.ok) {
              throw new Error(`HTTP error! Status: ${response.status}`);
            }
            // Parse the JSON response
            return response.json();
          })

          .then(data => {
            console.log("Raw data received:", data);

            // Clear container
            dataContainer.innerText = "";

            // Create the paragraph element
            const newDataElement = document.createElement("p");
            newDataElement.innerText = `"${data.q}" — ${data.a}`;
            dataContainer.appendChild(newDataElement);
          });
      });
    </script>
  </body>
</html>
```

* ファイルを保存し、`hs upload`を実行してHubSpotにアップロードします。ターミナルのプロンプトに従って、ローカルのソースとアップロード先のパスを選択します。このチュートリアルでは、各プロンプトで**Enter**キーを押してデフォルトのパスを使用します。

次に、HubSpotでテンプレートから新しいページを作成します。

* `hs open website-pages`を実行して、HubSpotアカウントのウェブサイトページのダッシュボードに移動します。

```shell theme={null}
hs open website-pages
```

<Alert type="info">
  `hs open --list`
  を実行して、開く操作に使用できるショートカットの完全なリストを確認してください。
</Alert>

* ブラウザーで、右上の［作成］をクリックして新しいページを作成します。
* ダイアログボックスで、使用する**ドメイン**を選択し、次に**ページ名**を割り当てます。クロスオリジン（CORS）エラーを回避するには、ページのドメインがサーバーレス関数の呼び出しに使用しているドメインに一致する必要があります。ドロップダウンメニューからそのドメインを選択するか、ページテンプレートのコード（`fetch('http://<domain>/hs/serverless/fetch-quote')`）でエンドポイントURLを更新して、このステップで選択したドメインを使用することができます。
* ［ページを作成］をクリックします。
* テンプレート選択画面で、新しいテンプレートを検索します。ラベルまたはファイル名を使用してテンプレートを検索できます（例：「サーバーレス関数サンプルページ」）。テンプレートはテーマの一部ではないため、［他のテンプレート］に表示されます。

![新しく作成されたページテンプレートを表示する［他のテンプレート］セクション](https://www.hubspot.jp/hubfs/Knowledge_Base_2023-24-25/select-new-template.png)

* ［テンプレートを選択］をクリックします。
* ページエディターで、右上の［プレビュー］をクリックします。

![ページエディターのプレビューボタン](https://www.hubspot.jp/hubfs/page-preview.png)

* ［新しいタブで開く］をクリックします。

新しいタブで、ボタンをクリックして見積もりを取得して表示できるようになりました。

![Zen Quotes APIから見積もりを取得して表示するサンプルボタンのデモ](https://www.hubspot.jp/hubfs/Knowledge_Base_2023-24-25/quote-button-demonstration.gif)

## 次のステップ

サードパーティーAPIとやりとりするサーバーレス関数の作成と実装が完了しました。次は、以下に挙げる方法でサーバーレス関数の使用を継続的に積み上げていきます。

* HubSpot APIまたはその他のサードパーティー認証APIを呼び出すための[リクエストに認証を含める](/cms/reference/serverless-functions/serverless-functions-in-projects#authentication)。
* ボタンとJavaScriptコードをページテンプレートにハードコーディングするのではなく、モジュールに実装する。これにより、移植性の高いオプションが提供され、モジュールが追加されている任意のページから関数を呼び出すことができます。このためには、モジュールを作成し、HTMLをモジュールの`module.html`ファイルに追加してJavaScriptを`module.js`ファイルに追加します。

<Tabs defaultSelected="0">
  <Tab tabId="0" title="module.html">
    ```html theme={null}
    <div class="serverless-container">
      <h2>Get quote</h2>
      <p class="subtitle">Click the button to fetch and display a quote.</p>
      <button id="data-button" type="button">Get quote!</button>
      <div id="data-container">
        <p class="data"></p>
      </div>
    </div>
    ```
  </Tab>

  <Tab tabId="1" title="module.js">
    ```js theme={null}
    const dataButton = document.getElementById("data-button");
    const dataContainer = document.getElementById("data-container");

    dataButton.addEventListener("click", function (e) {
      console.log("Button clicked!");

      // Show loading state
      dataContainer.innerText = "Loading...";

      fetch("http://meowmix-2272014.hs-sites.com/hs/serverless/fetch-quote")
        .then(response => {
          if (!response.ok) {
            throw new Error(`HTTP error! Status: ${response.status}`);
          }
          // Parse the JSON response
          return response.json();
        })

        .then(data => {
          console.log("Raw data received:", data);

          // Clear container
          dataContainer.innerText = "";

          // Create the paragraph element
          const newDataElement = document.createElement("p");
          newDataElement.innerText = `"${data.q}" — ${data.a}`;
          dataContainer.appendChild(newDataElement);
        });
    });
    ```
  </Tab>
</Tabs>
