summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--hooks/useAppQuery.js26
-rw-r--r--hooks/useAuthenticatedFetch.js38
-rw-r--r--index.html17
4 files changed, 83 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b72f9be
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*~
+*.swp
diff --git a/hooks/useAppQuery.js b/hooks/useAppQuery.js
new file mode 100644
index 0000000..7218274
--- /dev/null
+++ b/hooks/useAppQuery.js
@@ -0,0 +1,26 @@
+/**
+ * A hook for querying your custom app data.
+ * @desc A thin wrapper around useAuthenticatedFetch and react-query's useQuery.
+ *
+ * @param {Object} options - The options for your query. Accepts 3 keys:
+ *
+ * 1. url: The URL to query. E.g: /api/widgets/1`
+ * 2. fetchInit: The init options for fetch. See: https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters
+ * 3. reactQueryOptions: The options for `useQuery`. See: https://react-query.tanstack.com/reference/useQuery
+ *
+ * @returns Return value of useQuery. See: https://react-query.tanstack.com/reference/useQuery.
+ */
+const useAppQuery = ({ url, fetchInit = {}, reactQueryOptions }) => {
+ const authenticatedFetch = useAuthenticatedFetch();
+ const fetch = useMemo(() => {
+ return async () => {
+ const response = await authenticatedFetch(url, fetchInit);
+ return response.json();
+ };
+ }, [url, JSON.stringify(fetchInit)]);
+
+ return useQuery(url, fetch, {
+ ...reactQueryOptions,
+ refetchOnWindowFocus: false,
+ });
+};
diff --git a/hooks/useAuthenticatedFetch.js b/hooks/useAuthenticatedFetch.js
new file mode 100644
index 0000000..e5f36a8
--- /dev/null
+++ b/hooks/useAuthenticatedFetch.js
@@ -0,0 +1,38 @@
+/**
+ * A hook that returns an auth-aware fetch function.
+ * @desc The returned fetch function that matches the browser's fetch API
+ * See: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
+ * It will provide the following functionality:
+ *
+ * 1. Add a `X-Shopify-Access-Token` header to the request.
+ * 2. Check response for `X-Shopify-API-Request-Failure-Reauthorize` header.
+ * 3. Redirect the user to the reauthorization URL if the header is present.
+ *
+ * @returns {Function} fetch function
+ */
+function useAuthenticatedFetch() {
+ const app = useAppBridge();
+ const fetchFunction = authenticatedFetch(app);
+
+ return async (uri, options) => {
+ const response = await fetchFunction(uri, options);
+ checkHeadersForReauthorization(response.headers, app);
+ return response;
+ };
+}
+
+function checkHeadersForReauthorization(headers, app) {
+ if (headers.get("X-Shopify-API-Request-Failure-Reauthorize") === "1") {
+ const authUrlHeader =
+ headers.get("X-Shopify-API-Request-Failure-Reauthorize-Url") ||
+ `/api/auth`;
+
+ const redirect = Redirect.create(app);
+ redirect.dispatch(
+ Redirect.Action.REMOTE,
+ authUrlHeader.startsWith("/")
+ ? `https://${window.location.host}${authUrlHeader}`
+ : authUrlHeader
+ );
+ }
+}
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..70c4936
--- /dev/null
+++ b/index.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang=en>
+ <head>
+ <meta charset=utf-8/>
+ <meta name="viewport" content="width=device-width, initial-scale=1"/>
+ <link rel="stylesheet" href="https://unpkg.com/@shopify/polaris@10.2.0/build/esm/styles.css"/>
+ </head>
+ <body>
+ <script src="https://unpkg.com/react@18"></script>
+ <script src="https://unpkg.com/react-query@3"></script>
+ <script src="https://unpkg.com/@shopify/app-bridge@3"></script>
+ <script src="https://unpkg.com/@shopify/app-bridge-utils@3"></script>
+ <script src="https://unpkg.com/@shopify/app-bridge-react@3"></script>
+ <script async type="text/javascript" src="js/useAuthenticatedFetch.js"></script>
+ <script async type="text/javascript" src="js/useAppQuery.js"></script>
+ </body>
+</html>