著者はCOVID-19救済基金を選択し、 Write forDOnationsプログラムの一環として寄付を受け取りました。

序章

GraphQL は、スキーマ定義言語とクエリ言語で構成されるAPIのクエリ言語であり、APIコンシューマーは、柔軟なクエリをサポートするために必要なデータのみをフェッチできます。 GraphQLを使用すると、開発者は、iOS、Android、アプリのWebバリアントなど、複数のクライアントのさまざまなニーズに対応しながらAPIを進化させることができます。 さらに、GraphQLスキーマは、APIにある程度の型安全性を追加すると同時に、APIのドキュメントの形式としても機能します。

Prisma は、オープンソースのデータベースツールキットです。 これは、3つの主要なツールで構成されています。

  • Prismaクライアント :Node.jsおよびTypeScript用の自動生成されたタイプセーフなクエリビルダー。
  • Prisma Migrate :宣言型データモデリングおよび移行システム。
  • Prisma Studio :データベース内のデータを表示および編集するためのGUI。

Prismaは、複雑なデータベースワークフロー(スキーマの移行や複雑なSQLクエリの作成など)に時間を費やすのではなく、付加価値機能の実装に集中したいアプリケーション開発者のために、データベースの操作を容易にします。

このチュートリアルでは、GraphQLとPrismaを組み合わせて使用します。これは、それらの責任が互いに補完し合うためです。 GraphQLは、フロントエンドやモバイルアプリなどのクライアントで使用するためのデータへの柔軟なインターフェイスを提供します。GraphQLは特定のデータベースに関連付けられていません。 これは、データが保存されるデータベースとの相互作用を処理するためにPrismaが登場する場所です。

DigitalOceanのAppPlatformは、インフラストラクチャを気にすることなく、クラウドにアプリケーションをデプロイしてデータベースをプロビジョニングするシームレスな方法を提供します。 これにより、クラウドでアプリケーションを実行する際の運用上のオーバーヘッドが削減されます。 特に、毎日のバックアップと自動フェイルオーバーを備えたマネージドPostgreSQLデータベースを作成する機能を備えています。 App Platformには、デプロイメントの合理化をサポートするネイティブNode.jsがあります。

Node.jsを使用して、JavaScriptでブログアプリケーション用のGraphQLAPIを構築します。 最初にApolloServerを使用して、メモリ内のデータ構造に裏打ちされたGraphQLAPIを構築します。 次に、APIをDigitalOceanAppPlatformにデプロイします。 最後に、Prismaを使用してメモリ内ストレージを置き換え、データをPostgreSQLデータベースに永続化して、アプリケーションを再度デプロイします。

チュートリアルの最後に、Node.js GraphQL APIをDigitalOceanにデプロイします。これは、HTTP経由で送信されたGraphQLリクエストを処理し、PostgreSQLデータベースに対してCRUD操作を実行します。

このプロジェクトのコードは、DigitalOceanCommunityリポジトリにあります。

前提条件

このガイドを開始する前に、次のものが必要です。

JavaScript Node.js 、GraphQL、およびPostgreSQLの基本的な知識は役に立ちますが、このチュートリアルでは厳密には必要ありません。

ステップ1—Node.jsプロジェクトを作成する

このステップでは、npmを使用してNode.jsプロジェクトをセットアップし、依存関係をインストールします apollo-servergraphql.

このプロジェクトは、このチュートリアル全体で構築およびデプロイするGraphQLAPIの基盤になります。

まず、プロジェクトの新しいディレクトリを作成します。

  1. mkdir prisma-graphql

次に、ディレクトリに移動して、空のnpmプロジェクトを初期化します。

  1. cd prisma-graphql
  2. npm init --yes

このコマンドは最小限を作成します package.json npmプロジェクトの構成ファイルとして使用されるファイル。

次の出力が表示されます。

Output
Wrote to /Users/yourusaername/workspace/prisma-graphql/package.json: { "name": "prisma-graphql", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }

これで、プロジェクトでTypeScriptを構成する準備が整いました。

次のコマンドを実行して、必要な依存関係をインストールします。

  1. npm install apollo-server graphql --save

これにより、プロジェクトに依存関係として2つのパッケージがインストールされます。

  • apollo-server :GraphQLリクエストの解決方法とデータのフェッチ方法を定義するために使用するHTTPライブラリ。
  • graphql :GraphQLスキーマを構築するために使用するライブラリです。

プロジェクトを作成し、依存関係をインストールしました。 次のステップでは、GraphQLスキーマを定義します。

ステップ2—GraphQLスキーマとリゾルバーを定義する

このステップでは、GraphQLスキーマと対応するリゾルバーを定義します。 スキーマは、APIが処理できる操作を定義します。 リゾルバーは、メモリ内のデータ構造を使用してこれらのリクエストを処理するためのロジックを定義します。これは、次のステップでデータベースクエリに置き換えます。

まず、という新しいディレクトリを作成します src ソースファイルが含まれます:

  1. mkdir src

次に、次のコマンドを実行して、スキーマのファイルを作成します。

  1. nano src/schema.js

次に、次のコードをファイルに追加します。

