Hi!
I’m making a Shopify app with the Shopify CLI as a scaffold. I’m trying to create a callable api endpoint that is authenticated, I’m quite new to shopify and Koa but this is what I’ve got so far.
This is the server.js (I removed some irrelevant stuff)
import createShopifyAuth, { verifyRequest } from "@shopify/koa-shopify-auth";
// ... setup context, etc
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;
// ... do stuff here
// Redirect to app with shop parameter upon auth
ctx.redirect(`/?shop=${shop}&host=${host}`);
},
})
);
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.error(`Failed to process webhook: ${error}`);
}
});
router.post(
"/graphql",
verifyRequest({ returnHeader: true }),
async (ctx, _next) => {
await Shopify.Utils.graphqlProxy(ctx.req, ctx.res);
}
);
router.post("/api/register", verifyRequest(), async (ctx) => {
// do db calls with data from request
});
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}`);
});
});
Here the important part is the /api/register route
router.post("/api/register", verifyRequest(), async (ctx) => {
// do db calls with data from request
});
From what I understand the verifyRequest() function is looking for the accessToken in the request.
Here is a simplified version of the frontend code, basically I’m just trying to call the endpoint with a simple fetch call.
const Index = () => {
const handleSubmit = React.useCallback(() => {
fetch("/api/register", {
method: "POST",
});
}, []);
return (
);
};
What happens with this setup is that the authentication in verifyRequest() fails and it redirects to /auth endpoint which does not have a shop property so it just returns a 400.
My question is how can I get the access token on the frontend so I can pass it in the request so the authentication does not fail? Or is there a better way of doing this?
Some of the posts and solutions I have looked at so far:
Returns Error 405 On Embedded Node.Js App
React/Node Embedded App, GET Call To Online Store API
Error 400 ‘Required Parameter Missing Or Invalid’ When Generating A Storefront Access Token
Thanks!