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 +41 -6
- package/dist/module.json +1 -1
- package/dist/runtime/admin-api-client.d.ts +1 -0
- package/dist/runtime/admin-api-client.js +1 -0
- package/dist/runtime/composables/useShopifyFetch.d.ts +8 -9
- package/dist/runtime/composables/useShopifyFetch.js +51 -42
- package/dist/runtime/server/utils/authenticate-admin.d.ts +2 -1
- package/dist/runtime/shopify-api.d.ts +1 -0
- package/dist/runtime/shopify-api.js +1 -0
- package/dist/runtime/shopify-app-session-storage-memory.d.ts +1 -0
- package/dist/runtime/shopify-app-session-storage-memory.js +1 -0
- package/dist/runtime/shopify-app-session-storage.d.ts +1 -0
- package/dist/runtime/shopify-app-session-storage.js +1 -0
- package/dist/runtime/storefront-api-client.d.ts +1 -0
- package/dist/runtime/storefront-api-client.js +1 -0
- package/dist/runtime/types.d.ts +2 -2
- package/package.json +37 -2
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
|
|
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
|
|
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
|
|
293
|
-
'
|
|
294
|
-
() =>
|
|
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
|
@@ -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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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";
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -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?:
|
|
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.
|
|
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": "^
|
|
91
|
+
"h3": "^2.0.0",
|
|
57
92
|
"isbot": "^5.1.36"
|
|
58
93
|
},
|
|
59
94
|
"devDependencies": {
|