[GoogleAPI] ワクワクしながら、ChatGPTでサーバーサイドコーディングする!
ChatGPTでサーバーサイドコーディングをしていきます!
最近、DjangoとGoogleAPIを試しているので、そのあたりを使用する前提でChatGPTに色々コーディングをしてもらおうと思います。
ワクワクが止まらないですね!
私は、フレームワークを使用したサーバーサイド経験は10年以上ありますが、Djangoは初級レベルですが、ChatGPTを使うとどの程度効率よくコーディングをできるかを試していきたいと思います。
amazon light sailのサーバーでDjangoを使用し、vimでさくさく作っていきます笑
頭の中だけで考えると疲れるので、図を書きながら整理する
考えたことをメモに書いていき、整理する
考えたことをメモをしていき、フロー図に書きながら何を作るかを考えていきます。頭の中だけで考えると、作業を中断した際にどこまで考えたか忘れてしまったり、内容の整理も視覚的にできるので、メモに書いていくのが良いです。それと頭の中だけで考えるのは疲れますし、、
最近GoogleフォームAPIを試したので、GoogleフォームAPIを使用した何かを作っていこうと思います。
ちなみに上記フローチャートはFigjamで作成しました!
画面や機能をフローに書き、プログラミング対象を整理する
次に、実際にプログラミングするに伴い、必要な画面や機能をフローに書き、プログラミングに必要な箇所を整理していきます。
青色のオブジェクトが、今回のプログラミング対象となります。
今回はChatGPTでどの程度コーディングをできるかを試すのが目的ですので、シンプルなものを開発しようと思い、Googleフォームの回答データを表示する画面を作っていこうと思います。
Googleフォームの機能でスプレットシートで回答データは確認できますが、それの画面版のようなイメージです。
[1] GoogleOauthの同意画面へリダイレクトする処理を作る
以前、GoogleAPIのリクエストの仕方をChatGPTに聞いたところ、正しく回答がもらえなかったので、今回リクエスト内容については明示的に指示を出すようにしました。
APIなど仕様がたびたび変わるものについては、ChatGPT側に最新情報が反映されていない感じですかね。
(それと以前、GoogleAPIのリクエストの仕方を整理するのにかなり時間がかかりましたが、整理済みですので今回はそこは時短できそうです。)
1-1.プロンプト(同意画面へリダイレクトする処理)
Djangoフレームワークで、GoogleOauthの同意画面にリダイレクトするviews.pyを作成してください
定数(定数は大文字。値は空を設定する)
redirect_uri
client_id
同意画面のURL
https://accounts.google.com/o/oauth2/v2/auth
パラメータ
scope=https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/calendar
access_type=offline
include_granted_scopes=true
response_type=code
redirect_uri=上記定数のredirect_uri
client_id=上記定数のclient_id
1-2.ChatGPTからの回答
from django.shortcuts import redirect
from django.conf import settings
def google_oauth_redirect(request):
# 定数の設定
REDIRECT_URI = settings.GOOGLE_OAUTH_REDIRECT_URI
CLIENT_ID = settings.GOOGLE_OAUTH_CLIENT_ID
# 同意画面のURLの構築
consent_url = "https://accounts.google.com/o/oauth2/v2/auth?"
consent_url += f"scope=https://www.googleapis.com/auth/drive%20https://www.googleapis.com/auth/calendar"
consent_url += "&access_type=offline"
consent_url += "&include_granted_scopes=true"
consent_url += "&response_type=code"
consent_url += f"&redirect_uri={REDIRECT_URI}"
consent_url += f"&client_id={CLIENT_ID}"
# リダイレクト
return redirect(consent_url)
1-3.プロンプト(上記に該当するurls.pyを作成)
上記に該当するurls.pyを作成してください
インポート
from . import views
1-4.ChatGPTからの回答
from django.urls import path
from . import views
urlpatterns = [
path('google/oauth/', views.google_oauth_redirect, name='google_oauth_redirect'),
]
1-5.動作確認
プログラムに反映し動作確認したところ、問題なくGoogleOAuthの同意画面にリダイレクトされました!
10分程度、ChatGPTと1、2回のストロークで出来てしまうところが、さすがChatGTPですね!生産性は爆上がりです!!
[a]アクセストークンを保存する仕様を検討する
a-1.アクセストークンを保存する際の案をいくつか出してみる
アクセストークン取得APIから取得したアクセストークンを、何かしらの方法で保存しておかないと、回答データ表示画面を閲覧するたびに毎回GoogleOauth同意画面から遷移しないといけない仕様になってしまうので、どのような方法で保存するかを検討していきます。
おそらくフレームワークのセッション(Cookie)あたりに保存がよさそうな気がしますが、ChatGPTに相談してみます。
a-2.プロンプト(アクセストークンを保存するアイディア)
アクセストークンを保存したいですがどのようなアイディアがありますか?
a-3.ChatGPTからの回答
[b]DjangoのセッションについてStudyする
セッションで保存する案が良さそうなので、この案で進めたいと思います。
ですが、Djangoのセッションは使用したことがないので、使用する前にそのあたりのStudyをしていします。
b-1.プロンプト(セッションに保存するコード)
アクセストークンをセッションに保存したいのですが、どのようなコードになりますか
b-2.ChatGPTからの回答
# views.py
from django.shortcuts import render, redirect
from django.http import HttpResponse
from django.conf import settings
def store_access_token(request, access_token):
"""
アクセストークンをセッションに保存する
"""
request.session['access_token'] = access_token
return HttpResponse("Access token stored successfully.")
def use_access_token(request):
"""
セッションからアクセストークンを取得する
"""
access_token = request.session.get('access_token')
if access_token:
# ここでアクセストークンを使用して必要な処理を実行する
return HttpResponse("Access token retrieved successfully: " + access_token)
else:
return HttpResponse("Access token not found in session.")
# 別のビューでアクセストークンをセッションに保存する例
def another_view(request):
# アクセストークンを取得するなどの処理が行われたとして、access_tokenが得られたとする
access_token = "example_access_token"
# アクセストークンをセッションに保存する
return store_access_token(request, access_token)
b-3.プロンプト(セッションの破棄されるタイミング)
上記のセッションは、どのタイミングで破棄されますか
b-4.ChatGPTからの回答
b-5.プロンプト(セッション保存時の安全性)
Djangoフレームワークで、セッションに値を保存するのは安全ですか?
暗号化はされますか?
b-6.ChatGPTからの回答
b-7.まとめ
セッション情報は暗号化された状態でDBに保存され、Cookieをキーにその情報を参照しにいくようなイメージでしょうか?
あとはセッションが残らないように、有効期限を気にしつつ、ログアウトなど特定の処理でセッションを破棄するのを忘れないようにしたほうがよさそうですね。
このあたりは他のサーバーサイドの処理でも同じことなのですが。
特に問題なく使用できそうなので、セッションに保存する案で進めたいと思います。
[2] アクセストークンAPIをリクエストし、アクセストークンを取得する処理を作る
アクセストークンの保存方法も決まったので、次の処理を作っていきます。
処理の流れとしては、GoogleOauthの同意画面にて許可ボタンを押下すると、[2]の処理に戻ってくるようなイメージで、アクセストークンを取得するための、認可コード(codeパラメータ)がリクエストパラメータとして渡されます。
この処理では、認可コードを使用しアクセストークンAPIをリクエストする処理を作っていきます。
2-1.プロンプト(同意画面からコールバックされ、アクセストークンAPIをリクエストする処理)
Djangoフレームワークで、GoogleOauthの同意画面からコールバックされるviews.pyを作成してください
パラメータチェック
code
チェックエラーの場合は処理を終了しエラー画面を表示する
アクセストークンAPIをリクエスト
URL
https://accounts.google.com/o/oauth2/token
パラメータ
code=リクエストパラメータcode
client_id=定数のclient_id
client_secret=定数のclient_id
redirect_uri=定数のredirect_uri
grant_type=authorization_code
セッションに保存する
アクセストークン
アクセストークンの有効期限日時
リフレッシュトークン
ブラウザを閉じたらセッションを破棄する
"view_google_form_responses"にリダイレクトする
2-2.ChatGPTからの回答
import requests
from django.shortcuts import redirect, render
from django.http import HttpResponse
from django.conf import settings
from datetime import datetime, timedelta
def google_oauth_callback(request):
# コールバックパラメータの取得
code = request.GET.get('code')
# codeパラメータのチェック
if not code:
return render(request, 'error.html', {'message': 'Error: Missing "code" parameter.'})
# 定数の設定
CLIENT_ID = settings.GOOGLE_OAUTH_CLIENT_ID
CLIENT_SECRET = settings.GOOGLE_OAUTH_CLIENT_SECRET
REDIRECT_URI = settings.GOOGLE_OAUTH_REDIRECT_URI
# アクセストークンAPIのURL
token_url = "https://accounts.google.com/o/oauth2/token"
# アクセストークンAPIへのリクエストパラメータの設定
token_params = {
'code': code,
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'redirect_uri': REDIRECT_URI,
'grant_type': 'authorization_code',
}
# アクセストークンAPIへのPOSTリクエスト
response = requests.post(token_url, data=token_params)
# レスポンスの処理
if response.status_code == 200:
# アクセストークンが正常に取得された場合の処理
token_data = response.json()
access_token = token_data.get('access_token')
refresh_token = token_data.get('refresh_token')
expires_in = token_data.get('expires_in')
if access_token:
# アクセストークン、有効期限、リフレッシュトークンをセッションに保存する
request.session['access_token'] = access_token
expiration_time = datetime.now() + timedelta(seconds=expires_in)
request.session['token_expiry'] = expiration_time.isoformat()
request.session['refresh_token'] = refresh_token
request.session.set_expiry(expires_in)
# ブラウザを閉じたらセッションを破棄する
# "view_google_form_responses"にリダイレクトする
return redirect('view_google_form_responses')
else:
return render(request, 'error.html', {'message': 'Error: Access token not found in response.'})
else:
# エラーが発生した場合の処理
return render(request, 'error.html', {'message': f'Error obtaining access token. Status code: {response.status_code}'})
2-3.動作確認
特段問題なくアクセストークンが取得できました!
かなり順調に進んでいます!
2-4.エラー時はエラーメッセージのみを画面に表示したほうが検証しやすい
「エラー発生時はエラー画面を表示する」と指示を出していましたが、検証段階だと、エラー画面をつくるのも面倒ですので、エラーメッセージのみを画面に表示したほうが効率よく検証できそうです。
アクセストークンの有効期限が切れてしまうとエラーになってしまいますし、まずはリッチに作り込むよりエラーの内容がわかれば良いかと。
2-5.ログをこまめに表示する
レスポンス内容などは、ログに表示させておくと検証しやすいので、ログ出力の指示も出した方が良いかと思いました。
[3] GoogleDriveAPIをリクエストし、Googleフォーム一覧データを取得する
次の処理は、GoogleDriveAPIをリクエストし、ドキュメントファイルを取得し、その中からGoogleフォームのファイルを抽出し、フォーム名を表示する画面を作っていきます。
特定のフォーム名を選択すると[4]の画面に遷移しフォームの回答データを閲覧できるようなイメージです。
Googleフォームのファイルを抽出は、レスポンスの内容を見ながら、特定の文字列と比較すれば抽出できるかも。といった感じでトライアンドエラーで設計しました。
3-1.プロンプト(GoogleDriveAPIをリクエストする処理を作る)
Djangoフレームワークで、GoogleDriveAPIをリクエストするviews.pyを作成してください
def名
view_google_form_responses
GoogleDriveAPIをリクエスト
https://www.googleapis.com/drive/v3/files
header
'Authorization: Bearer [セッションから取得したアクセストークン]'
header 'Accept: application/json'
JSONレスポンスを画面で使用できるようにcontextに設定してください "mimeType"が、 "application/vnd.google-apps.form"であるもののみを設定する
logger.infoでJSONレスポンスの内容をログに表示する
templete
"APP_TEST/view_google_form_responses.html"
エラー発生時は、HttpResponseを使用し、エラーメッセージのみを画面に表示してください
3-2.ChatGPTからの回答
import requests
import logging
from django.http import HttpResponse
from django.shortcuts import render
logger = logging.getLogger(__name__)
def view_google_form_responses(request):
# セッションからアクセストークンを取得
access_token = request.session.get('access_token')
# アクセストークンがない場合はエラーメッセージを表示
if not access_token:
return HttpResponse('Error: Access token not found.', status=400)
# Google Drive APIにアクセスするためのURL
url = "https://www.googleapis.com/drive/v3/files"
# リクエストヘッダーの設定
headers = {
'Authorization': f'Bearer {access_token}',
'Accept': 'application/json',
}
# Google Drive APIへのGETリクエスト
response = requests.get(url, headers=headers)
# リクエストエラーの場合はエラーメッセージを表示
if response.status_code != 200:
return HttpResponse(f'Error: Request failed with status code {response.status_code}.', status=400)
# JSONレスポンスから"mimeType"が "application/vnd.google-apps.form" のものだけを抽出
files = [file for file in response.json().get('files', []) if file.get('mimeType') == 'application/vnd.google-apps.form']
# JSONレスポンスをログに表示
logger.info("Google Drive API response: %s", response.json())
# コンテキストに設定
context = {
'files': files
}
# テンプレートをレンダリングして画面に表示
return render(request, 'APP_TEST/view_google_form_responses.html', context)
3-3.プロンプト(フォーム一覧を表示するhtmlを作る)
bootstrapでフォーム画面のhtmlを作成してください
css、jsは以下をインポートすること https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js
PC、スマートフォンの画面にレスポンシブで対応させること
日本語を使用する
サイト名は「パピプペポ」
フォームの内容はプルダウン一つのみ
フォームの内容は変数とする。
フォームの変数名はfilesとする
フォームの変数は、以下JSON形式のように設定される
{ "kind": "drive#fileList", "incompleteSearch": false, "files": [ { "kind": "drive#file", "mimeType": "application/vnd.google-apps.form", "id": “xxxxx”, "name": "無題のフォーム" }, { "kind": "drive#file", "mimeType": "application/vnd.google-apps.form", "id": “xxxxx”, "name": "無題のフォーム" } ] }
フォームのnameは上記JSONのname
フォームのvalueは上記JSONのid
フォームのパラメータ名はformId
プルダウンの説明として、'回答データを確認したいフォームを選択してください'
確認ボタン押下時にview_google_form_answersにPOSTする
入力値にバリデーションを設定する
画面フッターにダミーのコピーライトをつける
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>フォーム画面</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<h1 class="mt-5">パピプペポ</h1>
<form action="view_google_form_answers" method="POST">
{% csrf_token %}
<div class="mb-3">
<label for="formSelect" class="form-label">回答データを確認したいフォームを選択してください</label>
<select class="form-select" id="formSelect" name="formId" required>
<option value="">選択してください</option>
{% for file in files %}
<option value="{{ file.id }}">{{ file.name }}</option>
{% endfor %}
</select>
<div class="invalid-feedback">
フォームを選択してください。
</div>
</div>
<button type="submit" class="btn btn-primary">確認</button>
</form>
<p class="mt-3">Copyright © 2024 パピプペポ. All rights reserved.</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
3-4.JSONの形式は、どのように指示すべきか?
JSON変数をhtmlから参照する際に、JSONの形式をどのように指示出すか迷いましたが、サンプルのJSONレスポンスをChatGPTに伝えた上で、参照するフィールドを指示したところ、上手くいきました。
ただJSONがシンプルな構造であればそれでも伝わるかと思いますが、複雑な構造だとダメでしたので、このあたりの試行錯誤は次の[4]のステップで話します。
3-5.動作確認
このあたりのロジックから 少しChatGPTとのストロークが増えましたが、1時間以内で、Googleフォーム名を画面に表示させることができました。
プルダウンを選択すると、フォーム名称が表示される形となります。
3-6.処理が複雑だと、ChatGPTとのストロークも増える
処理をする内容が多くなると1発では完成せず、動作確認しつつ都度ChatGPTで修正版の指示を出していくような流れで作っていきました。
それとステップ[3]からはhtmlも増えましたしね。表示仕様については指示をだしましたが、デザインについては時間がかかりそうだったので、ほぼノータッチです。。
htmlで、POSTするAction箇所がURLタグで作成する旨の指示がなかったりでエラーになることもありました。他にもあった気がしますが。
[4] Googleフォームの回答データを表示する
最終ステージです!
GoogleフォームAPIをリクエストし、回答データを画面に表示するだけの処理ですが、APIからのレスポンスされるJSONデータの構造が複雑で、この部分をChatGPTに伝えるところで苦労しました。
4-1.プロンプト(GoogleFormsAPIをリクエストし、回答データを画面に表示する)
Djangoフレームワークで、GoogleFormsAPIをリクエストし、レスポンス内容を画面に表示するviews.pyを作成してください
def名
view_google_form_answers
リクエストパラメータ
formId
質問内容を取得
GoogleFormsAPIをリクエスト
https://forms.googleapis.com/v1/forms/[リクエストパラメータformId]'
header 'Authorization: Bearer [セッションから取得したアクセストークン]'
header 'Accept: application/json'
logger.infoでJSONレスポンスの内容をログに表示する
回答内容を取得
GoogleFormsAPIをリクエスト
https://forms.googleapis.com/v1/forms/[リクエストパラメータformId]/responses
header 'Authorization: Bearer [セッションから取得したアクセストークン]'
header 'Accept: application/json'
logger.infoでJSONレスポンスの内容をログに表示する
JSONレスポンスを画面で使用できるようにcontextに設定してください
templete
"APP_TEST/view_google_form_answers.html"
エラー発生時は、HttpResponseを使用し、エラーメッセージのみを画面に表示してください
4-2.ChatGPTからの回答
import requests
import logging
from django.http import HttpResponse
from django.shortcuts import render
logger = logging.getLogger(__name__)
def view_google_form_answers(request):
# リクエストパラメータからフォームIDを取得
form_id = request.GET.get('formId')
# フォームIDがない場合はエラーメッセージを表示
if not form_id:
return HttpResponse('Error: formId parameter is required.', status=400)
# Google Forms APIにアクセスするためのURL
form_info_url = f"https://forms.googleapis.com/v1/forms/{form_id}"
form_responses_url = f"https://forms.googleapis.com/v1/forms/{form_id}/responses"
# リクエストヘッダーの設定
headers = {
'Authorization': f'Bearer {request.session.get("access_token")}',
'Accept': 'application/json',
}
# Google Forms APIからフォーム情報を取得
form_info_response = requests.get(form_info_url, headers=headers)
# リクエストエラーの場合はエラーメッセージを表示
if form_info_response.status_code != 200:
return HttpResponse(f'Error: Failed to fetch form info with status code {form_info_response.status_code}.', status=400)
# Google Forms APIから回答情報を取得
form_responses_response = requests.get(form_responses_url, headers=headers)
# リクエストエラーの場合はエラーメッセージを表示
if form_responses_response.status_code != 200:
return HttpResponse(f'Error: Failed to fetch form responses with status code {form_responses_response.status_code}.', status=400)
# レスポンス内容をログに表示
logger.info("Form info response: %s", form_info_response.json())
logger.info("Form responses response: %s", form_responses_response.json())
# フォーム情報と回答情報をコンテキストに設定
context = {
'form_info': form_info_response.json(),
'form_responses': form_responses_response.json(),
}
# テンプレートをレンダリングして画面に表示
return render(request, 'APP_TEST/view_google_form_answers.html', context)
4-3.仕様が複雑な場合は、人力でコーディングしたほうがよさそう
上記のロジックのままだと、JSONデータの構造がそのままですのでhtml側が複雑になってしまいます。ですのでhtml側で参照しやすくするため、views.py側で加工することにしました。
ケースバイケースですが、仕様が複雑な箇所は自分でロジックを組んでしまった方が早い気もします。
ちなみに、ステップ[3]で実施した、サンプルレスポンスをChatGPTに伝える作戦でJSON形式を解釈してくれるかも。と思い試してみましたが、JSON形式が複雑であったせいか、サンプル通り解釈されませんでした。。
4-4.プロンプト(回答データを表示するhtmlを作る)
bootstrapで表のhtmlを作成してください
css、jsは、headタグに以下をインポートすること
https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css
https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js
PC、スマートフォンの画面にレスポンシブで対応させること
bootstrapのグリッドカラムは10とする
日本語を使用する
ヘッダーに「パピプペポ」と表示し、背景色は、未設定とする
フォーム選択画面へ戻るリンクを用意し、ヘッダーの下に配置し、bootstrapのcustom-linkのcssを使用する
上記リンクは、urlタグを使用し、'APP_TEST:view_google_form_responses'へリンク
フォーム名は変数documentTitleを表示する
表の中身は変数を表示する 表の見出しの背景色は、bootstrapの青色とする
表のtheadタグは、list変数q_listをループして表示する
表のtbodyタグは、list変数res_listをループし、要素であるlist変数qa_listでループし表示する
フッターには、コピーライトを設置し、背景色は未設定とする
4-5.ChatGPTからの回答
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bootstrap Table</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<!-- ヘッダー -->
<header class="py-4">
<div class="container">
<h1 class="mb-0">パピプペポ</h1>
<!-- フォーム選択画面へ戻るリンク -->
<a href="{% url 'APP_TEST:view_google_form_responses' %}" class="btn btn-link text-decoration-none">フォーム選択画面へ戻る</a>
</div>
</header>
<div class="container mt-4">
<!-- フォーム名 -->
<h2>{{ documentTitle }}</h2>
<!-- 表示テーブル -->
<div class="table-responsive">
<table class="table table-bordered">
<thead class="table-primary">
<tr>
<!-- theadをループして表示 -->
{% for header in q_list %}
<th>{{ header }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
<!-- res_listをループしてtbodyを生成 -->
{% for response in res_list %}
<tr>
<!-- qa_listをループしてセルを生成 -->
{% for qa in response %}
<td>{{ qa }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<!-- フッター -->
<footer class="py-4 text-center">
<div class="container">
© 2024 パピプペポ
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
4-6.動作確認
やっと完成しました!
この最終ステージを完成させるのにトータルで5時間ぐらいがかかってしまいました。。
4-7.Googleフォームは質問形式が複数あり、レスポンスのパターンが多いため、処理が複雑だった
そもそもGoogleフォームは汎用的に質問内容をカスタマイズできるので、その分レスポンスのパターンも多く、処理が複雑になります。(そのパターン分指示を出す必要があります。)
質問形式が変わると返却されるレスポンスのフィールド項目が変わったり、未回答だとフィールドごとなかったりと、考慮すべき点が多く結構大変でした。
おわりに
最後まで読んでいただきありがとうございます!
指示出しを考えるのに苦労したのと、思った通りの回答が返ってこず、指示内容を何度も修正したり、人力で部分的にロジック修正したりで、試行錯誤の連続でした。
今回は結構時間がかかったものの、次回以降で使える内容も多く、得られるものは大きかったとおもます。
その他
(一旦エラー画面や画面デザインは置いておいて)
設計をしていると色々とカスタマイズしたくなります。
アクセストークンは有効期限が切れるものですので、期限が切れたらリフレッシュトークンAPIをリクエストし、再度アクセストークンを取得することで、同意画面からやり直さなくて済みます。
またAPIのレスポンス内容をDBに保存し、一定時間が経過するまでAPIリクエストをせずにDBから情報を取得することで、APIのリクエスト回数を節約できます。
などなど色々とやりたくなりますね。
おまけ
最近、ChatGPTを使用し、色々なことを模索しています。
もしよければ、以下の記事も見て頂けると嬉しいです!