Cách tạo ứng dụng xử lý tệp trong GraphQL và Vue
Trong hướng dẫn này, ta sẽ xem xét cách xử lý tải lên file trong GraphQL bằng cách xây dựng một ứng dụng đầy đủ. Hướng dẫn này sẽ được chia thành hai phần chính: xây dựng API GraphQL và tạo ứng dụng giao diện user . API GraphQL sẽ được xây dựng bằng Apollo Server và ứng dụng giao diện user sẽ được xây dựng với Vue.js và Vue Apollo.Yêu cầu
Hướng dẫn này giả định bạn thấy phù hợp với GraphQL và Vue. Để được hướng dẫn thêm, bạn có thể học từ hướng dẫn GraphQL với Node này và hướng dẫn xây dựng blog với Vue, GraphQL và Apollo Client này .
Những gì ta sẽ xây dựng
Với mục đích của hướng dẫn này, ta sẽ xây dựng một ứng dụng album ảnh, nơi user có thể tải lên cũng như xem ảnh của họ. Tất cả các bức ảnh sẽ được tải trực tiếp lên Cloudinary . Dưới đây là bản demo nhanh về ứng dụng cuối cùng:
Bước 1 - Nhận các khóa Cloudinary
Trước khi đi sâu vào mã, hãy đảm bảo ta có khóa Cloudinary tại chỗ. Nếu bạn chưa có account với họ, bạn có thể đăng ký miễn phí . Nếu không, hãy đăng nhập vào trang tổng quan của bạn, nơi bạn sẽ thấy chi tiết account cùng với các khóa của bạn .
Bước 2 - Xây dựng server GraphQL
Bây giờ, hãy bắt đầu xây dựng server GraphQL. Đầu tiên, hãy tạo folder dự án của ta :
$ mkdir graphql-vue-photo-upload && cd graphql-vue-photo-upload $ mkdir server && cd server $ npm init -y
Tất cả mã liên quan đến API GraphQL sẽ nằm trong folder server
. Tiếp theo, hãy cài đặt các phụ thuộc cần thiết cho server GraphQL của ta :
$ npm install graphql apollo-server cloudinary dotenv
Ngoài việc cài đặt Apollo Server và dependencies của nó, ta cũng cài đặt thư viện Cloudinary Node.js và một gói để đọc các biến môi trường.
Sau khi cài đặt xong, ta có thể bắt đầu xây dựng server GraphQL. Tạo một folder src
mới và tạo một index.js
mới bên trong nó, sau đó thêm mã sau vào đó:
// 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}`))
Tiếp theo, ta cần tạo schemas và các trình phân giải mà server GraphQL của ta tham chiếu. Ta sẽ bắt đầu với việc tạo schemas . Tạo một schema.js
bên trong folder src
và dán đoạn mã sau vào đó:
// 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
Ở đây, ta xác định loại Photo
bao gồm hai trường: tên file của ảnh và đường dẫn đến ảnh thực. Sau đó, ta xác định một truy vấn allPhotos
duy nhất để tìm nạp tất cả các ảnh đã tải lên. Cuối cùng, ta có một đột biến để tải ảnh lên. Đột biến uploadPhoto
chấp nhận một đối số duy nhất, đó là ảnh sẽ được tải lên. Đối số thuộc loại Upload
vô hướng, được cung cấp cho Server Apollo của tôi, vì nó có hỗ trợ tải lên file được tích hợp sẵn. Sự đột biến sẽ trả về ảnh đã tải lên.
Tiếp theo, điều cần làm là tạo các trình phân giải. Vẫn bên trong folder src
, tạo một resolvers.js
và thêm mã bên dưới vào đó:
// 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
Đầu tiên, ta kéo vào thư viện Cloudinary và cấu hình nó theo thông tin đăng nhập của ta , nhận được từ các biến môi trường. Sau đó, ta tạo một mảng trống, mảng này sẽ chứa ảnh của ta . Tiếp theo, ta xác định trình phân giải cho truy vấn allPhotos
, trình giải quyết trả về mảng ảnh.
Đối với đột biến uploadPhoto
, Apollo Server sẽ trả về file đã chọn dưới dạng Promise
, file này chứa một loạt các chi tiết về file , chẳng hạn như: createReadStream
, filename
, mimetype
và encoding
. Trong hướng dẫn này, ta sẽ chỉ sử dụng hai cái đầu tiên, vì vậy ta extract chúng từ đối tượng. Sử dụng createReadStream
, ta truyền file trực tiếp lên Cloudinary. Vì nó là một hoạt động không đồng bộ, ta bọc nó trong một Promise
và await
nó. Nếu Promise
đã được giải quyết, tức là file đã được tải lên Cloudinary thành công, ta tạo một đối tượng mới chứa tên file đã tải lên và đường dẫn Cloudinary đến file . Sau đó, ta đẩy đối tượng mới vào mảng ảnh và cuối cùng trả lại đối tượng mới.
Cuối cùng, nếu có lỗi khi tải file lên Cloudinary, ta có thể ghi lại lỗi.
Trước khi kết thúc API GraphQL, hãy nhanh chóng thêm các biến môi trường của ta . Tạo file .env
trực tiếp trong folder server
và thêm mã bên dưới vào đó:
// server/.env CLOUD_NAME=YOUR_CLOUD_NAME API_KEY=YOUR_API_KEY API_SECRET=YOUR_API_SECRET
Hãy nhớ thay thế phần giữ chỗ bằng chi tiết account thực của bạn.
Cuối cùng, hãy khởi động server :
$ node src/index.js
Server phải chạy trên [http://localhost:4000](http://localhost:4000)
Bước 3 - Xây dựng ứng dụng Front-end
Như đã đề cập, ứng dụng frontend sẽ được xây dựng với Vue.js, vì vậy hãy tạo một ứng dụng Vue.js mới bằng cách sử dụng Vue CLI:
$ vue create client
Khi được yêu cầu , hãy nhấn enter để chọn cài đặt trước mặc định. Khởi động ứng dụng và để nó chạy trong khi ta xây dựng trên nó:
$ cd client $ yarn serve
Ứng dụng sẽ chạy trên http: // localhost: 8080
Khi ứng dụng Vue được tạo, hãy cài đặt các phụ thuộc cần thiết:
$ npm install vue-apollo graphql-tag graphql apollo-cache-inmemory apollo-client apollo-upload-client
Ngoài những phần phụ thuộc này, cái mới và tôi muốn chỉ ra là [apollo-upload-client](https://github.com/jaydenseric/apollo-upload-client)
. Đó là một gói dành cho Apollo Client cho phép ta gửi các yêu cầu nhiều phần GraphQL. Nó sẽ được sử dụng thay cho apollo-link
.
Tiếp theo, hãy cấu hình Vue Apollo và các phụ thuộc này. Cập nhật main.js
như bên dưới:
// 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')
Bạn sẽ nhận thấy ở đây ta đang sử dụng createUploadLink
từ apollo-upload-client
để tạo liên kết ApolloClient
, chuyển tới nó điểm cuối API GraphQL của ta .
Để cung cấp cho ứng dụng của ta một chút phong cách, ta sẽ đưa vào UIKit. Thêm dòng bên dưới vào phần head
của index.html
:
<!-- client/public/index.html --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.1.5/css/uikit.min.css" />
Bước 4 - Tìm nạp ảnh
Ta sẽ bắt đầu với truy vấn GraphQL để tìm nạp tất cả ảnh của ta . Tạo một folder graphql
bên trong folder client/src
và với trong, tạo file AllPhotos.js
và dán mã bên dưới vào đó:
// client/src/graphql/AllPhotos.js import gql from 'graphql-tag' export default gql`query allPhotos { allPhotos { filename path } }`
Đối với mục đích học tập của hướng dẫn này, ta sẽ chỉ sử dụng thành phần App.vue
. Vì vậy, hãy cập nhật nó như dưới đây:
// 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>
Trong đối tượng apollo
, ta thêm truy vấn ALL_PHOTOS
để tìm nạp tất cả ảnh và lưu nó trong allPhotos
. Sau khi allPhotos
được điền dữ liệu từ API GraphQL của ta , ta sẽ hiển thị ảnh bằng cách lặp qua chúng.
Nếu ta xem ứng dụng của bạn , ta sẽ nhận được một cái gì đó tương tự như bên dưới:
Bước 5 - Tải lên ảnh
Tất nhiên, ta cần phải tải lên một số ảnh trước khi ta có thể xem chúng. Hãy thực hiện điều đó ngay bây giờ. Vẫn bên trong folder graphql
, hãy tạo UploadPhoto.js
và dán mã bên dưới vào đó:
// client/src/graphql/UploadPhoto.js import gql from 'graphql-tag' export default gql`mutation uploadPhoto($photo: Upload!) { uploadPhoto(photo: $photo) { filename path } }`
Tiếp theo, thêm đoạn mã bên dưới vào phần template
của App.vue
, ngay bên dưới tiêu đề Album ảnh :
// client/src/App.vue <div class="uk-margin"> <input type="file" accept="image/*" @change="uploadPhoto"> </div>
Ở đây, ta có một trường đầu vào file chỉ chấp nhận hình ảnh. Khi thay đổi trường đầu vào, một phương thức uploadPhoto
được kích hoạt.
Trong phần script
, hãy thêm:
// 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 }); } }); } }
Ta extract target
từ sự kiện đầu vào, sau đó gọi phương thức mutate
, chuyển cho nó đột biến UPLOAD_PHOTO
cũng như đối số bắt buộc (thông qua đối tượng variables
). Ta lấy file đã chọn từ các files
trên đối tượng target
. Sau khi đột biến được thực thi, ta cập nhật bộ nhớ cache bằng cách thêm ảnh mới tải lên vào mảng allPhotos
.
Kết luận
Vì vậy, trong hướng dẫn này, ta đã thấy cách xử lý tải lên file trong GraphQL bằng Apollo Server ở phía server và Vue và Vue Apollo ở phía client . Mặc dù ta đã sử dụng Cloudinary để lưu trữ ảnh của bạn , nhưng bạn cũng có thể bọc nó cho bất kỳ dịch vụ lưu trữ cloud nào khác hoặc thậm chí bạn có thể lưu trực tiếp vào hệ thống file local của riêng mình.
Các tin liên quan