shopify-nuxt 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,6 +16,26 @@ It builds on the `@shopify/shopify-api` package and creates a Nuxt module layer
16
16
 
17
17
  > **Note**: this package will enable your app's backend to work with Shopify APIs, and by default it will behave as an [embedded app](https://shopify.dev/docs/apps/auth/oauth/session-tokens). It uses the CDN-based [Shopify App Bridge](https://shopify.dev/docs/apps/tools/app-bridge) for frontend authentication.
18
18
 
19
+ ## How is this different from `@nuxtjs/shopify`?
20
+
21
+ [`@nuxtjs/shopify`](https://shopify.nuxtjs.org) and `shopify-nuxt` solve completely different problems — you may even use both in the same project.
22
+
23
+ | | [`@nuxtjs/shopify`](https://shopify.nuxtjs.org) | `shopify-nuxt` (this package) |
24
+ | ----------------------------- | ----------------------------------------------- | ------------------------------------------- |
25
+ | **Use case** | Headless storefront | Shopify admin app (embedded) |
26
+ | **Who it's for** | Merchants building custom storefronts | Developers building Shopify apps |
27
+ | **Authentication** | Static access token in config | OAuth + per-merchant session token exchange |
28
+ | **Runs inside Shopify Admin** | No | Yes (App Bridge iframe) |
29
+ | **App Bridge** | No | Yes — CDN-based, auto-injected |
30
+ | **Webhooks** | No | Yes — HMAC-verified |
31
+ | **Billing API** | No | Yes |
32
+ | **Per-shop session storage** | No | Yes — pluggable adapters |
33
+ | **Equivalent to** | Shopify Hydrogen / storefront SDKs | `@shopify/shopify-app-react-router` |
34
+
35
+ **Use `@nuxtjs/shopify`** if you want to build a custom Nuxt storefront that queries products, collections, and customer data from a Shopify store using the Storefront or Admin API with a static token.
36
+
37
+ **Use `shopify-nuxt`** if you are building a Shopify app — something that runs inside the Shopify Admin, authenticates merchants via OAuth, handles session tokens, manages webhooks, and optionally charges merchants via the Billing API.
38
+
19
39
  ## Requirements
20
40
 
21
41
  To follow these usage guides, you will need to:
@@ -283,21 +303,36 @@ const shop = shopify.config.shop
283
303
 
284
304
  ### Authenticated fetch
285
305
 
286
- Use `useShopifyFetch()` for client-side API calls that automatically include the session token:
306
+ Use `useShopifyFetch()` for API calls that automatically include the Shopify session token. It works on both client and server — on the client it fetches a session token via App Bridge, on the server it forwards the incoming `Authorization` header.
287
307
 
288
308
  ```vue
289
309
  <script setup>
290
- const shopifyFetch = useShopifyFetch()
310
+ const { data: shop } = await useAsyncData(
311
+ 'shop',
312
+ () => useShopifyFetch('/api/shop'),
313
+ { server: false }
314
+ )
315
+ </script>
316
+ ```
317
+
318
+ With generic types for full type safety:
319
+
320
+ ```vue
321
+ <script setup lang="ts">
322
+ interface ShopData {
323
+ shop: { name: string; currencyCode: string }
324
+ }
291
325
 
292
- const { data: products } = await useAsyncData(
293
- 'products',
294
- () => shopifyFetch('/api/products'),
326
+ const { data } = await useAsyncData(
327
+ 'shop',
328
+ () => useShopifyFetch<ShopData>('/api/shop'),
295
329
  { server: false }
296
330
  )
331
+ // data.value.data.shop.name is fully typed
297
332
  </script>
298
333
  ```
299
334
 
300
- > **Important**: Always use `server: false` with `useShopifyFetch()` — session tokens are only available on the client side within the Shopify admin iframe.
335
+ > **Important**: Always use `server: false` with `useShopifyFetch()` in `useAsyncData` — session tokens are only available on the client side within the Shopify admin iframe.
301
336
 
302
337
  ## Using Polaris components
303
338
 
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shopify-nuxt",
3
3
  "configKey": "shopify",
4
- "version": "0.0.11",
4
+ "version": "0.0.13",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
@@ -0,0 +1 @@
1
+ export * from '@shopify/admin-api-client';
@@ -0,0 +1 @@
1
+ export * from "@shopify/admin-api-client";
@@ -1,13 +1,12 @@
1
1
  import type { NitroFetchRequest, TypedInternalResponse, AvailableRouterMethod } from 'nitropack/types';
2
2
  import type { RouterMethod } from 'h3';
3
3
  type ShopifyFetchData<T, R extends NitroFetchRequest, M extends RouterMethod> = [T] extends [undefined] ? TypedInternalResponse<R, unknown, M> : T;
4
- interface ShopifyFetchFunction {
5
- <T = undefined, R extends NitroFetchRequest = NitroFetchRequest, M extends AvailableRouterMethod<R> = 'get' extends AvailableRouterMethod<R> ? 'get' : AvailableRouterMethod<R>>(url: R, options?: Omit<RequestInit, 'method'> & {
6
- method?: Uppercase<M> | M;
7
- }): Promise<{
8
- data: ShopifyFetchData<T, R, Extract<Lowercase<M>, RouterMethod>>;
9
- response: Response;
10
- }>;
11
- }
12
- export declare function useShopifyFetch(): ShopifyFetchFunction;
4
+ type QueryParams = Record<string, string | number | boolean | null | undefined>;
5
+ export declare function useShopifyFetch<T = undefined, R extends NitroFetchRequest = NitroFetchRequest, M extends AvailableRouterMethod<R> = 'get' extends AvailableRouterMethod<R> ? 'get' : AvailableRouterMethod<R>>(url: R, options?: Omit<RequestInit, 'method'> & {
6
+ method?: Uppercase<M> | M;
7
+ query?: QueryParams;
8
+ }): Promise<{
9
+ data: ShopifyFetchData<T, R, Extract<Lowercase<M>, RouterMethod>>;
10
+ response: Response;
11
+ }>;
13
12
  export {};
@@ -1,52 +1,61 @@
1
1
  import { useNuxtApp, useRequestEvent } from "#app";
2
- export function useShopifyFetch() {
2
+ export async function useShopifyFetch(url, options) {
3
+ const { query, ...opts } = options ?? {};
3
4
  if (import.meta.server) {
4
5
  const event = useRequestEvent();
5
- return (async (url, options = {}) => {
6
- const headers = {};
7
- const authHeader = event?.headers.get("authorization");
8
- if (authHeader) {
9
- headers["Authorization"] = authHeader;
10
- }
11
- if (options.headers) {
12
- const incoming = new Headers(options.headers);
13
- incoming.forEach((value, key) => {
14
- headers[key] = value;
15
- });
16
- }
17
- const { method, ...rest } = options;
18
- const response = await globalThis.$fetch.raw(url, {
19
- ...rest,
20
- method,
21
- headers
6
+ const headers2 = {};
7
+ const authHeader = event?.headers.get("authorization");
8
+ if (authHeader) {
9
+ headers2["Authorization"] = authHeader;
10
+ }
11
+ if (opts.headers) {
12
+ const incoming = new Headers(opts.headers);
13
+ incoming.forEach((value, key) => {
14
+ headers2[key] = value;
22
15
  });
23
- return { data: response._data, response };
16
+ }
17
+ const { method, ...rest } = opts;
18
+ const fetchResponse2 = await globalThis.$fetch.raw(url, {
19
+ ...rest,
20
+ method,
21
+ headers: headers2,
22
+ query
24
23
  });
24
+ return { data: fetchResponse2._data, response: fetchResponse2 };
25
25
  }
26
26
  const nuxtApp = useNuxtApp();
27
- return (async (url, options = {}) => {
28
- const shopify = nuxtApp.$shopify;
29
- if (!shopify) {
30
- throw new Error(
31
- "Shopify App Bridge is not available. Make sure the app is loaded within the Shopify Admin."
32
- );
33
- }
34
- const token = await shopify.idToken();
35
- const headers = new Headers(options.headers || {});
36
- headers.set("Authorization", `Bearer ${token}`);
37
- const response = await fetch(url, {
38
- ...options,
39
- headers
40
- });
41
- if (!response.ok) {
42
- throw new Error(
43
- `Shopify fetch failed: ${response.status} ${response.statusText}`
44
- );
45
- }
46
- const contentType = response.headers.get("content-type");
47
- if (contentType?.includes("application/json")) {
48
- return { data: await response.json(), response };
27
+ const shopify = nuxtApp.$shopify;
28
+ if (!shopify) {
29
+ throw new Error(
30
+ "Shopify App Bridge is not available. Make sure the app is loaded within the Shopify Admin."
31
+ );
32
+ }
33
+ const token = await shopify.idToken();
34
+ const headers = new Headers(opts.headers || {});
35
+ headers.set("Authorization", `Bearer ${token}`);
36
+ let resolvedUrl = url;
37
+ if (query) {
38
+ const params = new URLSearchParams();
39
+ for (const [key, value] of Object.entries(query)) {
40
+ if (value !== null && value !== void 0) {
41
+ params.set(key, String(value));
42
+ }
49
43
  }
50
- return { data: await response.text(), response };
44
+ const qs = params.toString();
45
+ if (qs) resolvedUrl += (resolvedUrl.includes("?") ? "&" : "?") + qs;
46
+ }
47
+ const fetchResponse = await fetch(resolvedUrl, {
48
+ ...opts,
49
+ headers
51
50
  });
51
+ if (!fetchResponse.ok) {
52
+ throw new Error(
53
+ `Shopify fetch failed: ${fetchResponse.status} ${fetchResponse.statusText}`
54
+ );
55
+ }
56
+ const contentType = fetchResponse.headers.get("content-type");
57
+ if (contentType?.includes("application/json")) {
58
+ return { data: await fetchResponse.json(), response: fetchResponse };
59
+ }
60
+ return { data: await fetchResponse.text(), response: fetchResponse };
52
61
  }
@@ -1,5 +1,6 @@
1
1
  import type { H3Event } from 'h3';
2
2
  import type { AdminContext } from '../../types.js';
3
+ import type { JwtPayload } from '@shopify/shopify-api';
3
4
  /**
4
5
  * Authenticate an admin request and return an authenticated admin context.
5
6
  *
@@ -14,4 +15,4 @@ import type { AdminContext } from '../../types.js';
14
15
  * })
15
16
  * ```
16
17
  */
17
- export declare function useShopifyAdmin(event: H3Event): Promise<AdminContext>;
18
+ export declare function useShopifyAdmin<T extends object = JwtPayload>(event: H3Event): Promise<AdminContext<T>>;
@@ -0,0 +1 @@
1
+ export * from '@shopify/shopify-api';
@@ -0,0 +1 @@
1
+ export * from "@shopify/shopify-api";
@@ -0,0 +1 @@
1
+ export * from '@shopify/shopify-app-session-storage-memory';
@@ -0,0 +1 @@
1
+ export * from "@shopify/shopify-app-session-storage-memory";
@@ -0,0 +1 @@
1
+ export * from '@shopify/shopify-app-session-storage';
@@ -0,0 +1 @@
1
+ export * from "@shopify/shopify-app-session-storage";
@@ -0,0 +1 @@
1
+ export * from '@shopify/storefront-api-client';
@@ -0,0 +1 @@
1
+ export * from "@shopify/storefront-api-client";
@@ -87,13 +87,13 @@ export interface ShopifyRuntimeConfig {
87
87
  /** Billing configuration */
88
88
  billing?: Record<string, any>;
89
89
  }
90
- export interface AdminContext {
90
+ export interface AdminContext<T extends object = JwtPayload> {
91
91
  /** The authenticated session */
92
92
  session: Session;
93
93
  /** Admin API client (graphql + rest) */
94
94
  admin: AdminApiContext;
95
95
  /** The decoded session token (embedded apps only) */
96
- sessionToken?: JwtPayload;
96
+ sessionToken?: T;
97
97
  /** Billing helpers */
98
98
  billing: BillingContext;
99
99
  /** CORS header helper */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shopify-nuxt",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "Shopify app integration for Nuxt - authentication, webhooks, billing, and App Bridge",
5
5
  "repository": "kiriminaja/shopify-nuxt",
6
6
  "license": "MIT",
@@ -15,6 +15,26 @@
15
15
  "./test-helpers": {
16
16
  "types": "./dist/runtime/server/test-helpers/index.d.ts",
17
17
  "import": "./dist/runtime/server/test-helpers/index.js"
18
+ },
19
+ "./shopify-api": {
20
+ "types": "./dist/runtime/shopify-api.d.ts",
21
+ "import": "./dist/runtime/shopify-api.js"
22
+ },
23
+ "./shopify-app-session-storage": {
24
+ "types": "./dist/runtime/shopify-app-session-storage.d.ts",
25
+ "import": "./dist/runtime/shopify-app-session-storage.js"
26
+ },
27
+ "./shopify-app-session-storage-memory": {
28
+ "types": "./dist/runtime/shopify-app-session-storage-memory.d.ts",
29
+ "import": "./dist/runtime/shopify-app-session-storage-memory.js"
30
+ },
31
+ "./admin-api-client": {
32
+ "types": "./dist/runtime/admin-api-client.d.ts",
33
+ "import": "./dist/runtime/admin-api-client.js"
34
+ },
35
+ "./storefront-api-client": {
36
+ "types": "./dist/runtime/storefront-api-client.d.ts",
37
+ "import": "./dist/runtime/storefront-api-client.js"
18
38
  }
19
39
  },
20
40
  "main": "./dist/module.mjs",
@@ -23,6 +43,21 @@
23
43
  "*": {
24
44
  ".": [
25
45
  "./dist/types.d.mts"
46
+ ],
47
+ "shopify-api": [
48
+ "./dist/runtime/shopify-api.d.ts"
49
+ ],
50
+ "shopify-app-session-storage": [
51
+ "./dist/runtime/shopify-app-session-storage.d.ts"
52
+ ],
53
+ "shopify-app-session-storage-memory": [
54
+ "./dist/runtime/shopify-app-session-storage-memory.d.ts"
55
+ ],
56
+ "admin-api-client": [
57
+ "./dist/runtime/admin-api-client.d.ts"
58
+ ],
59
+ "storefront-api-client": [
60
+ "./dist/runtime/storefront-api-client.d.ts"
26
61
  ]
27
62
  }
28
63
  },
@@ -53,7 +88,7 @@
53
88
  "@shopify/shopify-app-session-storage": "^5.0.0",
54
89
  "@shopify/shopify-app-session-storage-memory": "^6.0.0",
55
90
  "@shopify/storefront-api-client": "^1.0.10",
56
- "h3": "^1.15.0",
91
+ "h3": "^2.0.0",
57
92
  "isbot": "^5.1.36"
58
93
  },
59
94
  "devDependencies": {