Skip to content

Instantly share code, notes, and snippets.

@Tommydemian
Created January 5, 2025 21:34
Show Gist options
  • Save Tommydemian/fe5896c66b5cba6b5b4a5139362758df to your computer and use it in GitHub Desktop.
Save Tommydemian/fe5896c66b5cba6b5b4a5139362758df to your computer and use it in GitHub Desktop.
Gist Description Verify a Shopify Theme App Embed in Remix (Server-Side) This code demonstrates how to: Fetch all themes and identify the main theme (role: MAIN). Retrieve settings_data.json from that theme, removing the multiline comment block that can break JSON.parse. Check whether a specific app embed block is set to disabled: false. It’s de…
import { json } from "@remix-run/node";
import type { LoaderFunction } from "@remix-run/node";
import { getMainThemeId, getSettingsData, isAppEmbedDisabled } from "~/lib/shopify.server";
export const loader: LoaderFunction = async ({ request }) => {
// Set up or retrieve your 'admin' Shopify client somehow
// e.g. const { admin } = await authenticate.admin(request);
const themeId = await getMainThemeId(admin);
let embedDisabled = true; // default to "off"
if (themeId) {
const settingsData = await getSettingsData(admin, themeId);
embedDisabled = isAppEmbedDisabled(settingsData, "dev-storelock");
}
return json({ embedDisabled });
};
/****************************************************
* shopify.server.ts
* A minimal Shopify + Remix helper for verifying
* theme app embed status via settings_data.json
****************************************************/
// If you have a specific type/interface for admin context, import it here.
// Example:
// import type { AdminApiContextWithoutRest } from "wherever/your-shopify-context-lives";
// 1. Query: Fetch themes
const GET_THEMES = `
query {
themes(first: 20) {
edges {
node {
id
role
name
}
}
}
}
`;
// 2. Query: Fetch settings_data.json from a given theme
const GET_THEME_FILE = `
query getThemeFile($id: ID!) {
theme(id: $id) {
files(filenames: ["config/settings_data.json"], first: 1) {
nodes {
filename
body {
... on OnlineStoreThemeFileBodyText {
content
}
}
}
}
}
}
`;
/**
* getMainThemeId
* Calls the admin.graphql(...) method to list themes, returning the ID of the main theme.
*/
export async function getMainThemeId(
admin: any // or AdminApiContextWithoutRest if you have that type
): Promise<string | undefined> {
const response = await admin.graphql(GET_THEMES);
const responseJson = await response.json();
// Transform data into an array of theme nodes
const themes = responseJson?.data?.themes?.edges ?? [];
// Find the one with role: MAIN
const mainTheme = themes.find((t: any) => t.node.role === "MAIN");
return mainTheme ? mainTheme.node.id : undefined;
}
/**
* getSettingsData
* Given the main theme ID, fetch config/settings_data.json
* from the Shopify Admin API, remove the multiline comment, then parse as JSON.
*/
export async function getSettingsData(admin: any, themeId: string) {
const response = await admin.graphql(GET_THEME_FILE, { variables: { id: themeId } });
const result = await response.json();
// Grab the raw content from the file
const rawContent = result?.data?.theme?.files?.nodes?.[0]?.body?.content ?? "";
// Remove the multi-line comment (/* ... */) if present
const cleaned = rawContent.replace(/\/\*[\s\S]*?\*\//, "");
// Parse the cleaned JSON
return JSON.parse(cleaned);
}
/**
* isAppEmbedDisabled
* Inspect the parsed settings_data.json to see if your app's block is disabled.
* @param parsedData - The result from getSettingsData(...)
* @param appHandle - Unique handle/identifier for your app (e.g. "dev-storelock")
* @returns boolean - true if the block is disabled or missing, false if it's enabled
*/
export function isAppEmbedDisabled(parsedData: any, appHandle: string): boolean {
const blocks = parsedData?.current?.blocks;
if (!blocks) {
// No blocks at all -> treat as disabled
return true;
}
// Loop over block IDs
for (const blockId of Object.keys(blocks)) {
const block = blocks[blockId];
// Check if the block type includes our app handle
if (block?.type?.includes(appHandle)) {
// If block.disabled === true, then yes, it's disabled
return block.disabled === true;
}
}
// If we never found a block matching our app handle, consider it disabled
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment