Frontend Calling Backend Code

In my experience, Shopify’s documentation leaves a lot to be desired when it comes to app development — or maybe I’m just looking in the wrong places.

At a high level, here’s what I’m trying to accomplish:

I need to build a theme extension that renders an input for users to enter a box software redemption code. When the form is submitted, we need to securely call an external endpoint using credentials that shouldn’t be exposed on the frontend. That endpoint will return a token, which we’ll then use to make another secure call — this time passing the token, activation code, and the customer’s email address — to redeem the software code.

Getting the theme extension up and running isn’t an issue, and I can create an endpoint in Remix (e.g., api.activation.jsx). However, I’m unsure how to securely communicate with that backend endpoint from the frontend.

I’ve seen mentions of app proxies, but the documentation on this is extremely limited — especially regarding development setups (presumably via ngrok, but that’s another rabbit hole). I get the impression these use cases aren’t common enough for detailed documentation.

I’m primarily a theme developer, so app development like this isn’t my strongest area, but I’m eager to learn. Unfortunately, Shopify’s docs can be frustrating to navigate at times. Also, I find Remix unnecessarily complex for this type of task (Next.js just makes more sense to me dev-wise) — but maybe that’s just me.

Any guidance would be greatly appreciated!

1 Like

Hi @cory_kelley

You can use useActionData to write the code that needs to call the backend endpoint in the action method, hopefully the official documentation of remix will be helpful to you!

https://remix.run/docs/en/main/route/action

Thanks @kyle_liu I’ll take a look at this!

Edit: I don’t think I can use useActionData because it’s a Remix method, and the theme extension JavaScript is just plain client-side JS. Theme extensions run in the storefront as injected scripts, while Remix methods run on the server or Shopify app backend. To make them work together, I’d need an intermediary solution.

If you’re using the Remix template, you’ll definitively need to set up an app proxy.
You can do that either in the shopify.app.toml file or the app configuration page.

Here’s an example of how it’s set up in the toml file:

[app_proxy]
url = "<app-url>"
subpath = "<app-name>"
prefix = "apps"

Then, if you want to call an API endpoint called /api/hello from the theme block you would make a call like so:

fetch('/apps/<app-name>/api/hello')

In the actual endpoint file api.hello.js you would add something like this:

import { authenticate } from 'app/shopify.server'

export const loader = async ({ request }) => {
  const { admin, liquid, session, storefront } = await authenticate.public.appProxy(request)

  // fetch the data you need using the admin and storefront APIs, or even your own
  // ...

  // return a response
  return Response.json({ shop: session.shop })
}

Hope that helps, I know I banged my head against the wall for hours until I figured out something this simple, so Shopify really needs to improve their docs, at least for their own app template.

Another bug you might come across to is that when you’re sending a GET request to your endpoint you’ll see an error that an endpoint doesn’t exist because you’re “sending a POST request”.
This happens only when you’re viewing the extension using the network URL, so use the regular store.myshopify.com URL to properly test.

Hope this helps.

1 Like

Amazing! I’ve seen this information before, but you’ve finally explained it in a way that’s actually easy to follow. Shopify definitely needs to clean up the documentation on this.

Thanks so much!

Side note—would we need to use something like ngrok for the app URL since it changes each time the server starts?

Not if you’re using the template since you have Cloudflare tunnels.
If you prefer ngrok, you can use that instead.

Your app URLs can also update each time the server restarts if your config includes this:

[build]
include_config_on_deploy = true
automatically_update_urls_on_dev = true

wow… every video I’ve watched folks struggle with this… you’re a life saver haha