Custom customer account page in Shopify custom app

Custom customer account page in Shopify custom app

YauhenV
Shopify Partner
2 0 0

Hello there.

 

I'm beginner in Shopify and I need to develop a small custom app using Shopify CLI v3.46.0 with Node js (Shopify App Template - Node). I went through all the points of the tutorial https://shopify.dev/docs/apps/getting-started/create and now I need to implement my version of the customer personal account for the store. And I have a few questions.

  1. Which custom app option should be used for my purposes - a classic custom app or an extention in a custom app? I'm going to use a custom app for only one my store.
  2. What should I do on the side of the test store or settings of the installed custom app to test the functionality of the customer personal account? I just run `npm run dev` and go to the hot-reload page, where I see the application as a store owner, not a customer. Should I use ngrok?
  3. I tried to figure out how to prepare a query for the GraphQL Admin API myself, but I got stuck. How do I get the ID of the logged in customer? Where is it stored?  If I hardcode the customerID, I will successfully get his data in the test environment. But how do this automaticly for authorized customer? Here is what I tried to do (all changes placed in one file index.js in web folder):

 

// @TS-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 GDPRWebhookHandlers from "./gdpr.js";

import '@shopify/shopify-api/adapters/node';
import {shopifyApi, LATEST_API_VERSION} from '@shopify/shopify-api';

// Set up shopifyApi for GraphQL
const shopifyGQL = shopifyApi({
  apiKey: process.env.SHOPIFY_API_KEY,
  apiSecretKey: process.env.SHOPIFY_API_SECRET_KEY,
  scopes: ['read_customers,write_customers'],
  hostName: 'ngrok-tunnel-address', //should we use this?
});

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();

// 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: GDPRWebhookHandlers })
);

// 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.get("/api/products/count", async (_req, res) => {
  const countData = await shopify.api.rest.Product.count({
    session: res.locals.shopify.session,
  });
  res.status(200).send(countData);
});

app.get("/api/products/create", async (_req, res) => {
  let status = 200;
  let error = null;

  try {
    await productCreator(res.locals.shopify.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 });
});

// This is my new functionality about customer info
app.get("/api/customer", async (req, res) => {
  try {
    const session = res.locals.shopify.session;
    const customerId = session.id; // customer id is here?
    const client = new shopifyGQL.clients.Graphql({ session });

    console.log('customerId: ', customerId);

    const response = await client.query({
      data: {
        query: `
          query($customerId: ID!) {
            customer(id: $customerId) {
              email
              firstName
              lastName
            }
          }
        `,
        variables: {
          customerId: "gid://shopify/Customer/6912233570594",
        },
      },
    });

    const customer = response.body.data.customer;
    res.json(customer);
  } catch (error) {
    console.error("Error retrieving customer details:", error);
    res.status(500).json({ error: "Failed to retrieve customer details" });
  }
});

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")));
});

app.listen(PORT);

 

And here is how it looks in the component (just small example):

 

import React from 'react';
import { LegacyCard, TextStyle } from '@shopify/polaris';
import {useAppQuery} from '../hooks/index.js';

export function ProfileView () {
	const {
		data,
		isLoading,
		isSuccess,
		isError,
	} = useAppQuery({
		url: "api/customer",
	});

	return (
		<LegacyCard
			title="Profile"
			sectioned
			primaryFooterAction={{
				loading: isLoading,
			}}
		>
			{ isSuccess && (
				<TextStyle>
					<p>
						Name: {data.firstName} {data.lastName}
					</p>
					<p>Email: {data.email}</p>
				</TextStyle>
			)}
			{ isError && (
				<p>error</p>
			)}
		</LegacyCard>
	);
}

 

 

Replies 0 (0)