プリズム-graphql/src / schema.js
const { gql } = require('apollo-server')

const typeDefs = gql`
  type Post {
    content: String
    id: ID!
    published: Boolean!
    title: String!
  }

  type Query {
    feed: [Post!]!
    post(id: ID!): Post
  }

  type Mutation {
    createDraft(content: String, title: String!): Post!
    publish(id: ID!): Post
  }
`

ここでは、を使用してGraphQLスキーマを定義します gql タグ付きテンプレート。 スキーマは型定義のコレクションです(したがって、 typeDefs)APIに対して実行できるクエリの形状を一緒に定義します。 これにより、GraphQLスキーマ文字列がApolloが期待する形式に変換されます。

スキーマには次の3つのタイプがあります。

  • Post:ブログアプリの投稿のタイプを定義し、4つのフィールドが含まれ、各フィールドの後にそのタイプが続きます。たとえば、 String.
  • Query:を定義します feed 角括弧とで示されるように複数の投稿を返すクエリ post 単一の引数を受け入れ、単一の引数を返すクエリ Post.
  • Mutation:を定義します createDraft ドラフトを作成するためのミューテーション Post そしてその publish を受け入れる突然変異 id とを返します Post.

すべてのGraphQLAPIにはqueryタイプがあり、mutationタイプがある場合とない場合があることに注意してください。 これらのタイプは通常のオブジェクトタイプと同じですが、すべてのGraphQLクエリのエントリポイントを定義するため、特別です。

次に、 posts への配列 src/schema.js ファイル、下 typeDefs 変数:

プリズム-graphql/src / schema.js
...
const posts = [
  {
    id: 1,
    title: 'Subscribe to GraphQL Weekly for community news ',
    content: 'https://graphqlweekly.com/',
    published: true,
  },
  {
    id: 2,
    title: 'Follow DigitalOcean on Twitter',
    content: 'https://twitter.com/digitalocean',
    published: true,
  },
  {
    id: 3,
    title: 'What is GraphQL?',
    content: 'GraphQL is a query language for APIs',
    published: false,
  },
]

あなたは posts 3つの事前定義された投稿を持つ配列。 それぞれの構造に注意してください post オブジェクトが一致する Post スキーマで定義したタイプ。 この配列は、APIによって提供される投稿を保持します。 次のステップでは、データベースとPrisma Clientが導入されたら、アレイを置き換えます。

次に、を定義します resolvers 下のオブジェクト posts 定義した配列:

プリズム-graphql/src / schema.js
...
const resolvers = {
  Query: {
    feed: (parent, args) => {
      return posts.filter((post) => post.published)
    },
    post: (parent, args) => {
      return posts.find((post) => post.id === Number(args.id))
    },
  },
  Mutation: {
    createDraft: (parent, args) => {
      posts.push({
        id: posts.length + 1,
        title: args.title,
        content: args.content,
        published: false,
      })
      return posts[posts.length - 1]
    },
    publish: (parent, args) => {
      const postToPublish = posts.find((post) => post.id === Number(args.id))
      postToPublish.published = true
      return postToPublish
    },
  },
  Post: {
    content: (parent) => parent.content,
    id: (parent) => parent.id,
    published: (parent) => parent.published,
    title: (parent) => parent.title,
  },
}


module.exports = {
  resolvers,
  typeDefs,
}

GraphQLスキーマと同じ構造に従ってリゾルバーを定義します。 スキーマのタイプのすべてのフィールドには、対応するリゾルバー関数があり、その責任は、スキーマ内のそのフィールドのデータを返すことです。 たとえば、 Query.feed() リゾルバーは、フィルタリングすることにより、公開された投稿を返します posts 配列。

リゾルバー関数は4つの引数を受け取ります。

  • parent:親は、リゾルバーチェーン内の前のリゾルバーの戻り値です。 トップレベルのリゾルバーの場合、親は undefined、以前のリゾルバが呼び出されていないため。 たとえば、 feed クエリ、 query.feed() リゾルバはで呼び出されます parentの値 undefined そして、のリゾルバー Post どこで呼ばれます parent から返されるオブジェクトです feed リゾルバ。
  • args:この引数は、クエリのパラメータを伝達します。たとえば、 post クエリ、受信します id フェッチされる投稿の。
  • context:各リゾルバーが書き込みおよび読み取りを行うことができるリゾルバーチェーンを通過するオブジェクト。これにより、リゾルバーは情報を共有できます。
  • info:クエリまたはミューテーションのAST表現。 このシリーズのパートIIIで詳細を読むことができます:GraphQLリゾルバーでの情報引数の謎解き

以来 contextinfo これらのリゾルバでは必要ありません。 parentargs 定義されています。

完了したら、ファイルを保存して終了します。

注:リゾルバーが、の4つのリゾルバーのように、リゾルバーの名前と同じフィールドを返す場合 Post、ApolloServerはそれらを自動的に解決します。 これは、これらのリゾルバーを明示的に定義する必要がないことを意味します。

-  Post: {
-    content: (parent) => parent.content,
-    id: (parent) => parent.id,
-    published: (parent) => parent.published,
-    title: (parent) => parent.title,
-  },

