Cloudflare Workersで、OpenAIなどの外部APIを呼び出す方法
npmにSDKを公開しているSaaSは数おおくありますが、Workersの性質上全てが動作するわけではありません。 今回はOpenAIのAPIを例に、SDKを使わずに外部APIを利用する方法を紹介します。 SDKを使わず […]
広告ここから
広告ここまで
目次
npmにSDKを公開しているSaaSは数おおくありますが、Workersの性質上全てが動作するわけではありません。
今回はOpenAIのAPIを例に、SDKを使わずに外部APIを利用する方法を紹介します。
SDKを使わずに、fetch APIを利用する
SDKを利用してエラーが発生した場合、Fetch APIを利用する代替策をとります。
例えば次のようなクラスを作成しましょう。
export class OpenAI {
private readonly apiURL = "https://api.openai.com"
private readonly apiVersion = "v1"
private readonly headers: HeadersInit = {}
public constructor(apiKey: string) {
this.headers = {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
}
}
private _createOpenAPIRequestURL(path: string) {
const paths = `${this.apiVersion}/${path}`.replace(/\/\//, "/")
return `${this.apiURL}/${paths}`
}
private async _fetch(input: RequestInfo, init: RequestInit) {
init.headers = {
...this.headers,
...this.headers,
}
const response = await fetch(input, init)
return response.json()
}
}
TypeScriptの型情報だけSDKから取り出すことも
一部のSDKでは、型情報やヘルパー関数などだけを利用できるケースがあります。
もしwrangler dev
で動かしてみてエラーが出ない様子であれば、次のように型情報などだけをSDKから借りれます。
import { CreateChatCompletionRequest, CreateChatCompletionResponse } from "openai";
export class OpenAI {
private readonly apiURL = "https://api.openai.com"
private readonly apiVersion = "v1"
private readonly headers: HeadersInit = {}
public constructor(apiKey: string) {
this.headers = {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
}
}
private _createOpenAPIRequestURL(path: string) {
const paths = `${this.apiVersion}/${path}`.replace(/\/\//, "/")
return `${this.apiURL}/${paths}`
}
private async _fetch(input: RequestInfo, init: RequestInit) {
init.headers = {
...this.headers,
...this.headers,
}
const response = await fetch(input, init)
return response.json()
}
public async createChatCompletion(body: CreateChatCompletionRequest): Promise<CreateChatCompletionResponse> {
const result = await this._fetch(this._createOpenAPIRequestURL('chat/completions'), {
method: "POST",
body: JSON.stringify(body)
})
return result as CreateChatCompletionResponse
}
}
abstractクラスを作って、汎用的に使う例
OpenAI以外にも、Basic認証を利用するAPIであれば同様の実装で対応できます。
そこで次のようなabstract class
を作る方法もとれます。
import { CreateChatCompletionRequest, CreateChatCompletionResponse } from "openai";
export abstract class FetchCLient {
protected readonly baseURL: string
protected readonly headers: HeadersInit = {}
public constructor(baseURL: string, apiKey: string) {
this.baseURL = baseURL
this.headers = {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
}
}
protected async _fetch<R = unknown>(input: RequestInfo, init: RequestInit): Promise<R> {
init.headers = {
...this.headers,
...this.headers,
}
const response = await fetch(input, init)
return response.json()
}
}
利用したいAPIに応じて、先ほどのクラスをextends
して利用します。
export class OpenAI extends FetchCLient {
public constructor(apiKey: string) {
super("https://api.openai.com/v1", apiKey)
}
public async createChatCompletion(body: CreateChatCompletionRequest): Promise<CreateChatCompletionResponse> {
const result = await this._fetch<CreateChatCompletionResponse>(`${this.baseURL}/chat/completions`, {
method: "POST",
body: JSON.stringify(body)
})
return result
}
}
この方法であれば、複数の外部APIを利用する開発でもコードの量を減らすことができます。
より汎用的にするならば・・・
APIの認証方法はBasic認証以外にも複数存在します。
そのため、認証処理部分だけ別途interface
やclass
を作成して抽象化すると、より幅広く利用できます。
class AnySaaS extends FetchClient {
constructor(jwtToken: string) {
super(“https://api.example.com”, {
authorizer: new JWTAuthorizer(),
token: jwtToken,
})
}
}
みたいな感じでしょうか。
今後Basic認証以外の方法も試す必要が出てきた場合には、トライしてみたいと思います。