Twilio Functions で CORS を正しく設定する方法:筆者が体験した失敗例と成功例

Twilio Functions で CORS を正しく設定する方法:筆者が体験した失敗例と成功例

Twilio Functions を使って Web クライアントと API 通信を行う際に避けて通れないのが CORS(クロスオリジン通信)の問題です。本記事では、筆者が実際に遭遇した「うまくいかなかった例」と「うまくいった例」をもとに、Twilio Functions における CORS 対応の正しい実装方法を解説します。エラーの原因がわからず悩んでいる方、Twilio をフロントエンドと組み合わせて使いたい方におすすめの内容です。

はじめに

Twilio Functions は、API サーバーを素早く構築できる便利な仕組みですが、Web クライアントと連携する際には CORS(Cross-Origin Resource Sharing) への対応が欠かせません。たとえば、別オリジンのフロントエンドから fetch() を使ってトークン発行エンドポイントなどを呼び出す場合、適切な CORS ヘッダーがレスポンスに含まれていないと、ブラウザによってリクエストがブロックされてしまいます。

本記事では、Twilio Functions における CORS 対応の具体的な実装方法を紹介するとともに、筆者が実際に遭遇した「うまくいかなかった例」と「うまくいった例」を比較しながら、CORS 対応のポイントを整理します。特に Next.js や v0 などフロントエンドの実装と組み合わせて使う方にとって参考になる内容を目指しています。

本記事で扱う内容

  • CORS の基本的な仕組みと Twilio Functions で必要となる理由
  • CORS 対応において陥りやすい実装ミスとその回避方法
  • Twilio.Response を使った正しいヘッダー設定方法

対象読者

  • Twilio Functions を使って Web クライアントと連携する API を構築したい方
  • Twilio Conversations や Flex Plugin などの SDK から Function を呼び出そうとして CORS に悩まされたことのある方

参考

CORSとは何か、なぜ必要か

CORS(Cross-Origin Resource Sharing)は、異なるオリジン間での HTTP リクエストをブラウザが制限・制御するための仕組みです。ここでいう「オリジン」とは、スキーム(http/https)、ホスト名、ポート番号の組み合わせを指します。たとえば https://example.comhttps://api.example.com は、同じドメインに見えても異なるオリジンと見なされます。

JavaScript を使ってフロントエンドから外部の API を呼び出す場合、もし CORS の仕組みがなければ、任意の Web サイトがユーザーの認証情報を持ったまま外部サーバーへリクエストを送り、意図しない情報の取得や操作が可能になってしまいます。CORS はこうしたクロスサイト攻撃を防ぐために設けられた、安全のためのルールです。

一方で、モダンな Web 開発においては、フロントエンドとバックエンドが異なるドメインやサーバーにホストされることが一般的です。たとえば、フロントエンドが http://localhost:3000 にあり、バックエンドの Twilio Function が https://xxxxx.twil.io にあるような構成では、正当なアクセスであっても CORS による制限に引っかかってしまう場合があります。

そのため、バックエンド側で明示的に「どのオリジンからのアクセスを許可するか」をヘッダーとして返すことが必要になります。Twilio Functions を利用する場合も例外ではなく、CORS に対応しない限り、ブラウザ経由の API 通信がブロックされ、開発・運用に支障をきたすことになります。

CORS 対応 Function の実装例

このセクションでは、Twilio Functions における CORS 対応に関して、筆者が実際に検証した「うまくいかなかった例」と「うまくいった例」を紹介し、それぞれのポイントや落とし穴について解説します。

うまくいかなかった例 1:Twilio.Response を使用しないケース

以下は、JWT トークンを返すための Function を定義したつもりの実装例です。

exports.handler = async function(context, event, callback) {
  const token = createAccessTokenSomehow(); // 仮のトークン生成関数

  const response = {
    token: token,
  };

  callback(null, response); // ← Twilio.Response を使っていない
};

このコードでは、Twilio のレスポンスオブジェクト Twilio.Response を使用せず、単にプレーンなオブジェクトを callback で返しています。このような実装の場合、HTTP レスポンスヘッダーが自動的には付与されず、Access-Control-Allow-Origin を含む CORS ヘッダーが設定されないため、ブラウザ側で以下のようなエラーが発生します。

