<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Posts tagged #images</title><description>All posts tagged with images on queen.raae.codes</description><link>https://queen.raae.codes/tag/images/</link><item><title>📝 ✨ ~ Little helper to check on images &gt; probe-image-size</title><link>https://queen.raae.codes/2023-03-28-probe-image-size/</link><guid isPermaLink="true">https://queen.raae.codes/2023-03-28-probe-image-size/</guid><description>I needed to weed out accounts with broken images for the search demo I&apos;ve been working on. It doesn&apos;t look so good with random broken images when demoing. The…</description><pubDate>Tue, 28 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I needed to weed out accounts with broken images for the search demo I&apos;ve been working on. It doesn&apos;t look so good with random broken images when demoing.&lt;/p&gt;
&lt;p&gt;The reason for the broken images is that it&apos;s been a while since I imported these accounts. So folks have changed their profile image, and some have left the platform. Instead of reimporting the roughly 5000 accounts I follow, I removed the ones with a broken image.&lt;/p&gt;
&lt;p&gt;To do so, I used probe-image-size because it throws an error when the image probed is unavailable. Meaning my code would be super tidy without me writing a helper function. I&apos;ve used it before to probe for an image&apos;s size, the actual use case, when doing Gatsby Image CDN stuff, as it needs to know the size of the original image, and sometimes all you have is an URL.&lt;/p&gt;
&lt;p&gt;But back to the task at hand:&lt;/p&gt;
&lt;p&gt;1️⃣ Get all account records&lt;br&gt;
2️⃣ Probe each account profile image&lt;br&gt;
3️⃣ Delete the account record if the image is unavailable&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// 1️⃣ Get account records
let page = await xata.db.accounts
  .select([&amp;quot;id&amp;quot;, &amp;quot;username&amp;quot;, &amp;quot;meta.profile_image_url&amp;quot;])
  .getPaginated({
    pagination: {
      size: 20,
    },
  });

const deleteBrokenImage = async (record) =&amp;gt; {
  try {
    // 2️⃣ Probe each account profile image
    await probe(record.meta.profile_image_url);
    console.log(&amp;quot;Do not delete &amp;quot;, record.username);
  } catch (error) {
    // 3️⃣ Delete the account record if the image is unavailable
    await xata.db.accounts.delete(record.id);
    console.log(&amp;quot;Deleted &amp;quot;, record.username);
  }
};

