Skip to content
This repository was archived by the owner on Apr 11, 2024. It is now read-only.

Revamp REST client to match new clients #1069

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/fifty-buckets-love.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@shopify/shopify-api": minor
"@shopify/storefront-api-client": patch
"@shopify/admin-api-client": patch
"@shopify/graphql-client": patch
---

Use the new GraphQL API clients in shopify-api to use all of the latest features, including automatic types for query / mutation return object and variables.

For more information and examples, see the [migration guide to v9](/packages/shopify-api/docs/migrating-to-v9.md#using-the-new-clients).
13 changes: 13 additions & 0 deletions .changeset/rich-ladybugs-own.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"@shopify/shopify-api": major
---

> [!NOTE]
> This change only affects apps that are using custom runtime adapters.
> If you're using a default adapter from this package, you don't need to make this change.

Changed `setAbstractFetchFunc` to accept a `fetch` API instead of one based on `NormalizedRequest` and `NormalizedResponse`.

With this change, we can return a `Response` object for requests with the upcoming clients, which can help make the interface for requests more familiar to users.

For more information and examples, see the [migration guide to v9](/packages/shopify-api/docs/migrating-to-v9.md#changes-to-runtime-adapters).
2 changes: 1 addition & 1 deletion packages/admin-api-client/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { createAdminApiClient } from "./admin-api-client";
export { AdminQueries, AdminMutations } from "./types";
export { AdminApiClient, AdminQueries, AdminMutations } from "./types";
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { GraphQLClient } from "../graphql-client/types";

export type InputMaybe<_R = never> = never;

export interface AllOperations {
Expand Down Expand Up @@ -27,7 +25,7 @@ export type OperationVariables<
};
};

export type ResponseWithType<T = any> = Omit<GraphQLClient["fetch"], "json"> & {
export type ResponseWithType<T = any> = Response & {
json: () => Promise<T>;
};

Expand Down
2 changes: 1 addition & 1 deletion packages/shopify-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ To do that, apps will need to:
1. Create an Admin API access token by going through [the OAuth flow](docs/guides/oauth.md).
1. Set up its own endpoints to:
1. [Fetch the current session](docs/guides/oauth.md#using-sessions) created in the OAuth process.
1. Create a [REST](docs/reference/clients/Rest.md) or [GraphQL](docs/reference/clients/Graphql.md) API client.
1. Create a [REST](docs/reference/clients/admin-rest.md) or [GraphQL](docs/reference/clients/admin-graphql.md) API client.
1. Use the client to query the appropriate [Admin API](https://shopify.dev/api/admin).

## Guides
Expand Down
3 changes: 1 addition & 2 deletions packages/shopify-api/adapters/cf-worker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ import {
webApiConvertHeaders,
webApiConvertRequest,
webApiConvertResponse,
webApiFetch,
} from '../web-api/adapter';

import {workerRuntimeString} from './adapter';

setAbstractFetchFunc(webApiFetch);
setAbstractFetchFunc(fetch);
setAbstractConvertRequestFunc(webApiConvertRequest);
setAbstractConvertResponseFunc(webApiConvertResponse);
setAbstractConvertHeadersFunc(webApiConvertHeaders);
Expand Down
47 changes: 35 additions & 12 deletions packages/shopify-api/adapters/mock/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import {
Headers as FetchHeaders,
Request,
RequestInit,
Response,
} from 'node-fetch';

import {
AbstractFetchFunc,
AdapterArgs,
AdapterHeaders,
canonicalizeHeaders,
Expand Down Expand Up @@ -33,30 +41,45 @@ export async function mockConvertHeaders(
return Promise.resolve(headers);
}

export async function mockFetch({
url,
method,
headers = {},
body,
}: NormalizedRequest): Promise<NormalizedResponse> {
export const mockFetch: AbstractFetchFunc = async (url, init) => {
const mockInit = init as RequestInit;

const request = new Request(url as string, mockInit);
const headers = Object.fromEntries(
new FetchHeaders(mockInit?.headers).entries(),
);

mockTestRequests.requestList.push({
url,
method,
url: request.url,
method: request.method,
headers: canonicalizeHeaders(headers),
body,
body: await request.text(),
});

const next = mockTestRequests.responseList.shift()!;
if (!next) {
throw new Error(
`Missing mock for ${method} to ${url}, have you queued all required responses?`,
`Missing mock for ${request.method} to ${url}, have you queued all required responses?`,
);
}
if (next instanceof Error) {
throw next;
}
return next;
}

const responseHeaders = new FetchHeaders();
Object.entries(next.headers ?? {}).forEach(([key, value]) => {
responseHeaders.set(
key,
typeof value === 'string' ? value : value.join(', '),
);
});

return new Response(next.body, {
status: next.statusCode,
statusText: next.statusText,
headers: responseHeaders as any,
}) as any;
};

export function mockRuntimeString() {
return 'Mock adapter';
Expand Down
19 changes: 0 additions & 19 deletions packages/shopify-api/adapters/node/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import type {IncomingMessage, ServerResponse} from 'http';

import fetch from 'node-fetch';

import {
AdapterArgs,
canonicalizeHeaders,
flatHeaders,
Headers,
NormalizedRequest,
NormalizedResponse,
Expand Down Expand Up @@ -72,22 +69,6 @@ export async function nodeConvertAndSetHeaders(
);
}

export async function nodeFetch({
url,
method,
headers = {},
body,
}: NormalizedRequest): Promise<NormalizedResponse> {
const resp = await fetch(url, {method, headers: flatHeaders(headers), body});
const respBody = await resp.text();
return {
statusCode: resp.status,
statusText: resp.statusText,
body: respBody,
headers: canonicalizeHeaders(Object.fromEntries(resp.headers.entries())),
};
}

export function nodeRuntimeString() {
return `Node ${process.version}`;
}
7 changes: 5 additions & 2 deletions packages/shopify-api/adapters/node/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import crypto from 'crypto';

import fetch from 'node-fetch';

import {
setAbstractFetchFunc,
setAbstractConvertRequestFunc,
Expand All @@ -8,18 +10,19 @@ import {
setAbstractConvertHeadersFunc,
setAbstractRuntimeString,
setCrypto,
AbstractFetchFunc,
} from '../../runtime';

import {
nodeFetch,
nodeConvertRequest,
nodeConvertIncomingResponse,
nodeConvertAndSendResponse,
nodeConvertAndSetHeaders,
nodeRuntimeString,
} from './adapter';

setAbstractFetchFunc(nodeFetch);
// For the purposes of this package, fetch correctly implements everything we need
setAbstractFetchFunc(fetch as any as AbstractFetchFunc);
setAbstractConvertRequestFunc(nodeConvertRequest);
setAbstractConvertIncomingResponseFunc(nodeConvertIncomingResponse);
setAbstractConvertResponseFunc(nodeConvertAndSendResponse);
Expand Down
22 changes: 1 addition & 21 deletions packages/shopify-api/adapters/web-api/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type {
NormalizedResponse,
NormalizedRequest,
} from '../../runtime';
import {addHeader, canonicalizeHeaders, flatHeaders} from '../../runtime';
import {addHeader, flatHeaders} from '../../runtime';

interface WebApiAdapterArgs extends AdapterArgs {
rawRequest: Request;
Expand Down Expand Up @@ -48,26 +48,6 @@ export async function webApiConvertResponse(
});
}

export async function webApiFetch({
headers,
method,
url,
body,
}: NormalizedRequest): Promise<NormalizedResponse> {
const resp = await fetch(url, {
method,
headers: flatHeaders(headers),
body,
});
const respBody = await resp.text();
return {
statusCode: resp.status,
statusText: resp.statusText,
body: respBody,
headers: canonicalizeHeaders(Object.fromEntries(resp.headers.entries())),
};
}

export function webApiRuntimeString(): string {
return 'Web API';
}
3 changes: 1 addition & 2 deletions packages/shopify-api/adapters/web-api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ import {
webApiConvertHeaders,
webApiConvertRequest,
webApiConvertResponse,
webApiFetch,
webApiRuntimeString,
} from './adapter';

setAbstractFetchFunc(webApiFetch);
setAbstractFetchFunc(fetch);
setAbstractConvertRequestFunc(webApiConvertRequest);
setAbstractConvertResponseFunc(webApiConvertResponse);
setAbstractConvertHeadersFunc(webApiConvertHeaders);
Expand Down
Loading