zefman / gridsome-source-instagram

A Gridsome source to load Instagram data
MIT License
9 stars 3 forks source link

Error: Cannot query field "display_url" on type "InstagramPhoto". #11

Open nigelwhite opened 4 years ago

nigelwhite commented 4 years ago

Thanks for a great plugin. It has been working fine until the last few days.

It still works fine in development, but Netlify won't build any more. I get this error message in the Netlify output of the Netlify build attempt. Full error message is below.

The instance I am using is to query for 1 photo only.

I know Instagram have been changing their API - making it a million times harder to use. So I wanted to compare notes with you. Is the plugin still working for you? Or do we all have to look for an alternative solution?

Best wishes Nigel


4:38:14 PM: Error: Cannot query field "display_url" on type "InstagramPhoto". 4:38:14 PM: GraphQL request:69:9 4:38:14 PM: 68 | id 4:38:14 PM: 69 | display_url 4:38:14 PM: | ^ 4:38:14 PM: 70 | taken_at_timestamp 4:38:14 PM: at throwError (/opt/build/repo/node_modules/gridsome/lib/app/build/executeQueries.js:21:13) 4:38:14 PM: at pMap (/opt/build/repo/node_modules/gridsome/lib/app/build/executeQueries.js:41:11) 4:38:14 PM: at Promise.resolve.then.element (/opt/build/repo/node_modules/p-map/index.js:47:21)

zefman commented 4 years ago

Hey @nigelwhite Thanks for this, yes I have noticed this problem too. Weirdly it seems to be intermittent sometimes and running the build again straight after fixes the problem.

So the way this plugin gets the image data is actually quite hacky, it loads a user's public profile and then parses the HTML and loads a JSON file that contains data about their most recent images. This is how the Gatsby Instagram plugin worked too so but I always knew it could break.

The error you are actually seeing is because for some reason the plugin didn't find any images so it thinks you are querying for a field that doesn't exist.

I will do some investigating and see if I can find the route cause.

nigelwhite commented 4 years ago

Brilliant thanks.

I noticed a strange thing, before my instance broke.... I was able to get a whole Gallery of recent images from my client's Instagram feed. These also built ok in Netlify. So the plugin exceeded its spec! When Netlify stopped building, my first hypothesis was that I was breaking it by asking it for more images than one. So I removed the Gallery feature and went back to querying for only one image. That didn't fix the Netlify build.

Good luck

zefman commented 4 years ago

Seems to be the same issue the gatsby plugin is having here: https://github.com/oorestisime/gatsby-source-instagram/issues/131

The problem seems to be that Instagram are throwing up a login page for certain IPs. That's why it works locally but not on Netlify.

zefman commented 4 years ago

I have tried a few things this evening to see if I could get around this.

It seems to only option might be for is to use their API with a token. I will need to look into what this involves.

asefcamel commented 4 years ago

Hello @zefman , first of all thank you for this awesome plugin. I'm getting the same issue also, did you find something to fix this?

MrMacStripe commented 4 years ago

FYI The guys over at the Gatsby plugin seem to have worked it out with another approach.

zefman commented 4 years ago

Hey, I will look at the Gatsby plugin to see how their solution works.

It might be that we have to add token-based authentication. Going forward that will probably be the only 100% reliable way to keep this working. It's a pain to setup though!

nigelwhite commented 4 years ago

Thanks for looking into it. I agree the token-based authentication looks a real pain. I dunno if I could ever lead my client though that.

MrMacStripe commented 4 years ago

I found another alternative that works in a simple html site test setup. but I cant get it to work in Gridsome - I think I am misunderstanding how to use external scripts ...

https://github.com/jsanahuja/InstagramFeed

Maybe, if this is another approach, this can be implemented into the plugin?

I agree that it's really not ideal to deal with the API tokens and clients.

ahoiadigital commented 4 years ago

I'm having the same issue.

@MrMacStripe Did you manage to get https://github.com/jsanahuja/InstagramFeed to work on your Gridsome site?

realgoatish commented 4 years ago

Hey @nigelwhite @MrMacStripe @ahoiadigital -

I ran into the same issue. Spent yesterday debugging and trying some possible fixes. Not sure if you're all using Netlify, but it definitely seems like Netlify's servers have been put on a blacklist for scraping IG's frontend the old-fashioned way. Maybe lots of people have been doing client-side scraping in their builds in a variety of projects.

