I have the following REST endpoints:
/orders/{id}
returns {
orderId,
orderItem,
customerId
}
/customers/{id}
returns {
customerId,
firstName,
lastName
}
I am limited by these two endpoints, which are going to be wrapped in my graphql schema.
I would like the following Schema:
type Order {
orderId: ID!,
orderItem: String,
customer: Customer
}
type Customer{
customerId: ID!
firstName: String!
lastName: String!
}
type Query {
getOrder(id: String!): Order,
getCustomer(id: String!): Customer
}
I'm wondering if it is possible to have GraphQL resolve the Customer object in the Order type? I understand that you cannot pass the result of a query into the parameter of another.
I have considered the resolver of getOrder
be:
const getOrderResolver = axios.get(`/orders/${id}`)
.then((ordersRes) => {
let customerId;
if(ordersRes.data.customerId !== null) {
customerId = ordersRes.data.customerId
axios.get(`/customers/${customerId}`)
.then((customerRes) => ({
return {
orderId: ordersRes.data.orderId
orderItem: ordersRes.data.orderItem
customer: {
customerId: customerRes.data.customerId
firstName: customerRes.data.firstName
lastName: customerRes.data.lastName
}
}
})
} else {
return {
orderId: ordersRes.data.orderId
orderItem: ordersRes.data.orderItem
customer: null
}
}
})
})
getCustomer
resolver
const getCustomerResolver = axios.get(`/customers/${customerId}`)
.then((customerRes) => ({
return {
customerId: customerRes.data.customerId
firstName: customerRes.data.firstName
lastName: customerRes.data.lastName
}
})
It seems with my solution, there will be the additional cost of always fetching the Customer
type whether or not it is queried within the getOrder
query. Is it possible to rewrite my GraphQL schema in a way that GraphQL would be able to resolve the Customer
type only when queried?
The limitation of my given my ORDERS
REST API only returns the CustomerId
makes it difficult to resolve in getOrder
, since the Customer
API requires a customerId
Each field on each type is backed by a function called the resolver which is provided by the GraphQL server developer. When a field is executed, the corresponding resolver is called to produce the next value. If a field produces a scalar value like a string or number, then the execution completes.
In many cases, you don't want to return a number or a string from an API. You want to return an object that has its own complex behavior. GraphQL is a perfect fit for this.
Generally, when caching data, the intuition is to put information that's fetched remotely into a local store from where it can be retrieved later on. With GraphQL, the naive approach would be to simply put the results of GraphQL queries into the store and simply return them whenever the same query is sent.
There's two things to keep in mind here:
So your getOrder
resolver can only worry about returning an order object:
const resolvers = {
Query: {
getOrder: (parent, args, context, info) => {
const response = await axios.get(`/orders/${args.id}`)
return response.data
},
...
},
...
}
Note that if the REST endpoint returns a response in the same "shape" as your field's type, there's no need to map the response data to your actual fields here -- just return response.data
.
Now we can add a resolver for the customer
field on our Order type:
const resolvers = {
Query: { ... },
Order: {
customer: (parent, args, context, info) => {
if (!parent.customerId) {
return null
}
const response = await axios.get('/customers/${parent.customerId}')
return response.data
},
},
...
}
Our parent field here will be getOrder
(or any other field that has an Order
type). The value of parent
will be whatever value you returned in that field's resolver (or if you returned a Promise, whatever that Promise resolved to). So we can use parent.customerId
for the call to the /customers
endpoint and return the data from the response. And again, this resolver will only be called if the customer
field is actually requested.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With