await Promise.all(page.records.map(deleteBrokenImage));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&apos;ve skipped pagination, so the example code will only go through the first 20 records. For the following 20 records and so forth, you&apos;d need to do &lt;code&gt;await page.nextPage()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For the complete code, look at the &lt;a href=&quot;https://github.com/queen-raae/xata-search-three-ways-demo/blob/main/src/api/clean.js&quot;&gt;demo code on Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;All the best,&lt;br&gt;
Queen Raae&lt;/p&gt;
&lt;br/&gt;&lt;ul&gt;&lt;li&gt;Queen Raae and family worked on several projects for Xata.&lt;/li&gt;&lt;/ul&gt;</content:encoded></item><item><title>🧱 🖼 ~ Skip handling remote images yourself; there is a plugin for that!</title><link>https://queen.raae.codes/2022-06-10-remote-image/</link><guid isPermaLink="true">https://queen.raae.codes/2022-06-10-remote-image/</guid><description>On yesterday&apos;s unauthorized and rum-fueled treasure hunt in the sharky waters around the Gatsby islands, we played around with remote images again. You get a…</description><pubDate>Fri, 10 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;On yesterday&apos;s &lt;a href=&quot;https://youtu.be/QB1Y8dWZpgM&quot;&gt;unauthorized and rum-fueled treasure hunt&lt;/a&gt; in the sharky waters around the Gatsby islands, we played around with remote images again.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/QB1Y8dWZpgM&quot;&gt;&lt;img src=&quot;./youtube-screengrab.jpg&quot; alt=&quot;YouTube Screengrab&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You get a genuinely atomic build by sourcing remote images into your Gatsby Data Layer as File nodes. Pointing to a URL you do not control on the world wide web is not a super robust solution.&lt;/p&gt;
&lt;h2&gt;The What?&lt;/h2&gt;
&lt;p&gt;Make use of &lt;a href=&quot;https://github.com/graysonhicks/gatsby-plugin-remote-images&quot;&gt;gatsby-plugin-remote-images&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/graysonhicks&quot;&gt;Grayson Hicks&lt;/a&gt;, Staff Software Engineer at Gatsby in our web-scraping ScrapingBee demo.&lt;/p&gt;
&lt;p&gt;Or, more broadly, utilizing the Gatsby Plugin Ecosystem.&lt;/p&gt;
&lt;h2&gt;The Why?&lt;/h2&gt;
&lt;p&gt;So we do not have to go through the motions of creating remote file nodes ourselves!&lt;/p&gt;
&lt;p&gt;Or, more broadly, let&apos;s not do it all in one plugin. Many plugins, for instance, create remote filed nodes, then realize the need to add an option to disable such functionality. Maybe the images should rather be uploaded to Cloudinary... Offloading the functionality to gatsby-plugin-remote-images such an option is no longer needed.&lt;/p&gt;
&lt;h2&gt;The How&lt;/h2&gt;
&lt;p&gt;Install the plugin:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;yarn add gatsby-plugin-remote-images
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and add it to your Gatsby configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;module.exports = {
  plugins: [
    {
      resolve: `gatsby-plugin-remote-images`,
      options: {
        nodeType: &amp;quot;CrowdcastWebinar&amp;quot;,
        imagePath: &amp;quot;coverSrc&amp;quot;,
        name: &amp;quot;cover&amp;quot;,
      },
    },
  ],
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create a file node for the remote image found at &lt;code&gt;coverSrc&lt;/code&gt; on a &lt;code&gt;CrowdcastWebinar&lt;/code&gt; node and make it available on &lt;code&gt;cover&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Take a look at yesterday&apos;s &lt;a href=&quot;https://github.com/queen-raae/gatsby-demo-web-scraping/pull/5/files&quot;&gt;Pull Request&lt;/a&gt; for the complete picture.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;There are some nifty options if the structure of your nodes is not as straightforward as for &lt;code&gt;CrowdcastWebinar&lt;/code&gt;. Check out the &lt;a href=&quot;https://github.com/graysonhicks/gatsby-plugin-remote-images&quot;&gt;gatsby-plugin-remote-images docs&lt;/a&gt; for those and more.&lt;/p&gt;
&lt;p&gt;If you would like to use the file with the Gatsby Image component, make sure you follow the &lt;a href=&quot;https://www.gatsbyjs.com/plugins/gatsby-plugin-image&quot;&gt;gatsby-plugin-image instructions&lt;/a&gt; as well.&lt;/p&gt;
&lt;p&gt; &lt;br&gt;
All the best,&lt;br&gt;
Queen Raae&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PS:&lt;/strong&gt; Next week, Grayson joins us to work on &lt;a href=&quot;https://github.com/graysonhicks/gatsby-plugin-remote-images/issues/87&quot;&gt;GatsbyImageCDN&lt;/a&gt; support 🎉&lt;/p&gt;
</content:encoded></item><item><title>☁️ 📥 ~ Your plugin should support both Gatsby Image CDN and downloading images as local file nodes</title><link>https://queen.raae.codes/2022-05-20-image-modes/</link><guid isPermaLink="true">https://queen.raae.codes/2022-05-20-image-modes/</guid><description>We added back support for downloading the YouTube thumbnails as local file nodes on yesterday&apos;s apparently deceiful and spam-packed treasure hunt in the sharky…</description><pubDate>Fri, 20 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We added back support for downloading the YouTube thumbnails as local file nodes on yesterday&apos;s apparently &lt;a href=&quot;https://youtu.be/MjcYzjYIFuI&quot;&gt;deceiful and spam-packed treasure hunt&lt;/a&gt; in the sharky waters around the Gatsby islands&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/raae/status/1527593806513709057?s=20&amp;amp;t=8HbGrJ66ytV2IfM0qvwhDw&quot;&gt;&lt;img src=&quot;./youtube-spam.png&quot; alt=&quot;Hi Queen Raae, Our team has reviewed your content, and we think you may need to make changes to make sure it doesn&apos;t violate our spam, deceptive practices and scams policy. In the meantime, we&apos;ve made the following content private&quot; title=&quot;YouTube Email&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We removed it in exchanging for ImageCDN on a &lt;a href=&quot;/2022-03-25-image-cdn-plugin/&quot;&gt;treasure hunt with Ward of Gatsby&lt;/a&gt;, but as we are working on a plugin, I later realized it should support both 🤦‍♀️&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/IDW2IfaHGIs&quot;&gt;&lt;img src=&quot;./../../03/25-image-cdn-plugin/youtube-screengrab.jpg&quot; alt=&quot;Screengrab of Ward stream&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;The What?&lt;/h2&gt;
&lt;p&gt;Let the user of our plugin (gatsby-source-youtube-oembed) decide if they want to utilize Gatsby ImageCDN or download remote images as local file nodes.&lt;/p&gt;
&lt;h2&gt;The Why?&lt;/h2&gt;
&lt;p&gt;We don&apos;t know where our users are deploying their sites as plugin authors. As far as I know, only Netlify and Gatsby Cloud support Gatsby ImageCDN at the moment, and it&apos;s pretty clear they&apos;ll charge extra for it in the future. Also, some Gatsby users deploy to their own servers or other static hosts.&lt;/p&gt;
&lt;h2&gt;The How&lt;/h2&gt;
&lt;p&gt;We added a plugin option letting the user configure their choice:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// gatsby-node.js
exports.pluginOptionsSchema = ({ Joi }) =&amp;gt; {
  return Joi.object({
    thumbnailMode: Joi.string().valid(&amp;quot;none&amp;quot;, &amp;quot;cdn&amp;quot;, &amp;quot;download&amp;quot;).default(&amp;quot;cdn&amp;quot;),
  });
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we made sure to check the plugin option in onCreateNode to decide what type of thumbnail node to make:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// gatsby-node.js
const { createRemoteFileNode } = require(&amp;quot;gatsby-source-filesystem&amp;quot;);

exports.onCreateNode = async (gatsbyUtils, pluginOptions) =&amp;gt; {
  const { node, actions, createNodeId, getCache } = gatsbyUtils;
  const { createNode, createNodeField } = gatsbyUtils.actions;

  if (node.internal.type === YOUTUBE_TYPE) {
    if (pluginOptions.thumbnailMode === &amp;quot;cdn&amp;quot;) {
      createNode({
        id: createNodeId(`Thumbnail &amp;gt;&amp;gt;&amp;gt; ${node.youTubeId}`),
        parent: node.id,
        youTubeId: node.youTubeId,
        url: node.oEmbed.thumbnail_url,
        mimeType: &amp;quot;image/jpeg&amp;quot;,
        filename: node.youTubeId + &amp;quot;.jpg&amp;quot;,
        height: node.oEmbed.thumbnail_height,
        width: node.oEmbed.thumbnail_width,
        internal: {
          type: &amp;quot;YouTubeThumbnail&amp;quot;,
          contentDigest: node.internal.contentDigest,
        },
      });
    } else if (pluginOptions.thumbnailMode === &amp;quot;download&amp;quot;) {
      const imageFile = await createRemoteFileNode({
        url: node.oEmbed.thumbnail_url,
        parentNodeId: node.id,
        getCache,
        createNode,
        createNodeId,
      });

      createNodeField({
        node: node,
        name: &amp;quot;thumbnailFileId&amp;quot;,
        value: imageFile.id,
      });
    }
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and we recheck it to customize the schema according to the chosen mode:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// gatsby-node.js
const {
  addRemoteFilePolyfillInterface,
} = require(&amp;quot;gatsby-plugin-utils/polyfill-remote-file&amp;quot;);

exports.createSchemaCustomization = (gatsbyUtils, pluginOptions) =&amp;gt; {
  const { actions, schema } = gatsbyUtils;

  if (pluginOptions.thumbnailMode === &amp;quot;cdn&amp;quot;) {
    const YouTubeType = `
      type YouTube implements Node {
        thumbnail: YouTubeThumbnail @link(from: &amp;quot;youTubeId&amp;quot; by: &amp;quot;youTubeId&amp;quot;)
      }
    `;

    const YouTubeThumbnailType = addRemoteFilePolyfillInterface(
      schema.buildObjectType({
        name: `YouTubeThumbnail`,
        fields: {
          youTubeId: &amp;quot;String!&amp;quot;,
        },
        interfaces: [`Node`, `RemoteFile`],
      }),
      {
        schema,
        actions,
      }
    );

    actions.createTypes([YouTubeType, YouTubeThumbnailType]);
  } else if (pluginOptions.thumbnailMode === &amp;quot;download&amp;quot;) {
    const YouTubeType = `
      type YouTube implements Node {
        thumbnail: File @link(from: &amp;quot;fields.thumbnailFileId&amp;quot; by: &amp;quot;id&amp;quot;)
      }
    `;
    actions.createTypes(YouTubeType);
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We should also update our docs, of course, with examples of how to get hold of the thumbnail image data in both modes, but that is for a later work session.&lt;/p&gt;
&lt;p&gt;To see a full diff check out the &lt;a href=&quot;https://github.com/queen-raae/gatsby-source-youtube-oembed/pull/11&quot;&gt;Pull Request on Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt; &lt;br&gt;
All the best,&lt;br&gt;
Queen Raae&lt;/p&gt;
</content:encoded></item><item><title>🖼 ☁️ ~ Gatsby ImageCDN on Netlify</title><link>https://queen.raae.codes/2022-04-05-netlify-cdn/</link><guid isPermaLink="true">https://queen.raae.codes/2022-04-05-netlify-cdn/</guid><description>The POW! web app is hosted on Netlify, and so is the soon to be released version of the marketing site. However, after upgrading to the latest version of…</description><pubDate>Tue, 05 Apr 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The POW! web app is hosted on Netlify, and so is the &lt;a href=&quot;https://pow-site.netlify.com/&quot;&gt;soon to be released&lt;/a&gt; version of the marketing site.&lt;/p&gt;
&lt;p&gt;However, after upgrading to the latest version of &lt;a href=&quot;https://github.com/queen-raae/gatsby-source-youtube-oembed&quot;&gt;@raae/gatsby-source-youtube-oembed&lt;/a&gt;, with support for Gatsby ImageCDN, my sourced YouTube Thumbnails went blank...&lt;/p&gt;
&lt;p&gt;Turns out support for Gatsby ImageCDN is not on by default; you need to set the environment variable &lt;code&gt;GATSBY_CLOUD_IMAGE_CDN&lt;/code&gt; set to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./env-var.png&quot; alt=&quot;GATSBY_CLOUD_IMAGE_CDN=true&quot;&gt;&lt;/p&gt;
&lt;p&gt;After doing so and triggering a &amp;quot;Clear cache and deploy site,&amp;quot; the YouTube Thumbnails came back, and all was good!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/nS36D2zUkvA&quot;&gt;&lt;img src=&quot;./video-screengrab.png&quot; alt=&quot;YouTube Thumbnail via Gatsby ImageCDN on Netlify&quot;&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt; &lt;br&gt;
All the best,&lt;br&gt;
Queen Raae&lt;/p&gt;
&lt;p&gt; &lt;br&gt;
&lt;strong&gt;PS:&lt;/strong&gt; Interested in learning how to add Gatsby ImageCDN support to your plugin? Check out &lt;a href=&quot;/2022-03-25-image-cdn-plugin/&quot;&gt;How to add support for Gatsby ImageCDN in your source plugin 🖼 ☁️&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>🖼 ☁️ ~ How to add support for Gatsby ImageCDN in your source plugin</title><link>https://queen.raae.codes/2022-03-25-image-cdn-plugin/</link><guid isPermaLink="true">https://queen.raae.codes/2022-03-25-image-cdn-plugin/</guid><description>On yesterday&apos;s unauthorized and rum-fueled treasure hunt in the sharky waters around the Gatsby islands, the great and powerful Ward Peeters helped us add…</description><pubDate>Fri, 25 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;On yesterday&apos;s unauthorized and rum-fueled &lt;a href=&quot;https://youtu.be/IDW2IfaHGIs&quot;&gt;treasure hunt&lt;/a&gt; in the sharky waters around the Gatsby islands, the great and powerful Ward Peeters helped us add Gatsby ImageCDN support to the YouTube oEmbed plugin.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/IDW2IfaHGIs&quot;&gt;&lt;img src=&quot;./youtube-screengrab.jpg&quot; alt=&quot;Screengrab of stream&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It was his third time on the show; if you missed the other two, have fun catching up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/TX5XPuHhz9o&quot;&gt;File System Route API to create a page per YouTube video&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/UsSJ_QNp6uo&quot;&gt;Deferred Static Generation (DSG) for older videos&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Why add Gatsby ImageCDN support?&lt;/h2&gt;
&lt;p&gt;Ward &lt;a href=&quot;https://youtu.be/IDW2IfaHGIs?t=4345&quot;&gt;said it best&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Faster builds (both local and in the cloud)&lt;/li&gt;
&lt;li&gt;Better performance in the browser compared to a CDN on a different domain&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Adding support for Gatsby ImageCDN still lets you deploy to other services like Netlify. Build time there will be faster, in the same way, it&apos;s faster locally, by only downloading the images in use by the site. The &amp;quot;old way&amp;quot; &lt;code&gt;createRemoteFileNode&lt;/code&gt; would eagerly download all images.&lt;/p&gt;
&lt;h2&gt;How to add Gatsby ImageCDN support?&lt;/h2&gt;
&lt;p&gt;We&apos;ll need to replace the use of &lt;code&gt;createRemoteFileNode&lt;/code&gt; with creating our own &lt;code&gt;RemoteFile&lt;/code&gt; nodes.&lt;/p&gt;
&lt;p&gt;Many CMSs already have their own asset node type they can extend with &lt;code&gt;RemoteFile&lt;/code&gt;, but for our plugin, we first need to create a new node type that extends &lt;code&gt;RemoteFile&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// gatsby-node.js
const {
  addRemoteFilePolyfillInterface,
} = require(&amp;quot;gatsby-plugin-utils/polyfill-remote-file&amp;quot;);

exports.createSchemaCustomization = ({ actions, schema }) =&amp;gt; {
  actions.createTypes([
    addRemoteFilePolyfillInterface(
      schema.buildObjectType({
        name: `YouTubeThumbnail`,
        fields: {
          youTubeId: &amp;quot;String!&amp;quot;,
        },
        interfaces: [`Node`, `RemoteFile`],
      }),
      {
        schema,
        actions,
      }
    ),
  ]);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;addRemoteFilePolyfillInterface&lt;/code&gt; makes sure the plugin works even if the Gatsby site is on a lower version than 4. To make sure it works on older versions even in develop, you want to add &lt;code&gt;polyfillImageServiceDevRoutes&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// gatsby-node.js
const {
  polyfillImageServiceDevRoutes,
} = require(&amp;quot;gatsby-plugin-utils/polyfill-remote-file&amp;quot;);

exports.onCreateDevServer = ({ app }) =&amp;gt; {
  polyfillImageServiceDevRoutes(app);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we are ready to replace our usage of &lt;code&gt;createRemoteFileNode&lt;/code&gt; in &lt;code&gt;onCreateNode&lt;/code&gt; 🎉&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;exports.onCreateNode = async (gatsbyUtils) =&amp;gt; {
  const { node, actions, reporter, createNodeId } = gatsbyUtils;
  const { createNodeField, createNode } = actions;

  if (node.internal.type === `YouTube`) {
    const youTubeThumbnailNodeId = createNodeId(
      `you-tube-thumbnail-${node.youTubeId}`
    );

    createNode({
      id: youTubeThumbnailNodeId,
      parent: node.id,
      youTubeId: node.youTubeId,
      url: node.oEmbed.thumbnail_url,
      mimeType: &amp;quot;image/jpeg&amp;quot;,
      filename: node.youTubeId + &amp;quot;.jpg&amp;quot;,
      height: node.oEmbed.thumbnail_height,
      width: node.oEmbed.thumbnail_width,
      internal: {
        type: `YouTubeThumbnail`,
        contentDigest: node.internal.contentDigest,
      },
    });

    createNodeField({
      node,
      name: `thumbnailFileId`,
      value: youTubeThumbnailNodeId,
    });

    reporter.info(
      `Created YouTubeThumbnail Node for ${node.youTubeId} thumbnail`
    );
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last step for us is to update the YouTube Node&apos;s schema customization to use the new node type &lt;code&gt;YouTubeThumbnail&lt;/code&gt; instead of &lt;code&gt;File&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// gatsby-node.js
actions.createTypes([
  `
    type YouTube implements Node {
      thumbnail: YouTubeThumbnail @link(from: &amp;quot;youTubeId&amp;quot; by: &amp;quot;youTubeId&amp;quot;)
    }
  `,
]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check out the &lt;a href=&quot;https://github.com/queen-raae/gatsby-source-youtube-oembed/pull/7/files&quot;&gt;Pull Request on GitHub&lt;/a&gt; for the complete diff!&lt;/p&gt;
&lt;p&gt; &lt;br&gt;
All the best,&lt;br&gt;
Queen Raae&lt;/p&gt;
</content:encoded></item><item><title>🖼 📥 ~ Source remote images the right way with Gatsby v4</title><link>https://queen.raae.codes/2022-02-17-source-remote-images/</link><guid isPermaLink="true">https://queen.raae.codes/2022-02-17-source-remote-images/</guid><description>Gatsby v4 has been out for a while, and with v4 came a much stricter approach to creating and modifying nodes. However there seems to be quite a bit of…</description><pubDate>Thu, 17 Feb 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Gatsby v4 has been out for a while, and with v4 came a much stricter approach to creating and modifying nodes.&lt;/p&gt;
&lt;p&gt;However there seems to be quite a bit of outdated documentation, as I experienced when looking into &lt;code&gt;createResolvers&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;An examples shows &lt;a href=&quot;https://www.gatsbyjs.com/docs/reference/graphql-data-layer/schema-customization/#feeding-remote-images-into-gatsby-image&quot;&gt;creating remote file nodes&lt;/a&gt; as part of a custom resolver. It worked for me locally but failed on Gatsby Cloud, and I found out it is no longer supported.&lt;/p&gt;
&lt;p&gt;There are also examples using the &lt;a href=&quot;https://www.gatsbyjs.com/docs/reference/release-notes/migrating-from-v3-to-v4/#___node-convention&quot;&gt;&lt;code&gt;___NODE Convention&lt;/code&gt; for linking nodes&lt;/a&gt; that will be deprecated in v5.&lt;/p&gt;
&lt;p&gt;In Gatsby Version 4:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gatsbyjs.com/docs/reference/release-notes/migrating-source-plugin-from-v3-to-v4/#2-data-mutations-need-to-happen-during-sourcenodes-or-oncreatenode&quot;&gt;All node creation and mutation need to happen during &lt;code&gt;sourceNodes&lt;/code&gt; or &lt;code&gt;onCreateNode&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gatsbyjs.com/docs/reference/release-notes/migrating-from-v3-to-v4/#dont-mutate-nodes-outside-of-expected-apis&quot;&gt;Mutation is only allowed using the action &lt;code&gt;createNodeField&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Combined, we get the following recommended way of sourcing remote images:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;exports.onCreateNode = async (gatsbyUtils) =&amp;gt; {
  const { node, actions, reporter, createNodeId, getCache } = gatsbyUtils;
  const { createNodeField, createNode } = actions;

  if (node.internal.type === `YouTube`) {
    const imageFile = await createRemoteFileNode({
      // The url of the remote file
      url: node.oEmbed.thumbnail_url,
      parentNodeId: node.id,
      getCache,
      createNode,
      createNodeId,
    });

    createNodeField({
      node,
      name: `thumbnailFileId`,
      value: imageFile.id,
    });

    reporter.info(`Created YouTube File Node for ${node.youTubeId} thumbnail`);
  }
};

exports.createSchemaCustomization = ({ actions }) =&amp;gt; {
  actions.createTypes(`
    type YouTube implements Node {
      thumbnail: File @link(from: &amp;quot;fields.thumbnailFileId&amp;quot;)
    }
  `);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example lifted from &lt;a href=&quot;https://github.com/queen-raae/gatsby-source-youtube-oembed/pull/3&quot;&gt;@raae/gatsby-source-youtube-oembed&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt; &lt;br&gt;
All the best,&lt;br&gt;
Queen Raae&lt;/p&gt;
</content:encoded></item></channel></rss>