最後に、スキーマとリゾルバーをエクスポートして、次のステップでそれらを使用してApolloServerでサーバーをインスタンス化できるようにします。

ステップ3—GraphQLサーバーを作成する

このステップでは、Apollo Serverを使用してGraphQLサーバーを作成し、サーバーが接続を受け入れることができるようにポートにバインドします。

まず、次のコマンドを実行して、サーバー用のファイルを作成します。

  1. nano src/server.js

次に、次のコードをファイルに追加します。

プリズム-graphql/src / server.js
const { ApolloServer } = require('apollo-server')
const { resolvers, typeDefs } = require('./schema')

const port = process.env.PORT || 8080

new ApolloServer({ resolvers, typeDefs }).listen({ port }, () =>
  console.log(`Server ready at: http://localhost:${port}`),
)

ここでは、サーバーをインスタンス化し、前の手順のスキーマとリゾルバーを渡します。

サーバーがバインドするポートは、 PORT 環境変数。設定されていない場合、デフォルトで次のようになります。 8080. The PORT 環境変数はAppPlatformによって自動的に設定され、サーバーがデプロイされると接続を受け入れることができるようにします。

ファイルを保存して終了します。

GraphQLAPIを実行する準備が整いました。 次のコマンドでサーバーを起動します。

  1. node src/server.js

次の出力が表示されます。

Output
Server ready at: http://localhost:8080

開始スクリプトをに追加することをお勧めします package.json サーバーへのエントリポイントが明確になるようにします。 さらに、これにより、AppPlatformはデプロイ後にサーバーを起動できるようになります。

これを行うには、次の行をに追加します "scripts" のオブジェクト package.json:

package.json
{
  "name": "prisma-graphql",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node ./src/server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "apollo-server": "^2.18.2",
    "graphql": "^15.3.0"
  }
}

終了したら、ファイルを保存して終了します。

これで、次のコマンドを使用してサーバーを起動できます。

  1. npm start

GraphQL APIをテストするには、出力からURLを開きます。これにより、GraphQLPlaygroundに移動します。

GraphQL Playgroundは、クエリとミューテーションを送信してAPIをテストできるIDEです。

たとえば、 feed 公開された投稿のみを返すクエリ。IDEの左側に次のクエリを入力し、再生ボタンを押してクエリを送信します。

query {
  feed {
    id
    title
    content
    published
  }
}

応答には、のタイトルが表示されます Subscribe to GraphQL Weekly そのURLと Follow DigitalOcean on Twitter そのURLで。

テストするには createDraft ミューテーション、次のミューテーションを入力します。

mutation {
  createDraft(title: "Deploying a GraphQL API to DigitalOcean") {
    id
    title
    content
    published
  }
}

再生ボタンを使用してミューテーションを送信すると、次のメッセージが表示されます。 Deploying a GraphQL API to DigitalOcean 以内 title 応答の一部としてのフィールド。

注:中括弧内のフィールドを追加または削除することで、ミューテーションから返すフィールドを選択できます。 createDraft. たとえば、 idtitle 次のミューテーションを送信できます。

mutation {
  createDraft(title: "Deploying a GraphQL API to DigitalOcean") {
    id
    title
  }
}

これで、GraphQLサーバーが正常に作成およびテストされました。 次のステップでは、プロジェクトのGitHubリポジトリを作成します。

ステップ4—GitHubリポジトリを作成する

このステップでは、プロジェクトのGitHubリポジトリを作成し、変更をプッシュして、GraphQLAPIをGitHubからAppPlatformに自動的にデプロイできるようにします。

からリポジトリを初期化することから始めます prisma-graphql フォルダ:

  1. git init

次に、次の2つのコマンドを使用して、コードをリポジトリにコミットします。

  1. git add src package-lock.json package.json
  2. git commit -m 'Initial commit'

変更がローカルリポジトリにコミットされたので、GitHubにリポジトリを作成し、変更をプッシュします。

GitHub に移動して、新しいリポジトリを作成します。 一貫性を保つために、リポジトリに prisma-graphql という名前を付けてから、リポジトリの作成をクリックします。

リポジトリが作成されたら、次のコマンドを使用して変更をプッシュします。これには、デフォルトのローカルブランチの名前を次のように変更することも含まれます。 main:

  1. git remote add origin [email protected]:your_github_username/prisma-graphql.git
  2. git branch -M main
  3. git push --set-upstream origin main

変更を正常にコミットしてGitHubにプッシュしました。 次に、リポジトリをApp Platformに接続し、GraphQLAPIをデプロイします。

ステップ5—AppPlatformへのデプロイ

このステップでは、前のステップで作成したGitHubリポジトリをDigitalOceanに接続し、変更をGitHubにプッシュしたときにGraphQLAPIが自動的にデプロイされるようにAppPlatformを構成します。

まず、アプリプラットフォームに移動し、アプリの起動ボタンをクリックします。

GitHubアカウントをリンクするためのボタンが表示されます。

それをクリックすると、GitHubにリダイレクトされます。

クリックインストールと承認そして、DigitalOceanにリダイレクトされます。

リポジトリを選択してください your_github_username/prisma-graphql 次へをクリックします。

