Retrieving Metaobject Data for Tax Rates in Shopify Checkout UI Extension

Hello everyone,

I hope this message finds you well.

I appreciate your taking the time to read my description. I am currently developing a Shopify app aimed at adjusting product prices in the cart during the checkout process based on the tax rates of different countries stored in metaobjects.

I have successfully initiated the app project using the Shopify app nodejs-react template and have implemented a CRUD functionality for managing tax rates by country in the app’s admin page. However, I am encountering an issue with the checkout UI extension. To utilize the tax rates stored in the metaobjects, I need to call the app admin frontend API from the checkout UI extension, but so far, it hasn’t been successful. (Or my idea maybe not wrong to get metaobject in checkout UI extension ).

I have reviewed similar issues in the community but have not found a solution that resolves my problem.

I would greatly appreciate any guidance or suggestions on how to effectively retrieve metaobject data within the checkout UI extension.

I have attached my code. Admin api and checkout UI extension code.
This is admin api (Myapp/web/frontend/index.js )

// -check
import { join } from "path";
import { readFileSync } from "fs";
import express from "express";
import serveStatic from "serve-static";

import shopify from "./shopify.js";
import productCreator from "./product-creator.js";
import PrivacyWebhookHandlers from "./privacy.js";
import { setupMetaobjects } from "./metaobject-setup.js";
import axios from "axios";

const PORT = parseInt(
  process.env.BACKEND_PORT || process.env.PORT || "3000",
  10
);

const STATIC_PATH =
  process.env.NODE_ENV === "production"
    ? `${process.cwd()}/frontend/dist`
    : `${process.cwd()}/frontend/`;

const app = express();

import cors from "cors";

// Add before your routes
app.use(
  cors({
    origin: "https://extensions.shopifycdn.com",
    credentials: true,
  })
);

// Set up Shopify authentication and webhook handling
app.get(shopify.config.auth.path, shopify.auth.begin());
app.get(
  shopify.config.auth.callbackPath,
  shopify.auth.callback(),
  shopify.redirectToShopifyOrAppRoot()
);
app.post(
  shopify.config.webhooks.path,
  shopify.processWebhooks({ webhookHandlers: PrivacyWebhookHandlers })
);

// If you are adding routes outside of the /api path, remember to
// also add a proxy rule for them in web/frontend/vite.config.js

app.use("/api/*", shopify.validateAuthenticatedSession());

app.use(express.json());

app.post("/api/setup-metaobjects", async (_req, res) => {
  try {
    const result = await setupMetaobjects(res.locals.shopify.session);
    res.status(200).send(result);
  } catch (error) {
    console.error("Failed to setup metaobjects:", error);
    res.status(500).send(error.message);
  }
});

// Add these routes with your existing API routes

app.get("/api/tax-rates", async (_req, res) => {
  const client = new shopify.api.clients.Graphql({
    session: res.locals.shopify.session,
  });

  const response = await client.request(`
    query GetTaxRates {
      metaobjects(type: "market_tax_rate", first: 100) {
        edges {
          node {
            id
            fields {
              key
              value
            }
          }
        }
      }
    }
  `);

  const taxRates = response.data.metaobjects.edges.map((edge) => {
    const fields = edge.node.fields;
    return {
      id: edge.node.id,
      countryCode: fields.find((f) => f.key === "country_code").value,
      taxRate: fields.find((f) => f.key === "tax_rate").value,
    };
  });

  res.status(200).json(taxRates);
});

app.get("/api/checkout/tax-rates/:countryCode", async (req, res) => {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
  res.header(
     "Access-Control-Allow-Headers",
     "Content-Type, Accept, Authorization, X-Requested-With, Application"
   );
  const { countryCode } = req.params;
  const client = new shopify.api.clients.Graphql({
    session: res.locals.shopify.session,
  });

  const response = await client.request(`
    query GetTaxRate {
      metaobjects(type: "market_tax_rate", first: 100) {
        edges {
          node {
            fields {
              key
              value
            }
          }
        }
      }
    }
  `);

  const taxRate =
    response.data.metaobjects.edges
      .find((edge) => {
        const fields = edge.node.fields;
        return (
          fields.find((f) => f.key === "country_code")?.value === countryCode
        );
      })
      ?.node.fields.find((f) => f.key === "tax_rate")?.value || "0";

  res.status(200).json({ taxRate });
});

app.use(shopify.cspHeaders());
app.use(serveStatic(STATIC_PATH, { index: false }));

app.use("/*", shopify.ensureInstalledOnShop(), async (_req, res, _next) => {
  return res
    .status(200)
    .set("Content-Type", "text/html")
    .send(
      readFileSync(join(STATIC_PATH, "index.html"))
        .toString()
        .replace("%VITE_SHOPIFY_API_KEY%", process.env.SHOPIFY_API_KEY || "")
    );
});

app.listen(PORT);

And the checkout UI extension component file. (Myapp/extensions/tax-checkout/src/checkout.jsx)

import {
  reactExtension,
  Banner,
  BlockStack,
  Grid,
  TextField,
  Select,
  useApi,
  useTranslate,
  useCartLines,
  useApplyAttributeChange,
  useApplyCartLinesChange,
  useBuyerJourney,
  Text,
  useShippingAddress,
  useStorage,
} from "@shopify/ui-extensions-react/checkout";
import { useState } from "react";
import countries from "./countries";
import { extend, extension } from "@shopify/checkout-ui-extensions";

export default reactExtension("purchase.checkout.block.render", () => (
  

I will be looking forward to reply.

Thank you in advance for your help!

Best regards,