GraphQLとVueでファイル処理アプリを構築する方法
序章
このチュートリアルでは、フルスタックアプリを構築して、GraphQLでファイルのアップロードを処理する方法について説明します。 このチュートリアルは、GraphQL APIの構築と、フロントエンドアプリの作成という2つの主要なセクションに分かれています。 GraphQL APIはApolloサーバーを使用して構築され、フロントエンドアプリはVue.jsとVueApolloを使用して構築されます。
前提条件
このチュートリアルは、GraphQLとVueに慣れていることを前提としています。 詳細なガイダンスについては、この GraphQL with Nodeチュートリアル、およびVue、GraphQL、およびApolloClientを使用してブログを構築するこのチュートリアルから学ぶことができます。
構築するもの
このチュートリアルでは、ユーザーが写真をアップロードしたり表示したりできるフォトアルバムアプリを作成します。 すべての写真はCloudinaryに直接アップロードされます。 以下は、最終的なアプリの簡単なデモです。
ステップ1—Cloudinaryキーを取得する
コードに飛び込む前に、Cloudinaryキーが設定されていることを確認しましょう。 まだアカウントをお持ちでない場合は、無料でサインアップできます。 それ以外の場合は、ダッシュボードにログインすると、アカウントの詳細とキーが表示されます。
ステップ2—GraphQLサーバーを構築する
それでは、GraphQLサーバーの構築を始めましょう。 まず、プロジェクトディレクトリを作成しましょう。
$ mkdir graphql-vue-photo-upload && cd graphql-vue-photo-upload $ mkdir server && cd server $ npm init -y
すべてのGraphQLAPI関連のコードは、server
ディレクトリ内にあります。 次に、GraphQLサーバーに必要な依存関係をインストールしましょう。
$ npm install graphql apollo-server cloudinary dotenv
Apollo Serverとその依存関係のインストールに加えて、CloudinaryNode.jsライブラリと環境変数を読み取るためのパッケージもインストールします。
インストールが完了したら、GraphQLサーバーの構築を開始できます。 新しいsrc
ディレクトリを作成し、その中に新しいindex.js
ファイルを作成してから、次のコードを追加します。
// server/src/index.js const { ApolloServer } = require('apollo-server') require('dotenv').config() const typeDefs = require('./schema') const resolvers = require('./resolvers') const server = new ApolloServer({ typeDefs, resolvers }) server.listen().then(({ url }) => console.log(`Server ready at ${url}`))
次に、GraphQLサーバーが参照するスキーマとリゾルバーを作成する必要があります。 スキーマの作成から始めます。 src
ディレクトリ内にschema.js
を作成し、次のコードを貼り付けます。
// server/src/schema.js const { gql } = require('apollo-server') const typeDefs = gql`type Photo { filename: String! path: String! } type Query { allPhotos: [Photo] } type Mutation { uploadPhoto(photo: Upload!): Photo! }` module.exports = typeDefs
ここでは、写真のファイル名と実際の写真へのパスの2つのフィールドで構成されるPhoto
タイプを定義します。 次に、アップロードされたすべての写真を取得するための単一のクエリallPhotos
を定義します。 最後に、写真をアップロードするためのミューテーションがあります。 uploadPhoto
ミューテーションは、アップロードされる写真である単一の引数を受け入れます。 引数はスカラータイプUpload
であり、ファイルアップロードのサポートが組み込まれているため、Apolloサーバーで使用できます。 ミューテーションはアップロードされた写真を返します。
次に行うことは、リゾルバーを作成することです。 まだsrc
ディレクトリ内に、resolvers.js
を作成し、その中に以下のコードを追加します。
// server/src/resolvers.js const cloudinary = require('cloudinary').v2 cloudinary.config({ cloud_name: process.env.CLOUD_NAME, api_key: process.env.API_KEY, api_secret: process.env.API_SECRET }) const photos = [] const resolvers = { Query: { allPhotos () { return photos } }, Mutation: { async uploadPhoto (parent, { photo }) { const { filename, createReadStream } = await photo try { const result = await new Promise((resolve, reject) => { createReadStream().pipe( cloudinary.uploader.upload_stream((error, result) => { if (error) { reject(error) } resolve(result) }) ) }) const newPhoto = { filename, path: result.secure_url } photos.push(newPhoto) return newPhoto } catch (err) { console.log(err) } } } } module.exports = resolvers
まず、Cloudinaryライブラリを取得し、環境変数から取得するクレデンシャルを構成します。 次に、写真を保持する空の配列を作成します。 次に、allPhotos
クエリのリゾルバーを定義します。これにより、写真の配列が返されます。
uploadPhoto
ミューテーションの場合、ApolloServerは選択されたファイルをPromise
として返します。これには、createReadStream
、[X161Xなどのファイルに関する詳細が含まれています。 ]、mimetype
およびencoding
。 このチュートリアルでは、最初の2つのみを使用するため、オブジェクトからそれらを抽出します。 createReadStream
を使用して、ファイルをCloudinaryに直接ストリーミングします。 非同期操作なので、Promise
とawait
でラップします。 Promise
が解決された場合、つまりファイルがCloudinaryに正常にアップロードされた場合、アップロードされたファイル名とファイルへのCloudinaryパスを含む新しいオブジェクトを作成します。 次に、新しいオブジェクトをphotos配列にプッシュし、最後に新しいオブジェクトを返します。
最後に、Cloudinaryへのファイルのアップロードでエラーが発生した場合は、エラーをコンソールログに記録できます。
GraphQL APIをまとめる前に、環境変数を簡単に追加しましょう。 .env
ファイルをserver
ディレクトリに直接作成し、その中に以下のコードを追加します。
// server/.env CLOUD_NAME=YOUR_CLOUD_NAME API_KEY=YOUR_API_KEY API_SECRET=YOUR_API_SECRET
プレースホルダーを実際のアカウントの詳細に置き換えることを忘れないでください。
最後に、サーバーを起動しましょう。
$ node src/index.js
サーバーは[1](http://localhost:4000)
で実行されている必要があります
ステップ3—フロントエンドアプリを構築する
すでに述べたように、フロントエンドアプリはVue.jsで構築されるので、VueCLIを使用して新しいVue.jsアプリを作成しましょう。
$ vue create client
プロンプトが表示されたら、Enterキーを押してデフォルトのプリセットを選択します。 アプリを起動し、ビルド中は実行したままにします。
$ cd client $ yarn serve
アプリはhttp:// localhost:8080で実行されている必要があります
Vueアプリが作成されたら、必要な依存関係をインストールしましょう。
$ npm install vue-apollo graphql-tag graphql apollo-cache-inmemory apollo-client apollo-upload-client
これらの依存関係のうち、新しく指摘したいのは[apollo-upload-client](https://github.com/jaydenseric/apollo-upload-client)
です。 これは、GraphQLマルチパートリクエストを送信できるようにするApolloクライアント用のパッケージです。 apollo-link
の代わりに使用されます。
次に、VueApolloとこれらの依存関係を構成しましょう。 main.js
を次のように更新します。
// client/src/main.js import { InMemoryCache } from 'apollo-cache-inmemory' import { ApolloClient } from 'apollo-client' import { createUploadLink } from 'apollo-upload-client' import Vue from 'vue' import VueApollo from 'vue-apollo' import App from './App.vue' Vue.config.productionTip = false Vue.use(VueApollo) const apolloClient = new ApolloClient({ link: createUploadLink({ uri: 'http://localhost:4000' }), cache: new InMemoryCache() }) const apolloProvider = new VueApollo({ defaultClient: apolloClient }) new Vue({ apolloProvider, render: h => h(App) }).$mount('#app')
ここでは、apollo-upload-client
のcreateUploadLink
を使用してApolloClient
リンクを作成し、それにGraphQLAPIエンドポイントを渡していることに気付くでしょう。
アプリに少しスタイルを与えるために、UIKitを使用します。 index.html
のhead
セクションに以下の行を追加します。
<!-- client/public/index.html --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.1.5/css/uikit.min.css" />
ステップ4—写真を取得する
すべての写真をフェッチするためのGraphQLクエリから始めます。 client/src
ディレクトリ内にgraphql
ディレクトリを作成し、でAllPhotos.js
ファイルを作成して、以下のコードをその中に貼り付けます。
// client/src/graphql/AllPhotos.js import gql from 'graphql-tag' export default gql`query allPhotos { allPhotos { filename path } }`
このチュートリアルの学習目的では、App.vue
コンポーネントのみを使用します。 それでは、以下のように更新しましょう。
// client/src/App.vue <template> <section class="uk-section"> <div class="uk-container uk-container-small"> <h2>Photo Album</h2> <div class="uk-grid uk-child-width-1-3@m"> <div class="uk-margin" v-for="(photo, index) in allPhotos" :key="index"> <div class="uk-card uk-card-default"> <div class="uk-card-media-top"> <img :src="photo.path"> </div> <div class="uk-card-body">{{ photo.filename }}</div> </div> </div> </div> </div> </section> </template> <script> import ALL_PHOTOS from "./graphql/AllPhotos"; export default { name: "app", apollo: { allPhotos: ALL_PHOTOS } }; </script>
apollo
オブジェクト内に、ALL_PHOTOS
クエリを追加して、すべての写真をフェッチし、allPhotos
に保存します。 allPhotos
にGraphQLAPIからのデータが入力されると、写真をループして表示します。
アプリを表示すると、次のようなものが表示されます。
ステップ5—写真をアップロードする
もちろん、写真を見る前にアップロードしておく必要があります。 それを実装しましょう。 graphql
ディレクトリ内に、UploadPhoto.js
を作成し、以下のコードを貼り付けます。
// client/src/graphql/UploadPhoto.js import gql from 'graphql-tag' export default gql`mutation uploadPhoto($photo: Upload!) { uploadPhoto(photo: $photo) { filename path } }`
次に、以下のスニペットをApp.vue
のtemplate
セクションのフォトアルバム見出しのすぐ下に追加します。
// client/src/App.vue <div class="uk-margin"> <input type="file" accept="image/*" @change="uploadPhoto"> </div>
ここには、画像のみを受け入れるファイル入力フィールドがあります。 入力フィールドを変更すると、uploadPhoto
メソッドがトリガーされます。
script
セクションに、次を追加します。
// client/src/App.vue import UPLOAD_PHOTO from "./graphql/UploadPhoto"; methods: { async uploadPhoto({ target }) { await this.$apollo.mutate({ mutation: UPLOAD_PHOTO, variables: { photo: target.files[0] }, update: (store, { data: { uploadPhoto } }) => { const data = store.readQuery({ query: ALL_PHOTOS }); data.allPhotos.push(uploadPhoto); store.writeQuery({ query: ALL_PHOTOS, data }); } }); } }
入力イベントからtarget
を抽出し、mutate
メソッドを呼び出して、UPLOAD_PHOTO
ミューテーションと必要な引数( [を介して)を渡します。 X170X]オブジェクト)。 target
オブジェクトのfiles
から選択したファイルを取得します。 ミューテーションが実行されたら、新しくアップロードされた写真をallPhotos
配列に追加してキャッシュを更新します。
結論
したがって、このチュートリアルでは、サーバー側でApollo Serverを使用し、クライアント側でVueとVue Apolloを使用して、GraphQLでファイルのアップロードを処理する方法を見てきました。 写真の保存にはCloudinaryを使用しましたが、他のクラウドストレージサービス用にラップしたり、独自のローカルファイルシステムに直接保存したりすることもできます。