๐Ÿ”ฎ Sanity Create is here. Writing is reinvented. Try now, no developer setup

Querying multiple types in Sanity with vanilla JS and using webhooks for Netlify rebuilds

46 replies
Last updated: Jan 20, 2022
How do I query multiple _types? I've tried using the OR operator, and making an array in the filter area, but neither seem to work. I can't find documentation for this specific issue either. Thanks!
Jan 11, 2022, 3:58 PM
Hey ๐Ÿ‘‹
*[_type in ["typeA", "typeB"]]
should do it I think. The result might be confusing though if you have fields with the same name in both types.
Jan 11, 2022, 4:15 PM
would it be better to do multiple queries and then join the arrays? I want to be able to have mixed-type results fed into a script loop in one payload
Jan 11, 2022, 4:19 PM
maybe this helps if I understand you correctly ?
*[_type in ["typeA", "typeB"]]{
  _type == "typeA" => {
    // field selection for typeA
  },
  _type == "typeB" => {
    // field selection for typeB
  }
}

Jan 11, 2022, 4:34 PM
Yes, I'll try that - thanks!
Jan 11, 2022, 4:39 PM
user Q
The filter part is working great, thanks! However, I'm getting an error with the projection part of it:
"expected '}' following object body"

Here is what I have:

let encodedFilter = encodeURIComponent('[_type in ["cabinet", "representative", "supremeCourt", "senator"]]');
                    let encodedProjection = encodeURIComponent(`_type== "cabinet" => {"_type"}, _type== "representative" => {"_type"}, _type== "supremeCourt" => {"_type"}, _type== "senator" => {"_type"}`)
                        fetch(`<https://6dyl9k59.api.sanity.io/v2021-01-01/data/query/production?query=*${encodedFilter}{${encodedProjection}}>`)
Jan 11, 2022, 6:12 PM
I'm only grabbing the _type for proof of syntax. I'll fill it in with the varied projections afterwards
Jan 11, 2022, 6:13 PM
user M
any ideas on how to have multiple projections?
Jan 11, 2022, 6:14 PM
Ah, I think I see the issue! It looks like you're technically on
v1
of the API since you have
2021-01-01
specified. If you configure it to use a date later than
2021-03-25
do you still get the error?
Jan 11, 2022, 6:25 PM
wow, I meant to put
2022-01-01
! I've updated it to that, and I still get the error
Jan 11, 2022, 6:27 PM
What frontend framework are you using?
Jan 11, 2022, 6:28 PM
none, vanilla JS.
Jan 11, 2022, 6:30 PM
Do you want to see my git repo? It's a very simple SPA
Jan 11, 2022, 6:32 PM
Sure! That would be helpful.
Jan 11, 2022, 6:32 PM
Which fields are you trying to get from your different types in your projections?
Jan 11, 2022, 8:02 PM
I have 4 kinds of documents &amp; a 5th option of combining them in one fetch, they each have proper projections in the file (I updated it since I pasted code into the thread)
Jan 11, 2022, 8:04 PM
What I mean is, what are the fields within those documents you're hoping to show? To clarify, your query should look something like this before being encoded:
const query = `[_type in ["cabinet", "representative", "supremeCourt", "senator"]]{
  _type == 'representative' => {
    //fields you're hoping to get from representative document
  }
 //other types
}`
What you currently have would be this:

const query = `[_type in ["cabinet", "representative", "supremeCourt", "senator"]]{
  _type == 'representative' => {
    "_type"
  }
  //etc
}`

Jan 11, 2022, 8:15 PM
let encodedProjection = encodeURIComponent(`
                        _type == "cabinet" => {"_type, cabinetName, 'confirmationCommittee': confirmationCommittee[]->.senateCommitteeName, position, succession, cardLink, cardLink2, cardBackImage"}, 
                        _type == "representative" => {"_type, representativeName, state->, party->, district, assumedOffice, 'leadershipPositions': leadershipPositions[]->.houseLeadershipName, 'committeeAssignments': committeeAssignments[]->.CommitteeName, cardLink, cardBackImage"}, 
                        _type == "supremeCourt" => {"_type, circuitAssignments[], justiceName, memberSince, nominatedBy, position, segalCoverScore, cardLink, cardBackImage"}, 
                        _type == "senator" => {"_type, senatorName, party->, state->, leadershipPositions[], 'committeeAssignments': committeeAssignments[]->.senateCommitteeName, reelectionYear, cardLink, assumedOffice, cardBackImage"}`
                    )
