0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Next.jsでSpotifyAPIを使用したWebアプリをvpsに公開するまで

Posted at

概要

この記事では、初心者の方がこの記事を読みながら周辺知識を調べてWebアプリを作成して公開までできるように、実際の環境構築から公開までした内容をまとめました。今回は環境構築しローカルで作動させるまでです。

使用するもの
  • Node.js
    JavaScriptでサーバーサイドプログラムを実行するためのランタイム環境。Next.jsを動作させる基盤として使用します。
  • Next.js
    Reactベースのフレームワークで、フロントエンドとバックエンド(APIエンドポイント)を統合的に開発できます。
  • NextAuth
    認証と認可を簡単に実装できるライブラリ。今回はSpotifyアカウントを利用したOAuth認証を設定します。
  • Prisma
    モダンなORM(Object-Relational Mapping)ツール。PostgreSQLとのデータベース操作ができます。
  • Recoil
    Reactで状態管理を行うためのライブラリ。アプリ内のグローバルな状態管理を行います。
  • Docker
    Dockerのコンテナを使用してPostgreSQLのデータベースを構築します。また、アプリ公開の際にDockerでコンテナ化して公開します。サーバーの環境設定不要で公開することができます。
  • PostgreSQL
    リレーショナルデータベース。Dockerで構築します。
  • Spotify API
    Spotifyの音楽データにアクセスするためのAPI。
  • Bootstrap
    アプリのレスポンシブルデザインにBootstrapのテンプレートを使用します。
公開アプリ

GitHub

開発環境の構築

Node.jsのインストール

Node.jsはJavaScriptの実行環境で、バックエンドや開発サーバーを動かすのに必要で、Next.jsでアプリ作成するために必須です。次のリンクから最新バージョンをダウンロードしてインストールします。

nvmのインストール

nvmは複数のNode.jsのバージョンを管理できるツールです。各種パッケージの依存関係のエラーを回避するため特定のバージョンで環境を設定します。

nvmを使って、Node.jsのバージョン18をインストールします。

nvm install 18

Node.jsのバージョン18を使用するように切り替えます。

nvm use 18

Next.jsのプロジェクト作成

VSCodeのターミナルを使用して、Next.jsのプロジェクトを作成するフォルダへ移動し次のコマンドでプロジェクトを作成します。

npx create-next-app@latest .

ターミナルでスクリプトが実行できない場合は以下を実行します。PowerShellのスクリプト実行ポリシーを変更してスクリプトを実行可能とします。

Set-ExecutionPolicy -Scope CurrentUser RemoteSigned

プロジェクトのフォルダ構成は以下のようになります。

project/
├── .next/                        
├── node_modules/           
├── public/                       
├── src/                          
│   ├── app/
│   │   ├── api/                  # バックエンドのAPIを作成
│   │   ├── components/           # 再利用するコンポーネントを作成
│   │   ├── favorites/            # お気に入りのページを作成
│   │   ├── fonts/
│   │   ├── playlists/            # プレイリストのページを作成
│   │   ├── top-tracks/           # トップトラックのページを作成
│   │   ├── global.css            
│   │   ├── layout.tsx            # アプリ全体のレイアウトを設定
│   │   ├── next.auth.d.ts        # NextAuth.jsで使用する型定義
│   │   └── page.tsx              # トップページ
│   ├── lib/                      # ユーティリティ関数を作成
│   └── types/                    # TypeScriptの型定義
├── .env                          # 環境変数ファイルを設定
├── .eslintrc.json                
├── next-env.d.ts                 
├── next.config.js                # Next.js設定
├── package-lock.json             
├── package.json                  
├── postcss.config.mjs            
├── README.md                     
├── tailwind.config.ts            
└── tsconfig.json                 

Dockerのインストール

アプリで使用するデータベースをPostgreSQLとし、Dockerコンテナを使用して起動することにしますので、Docker Desktopをインストールします。

PostgreSQLの設定

Dockerを使ってPostgreSQLの環境を構築します。初めにDockerのコンテナを設定・起動するためのファイルを作成します。

