Referencing GraphQL variable used in query in Gatsby template? - javascript

I am aggregating all posts that have a certain tag in it's tag list:
export const Search = graphql`
query SearchResults($tag: String = "") {
allMarkdownRemark(filter: { frontmatter: { tags: { in: [$tag] } } }) {
nodes {
frontmatter {
title
date
}
}
}
}
`
The query works great, but I want to also be able to dynamically show the tag that is being queried for. How can I pass this information through?
For example: Searching results for tag: Java which would be the value inside $tag in the graphql query.
I tried to pull from the URL, but it's rendered in node, so I don't have access to the window object and it felt a bit hacky anyway.

I got it. Passing in props.pageContext gives you access to the contextual information passed in via gatsby-node.

Related

Graphql query using a variable for one of the arguments

I'm using Gatsby and Graphql to build a website and I'm using the 'createPage' API from Gatsby to create all my pages, sourcing the data from the graphql CMS https://hygraph.com/.
I can pass over dynamic values to my template just fine using the 'gatsby-node.js' file and createPage's 'context' like so:
createPage({
path: `/path-to-my-page',
component: require.resolve(`./src/my-template.js`),
context: {
product_attribute: "metal",
product_attributeValue: "silver"
},
})
When I come to create the page query in my template, I have passed over one dynamic value to my template, the "product_attributeValue" and the following code works just fine:
export const pageQuery = graphql`
query ProductListingByAttributeQuery(
$product_attribute: String
$product_attributeValue: String
) {
gcms {
products(
orderBy: updatedAt_DESC
where: {
metal: $product_attributeValue
}
) {
slug
title
image
content
}
}
}
`
But what I really want to do is use the "product_attribute" variable in the "where" instead of the string 'metal' like so:
where: {
$product_attribute: $product_attributeValue
}
But I get syntax errors saying it doesn't like that I've used $product_attribute in the where: {...} object. The error says "Syntax Error: Expected Name, found $".
Is it possible to do what I want to do here? In that I want to use the variable 'product_attribute' instead of a string?
I don't think you can use "standard" GraphQL syntax in Gatsby's umbrella, since Gatsby ships its own GraphQL implementation where where filter is not a valid operator. If you test the query in the GraphiQL playground, it will break (output the same error message) since it's not a valid option.
You are passing the context values properly but using them in an invalid way, you may want to use filter, along with in, ni, nin operators. More references: https://www.gatsbyjs.com/docs/graphql-reference/
For example:
export const pageQuery = graphql`
query ProductListingByAttributeQuery(
$product_attribute: String
$product_attributeValue: String
) {
gcms(productType: {eq: $product_type}) {
products {
slug
title
image
content
}
}
}
`
Note: this is not intended to be a copy/paste. It needs to be tweaked and refined
Something like this should work but as I said, test it and built the query using the GraphiQL playground (at localhost:8000/___graphql), it's really helpful

How to get and set a ref for a newly cached related object in Apollo client InMemoryCache?

I have a set of related items like so:
book {
id
...
related_entity {
id
...
}
}
which apollo caches as two separate cache objects, where the related_entity field on book is a ref to an EntityNode object. This is fine, the related entity data is also used elsewhere outside of the context of a book so having it separate works, and everything seems well and good and updates as expected...except in the case where the related entity does not exist on the initial fetch (and thus the ref on the book object is null) and I create one later on.
I've tried adding an update function to the useMutation hook that creates the aforementioned related_entity per their documentation: https://www.apollographql.com/docs/react/caching/cache-interaction/#example-adding-an-item-to-a-list like this:
const [mutateEntity, _i] = useMutation(CREATE_OR_UPDATE_ENTITY,{
update(cache, {data}) {
cache.modify({
id: `BookNode:${bookId}`,
fields: {
relatedEntity(_i) {
const newEntityRef = cache.writeFragment({
fragment: gql`
fragment NewEntity on EntityNode {
id
...someOtherAttr
}`,
data: data.entityData
});
return newEntityRef;
}
}
})
}
});
but no matter what I seem to try, newEntityRef is always undefined, even though the new EntityNode is definitely in the cache and can be read just fine using the exact same fragment. I could give up and just force a refetch of the Book object, but the data is already right there.
Am I doing something wrong/is there a better way?
Barring that is there another way to get a ref for a cached object given you have its identifier?
It looks like this is actually an issue with apollo-cache-persist - I removed it and the code above functions as expected per the docs. It also looks like I could instead update to the new version under a different package name apollo3-cache-persist, but I ended up not needing cache persistence anyway.

Using Apollo's writeFragment to update nested list

I am working on a application in which a ship can be configured using rudders and other stuff. The database structure is sort of nested, and so far I have been keeping my GraphQL queries in correspondence with the database.
That means: I could fetch a ship using some query ship(projectId, shipId), but instead I am using a nested query:
query {
project(id:1) {
id
title
ship(id:1) {
id
name
rudders {
id
position
}
}
}
}
Such a structure of course leads to a lot of nested arrays. For example, if I have just added a new rudder, I would have to retrieve using cache.readQuery, which gives me the project object rather than the rudder list. To add the rudder to the cache, I'd get a long line with nested, destructured objects, making the code hard to read.
So I thought of using GraphQL fragments. On the internet, I see them being used a lot to prevent having to re-type several fields on extensive objects (which I personally find very useful as well!). However, there are not so many examples where a fragment is used for an array.
Fragments for arrays could save all the object destructuring when appending some data to an array that is nested in some cached query. Using Apollo's readFragment and writeFragment, I managed to get something working.
The fragment:
export const FRAGMENT_RUDDER_ARRAY = gql`
fragment rudderArray on ShipObject {
rudders {
id
position
}
}
`
Used in the main ship query:
query {
project(id: ...) {
id
title
ship(id: ...) {
id
name
...rudderArray
}
}
}
${RUDDER_FRAGMENT_ARRAY}
Using this, I can write a much clearer update() function to update Apollo's cache after a mutation. See below:
const [ createRudder ] = useMutation(CREATE_RUDDER_MUTATION, {
onError: (error) => { console.log(JSON.stringify(error))},
update(cache, {data: {createRudder}}) {
const {rudders} = cache.readFragment({
id: `ShipObject:${shipId}`,
fragment: FRAGMENT_RUDDER_ARRAY,
fragmentName: 'rudderArray'
});
cache.writeFragment({
id: `ShipObject:${shipId}`,
fragment: FRAGMENT_RUDDER_ARRAY,
fragmentName: 'rudderArray',
data: {rudders: rudders.concat(createRudder.rudder)}
});
}
});
Now what is my question? Well, since I almost never see fragments being used for this end, I find this working well, but I am wondering if there's any drawbacks to this.
On the other hand, I also decided to share this because I could not find any examples. So if this is a good idea, feel free to use the pattern!

Add graphQL fragment to schema, and have available for all queries

The following executes correctly in graphiQL
fragment BookGridFields on Book {
_id
title
}
{
allBooks {
...BookGridFields
}
}
My question is, it possible to specify the fragment right in my schema, right below where my Book type is defined, like so
type Book {
_id: String
title: String
pages: Int
weight: Float
authors: [Author]
}
fragment BookGridFields on Book {
_id
title
}
So that I could just run queries like this
{
allBooks {
...BookGridFields
}
}
without needing to define the fragment as part of my query.
Currently the above errors with
Unknown fragment \"BookGridFields\"
Per the graphql docs, I see that fragments are a part of the query api and not valid syntax for setting up a schema. This leads me to conclude that it is not currently possible to specify a fragment in a schema.
https://graphql.org/learn/queries/#fragments

How to save a Mongo document's own _id in a nested field?

This Meteor server code tries to copy the newly created property _id into a sub document but failed to do so.
How can it be done?
edit:
The code uses matb33:collection-hooks.
MyCollection.after.insert(function(userId, doc) {
if (doc.element === 'myString') {
doc.values[0]._id = doc._id;
}
});
Mutating the doc in the after hooks of matb33:collection-hooks will not cause additional queries to be run. You will need to explicitly update the document if you wish to do so.
However, in this particular case, if you really need the duplicate _id in the document, you could generate an _id and specify it when inserting the document.
You can probably use MyCollection._makeNewID() method, as this API has not changed for a few years and it is what the Mongo package uses internally.
const _id = MyCollection._makeNewID();
const doc = {
_id,
values: [
{
_id,
...
}, {
...
}
]
};
MyCollection.insert(doc);

Categories