Access to fetch at 'https://****.twil.io/token' from origin 'null' has been blocked by CORS policy: 
No 'Access-Control-Allow-Origin' header is present on the requested resource.

このようなエラーは、ローカル HTML ファイルや異なるオリジンから fetch() を用いて Function にアクセスした場合に一般的に発生します。

うまくいかなかった例 2:Content-Type を指定していないケース

次に紹介するのは、Twilio.Response を使用し、CORS に必要なヘッダーも設定しているにもかかわらず、依然としてエラーが発生した実装例です。

exports.handler = async function(context, event, callback) {
  const response = new Twilio.Response();
  response.appendHeader('Access-Control-Allow-Origin', '*');
  response.appendHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
  response.appendHeader('Access-Control-Allow-Headers', 'Content-Type');

  const token = createAccessTokenSomehow(); // 仮のトークン生成関数
  response.setBody({ token: token });

  callback(null, response);
};

この実装では、CORS に関するヘッダーは適切に設定されていますが、Content-Type ヘッダーが明示的に追加されていません。その結果、一部のブラウザやクライアント環境において、レスポンスが不正と見なされ、以下のようなエラーが出力されました。

Access to fetch at 'https://****.twil.io/token' from origin 'null' has been blocked by CORS policy: 
No 'Access-Control-Allow-Origin' header is present on the requested resource.

このエラーは一見 Access-Control-Allow-Origin に関するものに見えますが、実際には レスポンス全体の形式が正しく解釈されていないこと に起因しており、Content-Type の明示がない場合、内部的にヘッダーが破棄される・無視されるケースがあることが原因です。

うまくいった例:Content-Type を含む正しいレスポンスの構築

以下は、CORS 対応に成功した Function の実装例です。

exports.handler = async function(context, event, callback) {
  const response = new Twilio.Response();
  response.appendHeader('Content-Type', 'application/json');
  response.appendHeader('Access-Control-Allow-Origin', '*');
  response.appendHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
  response.appendHeader('Access-Control-Allow-Headers', 'Content-Type');

  const token = createAccessTokenSomehow(); // 仮のトークン生成関数
  response.setBody({ token: token });

  callback(null, response);
};

このコードでは、Twilio.Response を用いてレスポンスを構築し、CORS に必要な各種ヘッダーに加えて、Content-Type: application/json を明示的に追加しています。これにより、ブラウザはレスポンスを正しく解釈し、クロスオリジン通信も問題なく完了することが確認できました。

このように、CORS の対応では「何を返すか」に加えて、「どのように返すか」も重要なポイントとなります。特に Twilio Functions においては、標準のレスポンス形式でヘッダーが適切に処理されないケースがあるため、Twilio.Response の利用と適切なヘッダー設定は必須と考えておくべきです。

まとめ

本記事では、Twilio Functions における CORS(Cross-Origin Resource Sharing)対応について、実際に遭遇した成功例・失敗例を交えながら解説しました。

特に重要なのは、Twilio Functions では CORS 対応を自動で行ってくれるわけではないという点です。Twilio.Response オブジェクトを明示的に使用し、Access-Control-Allow-Origin をはじめとする各種 CORS ヘッダーを手動で追加する必要があります。さらに、Content-Type の指定漏れがレスポンス全体の無効化につながるなど、見落としやすい落とし穴も存在します。

  • Twilio.Response を必ず使用する
  • Access-Control-Allow-Origin を明示的に設定する(本番ではオリジンを限定)
  • Access-Control-Allow-MethodsAccess-Control-Allow-Headers を必要に応じて追加する
  • Content-Type(例: application/json)を明示的に指定する

CORS エラーは一見すると原因が分かりにくく、開発中のストレス要因になりがちです。しかし、構造的に正しく設計・対応すれば、安定した API 通信が可能になります。Twilio Functions をフロントエンドと安全に連携させるための第一歩として、本記事の内容が役立てば幸いです。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.