Jan 11, 2022, 8:17 PM
this should be what is in the file...I'm looking at github on my browser and that is what I copied this from
Jan 11, 2022, 8:18 PM
it is the same projections as the individual ones above the "all" fetch
Jan 11, 2022, 8:18 PM
Got it. I was basing that off of what you'd shared in this thread. Can you try changing
=>
to
=>
?
Jan 11, 2022, 8:21 PM
sure! one sec
Jan 11, 2022, 8:25 PM
sure! one sec
Jan 11, 2022, 8:25 PM
This is now what I get. BTW thank you for this help!
{description: "Attribute or a string key expected", type: "queryParseError"}
Jan 11, 2022, 8:27 PM
Happy to help! Let me play around with this a bit more....
Jan 11, 2022, 8:30 PM
absolutely, I'm not planning on continuing work on this until tomorrow.
Jan 11, 2022, 8:32 PM
OK! There were some extra apostrophes in your projection. I can get the query to work if you change it to the following:
let encodedFilter = encodeURIComponent('[_type in ["cabinet", "representative", "supremeCourt", "senator"]]');
let encodedProjection = encodeURIComponent(`
                        _type == "cabinet" => {_type, cabinetName, 'confirmationCommittee': confirmationCommittee[]->.senateCommitteeName, position, succession, cardLink, cardLink2, cardBackImage}, 
                        _type == "representative" => {_type, representativeName, state->, party->, district, assumedOffice, 'leadershipPositions': leadershipPositions[]->.houseLeadershipName, 'committeeAssignments': committeeAssignments[]->.CommitteeName, cardLink, cardBackImage}, 
                        _type == "supremeCourt" => {_type, circuitAssignments[], justiceName, memberSince, nominatedBy, position, segalCoverScore, cardLink, cardBackImage}, 
                        _type == "senator" => {_type, senatorName, party->, state->, leadershipPositions[], 'committeeAssignments': committeeAssignments[]->.senateCommitteeName, reelectionYear, cardLink, assumedOffice, cardBackImage}
                    `)
                        fetch(`<https://6dyl9k59.api.sanity.io/v2022-01-01/data/query/production?query=*${encodedFilter}{${encodedProjection}}>`)
                        .then(res => res.json())
                        .then(justice => justice.result.forEach((item)=>{
                                cardGrid.append(createCard(item));
                            }),
                            main.append(cardGrid))