アプリをデプロイするリージョンを選択し、次へをクリックします。

ここで、アプリの構成をカスタマイズできます。 コマンドの実行npm start. デフォルトでは、AppPlatformはHTTPポートをに設定します 8080、これは、GraphQLサーバーをバインドするように構成したのと同じポートです。

次へをクリックすると、プランを選択するように求められます。

Basic を選択し、 Launch BasicAppをクリックします。 アプリページにリダイレクトされ、初期展開の進行状況が表示されます。

ビルドが完了すると、アプリがデプロイされたことを示す通知が届きます。

これで、アプリ名の下のURLでデプロイされたGraphQLAPIにアクセスできます。 それは下になります ondigitalocean.app サブドメイン。 URLを開くと、チュートリアルのステップ3で行ったのと同じ方法でGraphQLPlaygroundが開きます。

これで、リポジトリがApp Platformに正常に接続され、GraphQLAPIがデプロイされました。 次に、アプリを進化させ、GraphQLAPIのメモリ内データをデータベースに置き換えます。

ステップ6—PostgreSQLでPrismaを設定する

これまでに作成したGraphQLAPIはメモリ内を使用していました posts データを格納する配列。 これは、サーバーが再起動すると、データへのすべての変更が失われることを意味します。 データが安全に保持されるようにするには、 posts PostgreSQLデータベースを使用して配列し、Prismaを使用してデータにアクセスします。

このステップでは、Prisma CLIをインストールし、最初のPrismaスキーマを作成し、Dockerを使用してローカルでPostgreSQLをセットアップし、Prismaをそれに接続します。

Prismaスキーマは、Prismaセットアップのメイン構成ファイルであり、データベーススキーマが含まれています。

次のコマンドを使用してPrismaCLIをインストールすることから始めます。

  1. npm install --save-dev @prisma/cli

Prisma CLIは、データベース移行の実行やPrismaクライアントの生成などのデータベースワークフローを支援します。

次に、Dockerを使用してPostgreSQLデータベースをセットアップします。 次のコマンドを使用して、新しいDockerComposeファイルを作成します。

  1. nano docker-compose.yml

次に、新しく作成したファイルに次のコードを追加します。

プリズム-graphql/docker-compose.yml
version: '3.8'
services:
  postgres:
    image: postgres:10.3
    restart: always
    environment:
      - POSTGRES_USER=test-user
      - POSTGRES_PASSWORD=test-password
    volumes:
      - postgres:/var/lib/postgresql/data
    ports:
      - '5432:5432'
volumes:
  postgres:

このDockerCompose構成ファイルは、マシンで公式のPostgreSQLDockerイメージを起動する役割を果たします。 The POSTGRES_USERPOSTGRES_PASSWORD 環境変数は、スーパーユーザー(管理者権限を持つユーザー)の資格情報を設定します。 また、これらの資格情報を使用してPrismaをデータベースに接続します。 最後に、PostgreSQLがデータを保存するボリュームを定義し、 5432 マシンのポートをDockerコンテナの同じポートに接続します。

ファイルを保存して終了します。

この設定が整ったら、次のコマンドを使用してPostgreSQLデータベースサーバーを起動します。

  1. docker-compose up -d

次のコマンドを使用して、データベースサーバーが実行されていることを確認できます。

  1. docker ps

これにより、次のようなものが出力されます。

Output
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 198f9431bf73 postgres:10.3 "docker-entrypoint.s…" 45 seconds ago Up 11 seconds 0.0.0.0:5432->5432/tcp prisma-graphql_postgres_1

PostgreSQLコンテナが実行されている状態で、Prismaセットアップを作成できます。 PrismaCLIから次のコマンドを実行します。

  1. npx prisma init

ベストプラクティスとして、PrismaCLIのすべての呼び出しにはプレフィックスを付ける必要があることに注意してください npx. これにより、ローカルインストールを使用していることが保証されます。

コマンドを実行した後、PrismaCLIはという新しいフォルダーを作成しました prisma あなたのプロジェクトで。 次の2つのファイルが含まれています。

  • schema.prisma:Prismaプロジェクトのメイン構成ファイル(データモデルを含める)。
  • .env:データベース接続URLを定義するためのdotenvファイル。

Prismaがデータベースの場所を認識していることを確認するには、 .env ファイル:

  1. nano prisma/.env

調整する DATABASE_URL 次のように見える環境変数:

プリズム-graphql/prisma / .env
DATABASE_URL="postgresql://test-user:test-password@localhost:5432/my-blog?schema=public"

データベースのクレデンシャルを使用していることに注意してください test-usertest-password、DockerComposeファイルで指定されています。 接続URLの形式の詳細については、 Prismadocsにアクセスしてください。

これでPostgreSQLが正常に起動し、Prismaスキーマを使用してPrismaが構成されました。 次のステップでは、ブログのデータモデルを定義し、PrismaMigrateを使用してデータベーススキーマを作成します。

ステップ7—PrismaMigrateを使用したデータモデルの定義

次に、作成したPrismaスキーマファイルでデータモデルを定義します。 次に、このデータモデルは、Prisma Migrateを使用してデータベースにマップされます。これにより、データモデルに対応するテーブルを作成するためのSQLステートメントが生成および送信されます。