docker-compose.yml
version: "3.9"
services:
  postgres:
    image: postgres:15
    container_name: postgres_container
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
volumes:
  postgres_data:

アプリでPostgreSQLを使用するための環境変数を設定します。

.env
DATABASE_URL="postgresql://my_user:my_password@localhost:5432/my_database?schema=public"

POSTGRES_USER=my_user
POSTGRES_PASSWORD=my_password
POSTGRES_DB=my_database

docker-compose.ymlの設定でpostgreSQLコンテナを起動します。

docker-compose up –d

コンテナ起動する前にDocker Desktopを起動してください。

コンテナを停止するときは以下のコマンドを入力します。

docker-compose down

アプリ起動

この段階で一度アプリを起動してみます。必要なパッケージをインストールします。

npm install

ローカル環境でアプリを起動します。

npm run dev

アプリ起動後に以下で確認します。

SpotifyAPIの連携

Spotify APIを使うことで、Spotifyアカウントのデータにアクセスし、音楽データを取得できます。Spotify Developer Dashboardでのアプリ登録、APIキーを取得します。

Spotify Web API を操作するための Node.js ライブラリ spotify-web-api-node をインストールします。このライブラリを使うと、SpotifyのAPIに簡単にアクセスできます。

npm install spotify-web-api-node
npm install --save-dev @types/spotify-web-api-node
npm install node-fetch
npm install ts-node --save-dev

SpotifyAPIを使用するため環境変数を設定します。

.env
DATABASE_URL="postgresql://my_user:my_password@localhost:5432/my_database?schema=public"

POSTGRES_USER=my_user
POSTGRES_PASSWORD=my_password
POSTGRES_DB=my_database

+ SPOTIFY_CLIENT_ID= # ここにSpotifyのクライアントIDを入力
+ SPOTIFY_CLIENT_SECRET= # ここにシークレットキーを自由に設定

Next.jsのアプリ構築

NextAuth

NextAuthは、簡単に認証機能を実装できるライブラリです。NextAuthを使用するためアプリの環境変数を設定します。

.env
DATABASE_URL="postgresql://my_user:my_password@localhost:5432/my_database?schema=public"

POSTGRES_USER=my_user
POSTGRES_PASSWORD=my_password
POSTGRES_DB=my_database

SPOTIFY_CLIENT_ID=
SPOTIFY_CLIENT_SECRET=

+ NEXTAUTH_SECRET= # ここにシークレットキーを自由に設定
+ NEXTAUTH_URL=http://localhost:3000

NextAuthを使用するためのAPIエンドポイントを設定します。

src\app\api\auth[...nextauth]\route.ts
import NextAuth, { NextAuthOptions } from "next-auth";
import { authOptions } from "@/lib/auth";

// NextAuthのインスタンスをエクスポート
const handler = NextAuth(authOptions as NextAuthOptions);

export { handler as GET, handler as POST };

Spotify認証の設定をします。

src\lib\auth.ts
import { Session } from "next-auth";
import { JWT } from "next-auth/jwt";
import SpotifyProvider from "next-auth/providers/spotify";
import { prisma } from "@/lib/prisma";

// Spotify Web APIから取得する情報
const scopes = [
  "user-read-private",
  "user-read-email",
  "user-top-read",
  "playlist-read-private",
  "playlist-read-collaborative",
  "playlist-modify-private",
  "playlist-modify-public",
  "user-read-playback-state",
  "user-read-currently-playing",
  "user-modify-playback-state",
  "user-library-read",
  "user-library-modify",
].join(" ");

