I am new to Shopify App Devlopment, especially the Shopify API.
I create a working app with the Shopify CLI and now want to communicate with the API.
I try to access following endpoint: https://{my_shop]/admin/api/2021-07/shop.json
I learned that I need some access token and the shop name to access this endpoint.
I created an access token under my private apps section.
But I dont know how to get the currently logged in store.
For example, when clicking a button in my frontend, I would like to call my endpoint, which in turn calls the Shopify API endpoint and retrieves the information. How do I do this the right way? And how do I get the currently logged in shop?
This is my code so far:
import "@babel/polyfill";
import dotenv from "dotenv";
import "isomorphic-fetch";
import createShopifyAuth, { verifyRequest } from "@shopify/koa-shopify-auth";
import Shopify, { ApiVersion } from "@shopify/shopify-api";
import Koa from "koa";
import next from "next";
import Router from "koa-router";
import axios from 'axios';
dotenv.config();
const port = parseInt(process.env.PORT, 10) || 8081;
const dev = process.env.NODE_ENV !== "production";
const app = next({
dev,
});
const handle = app.getRequestHandler();
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:\/\//, ""),
API_VERSION: ApiVersion.October20,
IS_EMBEDDED_APP: true,
// This should be replaced with your preferred storage strategy
SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});
// Storing the currently active shops in memory will force them to re-login when your server
restarts. You should
// persist this object in your app.
const ACTIVE_SHOPIFY_SHOPS = {};
app.prepare().then(async () => {
const server = new Koa();
const router = new Router();
server.keys = [Shopify.Context.API_SECRET_KEY];
server.use(
createShopifyAuth({
async afterAuth(ctx) {
// Access token and shop available in ctx.state.shopify
const { shop, accessToken, scope } = ctx.state.shopify;
const host = ctx.query.host;
ACTIVE_SHOPIFY_SHOPS[shop] = scope;
const response = await Shopify.Webhooks.Registry.register({
shop,
accessToken,
path: "/webhooks",
topic: "APP_UNINSTALLED",
webhookHandler: async (topic, shop, body) =>
delete ACTIVE_SHOPIFY_SHOPS[shop],
});
if (!response.success) {
console.log(
`Failed to register APP_UNINSTALLED webhook: ${response.result}`
);
}
// Redirect to app with shop parameter upon auth
ctx.redirect(`/?shop=${shop}&host=${host}`);
},
})
);
router.get("/test2", verifyRequest(), async(ctx, res) => {
const {shop, accessToken } = ctx.session;
console.log(shop);
console.log(accessToken);
})
router.get("/test", async (ctx) => {
const config = {
headers: {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': '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'
}
}
await axios.get('${the_store_name_belongs_here}/admin/api/2021-07/shop.json', config).then(res => {
ctx.body = res.data;
});
});
const handleRequest = async (ctx) => {
await handle(ctx.req, ctx.res);
ctx.respond = false;
ctx.res.statusCode = 200;
};
router.post("/webhooks", async (ctx) => {
try {
await Shopify.Webhooks.Registry.process(ctx.req, ctx.res);
console.log(`Webhook processed, returned status code 200`);
} catch (error) {
console.log(`Failed to process webhook: ${error}`);
}
});
router.post(
"/graphql",
verifyRequest({ returnHeader: true }),
async (ctx, next) => {
await Shopify.Utils.graphqlProxy(ctx.req, ctx.res);
}
);
router.get("(/_next/static/.*)", handleRequest); // Static content is clear
router.get("/_next/webpack-hmr", handleRequest); // Webpack content is clear
router.get("(.*)", async (ctx) => {
const shop = ctx.query.shop;
// This shop hasn't been seen yet, go through OAuth to create a session
if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
ctx.redirect(`/auth?shop=${shop}`);
} else {
await handleRequest(ctx);
}
});
server.use(router.allowedMethods());
server.use(router.routes());
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
Please have a look at my attempts - endpoint /test
and endpoint /test2
.
test2 is not working. ctx.session is null. ctx itself is null. Why?
test1
is working when I hard code my shops name into the url, then I get the desired data. But how do I put a shop variable inside? That's my struggle.
Shopify App Bridge is a JavaScript library that helps you embed your app directly inside Shopify. Shopify App Bridge works with apps embedded in Shopify Admin, and the Shopify Mobile and Shopify POS apps to help you create a seamless experience for merchants. Read the documentation to learn more.
First of all, it is not good practice to use MemorySessionStorage in production environment due to its limitations, you can find a good explanation here
MemorySessionStorage exists as an option to help you get started developing your apps as quickly as possible...
So, implement a CustomSessionStorage (refer to the doc above), you will have access to the session that stores data such as shop, accessToken, scope among others. As long as authenticated requests are made, providing the JWT in the header, you can get the context properly working.
e.g. (react-koa):
//client.js
import { useAppBridge } from "@shopify/app-bridge-react";
import { getSessionToken } from "@shopify/app-bridge-utils";
function Index() {
const app = useAppBridge();
async function getProducts() {
const token = await getSessionToken(app);
const response = await fetch("/api/products", {
headers: { "Authorization": `Bearer ${token}` }
});
const result = await response.json();
console.log(result);
}
return (<></>);
}
and then...
// server.js
router.get("/api/products", verifyRequest({ returnHeader: true }), async (ctx) => {
// Load the current session to get the `accessToken`.
// pass a third parameter clarifying the accessMode (isOnline = true by default)
const session = await Shopify.Utils.loadCurrentSession(ctx.req, ctx.res);
// Create a new client for the specified shop.
const client = new Shopify.Clients.Rest(session.shop, session.accessToken);
// Use `client.get` to request the specified Shopify REST API endpoint, in this case `products`.
const products = await client.get({
path: 'products',
});
ctx.body = results.body;
ctx.res.status = 200;
});
more details here.
Using axios, you can define it as a hook (working example using TypeScript):
import axios from 'axios';
import { useAppBridge } from '@shopify/app-bridge-react';
import { getSessionToken } from '@shopify/app-bridge-utils';
function useAxios() {
const app = useAppBridge();
const instance = axios.create();
instance.interceptors.request.use(async function (config) {
const token = await getSessionToken(app);
config.headers['Authorization'] = `Bearer ${token}`;
return config;
});
return [instance];
}
export default useAxios;
// index.js
// ...
const [axios] = useAxios();
// ...
const result = await axios.get('/api/products');
console.log(result.data);
// ...
Hope this helps anyone who is still looking for help.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With