Jan 11, 2022, 10:24 PM
One thing that may be helpful to look into is installing the JS Client on your project. This will allow you to pass a config to your Sanity Client and avoid having to use
encodeURI
. I'll try to work up an example for you.
Jan 11, 2022, 10:27 PM
One thing that may be helpful to look into is installing the JS Client on your project. This will allow you to pass a config to your Sanity Client and avoid having to use
encodeURI
. I'll try to work up an example for you.
Jan 11, 2022, 10:27 PM
wonderful, thank you! I was trying to use the minimum number of tools/npm packages on this site, really keep it as vanilla as possible...but maybe I went a step too far?
Jan 11, 2022, 10:29 PM
wonderful, thank you! I was trying to use the minimum number of tools/npm packages on this site, really keep it as vanilla as possible...but maybe I went a step too far?
Jan 11, 2022, 10:29 PM
user M
you mentioned using the JS client - which (as far as I'm aware) is a node.js package. How does this work if I'm hosting on Netlify which only (again, as far as I understand) has node.js operations during its build cycle? Sorry if this is an obvious question, but I've read a TON of your documentation and haven't found the answer.
Jan 18, 2022, 5:09 PM
Nope, you're correct! I realize now that you won't be able to use a node package in a vanilla JS app. Apologies!
Jan 18, 2022, 5:46 PM
So, my understanding is that Sanity is very jamstack oriented. I'm moving from traditional, hard-coded HTML/CSS, and just learned the basics of JS. Therefore, I'm trying to minimize dependencies &amp; frameworks, but I want to avoid anything where my clients will need to rebuild a netlify deployment after adding/altering Sanity data. How would one do that if using the JS client? If you can't, where is this described/discussed?
Jan 18, 2022, 5:49 PM
This would have more to do with Netlify than Sanity. If your site is statically generated, you'll need to rebuild it each time you publish new content. Content is available in your Content Lake as soon as it's added. You'll need to set up something like webhooks to automatically trigger a site rebuild when content is published if you're trying to keep your clients from having to do it.
Jan 18, 2022, 5:56 PM
Ahhh, I believe I need to look into webhooks! I didn't know that was the missing piece. Thanks again.

A soft suggestion: have an article/video/doc showing how to do a vanilla JS, frameworkless project that auto-triggers a rebuild upon some sort of "rebuild" button or something. It seems like that's a core use-case that isn't covered in your current docs, no?
Jan 18, 2022, 6:00 PM
That's a good suggestion! I actually intended to write a guide to using Sanity with vanilla JS/CSS/HTML waaaay back when I first started using it since I didn't know any frameworks at the time.
Jan 18, 2022, 6:04 PM
Yaay!! I just finished a draft of some slick logic that uses the query params to generate a fetch command. If you'd like, I can send it to you once I have it tightened up a bit...
It allows for unlimited number of types, filters, and sorts without coding any specifics other than the projections, which are done in one big array. I'm not done, but if I understand this all correctly, it would be a really powerful piece of logic
Jan 18, 2022, 6:07 PM
That way sort and filter commands just change the query params, and you can always share the specific set of filter/sort settings
Jan 18, 2022, 6:08 PM
It seems a shame to have to use a framework with Sanity, when one of its upsides is how lightweight it is. Amazing for small projects!
Jan 18, 2022, 6:09 PM
Yeah, I'd love to see it! If you'd like, you can also publish it as a snippet or guide on the Exchange . That way you the get credit for you work!
Jan 18, 2022, 6:12 PM
Sure, I'd just want to have confirmation from you or someone else that I didn't make a rookie mistake. I just learned JS a few months ago!
Jan 18, 2022, 6:14 PM
actually, here is what I have so far:
            //Set up query vars
            const projectId = '6dyl9k59';
            const apiDate = 'v2022-01-01';
            const dataSet = 'production';
            const encodedProjection = encodeURIComponent(`_type == 'secretaries' => {_type, name, 'confirmationCommittee': confirmationCommittee[]->.senateCommitteeName, position, succession, cardLink, cardLink2, cardBackImage}, _type == 'representatives' => {_type, name, state->, party->, district, assumedOffice, 'leadershipPositions': leadershipPositions[]->.houseLeadershipName, 'committeeAssignments': committeeAssignments[]->.CommitteeName, cardLink, cardBackImage}, _type == 'justices' => {_type, circuitAssignments[], name, memberSince, nominatedBy, position, segalCoverScore, cardLink, cardBackImage}, _type == 'senators' => {_type, name, party->, state->, leadershipPositions[], 'committeeAssignments': committeeAssignments[]->.senateCommitteeName, reelectionYear, cardLink, assumedOffice, cardBackImage}`);
            const type = params.type.replace(' ', ', ');
            const decodedFilter = decodeURI(params.filter);
            const preFilter = decodedFilter.replace('+', ' && ');
            const filter = preFilter.replace('_', ' == ');
            let leftFilter;
            let rightFilter;
            let encodedFilter;
              
            
            //check for filter parameters
            function createFilter(){
                if(params.filter) {
                    leftFilter = `[_type in [${type}] && ${filter}]`;
                } else {
                    leftFilter = `[_type in [${type}]]`;
                }
                return leftFilter;
            };
            
            createFilter();


            //check for sorting parameters, then create encoded filter
            function createSort() {
                if(params.sort) {
                    const sort = params.sort.replace('_', ' ')
                     rightFilter = `order(${sort})`;
                     encodedFilter = encodeURIComponent(`${leftFilter} | ${rightFilter}`);
                     console.log(`${leftFilter} | ${rightFilter}`)
                } else {
                     encodedFilter = encodeURIComponent(leftFilter);
                     console.log(`${leftFilter}`)
                }
                return encodedFilter;
            }

            createSort();
         


            //fetch data & call the createCard function
            fetch(`https://${projectId}.<http://api.sanity.io/${apiDate}/data/query/${dataSet}?query=*${encodedFilter}{${encodedProjection}}`)|api.sanity.io/${apiDate}/data/query/${dataSet}?query=*${encodedFilter}{${encodedProjection}}`)>
            .then(res => res.json())
            .then(cardData => cardData.result.forEach((item)=>{
                    cardGrid.append(createCard(item));
                }),
                main.append(cardGrid));
I need to make it so that multiple sorts can be chained, but I believe that's the only part left for this. I am just starting to write the sort/filter button code, but I've never done it before
๐Ÿ˜ฌ
Jan 18, 2022, 6:16 PM
Awesome! I'll play around with this and give you any feedback I have!
Jan 18, 2022, 6:20 PM
wonderful! Have a great day
Jan 18, 2022, 6:22 PM
You as well!
Jan 18, 2022, 6:23 PM
BTW I was looking into the webhook solution for netlify, and I found a plugin on the Sanity Exchange that already takes care of this! I've not tried it yet...

https://www.sanity.io/plugins/sanity-plugin-dashboard-widget-netlify
Jan 20, 2022, 6:36 PM

Sanityโ€“ build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?