GraphQL Query 一般來說都會回傳 Array,但若要回傳 sum()
或 count()
結果則為單一 Scalar 值,這在 Apollo GraphQL 與 Knex 該如何實現呢 ?
Version
macOS Catalina 10.15.1
WebStorm 2019.2.4
MySQL 8.0.18
Node 13.1.0
Knex 0.20.2
Apollo GraphQL 2.9.6
Wink-fp 0.1.30
Apollo GraphQL
src/index.js
import { ApolloServer, gql } from 'apollo-server'
import knex from 'knex'
let mySQL = knex({
client: 'mysql',
connection: {
host: 'localhost',
port: 3306,
user: 'root',
password: 'demo',
database: 'Bookshelf'
}
})
let typeDefs = gql`
type Query {
totalPrice: Int
}
`
let totalPrice = () => mySQL('books')
.sum({ sum: 'price' })
.then(x => x[0].sum)
let resolvers = {
Query: {
totalPrice
}
}
let apolloServer = new ApolloServer({ typeDefs, resolvers })
apolloServer.listen()
.then(({ url }) => `GraphQL Server ready at ${url}`)
.then(console.log)
15 行
let typeDefs = gql`
type Query {
totalPrice: Int
}
`
定義 totalPrice
query,僅回傳 Int
。
21 行
let totalPrice = () => mySQL('books')
.sum({ sum: 'price' })
.then(x => x[0].sum)
Knex 提供 sum()
,相當於 SQL 的 sum()
,若要提供 alias,則傳入 object,key 為 alias 名稱,value 為 sum()
所要計算的 column。
但 sum()
回傳為 array,因此必須在 then()
中取得第一筆資料後,再取其 sum
column。
嚴格來說,這算 Knex 問題,如 Sequelize 的
sum()
會直接回傳 scalar,但 Knex 回傳 array,只能在then()
中自己處理
Ramda
src/index.js
import { ApolloServer, gql } from 'apollo-server'
import knex from 'knex'
import { compose, head, pluck } from 'ramda'
let mySQL = knex({
client: 'mysql',
connection: {
host: 'localhost',
port: 3306,
user: 'root',
password: 'demo',
database: 'Bookshelf'
}
})
let typeDefs = gql`
type Query {
totalPrice: Int
}
`
let totalPrice = () => mySQL('books')
.sum({ sum: 'price' })
.then(compose(head, pluck('sum')))
let resolvers = {
Query: {
totalPrice
}
}
let apolloServer = new ApolloServer({ typeDefs, resolvers })
apolloServer.listen()
.then(({ url }) => `GraphQL Server ready at ${url}`)
.then(console.log)
21 行
let totalPrice = () => mySQL('books')
.sum({ sum: 'price' })
.then(compose(head, pluck('sum')))
then()
的 callback 亦可使用 Ramda 改寫,組合 pluck()
與 head()
。
Point-free
src/index.js
import { ApolloServer, gql } from 'apollo-server'
import knex from 'knex'
import { compose, head, pluck, useWith, identity } from 'ramda'
let mySQL = knex({
client: 'mysql',
connection: {
host: 'localhost',
port: 3306,
user: 'root',
password: 'demo',
database: 'Bookshelf'
}
})
let typeDefs = gql`
type Query {
totalPrice: Int
}
`
let tug = useWith(
compose(head, pluck), [identity, identity]
)
let totalPrice = () => mySQL('books')
.sum({ sum: 'price' })
.then(tug('sum'))
let resolvers = {
Query: {
totalPrice
}
}
let apolloServer = new ApolloServer({ typeDefs, resolvers })
apolloServer.listen()
.then(({ url }) => `GraphQL Server ready at ${url}`)
.then(console.log)
22 行
let tug = useWith(
compose(head, pluck), [identity, identity]
)
let totalPrice = () => mySQL('books')
.sum({ sum: 'price' })
.then(tug('sum'))
由於 Knex 特性,如 sum()
、count()
、avg()
… 等都會遇到此需求,可自行組合出 tug()
,將來只要傳入 alias 即可。
Wink-fp
server/src/index.js
import { ApolloServer, gql } from 'apollo-server'
import knex from 'knex'
import { tug } from 'wink-fp'
let mySQL = knex({
client: 'mysql',
connection: {
host: 'localhost',
port: 3306,
user: 'root',
password: 'demo',
database: 'Bookshelf'
}
})
let typeDefs = gql`
type Query {
totalPrice: Int
}
`
let totalPrice = () => mySQL('books')
.sum({ sum: 'price' })
.then(tug('sum'))
let resolvers = {
Query: {
totalPrice
}
}
let apolloServer = new ApolloServer({ typeDefs, resolvers })
apolloServer.listen()
.then(({ url }) => `GraphQL Server ready at ${url}`)
.then(console.log)
22 行
let totalPrice = () => mySQL('books')
.sum({ sum: 'price' })
.then(tug('sum'))
Wink-fp 已經提供 tug()
,可直接使用。
tug()
String -> [{k: v}] -> v
回傳 array 第一筆資料指定 key 的 value
GraphQL Playground
query {
totalPrice
}
totalPrice
query 回傳為單一值 600
。
Conclusion
- 由於 Knex 所有回傳都是 array,僅管
sum()
回傳為單一值,依然躲在 array 下,必須自行在then()
中處理 then()
的 callback 亦可使用 Ramda 或 Wink-fp 的 function 使其 point-free