GraphQL + Node.js -データベースとORM編-
ここからはデータベースとORMについてさわっていきます。使用するデータベースはPostgresQL、ORMはPrismaです。
そもそもORMとは
要するにデータベースとオブジェクト指向言語をつなぐものです。
Prismaについて
また公式ページには”なぜPrismaとGraphQLなのか?”が記されているので気になる人は是非(公式:Prismaについて)
ここから新しくアプリケーションを作り上げるのですが予めPostgresQLのインストールと起動を予めしておいてください。
PostgresQLのインストール(共にQiitaから)
Windowsユーザー:https://qiita.com/tom-sato/items/037b8f8cb4b326710f71
Macユーザー:https://qiita.com/yorokobi_kannsya/items/f77d074e382a88dae971
新しいプロジェクトを作成するのでターミナルで例のように
mkdir todo-prisma
cd todo-prisma
npm init -y
次にPrismaのCLIをインストールし、その後初期化します。
npm install @prisma/cli --save-dev
npx prisma init
prismaのディレクトリ内にprisma.schemaそれと.envファイルができているはずです。
まずprisma/prisma.schemaを見ていきましょう。
// prisma/prisma.schema
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
上からdatasourceはデータベースについても情報を書いていくところです。おそらくデフォルトでpostgresqlになっているはずなので変える必要はありません。その下のurlは.envファイルにDATABASE_URLが定義されているので以下のように自身で変更してください。
postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=SCHEMA
HOSTやPORTはすでに決まっています。PostgresQLであればlocalhost:5432です。
DATABASE_URL="postgresql://hondaao:postgres@localhost:5432/prisma”
筆者の場合は上のようになります。データベース名はprisma です。schemaは定義しなくても問題ありません(自動でpublicという名前のスキーマができます。)
次にデータモデルをかいていきます。今回は簡単なTodoアプリを作るのでこんな感じで
// prisma/prisma.schema
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Todo {
id Int @default(autoincrement()) @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
content String
isDone Boolean @default(false)
}
最終的にprisma/prisma.schemaは上のようになります。autoincrement()で自動的に増加するidを生成してくれます。
変更したら次のコマンドでマイグレーションしましょう。
npx prisma migrate dev --preview-feature --name init
npx prisma generate
prisma/migration下にmigration.sqlが作られているはずです。
また、以下のコマンドでデータベースが作られているか確認しましょう。
psql -l
次にsrc/index.jsを変更していきます。先ほどと同じようにexpressとapollo-server-expressをインストールしておいてください。
// src/index.js
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()
const express = require('express');
const { ApolloServer, gql } = require('apollo-server-express')
const typeDefs = gql'
type Query {
listing: [Todo!]!
getTodo(id: Int!): Todo!
}
type Mutation {
addTodo(content: String!): Todo!
}
type Todo {
id: ID!
content: String!
createdAt: String!
updatedAt: String
isDone: Boolean!
}
'
const resolvers = {
Query: {
listing: (_,__,context) => {
return context.prisma.todo.findMany()
},
getTodo(_, { id }, context) => {
return context.prisma.todo.findUnique({ where: {id} })
}
},
Mutation: {
addTodo: async(_, { content }, context ) => {
const newTodo = await context.prisma.todo.create({
data: {
content
}
})
return newTodo
}
}
}
const server = new ApolloServer({
typeDefs,
resolvers,
context: {
prisma
}
})
const app = express();
server.applyMiddleware({ app });
app.listen({ port: 4000 }, () =>
console.log('🚀 Server ready at http://localhost:4000/graphql')
)
typeDefsやresolversについては、これまででさわってきたのでわかると思います。今回新しくcontextとその中にprismaが追加されているのが わかると思います。これによりcontext.prismaに全てのresolver関数でアクセスできます。実際にresolver関数でcontext.prisma.todoによってデータベースにアクセスしているのがわかると思います。
context.prisma.todo.findManyで複数のTodoを context.prisma.todo.findUnique({ where: {id} }) で一致するidのTodoをひとつ取り出すという意味になります。
node src/index.jsでサーバーを起動してPlaygroundにとんでください。さっそく新しくTodoを追加しましょう。
mutation {
addTodo(content: "Learing GraphQL") {
content
isDone
}
}
以下のようになっていればOKです。またQueryのlistingも試してみてください。先ほど作ったデータがかえってくるはずです。
続いて、Prisma Studioを見ていきます。これは自分がつくったデータベース上のデータをわかりやすく可視化してくれているので便利です。次のコマンドでブラウザのhttp://localhost:5555に移動するはずです。
npx prisma studio
ページが開いたら既につくったテーブルのTodoがあるはずなので中に入ると下のようになります。
idが1の先ほどつくったデータがあるはずです。また、エラーなどでうまく動かなかった人はGithubのコードをおいておくので確認してみてください。またPostgresQLでエラーがでている人などは権限やパスワードの確認をしてみてください。
Github:Todo App Tutorial
最後にCRUDでいうUPDATEとDELETE機能を追加します。先に進みたい方はスキップしていただいても構いません。Todoの作成のようにまずはtypeDefsのtype MutationにupdateTodoとdeleteTodoを追加しましょう。
type Mutation {
addTodo(content: String!): Todo
updateTodo(id: Int!, content: String!): Todo
deleteTodo(id: Int!): String
}
アップデートはidとcontentを引数にとります。まずidを検索してcontentで内容を変更します。デリート機能はidで登録したTodoを検索し削除します。deleteTodoでは削除後に削除完了したことを伝えるメッセージを送信するのでString型を返します。
続いてresolverも記述していきます。先ほどのaddTodoに加えtype Mutationに定義したupdateTodo, deleteTodoを書き足していきます。
Mutation: {
addTodo: async(_, { content }, context ) => {
const newTodo = await context.prisma.todo.create({
data: {
content
}
})
return newTodo
},
updateTodo: async(_, { id, content }, context ) => {
const updateTodo = await context.prisma.todo.update({
where: {
id
},
data: {
content
}
})
return updateTodo
},
deleteTodo: async(_, { id }, context ) => {
try {
await context.prisma.todo.delete({ where: { id }})
return "todo was deleted!"
} catch (error) {
console.log(error)
return "Failed"
}
}
次は今作ったAppにログイン機能を付け足します。