The other lib that @MrMacStripe linked to gave me the idea of just doing it as an async client-side data call, at least for the time being until this plugin can be fixed. I too was unable to get that lib working on Gridsome, but then I realized it's much easier to just do exactly what this plugin does in a more compact way via the mounted hook, e.g.

<template>
  <div v-if="photos">
    <div v-for="(post, index) in photos.edges"
      :key="index">
       <a :href="`https://www.instagram.com/p/${post.node.shortcode}/`">
         <img
           :src="post.node.display_url"
           :alt="post.node.accessibility_caption"
         />
       </a>
     </div>
   </div>
</template>

................

<script>

import axios from 'axios'

export default {
    name: 'Footer',
    data () {
      return {
        photos: null
      }
    },
    async mounted () {
      try {
        const igProfileJson = await axios.get(
          `https://www.instagram.com/YOUR_INSTAGRAM_USERNAME/?__a=1`
        )
        this.photos = igProfileJson.data.graphql.user.edge_owner_to_timeline_media

        // might want to log this during your testing so you can see the object you need to traverse
        console.log(this.photos)
      } catch (error) {
        console.log(error)
      }
    }
  }
}

Note that I'm appending ?__a=1 to the Instagram URL because that gives you a straight JSON response of the user's profile data. It just makes for less wrangling you have to do with the response from the Axios call.

This response gives you their full profile data object, containing 12 images. If you only want 6 for example, you can just use standard array methods e.g.

const igProfileJson = await axios.get(
  `https://www.instagram.com/YOUR_INSTAGRAM_USERNAME/?__a=1`
)
igProfileJson.data.graphql.user.edge_owner_to_timeline_media.edges.splice(6, 6)
this.photos = igProfileJson.data.graphql.user.edge_owner_to_timeline_media

It's also worth noting that doing it this way gives you access to a fuller spectrum of profile data and not just the images array. This opens up the full range of data-usage possibilities from the other Lib that MrMacStripe linked to https://github.com/jsanahuja/InstagramFeed

Obviously you lose the benefit of having it in your GraphQL data layer. But you're still just querying JSON, so it can be handled in much the same way in your template code. If Netlify's servers are somehow blacklisted (at least as far as normal non-API-authed scrapes go), this seems like a good case for pushing the behavior to the client - again, at least until maybe this plugin can come up with a more durable fix.

nigelwhite commented 4 years ago

@mflamb thank you for documenting your solution so carefully. It looks neat.

I think I have everything right. All I did is copy your code and put my friend's name in YOUR_INSTAGRAM_USERNAME. But I am getting nothing logged to the console and [Vue warn]: Property or method "photos" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. That's strange as we can see we have data of photos: null.

I am in Gridsome. Axios is in my package.json and I am importing it in main.js.

snikidev commented 4 years ago

Just for the record, I'm building with Vercel, my build fails there as well, so it's not just Netlify. I get the same

Error: Cannot query field "display_url" on type "InstagramPhoto".
--
GraphQL request:27:9
       node {
         display_url
         ^
         edge_media_to_caption {
asefcamel commented 4 years ago

@mflamb thank you for documenting your solution so carefully. It looks neat.

I think I have everything right. All I did is copy your code and put my friend's name in YOUR_INSTAGRAM_USERNAME. But I am getting nothing logged to the console and [Vue warn]: Property or method "photos" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. That's strange as we can see we have data of photos: null.

I am in Gridsome. Axios is in my package.json and I am importing it in main.js.

just paste this in the data, photos: { edges: {} }

bytewise commented 3 years ago
<template>
  <div v-if="photos">
    <div v-for="(post, index) in photos.edges"
      :key="index">
       <a :href="`https://www.instagram.com/p/${post.node.shortcode}/`">
         <img
           :src="post.node.display_url"
           :alt="post.node.accessibility_caption"
         />
       </a>
     </div>
   </div>
</template>

................

<script>

import axios from 'axios'

export default {
    name: 'Footer',
    data () {
      return {
        photos: null
      }
    },
    async mounted () {
      try {
        const igProfileJson = await axios.get(
          `https://www.instagram.com/YOUR_INSTAGRAM_USERNAME/?__a=1`
        )
        this.photos = igProfileJson.data.graphql.user.edge_owner_to_timeline_media

        // might want to log this during your testing so you can see the object you need to traverse
        console.log(this.photos)
      } catch (error) {
        console.log(error)
      }
    }
  }
}

@letslamb i'm trying out your solution but i get the error message :

TypeError: igProfileJson.data.graphql is undefined

I've implemented the code as in your example. So i'm wondering, is this still working for you? Or am i doing something wrong?