ブログを作成しているので、アプリケーションの主なエンティティはユーザー投稿になります。 このステップでは、 Post と同様の構造を持つモデル Post GraphQLスキーマを入力します。 後のステップで、アプリを進化させ、 User モデル。

注: GraphQL APIは、データベースの抽象化レイヤーと見なすことができます。 GraphQL APIを構築する場合、GraphQLスキーマがデータベーススキーマに非常に似ているのが一般的です。 ただし、抽象化として、2つのスキーマは必ずしも同じ構造である必要はないため、APIを介して公開するデータを制御できます。 これは、一部のデータがAPIレイヤーに対して機密または無関係であると見なされる可能性があるためです。

Prismaは、独自のデータモデリング言語を使用して、アプリケーションデータの形状を定義します。

あなたの schema.prisma プロジェクトのフォルダからのファイル package.json 位置しています:

  1. nano prisma/schema.prisma

注:端末から、現在のフォルダを確認できます。 pwd コマンド。現在の作業ディレクトリを出力します。 さらに、ファイルをリストする ls コマンドは、ファイルシステムをナビゲートするのに役立ちます。

次のモデル定義を追加します。

プリズム-graphql/prisma / schema.prisma
...
model Post {
  id        Int     @default(autoincrement()) @id
  title     String
  content   String?
  published Boolean @default(false)
}

と呼ばれるモデルを定義しています Post フィールドの数で。 モデルはデータベーステーブルにマップされます。 フィールドは、個々のを表します。

The id フィールドには、次のフィールド属性があります。

  • @default(autoincrement()):これにより、列の自動インクリメントのデフォルト値が設定されます。
  • @id:これにより、列がテーブルの主キーとして設定されます。

完了したら、ファイルを保存して終了します。

モデルが配置されたら、PrismaMigrateを使用してデータベースに対応するテーブルを作成できます。 これは、 migrate dev 移行ファイルを作成して実行するコマンド。

ターミナルを再度開き、次のコマンドを実行します。

  1. npx prisma migrate dev --preview-feature --name "init" --skip-generate

これにより、次のようなものが出力されます。

Output
PostgreSQL database my-blog created at localhost:5432 Prisma Migrate created and applied the following migration(s) from new schema changes: migrations/ └─ 20201201110111_init/ └─ migration.sql Everything is now in sync.

このコマンドは、ファイルシステム上に新しい migration を作成し、それをデータベースに対して実行してデータベーススキーマを作成します。 コマンドに提供されるオプションの概要は次のとおりです。

  • --preview-feature:PrismaMigrateは現在preview 状態であるため、必須です。
  • --name "init":移行の名前を指定します(ファイルシステム上に作成された移行フォルダーに名前を付けるために使用されます)。
  • --skip-generate:Prismaクライアントの生成をスキップします(これは次のステップで実行されます)。

君の prisma/migrations これで、ディレクトリにSQL移行ファイルが追加されます。 このアプローチにより、データベーススキーマへの変更を追跡し、本番環境で同じデータベーススキーマを作成できます。

注:すでにPrismaMigrateを使用している場合 my-blog データベースとの移行の間に矛盾があります prisma/migration フォルダとデータベーススキーマは、次の出力でデータベースをリセットするように求められます。

Output
? We need to reset the PostgreSQL database "my-blog" at "localhost:5432". All data will be lost. Do you want to continue? › (y/N)

これを解決するには、次のように入力します y これにより、データベースがリセットされます。 これにより、データベース内のすべてのデータが失われることに注意してください。

これで、データベーススキーマが作成されました。 次のステップでは、Prisma Clientをインストールし、GraphQLリゾルバーで使用します。

ステップ8—GraphQLリゾルバーでのPrismaクライアントの使用

Prisma Clientは、自動生成されたタイプセーフなオブジェクトリレーショナルマッパー(ORM)であり、Node.jsアプリケーションからデータベース内のデータをプログラムで読み書きするために使用できます。 このステップでは、プロジェクトにPrismaClientをインストールします。

ターミナルを再度開き、PrismaClientnpmパッケージをインストールします。

  1. npm install @prisma/client

注: Prisma Clientは、Prismaスキーマに基づいてコードを生成することにより、豊富なオートコンプリートを提供します。 node_modules フォルダ。 コードを生成するには、 npx prisma generate 指図。 これは通常、新しい移行を作成して実行した後に行われます。 ただし、最初のインストールでは、これは必要ありません。これは、 postinstall 針。

データベースとGraphQLスキーマが作成され、Prisma Clientがインストールされたら、GraphQLリゾルバーでPrisma Clientを使用して、データベースのデータを読み書きします。 これを行うには、 posts これまでデータを保持するために使用した配列。

次のファイルを作成することから始めます。

  1. nano src/db.js

以下を追加します。

プリズム-graphql/src / db.js
const { PrismaClient } = require('@prisma/client')

module.exports = {
  prisma: new PrismaClient(),
}

これにより、Prisma Clientがインポートされ、そのインスタンスが作成され、リゾルバーで使用するインスタンスがエクスポートされます。

