Complete polyfill system for image CDN functionality, providing backward compatibility for remote file processing in older Gatsby versions with full GraphQL schema integration and HTTP route handling.
Adds RemoteFile interface to schema types, enabling remote file processing in Gatsby versions without native image CDN support.
/**
* Adds RemoteFile interface polyfill to schema types
* @param type - Schema type to enhance with RemoteFile interface
* @param config - Configuration object with schema, actions, and store
* @returns Enhanced type with RemoteFile interface
*/
function addRemoteFilePolyfillInterface<T>(
type: T,
config: {
schema: SchemaBuilder;
actions: Actions;
store: Store;
}
): T;Usage Examples:
import { addRemoteFilePolyfillInterface } from "gatsby-plugin-utils/polyfill-remote-file";
exports.createSchemaCustomization = ({ actions, schema, store }) => {
actions.createTypes([
addRemoteFilePolyfillInterface(
schema.buildObjectType({
name: 'MyAsset',
fields: {
title: 'String!',
description: 'String',
url: 'String!',
mimeType: 'String!'
},
interfaces: ['Node', 'RemoteFile']
}),
{ schema, actions, store }
)
]);
};
// For existing types
const MyImageType = schema.buildObjectType({
name: 'MyImage',
fields: {
src: 'String!',
alt: 'String',
width: 'Int',
height: 'Int'
}
});
const enhancedType = addRemoteFilePolyfillInterface(MyImageType, {
schema,
actions,
store
});Adds image service routes to development server for local image processing when native image CDN is not available.
/**
* Adds image service routes for development server
* @param app - Express application instance
* @param store - Gatsby store instance (optional)
*/
function polyfillImageServiceDevRoutes(
app: Application,
store?: Store
): void;Usage Examples:
import { polyfillImageServiceDevRoutes } from "gatsby-plugin-utils/polyfill-remote-file";
exports.onCreateDevServer = ({ app, store }) => {
polyfillImageServiceDevRoutes(app, store);
};
// With feature detection
import { hasFeature } from "gatsby-plugin-utils";
exports.onCreateDevServer = ({ app, store }) => {
if (!hasFeature('image-cdn')) {
polyfillImageServiceDevRoutes(app, store);
}
};Manually adds file and image processing routes to Express application.
/**
* Adds file and image processing routes to Express app
* @param app - Express application instance
* @param store - Gatsby store instance (optional)
* @returns Modified Express application
*/
function addImageRoutes(
app: Application,
store?: Store
): Application;Usage Examples:
import { addImageRoutes } from "gatsby-plugin-utils/polyfill-remote-file";
import express from "express";
const app = express();
addImageRoutes(app, store);
// Available routes:
// GET /_gatsby/file/:url/:filename - Serves remote files
// GET /_gatsby/image/:url/:params/:filename - Serves processed imagesChecks if image CDN is enabled via environment variables.
/**
* Checks if image CDN is enabled via environment variables
* @returns Boolean indicating if image CDN is enabled
*/
function isImageCdnEnabled(): boolean;Usage Examples:
import { isImageCdnEnabled } from "gatsby-plugin-utils/polyfill-remote-file";
if (isImageCdnEnabled()) {
console.log("Using Gatsby Cloud Image CDN");
} else {
console.log("Using local image processing");
}
// Environment variables that enable image CDN:
// GATSBY_CLOUD_IMAGE_CDN=1
// GATSBY_CLOUD_IMAGE_CDN=trueReturns GraphQL field definitions for remote file functionality.
/**
* Returns remote file GraphQL field definitions
* @param enums - Remote file enums for GraphQL types
* @param actions - Gatsby actions
* @param store - Gatsby store (optional)
* @returns Object with field configurations
*/
function getRemoteFileFields(
enums: RemoteFileEnums,
actions: Actions,
store?: Store
): {
id: string;
mimeType: string;
filename: string;
filesize: string;
width: string;
height: string;
publicUrl: FieldConfig;
resize: FieldConfig;
gatsbyImage: FieldConfig;
};interface IRemoteFileNode extends Node {
/** URL of the remote file */
url: string;
/** MIME type of the file */
mimeType: string;
/** Original filename */
filename: string;
/** File size in bytes (optional) */
filesize?: number;
}
interface IRemoteImageNode extends IRemoteFileNode {
/** Image width in pixels */
width: number;
/** Image height in pixels */
height: number;
/** Placeholder image URL (optional) */
placeholderUrl?: string;
}
/**
* Type guard to check if a remote file is an image
* @param node - Remote file node to check
* @returns Boolean indicating if node is an image
*/
function isImage(node: { mimeType: string }): node is IRemoteImageNode;type ImageFormat = "jpg" | "png" | "webp" | "avif" | "auto";
type ImageLayout = "fixed" | "constrained" | "fullWidth";
type ImageCropFocus =
| "center"
| "top"
| "right"
| "bottom"
| "left"
| "entropy"
| "edges"
| "faces";
type ImageFit =
| "cover"
| "contain"
| "fill"
| "inside"
| "outside";
interface WidthOrHeight {
width?: number;
height?: number;
}
interface CalculateImageSizesArgs extends WidthOrHeight {
fit: ImageFit;
layout: ImageLayout;
outputPixelDensities: Array<number>;
breakpoints?: Array<number>;
aspectRatio?: number;
}The polyfill system creates the following HTTP routes for image processing:
GET /_gatsby/file/:url/:filenameGET /_gatsby/image/:url/:params/:filenamew - widthh - heightfm - format (jpg, png, webp, avif)q - quality (1-100)// gatsby-node.js
const { hasFeature } = require("gatsby-plugin-utils");
const { addRemoteFilePolyfillInterface } = require("gatsby-plugin-utils/polyfill-remote-file");
exports.createSchemaCustomization = ({ actions, schema, store }) => {
// Check if native image CDN is available
if (hasFeature('image-cdn')) {
// Use native RemoteFile interface
actions.createTypes(`
type Asset implements Node & RemoteFile {
id: ID!
title: String!
url: String!
mimeType: String!
filename: String!
}
`);
} else {
// Use polyfill interface
actions.createTypes([
addRemoteFilePolyfillInterface(
schema.buildObjectType({
name: 'Asset',
fields: {
title: 'String!',
url: 'String!',
mimeType: 'String!',
filename: 'String!',
description: 'String'
},
interfaces: ['Node', 'RemoteFile']
}),
{ schema, actions, store }
)
]);
}
};
exports.onCreateDevServer = ({ app, store }) => {
if (!hasFeature('image-cdn')) {
const { polyfillImageServiceDevRoutes } = require("gatsby-plugin-utils/polyfill-remote-file");
polyfillImageServiceDevRoutes(app, store);
}
};
exports.sourceNodes = ({ actions, createNodeId, createContentDigest }) => {
const { createNode } = actions;
// Create asset nodes that will work with both native and polyfill
const assets = [
{
title: "Hero Image",
url: "https://example.com/hero.jpg",
mimeType: "image/jpeg",
filename: "hero.jpg"
}
];
assets.forEach(asset => {
createNode({
...asset,
id: createNodeId(`asset-${asset.title}`),
parent: null,
children: [],
internal: {
type: 'Asset',
contentDigest: createContentDigest(asset)
}
});
});
};With the polyfill in place, you can query remote files using standard GraphQL:
query {
allAsset {
nodes {
id
title
url
filename
mimeType
# Available through polyfill
publicUrl
# Image-specific fields (when applicable)
gatsbyImage(width: 800, height: 600) {
images {
sources {
srcSet
type
}
fallback {
src
srcSet
}
}
width
height
}
resize(width: 400, height: 300) {
src
width
height
}
}
}
}try {
const enhancedType = addRemoteFilePolyfillInterface(type, config);
} catch (error) {
console.error("Failed to add RemoteFile interface:", error);
// Handle graceful degradation
}
// Route error handling
exports.onCreateDevServer = ({ app, store, reporter }) => {
try {
polyfillImageServiceDevRoutes(app, store);
reporter.success("Image service routes added");
} catch (error) {
reporter.error("Failed to add image routes", error);
}
};