variantId Returns null for Virtual Product

Topic summary

A developer is encountering an issue with Shopify’s GraphQL Storefront API when querying virtual products. While the API successfully returns variant IDs for most products, it returns null for virtual products.

Technical Details:

  • Using a GraphQL query to fetch product and variant data by product ID
  • The query targets the node field with Product type and requests the first variant
  • Code extracts the variant ID from the full GID format

Problem Scope:

  • Virtual products specifically return null variant data
  • Unable to retrieve general product details for virtual products
  • Standard products work correctly with the same query

Current Status:
The developer is seeking community input on whether others have experienced similar issues or if there are known limitations with virtual products in the Storefront API. No solutions or responses have been provided yet.

Summarized with AI on October 27. AI used: claude-sonnet-4-5-20250929.

Hi all,

I’m facing an issue while using the Shopify GraphQL Storefront API to fetch variant IDs using a product ID. For most products, the data is returned correctly. However, when I try to fetch the variant ID for a virtual product, the node returns a null value.

Additionally, I’m unable to retrieve general product details for virtual products. The problem seems to occur only when accessing product details for virtual products.

Has anyone else experienced this issue or know if there are any limitations when dealing with virtual products in the GraphQL Storefront API?

import { json } from “@remix-run/node”;
import {authenticate} from ‘../shopify.server’;

export async function loader({ request }) {
const url = new URL(request.url);
const productId = url.searchParams.get(“productId”);
const formatedId = gid://shopify/Product/${productId};

const { storefront } = await authenticate.public.appProxy(request);

const response = await storefront.graphql(
query getProduct($id: ID!) { node(id: $id) { __typename ... on Product { id title variants(first: 1) { edges { node { id title } } } } } },
{ variables: { id: formatedId } }
);

const jsonResponse = await response.json();

let variantId = null;

if (
jsonResponse?.data?.node?.__typename === “Product” &&
jsonResponse.data.node.variants.edges.length > 0
) {
const fullVariantId = jsonResponse.data.node.variants.edges[0].node.id;
variantId = fullVariantId.split(“/”).pop();
}

return json({ variantId }, {
headers: {
“Access-Control-Allow-Origin”: “*”,
},
});
}