FROM CACHE - jp_header

GraphQLを使用して商品の情報を取得する方法(node)

解決済

GraphQLを使用して商品の情報を取得する方法(node)

sen25
Shopify Partner
15 2 0

お世話になっております。

 

現在アプリ開発を行っているのですがアプリ管理画面に商品情報を表示させたいのですgraphqlを使用しての取得がうまくいかないため方法をご教授いただきたいです。

現在の web/index.js

 

  app.use(
    "/api/*",
    verifyRequest(app, {
      billing: billingSettings,
    })
  );

  app.use("/api/graphql", async (req, res) => {
    const session = await Shopify.Utils.loadCurrentSession(req, res);
    const client = new Shopify.Clients.Graphql(
       session.shop,
       session.accessToken
    );
}

 

 表示したいjsxファイルにて axios.get("api/graphql")を実行しています。

 

 
アプリを起動して該当ページへ遷移すると以下のようなメッセージが表示されてしまいうまく実行ができません。
 InvalidShopError: Received invalid shop argument

sessionがうまくいっていないと思い、

   const client = new Shopify.Clients.Graphql(
      "hoge.myshopify.com",
      "aaaaaaaアクセストークン"
    );

clientへ定数で渡してみても同じような結果になってしまいました。

 

そもそも記載場所がweb/index.jsではないのでしょうか?

エラー解決方法やgraphqlの実装方法のご教授をよろしくお願いいたします。

1 件の受理された解決策
sen25
Shopify Partner
15 2 0

成功

お世話になっております。

 

graphql無事呼び出し成功しました。

 

今までローカルに作成したDBに対してaxiosを使用して下記のようなweb/index.jsに自作したDBの操作を呼び出してデータの取得を行っていました。

 

  app.get("/api/hoge", async (req, res) => {
    const datas = await prisma.hoge.findMany({
      orderBy: [
        {
          id: "desc",
        },
      ],
    });
    return res.json(datas);
  });

 

テンプレート使ってアプリ作成したときに作成される app.get("/api/products/count") も同様にaxiosで呼び出しを行ったときに InvalidShopError が生じておりました。

 

テンプレート使用しアプリ作成しコード確認した際に、web/frontend/components/ProductCard.jsxにて

graphql.PNG

useAppQuery使用して呼び出していたためGraphqlのクエリとかは公式のドキュメント見ながら作成してuseAppQueryで呼び出したら無事に成功しました。

 

動作確認できたためこの質問はクローズさせていただきます。

皆様ご回答いただきましたありがとうございます。

 

 

元の投稿で解決策を見る

5件の返信5

株式会社フルバランス
Shopify Partner
1540 550 699

Sen25 様

 

お世話になっております。

フルバランスの道家と申します。

 

ご質問確認いたしました。

直接的な回答はもっておらず恐縮ですが、

添付画像のような書き方を試してみるのはいかがでしょうか。

①app.use → app.get

②loadCurrentSessionに第3引数app.get("use-online-tokens”)を渡す。

スクリーンショット 2022-11-30 17.59.34.png

 

画像の出典元になります。

https://shopify.dev/apps/getting-started/build-app-example/backend#step-3-connect-the-database-and-a...

 

 

なにかヒントになれば幸いです。

 

私たちの励みにもなりますので、

お役に立てていればBest Answerボタンを押して頂ければ嬉しいです。

 

フルバランス 道家

株式会社フルバランス(Shopify Experts)
Shopify専門のEC成長支援会社です。ストアの新規構築から運用や改善のサポートなどShopifyに関する幅広いサービスを行なっております。
ECの技術・実務・成長、お悩みのことがあれば、お気軽にご相談ください。
『すべてのブランドの特大成長エンジンを搭載する』 株式会社フルバランス
sen25
Shopify Partner
15 2 0

道家様ご返信ありがとうございます。

 

返信の通り引数にapp.get("use-online-tokens”)追加して実行してみたのですが同じエラー内容が表示されうまくいきませんでした。

公式のサンプルアプリを参考に記述を行っているのですが公式のほうではsessiondbへの値を格納しているのですがこれが関係しているのでしょうか?現在web/index.jsではinitialize部分は特にいじっていないため添付した画像の上の部分と全く同じになっています。

question.PNG

 

 

株式会社フルバランス
Shopify Partner
1540 550 699

Sen25 様

 

返信が遅くなり申し訳ありません。

sessionが取れていないとのことで、Authがうまく行っていないかと推測しますが、Auth関連の記載は特に編集などされておりませんでしょうか。

 

なにかヒントになれば幸いです。

株式会社フルバランス(Shopify Experts)
Shopify専門のEC成長支援会社です。ストアの新規構築から運用や改善のサポートなどShopifyに関する幅広いサービスを行なっております。
ECの技術・実務・成長、お悩みのことがあれば、お気軽にご相談ください。
『すべてのブランドの特大成長エンジンを搭載する』 株式会社フルバランス
sen25
Shopify Partner
15 2 0

お世話になっております。

ご返信ありがとうございます。

 

middlewareやhelpersなどテンプレート使用して作成した際に作成されるファイルは編集は加えておりません。

web/index.jsにはローカルにDB作成してprisma使用してデータの格納や取得を行っているため処理を追記はしていますが元からあったコードは削除はしておりません。

現在のweb/index.jsは以下の通りです。

// @TS-nocheck
import { join } from "path";
import { readFileSync } from "fs";
import express from "express";
import cookieParser from "cookie-parser";
import { Shopify, LATEST_API_VERSION } from "@shopify/shopify-api";

import applyAuthMiddleware from "./middleware/auth.js";
import verifyRequest from "./middleware/verify-request.js";
import { setupGDPRWebHooks } from "./gdpr.js";
import productCreator from "./helpers/product-creator.js";
import redirectToAuth from "./helpers/redirect-to-auth.js";
import { BillingInterval } from "./helpers/ensure-billing.js";
import { AppInstallations } from "./app_installations.js";
import { PrismaClient } from "@prisma/client";
import JSONbig from "json-bigint";
import cors from "cors";
import createApp from "@shopify/app-bridge";

const USE_ONLINE_TOKENS = false;

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

// TODO: There should be provided by env vars
const DEV_INDEX_PATH = `${process.cwd()}/frontend/`;
const PROD_INDEX_PATH = `${process.cwd()}/frontend/dist/`;

const DB_PATH = `${process.cwd()}/database.sqlite`;

Shopify.Context.initialize({
  API_KEY: process.env.SHOPIFY_API_KEY,
  API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
  SCOPES: process.env.SCOPES.split(","),
  HOST_NAME: process.env.HOST.replace(/https?:\/\//, ""),
  HOST_SCHEME: process.env.HOST.split("://")[0],
  API_VERSION: LATEST_API_VERSION,
  IS_EMBEDDED_APP: true,
  // This should be replaced with your preferred storage strategy
  // See note below regarding using CustomSessionStorage with this template.
  SESSION_STORAGE: new Shopify.Session.SQLiteSessionStorage(DB_PATH),
  ...(process.env.SHOP_CUSTOM_DOMAIN && {
    CUSTOM_SHOP_DOMAINS: [process.env.SHOP_CUSTOM_DOMAIN],
  }),
});

const prisma = new PrismaClient();

// NOTE: If you choose to implement your own storage strategy using
// Shopify.Session.CustomSessionStorage, you MUST implement the optional
// findSessionsByShopCallback and deleteSessionsCallback methods.  These are
// required for the app_installations.js component in this template to
// work properly.

Shopify.Webhooks.Registry.addHandler("APP_UNINSTALLED", {
  path: "/api/webhooks",
  webhookHandler: async (_topic, shop, _body) => {
    await AppInstallations.delete(shop);
  },
});

// The transactions with Shopify will always be marked as test transactions, unless NODE_ENV is production.
// See the ensureBilling helper to learn more about billing in this template.
const BILLING_SETTINGS = {
  required: false,
  // This is an example configuration that would do a one-time charge for $5 (only USD is currently supported)
  // chargeName: "My Shopify One-Time Charge",
  // amount: 5.0,
  // currencyCode: "USD",
  // interval: BillingInterval.OneTime,
};

// This sets up the mandatory GDPR webhooks. You’ll need to fill in the endpoint
// in the “GDPR mandatory webhooks” section in the “App setup” tab, and customize
// the code when you store customer data.
//
// More details can be found on shopify.dev:
// https://shopify.dev/apps/webhooks/configuration/mandatory-webhooks
setupGDPRWebHooks("/api/webhooks");

// export for test use only
export async function createServer(
  root = process.cwd(),
  isProd = process.env.NODE_ENV === "production",
  billingSettings = BILLING_SETTINGS
) {
  const app = express();

  app.use(express.json());

  /////////////////////////////////////////////////////
  // ローカルのDBからデータ取得を追記した部分
  // data全件取得
  app.get("/api/getdata", async (req, res) => {
    const hoge = await prisma.data.findMany({
      orderBy: [
        {
          id: "desc",
        },
      ],
    });

    return res.json(hoge);
  });
 
  /* 他処理は割愛。上記のように実装してます。 */

  //////////////////////////////////////////////////////////////

 
  app.set("use-online-tokens", USE_ONLINE_TOKENS);
  app.use(cookieParser(Shopify.Context.API_SECRET_KEY));

  applyAuthMiddleware(app, {
    billing: billingSettings,
  });

  // Do not call app.use(express.json()) before processing webhooks with
  // Shopify.Webhooks.Registry.process().
  // See https://github.com/Shopify/shopify-api-node/blob/main/docs/usage/webhooks.md#note-regarding-use-of-body-parsers
  // for more details.
  app.post("/api/webhooks", async (req, res) => {
    try {
      await Shopify.Webhooks.Registry.process(req, res);
      console.log(`Webhook processed, returned status code 200`);
    } catch (e) {
      console.log(`Failed to process webhook: ${e.message}`);
      if (!res.headersSent) {
        res.status(500).send(e.message);
      }
    }
  });

  // All endpoints after this point will require an active session
  // この時点以降のすべてのエンドポイントでは、アクティブなセッションが必要です。
  app.use(
    "/api/*",
    verifyRequest(app, {
      billing: billingSettings,
    })
  );

  app.get("/api/products/count", async (req, res) => {
    const session = await Shopify.Utils.loadCurrentSession(
      req,
      res,
      app.get("use-online-tokens")
    );
    const { Product } = await import(
      `@shopify/shopify-api/dist/rest-resources/${Shopify.Context.API_VERSION}/index.js`
    );

    const countData = await Product.count({ session });
    res.status(200).send(countData);
  });

  app.get("/api/products/create", async (req, res) => {
    const session = await Shopify.Utils.loadCurrentSession(
      req,
      res,
      app.get("use-online-tokens")
    );
    let status = 200;
    let error = null;

    try {
      await productCreator(session);
    } catch (e) {
      console.log(`Failed to process products/create: ${e.message}`);
      status = 500;
      error = e.message;
    }
    res.status(status).send({ success: status === 200, error });
  });

  app.use((req, res, next) => {
    const shop = Shopify.Utils.sanitizeShop(req.query.shop);
    if (Shopify.Context.IS_EMBEDDED_APP && shop) {
      res.setHeader(
        "Content-Security-Policy",
        `frame-ancestors https://${encodeURIComponent(
          shop
        )} https://admin.shopify.com;`
      );
    } else {
      res.setHeader("Content-Security-Policy", `frame-ancestors 'none';`);
    }
    next();
  });

  if (isProd) {
    const compression = await import("compression").then(
      ({ default: fn }) => fn
    );
    const serveStatic = await import("serve-static").then(
      ({ default: fn }) => fn
    );
    app.use(compression());
    app.use(serveStatic(PROD_INDEX_PATH, { index: false }));
  }

  app.use("/*", async (req, res, next) => {
    if (typeof req.query.shop !== "string") {
      res.status(500);
      return res.send("No shop provided");
    }

    const shop = Shopify.Utils.sanitizeShop(req.query.shop);
    const appInstalled = await AppInstallations.includes(shop);

    if (!appInstalled && !req.originalUrl.match(/^\/exitiframe/i)) {
      return redirectToAuth(req, res, app);
    }

    if (Shopify.Context.IS_EMBEDDED_APP && req.query.embedded !== "1") {
      const embeddedUrl = Shopify.Utils.getEmbeddedAppUrl(req);

      return res.redirect(embeddedUrl + req.path);
    }

    const htmlFile = join(
      isProd ? PROD_INDEX_PATH : DEV_INDEX_PATH,
      "index.html"
    );

    return res
      .status(200)
      .set("Content-Type", "text/html")
      .send(readFileSync(htmlFile));
  });

  return { app };
}

createServer().then(({ app }) => app.listen(PORT));

 

もともとあるapi/products/countを呼び出してみても質問に記載してあるエラーが表示され実行できませんでした。

現在以下のドキュメントを見てセッショントークンについてなど勉強をおこなっていますが正直あまりわかっておりません。

https://shopify.dev/apps/auth/oauth/session-tokens/getting-started

 

なにか添付したコードの記載方法の間違いやこの問題を解消できる部分がありましたらご教授いただければ幸いです。よろしくお願いいたします。

sen25
Shopify Partner
15 2 0

成功

お世話になっております。

 

graphql無事呼び出し成功しました。

 

今までローカルに作成したDBに対してaxiosを使用して下記のようなweb/index.jsに自作したDBの操作を呼び出してデータの取得を行っていました。

 

  app.get("/api/hoge", async (req, res) => {
    const datas = await prisma.hoge.findMany({
      orderBy: [
        {
          id: "desc",
        },
      ],
    });
    return res.json(datas);
  });

 

テンプレート使ってアプリ作成したときに作成される app.get("/api/products/count") も同様にaxiosで呼び出しを行ったときに InvalidShopError が生じておりました。

 

テンプレート使用しアプリ作成しコード確認した際に、web/frontend/components/ProductCard.jsxにて

graphql.PNG

useAppQuery使用して呼び出していたためGraphqlのクエリとかは公式のドキュメント見ながら作成してuseAppQueryで呼び出したら無事に成功しました。

 

動作確認できたためこの質問はクローズさせていただきます。

皆様ご回答いただきましたありがとうございます。