はじめに
前回の記事↓
Next.jsにdaisyUIを導入する(サンプルページ付き) ― ReactでWebアプリ開発〈2〉
開発予定のWebアプリ内でSpotifyが提供するWeb APIを利用しようと考えているので、一度、試しに使ってみることにしました。利用するのは初めてなので、今回の記事では、公式ドキュメントに従ってApp作成からアクセストークンの取得までを行ってみます。
準備
APIを使うためには、開発者ページで自分のAppを作成する必要があります。
まず以下のサイトにアクセスし、自分のSpotifyアカウントでログインします(freeまたはpremiumのSpotifyアカウントを持っている必要があります)。その後、右上のボタンからDashboardページに移動します。

Dashboardには自分のAppの一覧が表示されています。まだAppが一つもないので、"create app"でAppを新規作成します。

App作成のためのフォームに移動するので、App nameやApp description、Redirect URIsといった必須項目を入力します。
ちなみに、Redirect URIsは認証ページでユーザーがログインした後にリダイレクトできるページです。ここに登録されているページ以外にリダイレクトしようとするとエラーになります。後から変更可能なので、開発段階ではhttp://localhost:3000/callbackなどを登録しておけば良いと思います。(ユーザーの認証が必要でない場合、特に利用しないです。詳細は、この後の認可フローの説明でまた出てきます。)

これでAppの作成は完了です。作成したAppのホームに移動すると、アクティブユーザー数などの様々な情報を見ることができます。また、Settingsボタンを押して設定のページに移動すると、"Client ID"と"Client secret"が確認できます。これらは次章のアクセストークンの取得で使用します。

