-
-
Notifications
You must be signed in to change notification settings - Fork 515
Add cloud code resolver example #771
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
1075d4e
a25a6e6
354c81b
9d9a3ca
8d6a2d8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -70,7 +70,7 @@ mutation createAGameScore { | |||||||||||
} | ||||||||||||
``` | ||||||||||||
|
||||||||||||
**Note:** The `id` is a [Relay Global Object Identification](https://facebook.github.io/relay/graphql/objectidentification.htm), it's **not** a Parse `objectId`. Most of the time the `Relay Node Id` is a `Base64` of the `ParseClass` and the `objectId`. | ||||||||||||
**Note:** The `id` is a [Relay Global Object Identification](https://facebook.github.io/relay/graphql/objectidentification.htm); it's **not** a Parse `objectId`. Most of the time the `Relay Node Id` is a `Base64` of the `ParseClass` and the `objectId`. | ||||||||||||
|
||||||||||||
## Update | ||||||||||||
|
||||||||||||
|
@@ -238,3 +238,95 @@ mutation aNestedMutation { | |||||||||||
} | ||||||||||||
} | ||||||||||||
``` | ||||||||||||
|
||||||||||||
## Cloud Code Resolvers | ||||||||||||
|
||||||||||||
If you need more control over how your mutations modify data than what Parse's auto-generated mutations can provide, Cloud Code functions can be used as custom resolvers. | ||||||||||||
|
||||||||||||
For example, if you have classes named `Item` and `CartItem` in the schema, you can create an `addToCart` custom mutation that tests whether a specific item is already in the user's cart. If found, the cart item's quantity is incremented by one. If not, a new `CartItem` object is created. | ||||||||||||
|
||||||||||||
The ability to branch your resolver logic in this way enables you to replicate functionality found in Parse's auto-generated `createCartItem` and `updateCartItem` mutations and combine those behaviors into a single custom resolver. | ||||||||||||
|
||||||||||||
```sh | ||||||||||||
# schema.graphql | ||||||||||||
extend type Mutation { | ||||||||||||
addToCart(id: ID!): CartItem! @resolve | ||||||||||||
} | ||||||||||||
``` | ||||||||||||
|
||||||||||||
```js | ||||||||||||
// main.js | ||||||||||||
Parse.Cloud.define("addToCart", async (req) => { | ||||||||||||
const { user, params: { id } } = req; | ||||||||||||
|
||||||||||||
// Decode the incoming base64 id to an objectId for Cloud Code use. | ||||||||||||
const decoded = Buffer.from(id, "base64").toString(); | ||||||||||||
const itemObjectId = decoded.split(":")[1]; | ||||||||||||
|
||||||||||||
// Query the user's current cart. | ||||||||||||
const itemQuery = new Parse.Query("Item"); | ||||||||||||
const item = await itemQuery.get(itemObjectId); | ||||||||||||
const cartItemQuery = new Parse.Query("CartItem"); | ||||||||||||
cartItemQuery.equalTo("item", item); | ||||||||||||
cartItemQuery.equalTo("user", user); | ||||||||||||
const [existingCartItem] = await cartItemQuery.find(); | ||||||||||||
let savedCartItem; | ||||||||||||
|
||||||||||||
if (existingCartItem) { | ||||||||||||
// The item is found in the user's cart; increment its quantity. | ||||||||||||
const quantity = await existingCartItem.get("quantity"); | ||||||||||||
existingCartItem.set("quantity", quantity + 1); | ||||||||||||
savedCartItem = await existingCartItem.save(); | ||||||||||||
} else { | ||||||||||||
// The item has not yet been added; create a new cartItem object. | ||||||||||||
const CartItem = Parse.Object.extend("CartItem"); | ||||||||||||
const cartItem = new CartItem(); | ||||||||||||
savedCartItem = await cartItem.save({ quantity: 1, item, user }); | ||||||||||||
} | ||||||||||||
|
||||||||||||
// Encode the Parse objectId to a Relay Global Object Identification | ||||||||||||
// (a special use-case base64 id string) for Parse GraphQL use. | ||||||||||||
const concatObjectId = `CartItem:${savedCartItem.id}`; | ||||||||||||
const cartItemId = Buffer.from(concatObjectId).toString("base64"); | ||||||||||||
|
||||||||||||
// Convert to a JSON object to handle adding the base64 id property. | ||||||||||||
const cartItemJson = savedCartItem.toJSON(); | ||||||||||||
Object.assign(cartItemJson, { id: cartItemId }); | ||||||||||||
return cartItemJson; | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What you think about ES6 shortcuts
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very nice. Thank you. |
||||||||||||
}); | ||||||||||||
``` | ||||||||||||
|
||||||||||||
```js | ||||||||||||
// Header | ||||||||||||
{ | ||||||||||||
"X-Parse-Application-Id": "APPLICATION_ID", | ||||||||||||
"X-Parse-Session-Token": "r:b0dfad1eeafa4425d9508f1c0a15c3fa" | ||||||||||||
} | ||||||||||||
``` | ||||||||||||
|
||||||||||||
```graphql | ||||||||||||
mutation addItemToCart { | ||||||||||||
addToCart(id: "SXRlbTpEbDVjZmFWclRI") { | ||||||||||||
id | ||||||||||||
quantity | ||||||||||||
} | ||||||||||||
} | ||||||||||||
``` | ||||||||||||
|
||||||||||||
The code above should resolve to something similar to this: | ||||||||||||
|
||||||||||||
```js | ||||||||||||
// Response | ||||||||||||
{ | ||||||||||||
"data": { | ||||||||||||
"addToCart": { | ||||||||||||
"id": "Q2FydEl0ZW06akVVTHlGZnVpQw==", | ||||||||||||
"quantity": 1 | ||||||||||||
} | ||||||||||||
} | ||||||||||||
} | ||||||||||||
``` | ||||||||||||
|
||||||||||||
**Note:** The `id` is a [Relay Global Object Identification](https://facebook.github.io/relay/graphql/objectidentification.htm); it's **not** a Parse `objectId`. Most of the time the `Relay Node Id` is a `Base64` of the `ParseClass` and the `objectId`. Cloud code does not recognize this type of `id`, so it must be converted to a Parse `objectId` for use in Cloud Code function queries. | ||||||||||||
|
||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To avoid some code like this
We need to inform developers that they can use May be add some reference about the graphql lib Link: https://github.com/graphql/graphql-relay-js#object-identification There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should I use my With that lib available (thanks for mentioning it!) I personally would use it in my own projects, but there's something to be said for using code examples that are a bit more vanilla JS in the documentation too. Your preference? |
||||||||||||
Decoding and encoding these Relay Global Object Identifications in Cloud Code is needed in order to seamlessly interface with your client-side GraphQL mutations. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to show how to pass the schema into the ParseGraphQL Server, here developers can think that a simple
schema.graphql
at the root of the folder can do the jobUh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. I could use the example found in this PR. It's the code I used in my own project.
I'm hesitant to include the custom schema setup code in this section, as it seems to only be a related topic, and not quite this same one. Would it be okay for me to include the custom schema setup in the
Getting Started
section instead? If so, it could be inserted in one of a couple places:Using Express.Js
sub-section (mixed in with the other ParseServer setup code), orAdding Custom Schema
(I think I would prefer this method)