I am trying to implement a graphQL API, it went well with queries but it's going not that well with mutations:
Here is my basic mutation using apollo-client and graphql-tag:
import gql from 'graphql-tag'
const addNewPlace = (place) => {
return client.mutate({
mutation: gql`
mutation {
addNewPlace(
input: {
$title: String!
}
) {
place { title }
}
}
`,
variables: {
title: place.title
}
})
}
Here I was trying to use variables. When changing the mutation to look like that one below, it is going smoothly however, it's not the right way to do id.
const addNewPlace = (place) => {
return client.mutate({
mutation: gql`
mutation {
addNewPlace(
input: {
title: "${place.title}"
}
) {
place { title }
}
}
`
})
}
Any idea where I made my mistake?
When using variables, there's three steps you need to take:
Add the variables to the request being sent. In Apollo, it's done by specifying a variables property inside the object you pass to mutate. You wrote:
variables: { title: place.title }
This is fine. We are sending some variable, called title, with whatever value, along with the request to our server. At this point, GraphQL doesn't even know about the variables.
Declare your variables inside your operation. You don't have to name your operation, but it's good practice to do so:
mutation AddNewPlace($title: String) {
Here, we are telling GraphQL we've included a variable called title. You could call it anything (foo for example), as long as it matched what you passed to the variables prop in #1. This step is important because A) GraphQL needs to know about the variable and B) it needs to know what type of variable you are passing in.
Finally, include the variable in your mutation, like this:
addNewPlace(input: { title: $title }) {
Be careful not to mix up your variable definition in step #2 with your input definition in step #3. Also, I'm assuming your typeDefs include some kind of input type like AddNewPlaceInput. Rather than passing in just title, you can pass in an object like this:
variables: { input: { title: place.title } }
Then your mutation looks like this:
mutation AddNewPlace($input: AddNewPlaceInput) {
addNewPlace(input: $input) {
# fields returned by the mutation
I would highly recommend enabling a GraphiQL endpoint so you can easily test your queries and mutations before implementing them on the client side.
Lastly, you may want to check and make sure the fields you are asking for in the mutation match your type definition. I'm just guessing here, but if your mutation resolves to a Place type, you wouldn't need to put place { title }, just title, unless your Place type actually has a place field.
Related
INTRODUCTION
In my app I have multiple contexts for managing and storing state.
In one of my scenarios, I have noticed that, as I have a UsersContext (which stores the users data), it is not necessary to store the user data of each post creator in my PostsContext, it could be better (for RAM) to just store its user id, and not to repeat the user data on it.
I have thought to use "parsers" inside my reducers logic before storing data, in order to remove the unnecessary fields of each object.
Is this a good idea / is a common pattern?
PROBLEM
The main problem I notice with this approach is: how to get the data correctly from both contexts? I mean, imagine the following component:
function Card({ content: { userData, image, description, location, date }) {
...
}
If I want to get the data from the contexts, and not from props, I have implemented two custom hooks (do not care about memoizations right now) which consumes a specific context:
/* HOOKS FOR GETTING SPECIFIC DATA FROM CONTEXT */
function useUpdatedPostData(postData) {
const posts = usePosts(); <-- consume PostsContext
return {
...postData,
...posts.getPost(postData.id) <--- Merging with data from context
}
}
function useUpdatedUserData(userData) {
const users = useUsers(); <-- consume UsersContext
return {
...userData,
...users.getUser(userData.id) <--- Merging with data from context
}
}
/* CONSUMER COMPONENT */
function Card({ content }) {
// The main problem:
const {
image,
description,
location,
date,
} = useUpdatedPostData(content);
const { username, avatar } = useUpdatedUserData(content.userData);
...
}
something which makes the code really difficult to read. Any tips?
How can I override a value in the Apollo cache?
I have a graphql query to fetch the user. This returns a user with a default currency. This currency can then be override from a select dropdown.
The query fetches paymentCurrencies from an API, then uses a client side resolver to set the first item in the array of paymentCurrencies to be the users currency
query me {
me {
username
currency #client
paymentCurrencies
}
}
When somebody selects a currency from the dropdown menu, I want to over the users currency with whatever they have selected.
I have something like this so far:
const onChange = e => {
const { value } = e.target
client.writeData({ user: { currency: value, username, __typename: "User" } })
}
I get the following error: Error writing result to store for query:
{"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GeneratedClientQuery"},"selectionSet":null}]}
Cannot read property 'selections' of null
Is using writeData is the correct method or should I be using writeQuery or something?
As described in the other answer you probably want a simple query and mutation setup. The client directive is used to extend your schema to hold client-only additional data. From your explanation, it sounds like you explicitly want this data to be syncronised with the server.
const ME_QUERY = gql`
query me {
me {
username
currency
paymentCurrencies
}
}
`;
const CURRENCY_MUTATION = gql`
mutation setCurrency($currency: String) {
setCurrency(currency: $currency) {
me {
username
currency
}
}
}
`;
function MyComponent() {
const { data } = useQuery(ME_QUERY);
const [setCurrency] = useMutation(CURRENCY_MUTATION);
const onChange = e => setCurrency({
variables: { currency: e.currentTarget.value },
});
return (
<>
<h2>{data && data.me.currency}</h2>
<select onChange={onChange}>
{/* your dropdown logic */}
</select>
</>
);
}
You get the idea. Apollo will now automatically update your cache. Make sure your mutation allows to query the updated user object.
For the automatic update to work your user needs to be recognised by the cache. You can do that by either adding an id field and selecting it in both the query and the mutation or implementing a dataIdFromObject function in Apollo Client 2.x that includes the username for __typename === 'User' or by using a type policy in Apollo Client 3.x. Find the docs here.
writeData should be used for changing fields at the root, for example:
{
yourState #client
}
In this case, you should use writeQuery. Additionally, this logic should really be extracted into a (local) mutation you can then call inside your component. When using writeQuery, the basic idea is to grab the existing data, make a copy and then transform it as needed:
const { me } = client.readQuery({ query: ME_QUERY })
const data = {
me: {
...me,
currency: value,
}
}
client.writeQuery({ query: ME_QUERY, data })
You can also use writeFragment to directly modify a single instance of an object in the cache. However, you need the object's cache key for this. Because the cache key is derived from the __typename and an id field, you should make sure the query includes an id field first. This is good practice regardless to ensure your cache can be updated easily (see here for more details). Then you can do something like this:
client.writeFragment({
id: 'User:42',
fragment: gql`
fragment UserCurrency on User {
currency #client
}
`,
data: {
currency: value,
},
})
It depends.
For persistent change (sync to server) you should just change user setting using mutation.
For session aware change - don't use user settings - copy this (from logged user properties) to separate value in global app state (redux/mobx or as in this case apollo local state).
In both cases rare problem can be with updating many components using changed data.
Redux/mobx solves this automatically.
Apollo HOCs won't rerender.
Hooks - updates partially (only the one with useQuery and useMutation pair), others will be updated only when rerendered.
Components build with <Query/> creates internally an observable then they will be updated, too.
Mutation have update and refetchQueries parameters for.
There is also a package for more complex use cases.
For my current project, I am working on a shop page. I am sending the data of each order made to my Laravel backend in order for it to be processed. Part of this data is an array with all the ordered products in it. I use JSON.stringify() on this array to avoid errors in the Laravel backend.
My issue is that when I stringify the array in a computed property, this also changed the data in my Vuex store for some reason. This in turn obviously causes quite a bit of error. Is this normal behavior, or am I doing something wrong? How can I avoid this from happening? Thanks in advance!
You can see my component's code in the snippet below.
import { mapGetters } from "vuex";
export default {
computed: {
...mapGetters({
order: "getOrder",
countries: "getCountries"
}),
orderData() {
let myOrder = this.order;
if (myOrder.products) {
myOrder.products = JSON.stringify(myOrder.products);
}
return myOrder;
},
country() {
return this.countries.find(
country => country.iso_3166_2 === this.order.country
).name;
}
},
methods: {
placeOrder() {
console.log(this.orderData);
}
}
};
</script>
You are assigning to myOrder the reference of this.order, subsequently all your modifications inside it will affect this.order (so you are mutating).
In this case, since you just want to modify products, you can shallow copy this.order just like this:
let myOrder = { ...this.order };
Then, all the properties at the first level will have different pointers, so you can change them without fear of mutations.
In the Apollo Server documentation, it describes the behaviour of the default resolver, which is quite handy.
I also gathered from somewhere else (another SO question if I recall), that you can override the default resolver function with your own, by passing a fieldResolver function into the options for the apollo-server instance:
const server = new ApolloServer({ typeDefs, resolvers,
fieldResolver: function (source, args, context, info) {
console.log("Field resolver triggered!")
return null;
}
});
What I would like to do is augment the default behaviour, rather than overriding it. Specifically, I am integrating with a REST API that returns fields in snake_case, whereas my schema is attempting to follow the advised convention of using camelCase for field names. I would like to wrap this field name conversion around the default resolver behaviour, rather than having to re-write it.
Alternatively, if somebody can point me to the source location for the default resolver implementation, I'd be happy enough to take that and adapt it either!
The default resolver is available through the graphql module:
const { defaultFieldResolver } = require('graphql')
However, converting a field from snake case to camel case can be done without calling the default resolver:
someField: (parent) => parent.some_field
If you want to create a reusable resolver function, you can do something like:
const camelCaseResolver = (parent, args, ctx, info) => {
return parent[_.snakeCase(info.fieldName)]
}
Or better yet, extract the logic into a schema directive:
class SnakeCaseDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field) {
field.resolve = async function (parent, args, ctx, info) {
return parent[_.snakeCase(info.fieldName)]
}
}
}
so I am working on relay for react application with server side rendering. I'm trying to use relay to get data from graphql endpoint.
I was kinda following this, using fetchQuery to get data by making request from compiled graphql by relay-compiler.
e.g.
import { graphql, fetchQuery } from 'react-relay'
const query = graphql`
query SomeQuery($id: ID) {
author(id: $id) {
name
...SomeFragment_authorData
}
}
`
const variables = { "id" : "1f" };
fetchQuery(env, query, variables)
.then((jsonData) => { console.log(jsonData); })
when it finishes running, it gives me some sort of this object:
{
author: {
__fragment: { ... }
...
}
}
Which I assume will be used by children components wrapped with createFragmentContainer() to get the real data.
Since I'm not using createFragmentContainer(), I'm not sure how to get the data properly, is there any way to transform above response into the real data?
any help would be appreciated!
Note:
At the moment this is what I do to get the data: env._network.fetch(query(), variables). It is working, but it doesn't seem right that I need to dig into private variable, in order to get the fetch that I provided to Network object.
You need to add a relay directive to your fragment spread:
...SomeFragment_authorData #relay(mask: false)
you can use relay environment and relay-runtime utils.
import {
createOperationDescriptor,
getRequest,
} from 'relay-runtime';
const request = getRequest(query);
const operation = createOperationDescriptor(request, variables);
environment.execute({ operation }).toPromise();