// NextAuthの設定
export const authOptions = {
  providers: [
    SpotifyProvider({
      clientId: process.env.SPOTIFY_CLIENT_ID!,
      clientSecret: process.env.SPOTIFY_CLIENT_SECRET!,
      authorization: `https://accounts.spotify.com/authorize?scope=${scopes}`,
    }),
  ],
  callbacks: {
    // NOTE: userはspotify側で自動で設定される(user.idはSpotifyのIDが設定される)
    async jwt({ token, account, user }: {
      token: JWT;
      account?: Record<string, any>;
      user?: Record<string, any>;
    }) {
      if (account && account.provider === "spotify") {
        console.log('token:', token);
        console.log('account:', account);
        console.log('user:', user);

        token.spotifyId = account.providerAccountId;
        token.accessToken = account.access_token;
        token.refreshToken = account.refresh_token;
        token.accessTokenExpires = Date.now() + account.expires_at * 1000;

        // 新規ユーザの場合、データベースにユーザを作成
        if (user) {
          await prisma.user.upsert({
            where: { spotifyId: user.id },
            update: {},
            create: {
              name: user.name || "",
              email: user.email || "",
              spotifyId: user.id,
              refreshToken: token.refreshToken as string,
            },
          });
        }
      }

      return token;
    },
    async session({ session, token }: {
      session: Session;
      token: JWT & { spotifyId?: string; accessToken?: string; refreshToken?: string };
    }) {
      if (token.spotifyId) {
        // NOTE: userにはNexAuthがデフォルトで提供するプロパティ(name, email, image等)が含まれる
        session.user.spotifyId = token.spotifyId;
      }

      return session;
    },
  },
  pages: {
    signIn: '/auth/signin',
  },
};

標準のセッションにSpotify IDを追加するため型定義をします。

src\app\next-auth.d.ts
import NextAuth from "next-auth";

declare module "next-auth" {
    interface Session {
        user: {
            name?: string | null;
            email?: string | null;
            image?: string | null;
            spotifyId?: string; // 追加したプロパティ
        };
    }
}

また、アプリ全体で使用するその他の型定義をします。

src\types\spotify.ts
export type Track = {
    id: string;
    name: string;
    artist: string;
    album: string;
    popularity: number;
    imageUrl: string;
    trackUrl: string;
};

export type Playlist = {
    id: string;
    name: string;
    description: string | null;
    imageUrl: string | null;
    tracks: Track[];
};

export type Device = {
    id: string;
    name: string;
};

export type CacheExpiry = {
    id: number;
    spotifyId: string;
    type: string;
    expiresAt: string; // Prismaから返ってくる日時はISO文字列のため
    createdAt: string; // Prismaから返ってくる日時はISO文字列のため
}

export type User = {
    id: string;  // SpotifyのユーザーID
    display_name: string; // ユーザー名
    email: string; // ユーザーのメールアドレス
    images?: { url: string }[]; // プロフィール画像のURL(オプション)
};

export type SpotifyData = {
    user?: User;
    topTracksIn4Weeks?: Track[];
    topTracksIn6Months?: Track[];
    topTracksInAllTime?: Track[];
    playlists?: (Playlist & { tracks: Track[] })[];
    devices?: Device[];
    savedTracks?: Track[];
}

export type FavoritePeriod = {
    id: number;
    favoriteId: number;
    startDate: string; // Prisma の DateTime 型は ISO 文字列として返される
    endDate?: string;  // nullable な場合はオプショナル
};

export type Favorite = {
    id: number;
    userSpotifyId: string;
    spotifyTrackId: string;
    spotifyTrackName: string;
    periods: FavoritePeriod[]; // 関連する期間のリスト
};

Prisma

Prismaは、データベース操作を簡素化するORMツールです。Prismaを使ってPostgreSQLと接続します。Prisma CLIツールをインストールします。

npm install prisma --save-dev
npm install @prisma/client

Prismaの設定ファイルを初期化するコマンドです。このコマンドを実行すると、Prismaの設定ファイル(prisma/schema.prisma)やデータベース接続設定を含むprismaディレクトリがプロジェクトに作成されます。これにより、Prismaを使用したデータベースモデルの作成やマイグレーションが行えるようになります。

npx prisma init

データベースで使用するデータの設定を行います。

prisma\schema.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id              Int      @id @default(autoincrement())
  name            String?
  email           String?  @unique
  spotifyId       String   @unique
  refreshToken    String
  createdAt       DateTime @default(now())
  updatedAt       DateTime @updatedAt

  schedules  Schedule[]
}

