GraphQLとVueでファイル処理アプリを構築する方法

提供:Dev Guides
移動先:案内検索

序章

このチュートリアルでは、フルスタックアプリを構築して、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に直接ストリーミングします。 非同期操作なので、Promiseawaitでラップします。 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-clientcreateUploadLinkを使用してApolloClientリンクを作成し、それにGraphQLAPIエンドポイントを渡していることに気付くでしょう。

アプリに少しスタイルを与えるために、UIKitを使用します。 index.htmlheadセクションに以下の行を追加します。

<!-- 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.vuetemplateセクションのフォトアルバム見出しのすぐ下に追加します。

// 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を使用しましたが、他のクラウドストレージサービス用にラップしたり、独自のローカルファイルシステムに直接保存したりすることもできます。