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.
3
Answers
There is no reference to any
ctx.session
in koa-shopify-auth documentation. What about this:Other solutions
You can store a Cookie after authentication
And then read it in future requests:
I faced this problem and resolved it by passing the shop as a query parameter.
I call the endpoint with:
And get the shop with:
Of course, you have to know the shop where you call the endpoint.
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
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):
and then…
more details here.
Using axios, you can define it as a hook (working example using TypeScript):
Hope this helps anyone who is still looking for help.