model Favorite {
  id                    Int              @id @default(autoincrement())
  userSpotifyId         String
  spotifyTrackId        String
  spotifyTrackName      String

  periods               FavoritePeriod[]

  @@unique([userSpotifyId, spotifyTrackId]) // 複合一意制約
}

model FavoritePeriod {
  id         Int       @id @default(autoincrement())
  favoriteId Int
  startDate  DateTime
  endDate    DateTime?

  favorite   Favorite  @relation(fields: [favoriteId], references: [id])
}

model SpotifyCache {
  id         Int       @id @default(autoincrement())
  spotifyId  String
  type       String
  expiresAt  DateTime
  createdAt  DateTime  @default(now())

  @@unique([spotifyId, type])
}

schema.prismaの設定にもとづきデータベースを作成します。

npx prisma migrate dev --name init

アプリで使用できるように設定します。

px prisma generate

データベースをブラウザで管理することができます。以下のコマンドを入力して、

npx prisma studio

以下にアクセスします。

Recoil

Recoilは、状態管理ライブラリで、Reactアプリケーションのデータの流れを管理します。Recoilをインストールします。

npm install recoil

コンポーネント間で共有する状態を設定します。

src\app\state\state.ts
import { atom } from "recoil";
import { Track, Playlist, Favorite } from "@/types/spotify"

export const savedTracksState = atom<Track[]>({
    key: "savedTracksState",
    default: [],
});

export const favoritesState = atom<Favorite[]>({
    key: "favoritesState",
    default: [],
});

export const tracksIn4WeeksState = atom<Track[]>({
    key: "tracksIn4WeeksState",
    default: [],
});

export const tracksIn6MonthsState = atom<Track[]>({
    key: "tracksIn6MonthsState",
    default: [],
});

export const tracksInAllTimeState = atom<Track[]>({
    key: "tracksInAllTimeState",
    default: [],
});

export const playlistsState = atom<Playlist[]>({
    key: "playlistsState",
    default: [],
});

export const selectedViewState = atom<"Chart" | "Top Tracks in 4 Weeks" | "Top Tracks in 6 Months" | "Top Tracks of All Time" | "Favorites">({
    key: "selectedViewState",
    default: "Chart",
});

export const selectedPlaylistState = atom<Playlist | null>({
    key: "selectedPlaylistState",
    default: null,
});

export const createPlaylistModeState = atom<boolean>({
    key: "createPlaylistModeState",
    default: false,
});

Echarts

Echartsは、データを可視化するためのグラフライブラリです。Echartsをインストールします。

npm install echarts echarts

Bootstrap Templatesの使用

Bootstrapテンプレートを使用して、アプリのデザインを迅速に整えることができます。

ダウンロードしたファイルをpublic\assetsに保存します(必要なものを抜粋、一部修正しています)。

アプリ完成

完成したアプリのフォルダ構成となります。

project/
├── .next/                        
├── node_modules/                 
├── prisma/                       
│   ├── migrations/
│   └── schema.prisma             # データベース関係設定
├── public/                       
│   └── assets/                   # Bootstrapのテンプレートファイル
├── src/                          
│   ├── app/
│   │   ├── api/                  # バックエンドのAPIを作成
│   │   ├── components/           # 再利用するコンポーネントを作成
│   │   ├── favorites/            # お気に入りのページを作成
│   │   ├── fonts/
│   │   ├── playlists/            # プレイリストのページを作成
│   │   ├── top-tracks/           # トップトラックのページを作成
│   │   ├── global.css            
│   │   ├── layout.tsx            # アプリ全体のレイアウトを設定
│   │   ├── next.auth.d.ts        # NextAuth.jsで使用する型定義
│   │   └── page.tsx              # トップページ
│   ├── lib/                      # ユーティリティ関数を作成
│   └── types/                    # TypeScriptの型定義
├── .env                          # 環境変数ファイルを設定
├── .eslintrc.json                
├── next-env.d.ts                 
├── next.config.js                # Next.js設定
├── package-lock.json             
├── package.json                  
├── postcss.config.mjs            
├── README.md                     
├── tailwind.config.ts            
└── tsconfig.json          
公開アプリ

GitHub

次回、公開サーバーへ公開するまでを説明します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?