はじめに
サーバレスだから導入も簡単なんだろうな🤗
と思っていたら盛大にハマってしまったので備忘録を兼ねて投稿します。
DockerコンテナからPlanetScaleに接続しようとすると下記エラーに遭遇したのでその対処法です。
また、記事の後半では動作確認のためNuxt3のCRUD機能も実装しちゃいます。
server does not allow insecure connections, client must use SSL/TLS
以下を前提として解説しています。
- PlanetScaleにデータベース作成済みであること
- Nuxt.jsプロジェクト作成済みであること
docker-compose.ymlの設定
PlanetScaleとの通信はSSL接続が必要らしい。
https://planetscale.com/docs/concepts/secure-connections
そのため、volumesでSSL証明書をマウントしておきます。
version: '3'
services:
web:
container_name: web
build: .
ports:
- "3000:3000" # Nuxt.js用のポート
- "5555:5555" # Prisma Studio用のポート
volumes:
- "../src:/var/www/html"
- "/etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt" # ←重要
tty: true
Dockerfileの設定
FROM node:latest
WORKDIR /var/www/html
RUN apt update && \
npm install -g npm
EXPOSE 3000
ENV HOST 0.0.0.0
Dockerコンテナを起動
コンテナをビルド、起動します。
docker compose up -d --build
webコンテナに入ります。
docker compose exec web bash
PlanetScaleの設定
登録~DB作成までの流れは下記の記事が参考になると思います。
PlanetScaleというサーバレスDBが凄く勢いのあるサービスらしいのでQuick Startやってみた - Qiita
PlanetScaleのブランチ管理画面にアクセスします。
「New password」ボタンをクリックして接続パスワードを生成します。
この時、画面に表示される以下の情報を控えておいてください。
- database
- username
- host
- password
.envファイルの設定
先ほど控えた情報をもとに.envファイルに接続情報を追記します。
DATABASE_URL="mysql://username:password@host/database?sslaccept=strict"
Prismaの設定
Prismaについての解説は本記事ではしません。
参考URLをご覧ください。
Prismaの導入とメリットを考える - Qiita
既存のNuxtプロジェクトのディレクトリに移動します。
cd hoge-project
Prismaをインストールします。
yarn add -D prisma
Prismaを初期化します。
npx prisma init
プロジェクトディレクトリに「prisma/schema.prisma」というファイルが生成されます。
schema.prismaファイルを開いてモデルを定義します。
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
uid String @unique @db.VarChar(255)
display_name String @db.VarChar(255)
created_at DateTime @default(now()) @db.Timestamp(0)
updated_at DateTime @default(now()) @updatedAt @db.Timestamp(0)
}
モデルの定義情報をPlanetScaleに反映させます。
npx prisma db push
Prisma Clientを生成します。
以下は、モデル定義を変更するたびに実行してください。
npx prisma generate
作成されたDBを確認する
Prisma Studioを起動してDBを確認します。
npx prisma studio
「localhost:5555」をブラウザで開くとアクセスできます。
CRUDを実装する
ファイル・ディレクトリ構造
CRUD機能を実装するにあたり、以下3つのファイルを作成します。
- composables/useUser.ts
- pages/test-prisma.vue
- server/api/user.ts
表示する側
<template>
タグ内にはシンプルにボタンのみを配置しています。
CSSフレームワークにVuetifyを使用しています。
各ボタンの@click
に対応する処理を<script>
タグ内に記載しています。
uuidの生成に@faker-js/fakerを使用しています。
onClickUpdateとonClickDeleteの「targetId」の値は必要に応じて修正してください。
<template>
<div>
<v-btn @click="onClickCreate">create</v-btn>
<v-btn @click="onClickRead">read</v-btn>
<v-btn @click="onClickUpdate">update</v-btn>
<v-btn @click="onClickDelete">delete</v-btn>
</div>
</template>
<script setup>
import {faker} from '@faker-js/faker'
const { createUser, readUser, updateUser, deleteUser } = useUser()
const onClickCreate = async () => {
await createUser({
uid: faker.datatype.uuid().substring(0, 16),
display_name: 'Test User'
})
}
const onClickRead = async () => {
const data = await readUser()
console.log(data)
}
const onClickUpdate = async () => {
await updateUser({
targetId: 1,
data: {
uid: faker.datatype.uuid().substring(0, 16),
display_name: 'テスト ユーザー'
}
})
}
const onClickDelete = async () => {
await deleteUser({
targetId: 1
})
}
</script>
実行するコード
再利用可能な関数としてVueコンポーザブルという機能を使います。
動作確認のため、かなりシンプルな実装になっています。
const useUser = () => {
/** CREATE */
const createUser = async (values) => {
const data = await $fetch('/api/user', {
method: 'post',
body: values,
})
await refreshNuxtData()
return data
}
/** READ */
const readUser = async () => {
const data = await $fetch('/api/user')
await refreshNuxtData()
return data
}
/** UPDATE */
const updateUser = async (values) => {
const data = await $fetch('/api/user', {
method: 'put',
body: values,
})
await refreshNuxtData()
return data
}
/** DELETE */
const deleteUser = async (values) => {
const data = await $fetch('/api/user', {
method: 'delete',
body: values,
})
await refreshNuxtData()
return data
}
return {
createUser, readUser, updateUser, deleteUser
}
}
export default useUser
API
データベースとデータをやり取りするためのAPIを作成します。
import { PrismaClient, Prisma } from '@prisma/client'
export default defineEventHandler(async (e) => {
const prisma = new PrismaClient()
const method = e.req.method
if (method === 'POST') {
const body = await readBody(e)
const user = await prisma.user.create({
data: body
})
return user
}
if (method === 'GET') {
const user = await prisma.user.findMany()
return user
}
if (method === 'PUT') {
const body = await readBody(e)
const user = await prisma.user.update({
where: { id: body.targetId },
data: body.data
})
return user
}
if (method === 'DELETE') {
const body = await readBody(e)
const user = await prisma.user.delete({
where: { id: body.targetId }
})
return user
}
})
ビルド実行
お疲れさまでした🍺
ビルドを実行します。
yarn dev
「localhost:3000/test-prisma」へアクセスしてください。
下図のような、ボタンだけの画面が表示されているはずです。
動作確認
Prisma Studio (localhost:5555
)へアクセスして各ボタンが機能しているか確認してみましょう。