保存して閉じます src/db.js ファイル。

次に、をインポートします prisma インスタンスに src/schema.js. これを行うには、 src/schema.js:

  1. nano src/schema.js

次にインポート prisma から ./db ファイルの先頭:

プリズム-graphql/src / schema.js
const { prisma } = require('./db')
...

次に、を削除します posts 配列:

プリズム-graphql/src / schema.js
-const posts = [
-  {
-    id: 1,
-    title: 'Subscribe to GraphQL Weekly for community news ',
-    content: 'https://graphqlweekly.com/',
-    published: true,
-  },
-  {
-    id: 2,
-    title: 'Follow DigitalOcean on Twitter',
-    content: 'https://twitter.com/digitalocean',
-    published: true,
-  },
-  {
-    id: 3,
-    title: 'What is GraphQL?',
-    content: 'GraphQL is a query language for APIs',
-    published: false,
-  },
-]

今、あなたは更新します Query データベースから公開された投稿をフェッチするリゾルバー。 を更新します resolvers.Query 次のリゾルバを持つオブジェクト:

プリズム-graphql/src / schema.js
...
const resolvers = {
  Query: {
    feed: (parent, args) => {
      return prisma.post.findMany({
        where: { published: true },
      })
    },
    post: (parent, args) => {
      return prisma.post.findOne({
        where: { id: Number(args.id) },
      })
    },
  },

ここでは、2つのPrismaClientクエリを使用しています。

  • findMany:投稿を取得します publish フィールドは false.
  • findOne:単一の投稿を取得します id フィールドは id GraphQL引数。

GraphQL仕様に従って、 ID タイプは、と同じ方法でシリアル化されます String. したがって、あなたはに変換します Number なぜなら id Prismaスキーマでは int.

次に、を更新します Mutation データベース内の投稿を保存および更新するためのリゾルバー。 を更新します resolvers.Mutation 次のリゾルバを持つオブジェクト:

プリズム-graphql/src / schema.js
const resolvers = {
  ...
  Mutation: {
    createDraft: (parent, args) => {
      return prisma.post.create({
        data: {
          title: args.title,
          content: args.content,
        },
      })
    },
    publish: (parent, args) => {
      return prisma.post.update({
        where: {
          id: Number(args.id),
        },
        data: {
          published: true,
        },
      })
    },
  },
}

2つのPrismaクライアントクエリを使用しています。

  • create:作成する Post 記録。
  • update:の公開フィールドを更新します Post そのレコード id クエリ引数の1つと一致します。

君の schema.js 次のようになります。

プリズム-graphql/src / schema.js
const { gql } = require('apollo-server')
const { prisma } = require('./db')

const typeDefs = gql`
  type Post {
    content: String
    id: ID!
    published: Boolean!
    title: String!
  }

  type Query {
    feed: [Post!]!
    post(id: ID!): Post
  }

  type Mutation {
    createDraft(content: String, title: String!): Post!
    publish(id: ID!): Post
  }
`

const resolvers = {
  Query: {
    feed: (parent, args) => {
      return prisma.post.findMany({
        where: { published: true },
      })
    },
    post: (parent, args) => {
      return prisma.post.findOne({
        where: { id: Number(args.id) },
      })
    },
  },
  Mutation: {
    createDraft: (parent, args) => {
      return prisma.post.create({
        data: {
          title: args.title,
          content: args.content,
        },
      })
    },
    publish: (parent, args) => {
      return prisma.post.update({
        where: {
          id: Number(args.id),
        },
        data: {
          published: true,
        },
      })
    },
  },
}

module.exports = {
  resolvers,
  typeDefs,
}

ファイルを保存して閉じます。

Prisma Clientを使用するようにリゾルバーを更新したので、サーバーを起動して、次のコマンドを使用してGraphQLAPIとデータベース間のデータのフローをテストします。

  1. npm start

繰り返しになりますが、次の出力が表示されます。

Output
Server ready at: http://localhost:8080

出力からのアドレスでGraphQLプレイグラウンドを開き、ステップ3と同じクエリを使用してGraphQLAPIをテストします。

次に、変更をコミットして、変更をAppPlatformにデプロイできるようにします。

コミットを回避するには node_modules フォルダと prisma/.env ファイル、作成することから始めます .gitignore ファイル:

  1. nano .gitignore

ファイルに以下を追加します。

プリズム-graphql/.gitignore
node_modules
prisma/.env

ファイルを保存して終了します。

次に、次の2つのコマンドを実行して、変更をコミットします。

  1. git add .
  2. git commit -m 'Add Prisma'

次に、AppPlatformのアプリにPostgreSQLデータベースを追加します。

ステップ9—AppPlatformでのPostgreSQLデータベースの作成と移行

このステップでは、PostgreSQLデータベースをAppPlatformのアプリに追加します。 次に、Prisma Migrateを使用して移行を実行し、デプロイされたデータベーススキーマがローカルデータベースと一致するようにします。

まず、 App Platformコンソールに移動し、手順5で作成したprisma-graphqlプロジェクトを選択します。

次に、コンポーネントタブに移動します。

+コンポーネントの作成をクリックし、データベースを選択すると、データベースを構成するページが表示されます。

Dev Database を選択し、 Create andAttachをクリックします。

コンポーネントページにリダイレクトされ、データベースを作成するためのプログレスバーが表示されます。

データベースが作成されたら、ローカルマシンからDigitalOcean上の本番データベースに対してデータベース移行を実行します。 移行を実行するには、ホストされているデータベースの接続文字列が必要です。 入手するには、[コンポーネント]タブのdbアイコンをクリックします。

接続の詳細で、ドロップダウンの接続文字列を選択し、データベースのURLをコピーします。これは次の構造になります。

postgresql://db:[email protected]_identifier.db.ondigitalocean.com:25060/db?sslmode=require

次に、ターミナルで次のコマンドを実行し、設定したことを確認します DATABASE_URL コピーしたのと同じURLに:

  1. DATABASE_URL="postgresql://db:[email protected]_identifier.db.ondigitalocean.com:25060/db?sslmode=require" npx prisma migrate deploy --preview-feature

これにより、PrismaMigrateを使用してライブデータベースに対して移行が実行されます。

移行が成功すると、次のメッセージが表示されます。

Output
PostgreSQL database db created at unique_identifier.db.ondigitalocean.com:25060 Prisma Migrate applied the following migration(s): migrations/ └─ 20201201110111_init/ └─ migration.sql

これで、DigitalOceanの本番データベースが正常に移行されました。これは、Prismaスキーマと一致します。

これで、次のコマンドを使用してGitの変更をプッシュすることでアプリをデプロイできます。

  1. git push

注:AppPlatformは DATABASE_URL 実行時にアプリケーションで使用できる環境変数。 Prisma Clientは、 env("DATABASE_URL") の中に datasource Prismaスキーマのブロック。

これにより、ビルドが自動的にトリガーされます。 App Platformコンソールを開くと、Deployingプログレスバーが表示されます。

展開が成功すると、正常に展開されましたメッセージが表示されます。

これで、デプロイされたGraphQLAPIをデータベースでバックアップしました。 Live App を開くと、GraphQLPlaygroundに移動します。 手順3と同じクエリを使用してGraphQLAPIをテストします。

最後のステップでは、次を追加してGraphQLAPIを進化させます。 User モデル。

ステップ10—ユーザーモデルの追加

ブログ用のGraphQLAPIには、という名前の単一のエンティティがあります Post. このステップでは、Prismaスキーマで新しいモデルを定義し、新しいモデルを利用するようにGraphQLスキーマを適応させることにより、APIを進化させます。 紹介します User 1対多の関係を持つモデル Post モデル。 これにより、投稿の作成者を表し、各ユーザーに複数の投稿を関連付けることができます。 次に、GraphQLスキーマを進化させて、APIを介してユーザーを作成し、投稿をユーザーに関連付けることができるようにします。

まず、Prismaスキーマを開き、以下を追加します。

プリズム-graphql/prisma / schema.prisma
...
model Post {
  id        Int     @id @default(autoincrement())
  title     String
  content   String?
  published Boolean @default(false)
  author    User?   @relation(fields: [authorId], references: [id])
  authorId  Int?
}

model User {
  id    Int    @id @default(autoincrement())
  email String @unique
  name  String
  posts Post[]
}

Prismaスキーマに以下を追加しました。

  • The User ユーザーを表すモデル。
  • 2つの関係フィールド: authorposts. 関係フィールドは、Prismaレベルでモデル間の接続を定義し、データベースには存在しません。 これらのフィールドは、Prismaクライアントを生成し、Prismaクライアントとの関係にアクセスするために使用されます。
  • The authorId フィールドは、によって参照されます @relation 属性。 Prismaは、接続するためにデータベースに外部キーを作成します PostUser.

の作成者フィールドに注意してください Post モデルはオプションです。 つまり、ユーザーに関連付けられていない投稿を作成できるようになります。

完了したら、ファイルを保存して終了します。

次に、次のコマンドを使用して、移行をローカルで作成して適用します。

  1. npx prisma migrate dev --preview-feature --name "add-user"

移行が成功すると、次のメッセージが表示されます。

Output
Prisma Migrate created and applied the following migration(s) from new schema changes: migrations/ └─ 20201201123056_add_user/ └─ migration.sql ✔ Generated Prisma Client to ./node_modules/@prisma/client in 53ms

このコマンドは、新しいテーブルとフィールドを利用できるようにPrismaClientも生成します。

次に、データベーススキーマがローカルデータベースと同じになるように、AppPlatformの本番データベースに対して移行を実行します。 ターミナルで次のコマンドを実行し、設定します DATABASE_URL App Platformから接続URLへ:

  1. DATABASE_URL="postgresql://db:[email protected]_identifier.db.ondigitalocean.com:25060/db?sslmode=require" npx prisma migrate deploy --preview-feature

あなたは以下を受け取ります:

Output
Prisma Migrate applied the following migration(s): migrations/ └─ 20201201123056_add_user/ └─ migration.sql

ここで、GraphQLスキーマとリゾルバーを更新して、更新されたデータベーススキーマを利用します。

を開きます src/schema.js ファイルと更新を追加 typeDefs 次のように:

プリズム-graphql/src / schema.js
...
const typeDefs = gql`
  type User {
    email: String!
    id: ID!
    name: String
    posts: [Post!]!
  }

  type Post {
    content: String
    id: ID!
    published: Boolean!
    title: String!
    author: User
  }

  type Query {
    feed: [Post!]!
    post(id: ID!): Post
  }

  type Mutation {
    createUser(data: UserCreateInput!): User!
    createDraft(authorEmail: String, content: String, title: String!): Post!
    publish(id: ID!): Post
  }

  input UserCreateInput {
    email: String!
    name: String
    posts: [PostCreateWithoutAuthorInput!]
  }

  input PostCreateWithoutAuthorInput {
    content: String
    published: Boolean
    title: String!
  }
`
...

この更新されたコードでは、GraphQLスキーマに次の変更を追加しています。

  • The User タイプ、の配列を返します Post.
  • The author フィールドに Post タイプ。
  • The createUser 突然変異、それは期待します UserCreateInput 入力タイプとして。
  • The PostCreateWithoutAuthorInput で使用される入力タイプ UserCreateInput の一部として投稿を作成するための入力 createUser 突然変異。
  • The authorEmail オプションの引数 createDraft 突然変異。

スキーマが更新されたら、スキーマに一致するようにリゾルバーを更新します。

を更新します resolvers 次のようなオブジェクト:

プリズム-graphql/src / schema.js
...
const resolvers = {
  Query: {
    feed: (parent, args) => {
      return prisma.post.findMany({
        where: { published: true },
      })
    },
    post: (parent, args) => {
      return prisma.post.findOne({
        where: { id: Number(args.id) },
      })
    },
  },
  Mutation: {
    createDraft: (parent, args) => {
      return prisma.post.create({
        data: {
          title: args.title,
          content: args.content,
          published: false,
          author: args.authorEmail && {
            connect: { email: args.authorEmail },
          },
        },
      })
    },
    publish: (parent, args) => {
      return prisma.post.update({
        where: { id: Number(args.id) },
        data: {
          published: true,
        },
      })
    },
    createUser: (parent, args) => {
      return prisma.user.create({
        data: {
          email: args.data.email,
          name: args.data.name,
          posts: {
            create: args.data.posts,
          },
        },
      })
    },
  },
  User: {
    posts: (parent, args) => {
      return prisma.user
        .findOne({
          where: { id: parent.id },
        })
        .posts()
    },
  },
  Post: {
    author: (parent, args) => {
      return prisma.post
        .findOne({
          where: { id: parent.id },
        })
        .author()
    },
  },
}

リゾルバーへの変更を分解してみましょう。

  • The createDraft ミューテーションリゾルバは現在、 authorEmail 作成されたドラフトと既存のユーザーの間に関係を作成するための引数(渡された場合)。
  • 新しい createUser ミューテーションリゾルバーは、ネストされた書き込みを使用してユーザーと関連する投稿を作成します。
  • The User.postsPost.author リゾルバは、を解決する方法を定義します postsauthor フィールドの場合 User また Post 照会されます。 これらは、Prismaの FluentAPIを使用してリレーションをフェッチします。

ファイルを保存して終了します。

サーバーを起動して、GraphQLAPIをテストします。

  1. npm start

テストすることから始めます createUser 次のGraphQLミューテーションを持つリゾルバー:

mutation {
  createUser(data: { email: "[email protected]", name: "Natalia" }) {
    email
    id
  }
}

これにより、ユーザーが作成されます。

次に、テストします createDraft 次のミューテーションを持つリゾルバー:

mutation {
  createDraft(
    authorEmail: "[email protected]"
    title: "Deploying a GraphQL API to App Platform"
  ) {
    id
    title
    content
    published
    author {
      id
      name
    }
  }
}

フェッチできることに注意してください author クエリの戻り値が Post. この例では、 Post.author リゾルバが呼び出されます。

最後に、変更をコミットしてプッシュし、APIをデプロイします。

  1. git add .
  2. git commit -m "add user model"
  3. git push

Prisma Migrateを使用してデータベーススキーマを正常に進化させ、GraphQLAPIで新しいモデルを公開しました。

結論

この記事では、PrismaとGraphQLを使用してGraphQL APIを構築し、それをDigitalOceanのAppPlatformにデプロイしました。 ApolloServerを使用してGraphQLスキーマとリゾルバーを定義しました。 次に、GraphQLリゾルバーでPrisma Clientを使用して、PostgreSQLデータベースのデータを永続化およびクエリしました。

次のステップとして、個々のユーザーをフェッチするクエリと、既存のドラフトをユーザーに接続するミューテーションを使用して、GraphQLAPIをさらに拡張できます。

データベース内のデータの調査に興味がある場合は、 PrismaStudioをチェックしてください。 必ずPrismaドキュメントにアクセスして、Prismaのさまざまな側面について学び、prisma-examplesリポジトリですぐに実行できるサンプルプロジェクトを調べてください。

このプロジェクトのコードは、DigitalOceanCommunityリポジトリにあります。