Nodejs với Firebase Cloud Functions: GraphQL API
Bài đăng này đã không được cập nhật trong 4 năm
Giới thiệu
Ở phần trước, chúng ta đã xây dựng xong phần Authentication cho API hoạt động trên Firebase Cloud Functions, trong phần này, chúng ta sẽ tiếp tục xây dựng API theo GraphQL và Firebase Realtime DB
Stack sử dụng lần này:
- Authentication Module có được từ phần trước
- Firebase Cloud Functions: Serverless (FaaS) Platform
- Firebase Realtime Database
- Express: Nodejs framework (router, middleware)
- Apollo Server: GraphQL server tools
Tìm hiểu và cài đặt
GraphQL
Để bắt đầu, chúng ta sẽ tìm hiểu xem GraphQL là cái quần què gì trước đã phải không các bạn? Ở trên viblo đã có nhiều bài giải thích rất cụ thể và dễ hiểu rồi, nên mình sẽ giải thích một cách ngắn gọn trong phạm vi bài viết này thôi. Các bạn có thể đọc bài viết này để hiểu rõ hơn về GraphQL nhé Mình sẽ tóm tắt lại bằng cách trích dẫn những ý chính của bài viết Cùng tìm hiểu về GraphQL
Vậy GraphQL là gì?
GraphQL là một Graph Query Language được dành cho API. Nó được phát triển bởi Facebook. GraphQL từ khi ra đời đã gần như thay thế hoàn toàn REST bởi sự hiệu quả, mạnh mẽ và linh hoạt hơn rất nhiều.
GraphQL khác REST ở chỗ nào?
Vấn đề mà REST đang gặp phải là nó việc phản hồi dữ liệu của REST trả về quá nhiều hoặc là quá ít. Trong cả 2 trường hợp thì hiệu suất của ứng dụng đều bị ảnh hưởng khá nhiều. Giải pháp mà GraphQL đưa ra là cho phép khai báo dữ liệu nơi mà một client có thể xác định chính xác dữ liệu mà mình cần từ một API.
Hơn nữa, thay vì có nhiều endpoint như REST, GraphQL chỉ có duy nhất một endpoint. Hai phía Client và Server tương tác với nhau thông qua giao thức POST
GraphQL Schema Definition Language (SDL)
Để Client và Server có chung sự thống nhất và hiểu nhau, chúng ta cần phải định nghĩa Schema để xem Client có thể truy cập được những dữ liệu gì tương tự Server trả về cho client những dữ liệu như thế nào. Ví dụ Schema của User
type Clothes {
name: String!
style: String!
}
(Dấu ! nghĩa là field của schema sẽ required)
Fetching Data (Query) và Mutation
Client sẽ phải chỉ ra những field nào cần thiết để gửi cho Server
Ví dụ
{
clothes {
name
style
}
}
Mutations là cách client truyền dữ liệu lên cho Server để thực hiện thao tác: Create, Update, Delete
Ví dụ:
mutation {
addClothes(name:"adidas_ultraboost", style:"sport") {
name
}
}
Trên đây là một số khái niệm cần sử dụng trong bài viết này, bây giờ đi vào code các bạn nhé
Dependencies
Ngoài init firebase như các bài viết ở phần trước, lần này chúng ta cần cài thêm một số thư viện nữa
npm install express
npm install graphql
npm install apollo-server-express
Thêm các thư viện cần thiết vào file index.js
const functions = require("firebase-functions")
const express = require('express')
const firebaseAdmin = require('firebase-admin')
const productValidate = require('./validate/product.store')
const { ApolloServer, gql } = require("apollo-server-express");
/* Express */
const app = express()
// for cloud
firebaseAdmin.initializeApp(functions.config().firebase)
.
.
.
const api = functions.https.onRequest(app)
module.exports = {
api,
}
Định nghĩa Schema cho Apollo Server
const typeDefs = gql`
type Clothes {
name: String
style: String
}
type Query {
clothes: [Clothes]
}
type Mutation {
addClothes(name: String!, style: String!): Clothes
}
`;
Mình sẽ định nghĩa một schema là Clothes. Trong Query sẽ có clothes
trả về mảng Clothes, tương tự Mutation sẽ có method addClothes
Định nghĩa resolver cho Apollo Server
const resolvers = {
Query: {
clothes: () =>
firebaseAdmin.database()
.ref("clothes")
.once("value")
.then(snap => snap.val())
.then(val => Object.keys(val).map(key => val[key]))
},
Mutation: {
addClothes: async (root, args) => {
const clothes = {name: args.name, style: args.style}
await firebaseAdmin.database().ref("clothes").push(clothes)
return clothes
}
}
};
Tương tự typeDef, Resolver sẽ cho Apollo Server biết nơi lấy dữ liệu.
- clothes: Để fetch toàn bộ data từ Firebase về, vì GraphQL cần dữ liệu kiểu mảng trong khi Firebase trả về Object vì vậy chúng ta cần biến đổi một chút
- addClothes: Thêm một bản ghi vào Firebase
Sau cùng chúng ta sẽ tạo một 1 instance của ApolloServer, sau đó apply vào express app
const server = new ApolloServer({ typeDefs, resolvers });
server.applyMiddleware({ app, path: "/products", cors: true });
File index.js hoàn chỉnh lúc này:
const functions = require("firebase-functions")
const express = require('express')
const firebaseAdmin = require('firebase-admin')
const productValidate = require('./validate/product.store')
/* Express */
const app = express()
// for cloud
firebaseAdmin.initializeApp(functions.config().firebase)
const { ApolloServer, gql } = require("apollo-server-express");
const typeDefs = gql`
type Clothes {
name: String
style: String
}
type Query {
clothes: [Clothes]
}
type Mutation {
addClothes(name: String!, style: String!): Clothes
}
`;
const resolvers = {
Query: {
clothes: () =>
firebaseAdmin.database()
.ref("clothes")
.once("value")
.then(snap => snap.val())
.then(val => Object.keys(val).map(key => val[key]))
},
Mutation: {
addClothes: async (root, args) => {
const clothes = {name: args.name, style: args.style}
await firebaseAdmin.database().ref("clothes").push(clothes)
return clothes
}
}
};
const server = new ApolloServer({ typeDefs, resolvers });
server.applyMiddleware({ app, path: "/products", cors: true });
const api = functions.https.onRequest(app)
module.exports = {
api,
}
Chúng ta bắt đầu deploy bằng cách sử dụng command firebase deploy --only functions
Thử nghiệm
Sau khi deploy thành công chúng ta có thể sử dụng một số tool GraphQL client như GraphiQL, Postman Mình sẽ sử dụng Postman để test thử các bạn nhé Như mình đã trình bày ở trên thì Client và phía Server giao tiếp với nhau qua phương thức POST, vì vậy chúng sẽ setting cho Postman tương tự hình dưới đây:
Mutation
Mình sẽ thêm một bản ghi vào DB và get lại tên bằng GraphQL query như hình dưới đây
Query
Lấy toàn bộ thuộc tính name
của tất cả bản ghi
Lấy thêm cả thuộc tính style nữa
Kết
Trên đây là bài tìm hiểu của mình về các dịch vụ của Google Firebase với Nodejs và kết hợp thêm một số thành phần khác mà mình tìm hiểu được. Ngoài những dịch vụ mình sử dụng 3 bài vừa rồi còn rất nhiều dịch vụ khác để tìm hiểu và áp dụng, hẹn gặp các bạn ở các bài viết sau
All rights reserved