アクセストークンの取得
SpotifyのWeb APIを呼び出す際は、常にアクセストークンをヘッダに含める必要があります。もし、アクセストークン無しでAPIを呼び出すと、以下のようなエラーが返ってきます。
{
"error": {
"status": 401,
"message": "No token provided"
}
}
アクセストークンの取得方法は、APIから取得したいデータの種類によって大きく以下の2つに分けられます。
- Client Credentials Flow(ユーザの認可不要)
- Authorization Code Flow(ユーザの認可必要)
(ちなみにこのようなAPIの認可フローはOAuth 2.0という標準プロトコルに沿って実装されているそうです。OAuthにつては、コチラのページが理解の助けになりました。 → 一番分かりやすい OAuth の説明 | Qiita)。
Client Credentials Flow
ユーザの認可(authorization)を必要としない認可フローです。手軽ですが、ユーザ情報にはアクセスできず、ユーザのライブラリを操作したりすることもできません。Spotifyから曲やアルバムのデータだけを取得したい場合に使用します。
フローの流れは以下の図のようになります。
フローの詳細
まず、先ほど取得した、AppのClient IDとClient secret、そして認可フローの方式を指定するgrant_typeを含んだHTTPリクエストを、アクセストークン取得用のエンドポイント(https://accounts.spotify.com/api/token)に送ります。(図中①)。
HTTPリクエストの詳細は以下の表の通りです。
| 項目 | 値 |
|---|---|
| メソッド | POST |
| ヘッダ | Authorization: Basic <base64 encoded client_id:client_secret> |
Content-Type: application/x-www-form-urlencoded |
|
| ボディ | grant_type=client_credentials |
Client IDとClient secretの渡し方が少し複雑です。まず、これらを":"でつないだ文字列client_id:client_secretを用意し、それをBase64という方式でエンコードした上でAuthorizationヘッダに埋め込みます。
ボディ(本体)には、認可フローの方式を指定したgrant_typeパラメータを含めます。Content-Typeヘッダで指定した通り、application/x-www-form-urlencoded形式(URLパラメータと同じkey1=value1&key2=value2のような形式)である必要があります。
レスポンスはJSON形式で返ってきます。含まれている情報は以下の表の通りです。
| キー | 説明 |
|---|---|
| access_token | 取得したアクセストークン。 |
| token_type | トークンタイプ。常に"Bearer"。 |
| expires_in | アクセストークンの有効時間(秒)。3600秒(1時間)。 |
これでアクセストークンの取得は完了です。以後、Web APIを呼び出す度に、ヘッダにAuthorization: Bearer <access_token>という形式でアクセストークンを含めることで、目的の情報を得ることができます。(図中②)
Node.js上で実行
以上の内容をjavascriptコードで書くと、以下のようになります。例として、Syd Barrettのアーティスト情報について取得してみました。
const client_id = process.env.CLIENT_ID;
const client_secret = process.env.CLIENT_SECRET;
// アクセストークンの取得(1)
async function getAccessToken() {
const response = await fetch('https://accounts.spotify.com/api/token', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + Buffer.from(client_id + ':' + client_secret).toString('base64'),
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
'grant_type': 'client_credentials'
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.access_token;
}
// Spotify Web APIからデータ取得(2)
async function getSpotifyData() {
try {
const token = await getAccessToken();
// Spotify Web APIへのリクエストを送る
// (例:Syd Barrett [artist ID: 6Lt3HS8R2v8Q4G7ZkUWa8R] のアーティスト情報を取得)
const response = await fetch('https://api.spotify.com/v1/artists/6Lt3HS8R2v8Q4G7ZkUWa8R', {
headers: {
'Authorization': 'Bearer ' + token
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error:', error);
}
}
getSpotifyData();
node.js用に書いているので、
node --env-file=.env client_credentials_flow.js
で簡単に実行できます(クライアントIDなどは.envファイルにまとめているので、オプションで指定しています。dotenvライブラリを使う方法もあります → Node.js の進化に伴い不要となったかもしれないパッケージたち | Zenn)。fetch()も追加ライブラリ無しで使えるようになっていて便利です。
レスポンスとして返ってきたJSONデータの出力結果はこんな感じです。(popularity: 38は納得いかないですね…)
{
external_urls: { spotify: 'https://open.spotify.com/artist/6Lt3HS8R2v8Q4G7ZkUWa8R' },
followers: { href: null, total: 345500 },
genres: [
'art rock',
'experimental',
'outsider',
'psychedelic folk',
'psychedelic rock'
],
href: 'https://api.spotify.com/v1/artists/6Lt3HS8R2v8Q4G7ZkUWa8R?locale=*',
id: '6Lt3HS8R2v8Q4G7ZkUWa8R',
images: [
{
url: 'https://i.scdn.co/image/f0cded2845369a436adb00b24c85d02b867d0f45',
height: 300,
width: 300
},
{
url: 'https://i.scdn.co/image/9be66078df9f6c16d5f7bd09c08d0eb9b2b20cd6',
height: 200,
width: 200
},
{
url: 'https://i.scdn.co/image/877b78cc2a8ff5c592d253deca15f9d912071e04',
height: 64,
width: 64
}
],
name: 'Syd Barrett',
popularity: 38,
type: 'artist',
uri: 'spotify:artist:6Lt3HS8R2v8Q4G7ZkUWa8R'
}
(ちなみに、先ほどのjsコードはほとんどChatGPTに出力させたものです。公式ページのURLを渡し、「ページの内容をnode.js用のjavascriptコードにしてください。」とお願いすると、この精度で出力してくれました。すごい。)
Authorization Code Flow
ユーザの認可が必要な認可フローです。ユーザの情報にアクセスしたい場合や、ユーザのライブラリを操作したい場合に使用します。認可を求める際には、スコープ(アクセス可能な範囲や操作権限)を設定します。使用例として、以下のような場合が挙げられます(かっこ内が必要なスコープです。公式ドキュメントで確認できます。)。
- ユーザが保存したアルバムを取得する(
user-library-read) - 新しい非公開プレイリストを作成する(
playlist-modify-private) - ユーザのライブラリに曲を保存する(
user-library-modify)
フローの流れは以下の図のようになります。
フローの詳細
まず、アクセストークン取得の前にユーザの認可が必要なため、認可用のエンドポイント(https://accounts.spotify.com/authorize)にリクエストを送る必要があります。(図中①)
メソッドにはGETを使い、必要な情報はクエリ文字列(URLパラメータ)で渡します。パラメータの種類は以下の表のとおりです。
| キー | 説明 | タイプ |
|---|---|---|
| client_id | クライアントID。 | 必須 |
| response_type | codeに設定。 |
必須 |
| redirect_uri | ユーザの認可後にリダイレクトするURI。App設定のRedirect URIsに登録されている必要がある(前述)。 | 必須 |
| state | クロスサイトリクエストフォージェリという攻撃を防ぐためのもの。ランダムに生成した文字列など。 | 任意(強く推奨) |
| scope | 認可を求めるスコープ。複数の場合は空白区切り。 | 任意 |
| show_dialog | ユーザが既にアプリを許可済みの場合にもう一度許可を求めるか。既定値はfalse。 |
任意 |
実際にリクエストを送ってみます。WebのSpotifyからログアウトした状態で、ブラウザ上でhttps://accounts.spotify.com/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=http://localhost:3000/callback&scope=user-library-modify"にアクセスしてみます。
すると、まずログイン画面が現れます(認証, Authentication)。

ここでログインすると、ユーザの許可を求める画面になります(認可, Authorization)。

同意、あるいはキャンセルした後、redirect_uriに指定したページにリダイレクトされます。
同意した場合、アクセストークン取得に必要なcode(リクエストに設定した場合、stateも)が返ってきます。これらの値は、リダイレクトされたURLのクエリ文字列に含まれています。(ちなみに、サーバを立ち上げていないので、サイトの方にはアクセスできていません)

ユーザがキャンセルした場合など、認可が失敗した場合はerror(とstate)が返ってきます。

ユーザが同意して、無事codeが取得できたら、それを用いてアクセストークンを取得します。Client Credentials Flowと同様に、アクセストークン取得用のエンドポイント(https://accounts.spotify.com/api/token)にHTTPリクエストを送ります(図中②)。
ただし、ボディに含める情報は多くなっています。codeパラメータに取得したcodeを渡し、redirect_uriパラメータには認可リクエストを送った際に渡したredirect_uriをそのまま渡します(検証用だそうで、必ず一致している必要があります)。具体的には以下の表の通りです。
| 項目 | 値 |
|---|---|
| メソッド | POST |
| ヘッダ | Authorization: Basic <base64 encoded client_id:client_secret> |
Content-Type: application/x-www-form-urlencoded |
|
| ボディ | grant_type=authorization_code& |
レスポンスはJSON形式で返ってきます。含まれている情報は以下の表の通りです。
| キー | 説明 |
|---|---|
| access_token | 取得したアクセストークン。 |
| token_type | トークンタイプ。常に"Bearer"。 |
| scope | 認可されたスコープのリスト。(空白区切り) |
| expires_in | アクセストークンの有効時間(秒)。3600秒(1時間)。 |
| refresh_token | 新しいアクセストークンの取得の際に使うリフレッシュトークン。 |
これでアクセストークンの取得は完了です。Client Credentials Flowと同様、Web APIを呼び出す度に、ヘッダにAuthorization: Bearer <access_token>という形式でアクセストークンを含めることで、目的の操作を行うことができます。(図中③)
また、アクセストークンは1時間で有効期限が切れますが、refresh_tokenを使用することで、再認可を求めずに新しいアクセストークンを取得することができます(図中④)。詳細は公式ドキュメントを参照してください(→ Refreshing tokens | Spotify for Developers)。
Node.js上で実行
以上の内容をjavascriptコードで書くと、以下のようになります。(例によって、ChatGPTに指示を出して生成させました。)
const express = require('express');
const app = express();
const port = 3000;
// Spotify APIの設定
const client_id = process.env.CLIENT_ID; // SpotifyのクライアントID
const client_secret = process.env.CLIENT_SECRET; // Spotifyのクライアントシークレット
const redirect_uri = 'http://localhost:3000/callback'; // リダイレクトURI
const scope = 'user-library-modify'; // 必要なスコープ
// ログイン用URLを準備
app.get('/login', (req, res) => {
const params = new URLSearchParams({
response_type: 'code',
client_id: client_id,
scope: scope,
redirect_uri: redirect_uri
});
// Spotifyの認可用エンドポイント(/authorize)へリクエストを送る (1)
const authUrl = 'https://accounts.spotify.com/authorize?' + params.toString();
res.redirect(authUrl);
});
// コールバックURLで認可コードを受け取り、アクセストークンを取得
app.get('/callback', async (req, res) => {
// クエリ文字列内のcodeを取得
const code = req.query.code;
if (!code) {
return res.status(400).send('No code provided');
}
try {
// アクセストークンを取得 (2)
const response = await fetch('https://accounts.spotify.com/api/token', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + Buffer.from(client_id + ':' + client_secret).toString('base64'),
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
redirect_uri: redirect_uri
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
const { access_token, refresh_token, expires_in } = data;
// アクセストークンを使ってSpotify APIにリクエストを送る (3)
// ここではアクセストークンを表示するだけ
res.send(`Access Token: ${access_token}<br>Refresh Token: ${refresh_token}<br>Expires In: ${expires_in}`);
} catch (error) {
console.error('Error fetching access token:', error);
res.status(500).send('Error fetching access token');
}
});
// サーバーを起動
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
サーバを立てる必要があるので、Node.jsのExpressを利用しています。必要な場合はインストールします。
npm install express
プログラムは、前章と同様に以下のコマンドで実行します。
node --env-file=.env authorization_code_flow.js
実行開始後、http://localhost:3000/loginにアクセスすると、先ほどのログイン画面や同意画面に移動します(ログイン済みの場合はスキップされます)。同意後、自動的にhttp://localhost:3000/callbackにリダイレクトされ、アクセストークンなどの情報が画面に表示されたら成功です。(ちなみに、stateパラメータに関しては省いていますが、実際の制作物ではセキュリティ的に使った方がいいと思われます。)
APIを通してライブラリに曲を保存する
せっかくアクセストークンを取得したので、何かユーザの情報に対して処理を行ってみようと思います。先ほどのコードではスコープをuser-library-modifyに設定しているので、APIを通して、ログインしたユーザのライブラリを変更することができます(曲の追加や削除など)。
ということで、ユーザが保存した曲のプレイリストである「お気に入りの曲」にトラックを一曲保存する処理をつけ足してみました。(注:実装を簡単にするために、グローバル変数を使ってアクセストークンを保管しています)
...
// Spotify APIの設定
const client_id = process.env.CLIENT_ID; // SpotifyのクライアントID
const client_secret = process.env.CLIENT_SECRET; // Spotifyのクライアントシークレット
const redirect_uri = 'http://localhost:3000/callback'; // リダイレクトURI
const scope = 'user-library-modify'; // 必要なスコープ
let access_token; // アクセストークン
// (中略)
app.get('/callback', async (req, res) => {
...
try {
// (中略)
const data = await response.json();
access_token = data.access_token; // アクセストークンを保存
res.send(`Access token successfully retrieved. access_token: ${access_token}`);
} catch (error) {
console.error('Error fetching access token:', error);
res.status(500).send('Error fetching access token');
}
});
app.get('/save-track', async (req, res) => {
if (!access_token) {
return res.status(400).send('No token provided')
}
try {
// アクセストークンを使ってSpotify APIにリクエストを送る (3)
// (例:「お気に入りの曲」に曲 [track ID: 6hnoeJAhNt0TLaX5yzPr2l] を1曲追加)
const response = await fetch('https://api.spotify.com/v1/me/tracks?ids=6hnoeJAhNt0TLaX5yzPr2l', {
method: 'PUT',
headers: {
'Authorization': 'Bearer ' + access_token
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
res.send('Track successfully saved');
} catch (error) {
console.error('Error:', error);
}
});
// サーバーを起動
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
サーバの起動後、/loginでログインした後に、/save-trackにアクセスします。うまくいった場合、自分のSpotifyを開いてみると、プレイリスト「お気に入りの曲」の中に"Love You"という曲が追加されているはずです。
(コチラのAPIエンドポイントを利用しました。 → Save Tracks for Current User | Spotify for Developers)
Development mode
ちなみに、他のアカウント(App作成元以外のアカウント)を用いてログインし、上記のプログラムを試してみたところ、うまくいきませんでした。どうやら、作成されたAppの初期状態はDevelopment modeになっており、その状態では操作できるアカウントは1つのみに限られているようです(App設定ページの"App Status"で確認できます)。
一応、Development modeでも、App設定ページの"User Management"欄に登録すれば、25人のユーザまでを操作できるようになります(ベータテスターや共同開発者を登録できます)。

本格的なアプリケーションとして、より多くのユーザが使用可能なものにするためには、Extended quota modeにする必要があります。申請と審査が必要だそうです(→ Quota modes | Spotify for Developers)。Extended quota modeはAPIリクエスト数の制限も緩和されるようで、制作物を公にリリースする場合はコチラを使った方が良さそうです。
おわりに
APIの認可フローの解読だけで結構時間がかかってしまいました…。次回、Spotify Web APIを使って、アルバムや曲の情報を取得してみようと思います。
次回の記事↓
