該賞金過期4天。此問題的答案有資格獲得 100聲望獎勵。 Stan Loona想引起更多人對這個問題的關注。
我是 GraphQL 的新手,但我的理解是,如果我有這樣的User型別:
type User {
email: String
userId: String
firstName: String
lastName: String
}
和這樣的查詢:
type Query {
currentUser: User
}
像這樣實作決議器:
Query: {
currentUser: {
email: async (_: any, __: any, ctx: any, ___: any) => {
const provider = getAuthenticationProvider()
const userId = await provider.getUserId(ctx.req.headers.authorization)
const { email } = await UserService.getUserByFirebaseId(userId)
return email;
},
firstName: async (_: any, __: any, ctx: any, ___: any) => {
const provider = getAuthenticationProvider()
const userId = await provider.getUserId(ctx.req.headers.authorization)
const { firstName } = await UserService.getUserByFirebaseId(userId)
return firstName;
}
}
// same for other fields
},
很明顯有問題,因為我正在復制代碼,并且每個請求的欄位都查詢資料庫一次。有沒有辦法防止代碼重復和/或快取資料庫呼叫?
我需要填充 MongoDB 欄位的情況如何?謝謝!
uj5u.com熱心網友回復:
我會像這樣重寫你的決議器:
// import ...;
type User {
email: String
userId: String
firstName: String
lastName: String
}
type Query {
currentUser: User
}
const resolvers = {
Query: {
currentUser: async (parent, args, ctx, info) {
const provider = getAuthenticationProvider()
const userId = await provider.getUserId(ctx.req.headers.authorization)
return UserService.getUserByFirebaseId(userId);
}
}
};
這應該可以作業,但是......有了更多資訊,代碼也會更好(見我的評論)。
您可以在此處閱讀有關決議器的更多資訊:https : //www.apollographql.com/docs/apollo-server/data/resolvers/
uj5u.com熱心網友回復:
一些東西:
1a. 父決議器
作為一般規則,任何給定的決議器都應該回傳足夠的資訊來決議子節點的值,或者回傳足夠的資訊讓子節點自己解決它。以Ruslan Zhomir的答案為例。這將執行一次資料庫查找并為子項回傳這些值。好處是您不必復制任何代碼。缺點是資料庫必須獲取所有欄位并回傳這些欄位。那里有一個權衡取舍的平衡行為。大多數情況下,最好為每個物件使用一個決議器。如果您開始不得不處理資料或從其他位置提取欄位,那么我通常會開始像您一樣添加欄位級決議器。
1b. 場級決議器
您展示的 ONLY 欄位級決議器(沒有父物件決議器)的模式可能很尷尬。舉你的例子。如果用戶未登錄,“預期”會發生什么?
我希望得到以下結果:
{
currentUser: null
}
但是,如果您僅構建欄位級決議器(沒有實際在資料庫中查找的父決議器),您的回應將如下所示:
{
currentUser: {
email: null,
userId: null,
firstName: null,
lastName: null
}
}
另一方面,如果您實際上在資料庫中查找足夠遠以驗證用戶是否存在,為什么不回傳該物件?這是我推薦單親決議器的另一個原因。同樣,一旦您開始處理其他資料源或其他屬性的昂貴操作,您就可以開始添加子決議器:
const resolvers = {
Query: {
currentUser: async (parent, args, ctx, info) {
const provider = getAuthenticationProvider()
const userId = await provider.getUserId(ctx.req.headers.authorization)
return UserService.getUserByFirebaseId(userId);
}
},
User: {
avatarUrl(parent) {
const hash = md5(parent.email)
return `https://www.gravatar.com/avatar/${hash}`;
},
friends(parent, args, ctx) {
return UsersService.findFriends(parent.id);
}
}
}
2a. 資料加載器
If you really like the child property resolvers pattern (there's a director at PayPal who EATS IT UP, the DataLoader pattern (and library) uses memoization with cache keys to do a lookup to the database once and cache that result. Each resolver asks the service to fetch the user ("here's the firebaseId"), and that service caches the response. The resolver code you have would be the same, but the functionality on the backend that does the database lookup would only happen once, while the others returned from cache. The pattern you're showing here is one that I've seen people do, and while it's often a premature optimization, it may be what you want. If so, DataLoaders are an answer. If you don't want to go the route of duplicated code or "magic resolver objects", you're probably better off using just a single resolver.
Also, make sure you're not falling victim to the "object of nulls" problem described above. If the parent doesn't exist, the parent should be null, not just all of the children.
2b. DataLoaders and Context
Be careful with DataLoaders. That cache might live too long or return values for people who didn't have access. It is generally, therefore, recommended that the dataLoaders get created for every request. If you look at DataSources (Apollo), it follows this same pattern. The class is instantiated on each request and the object is added to the Context (ctx in your example). There are other dataLoaders that you would create outside of the scope of the request, but you have to solve Least-Used and Expiration and all of that if you go that route. That's also an optimization you need much further down the road.
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/336685.html
