Skip to content

Commit ce8ace8

Browse files
committed
feat: support path-index call style (client["/path"].GET(...))
1 parent 8e5fa3a commit ce8ace8

File tree

3 files changed

+46
-48
lines changed

3 files changed

+46
-48
lines changed

packages/openapi-fetch/src/index.d.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,13 @@ export type ClientMethod<
167167
: [Init & { [key: string]: unknown }]
168168
) => Promise<FetchResponse<Paths[Path][Method], Init, Media>>;
169169

170-
export interface Client<Paths extends {}, Media extends MediaType = MediaType> {
170+
export type ClientForPath<PathInfo extends Record<HttpMethod, {}>, Media extends MediaType> = {
171+
[Method in keyof PathInfo as Uppercase<string & Method>]: <
172+
Init extends MaybeOptionalInit<PathInfo, Method>,
173+
>() => Promise<FetchResponse<PathInfo[Method], Init, Media>>;
174+
};
175+
176+
export type Client<Paths extends Record<string, Record<HttpMethod, {}>>, Media extends MediaType = MediaType> = {
171177
/** Call a GET endpoint */
172178
GET: ClientMethod<Paths, "get", Media>;
173179
/** Call a PUT endpoint */
@@ -188,7 +194,9 @@ export interface Client<Paths extends {}, Media extends MediaType = MediaType> {
188194
use(...middleware: Middleware[]): void;
189195
/** Unregister middleware */
190196
eject(...middleware: Middleware[]): void;
191-
}
197+
} & {
198+
[Path in keyof Paths]: ClientForPath<Paths[Path], Media>;
199+
};
192200

193201
export default function createClient<Paths extends {}, Media extends MediaType = MediaType>(
194202
clientOptions?: ClientOptions,

packages/openapi-fetch/src/index.js

+23-33
Original file line numberDiff line numberDiff line change
@@ -176,39 +176,10 @@ export default function createClient(clientOptions) {
176176
return { error, response };
177177
}
178178

179-
return {
180-
/** Call a GET endpoint */
181-
async GET(url, init) {
182-
return coreFetch(url, { ...init, method: "GET" });
183-
},
184-
/** Call a PUT endpoint */
185-
async PUT(url, init) {
186-
return coreFetch(url, { ...init, method: "PUT" });
187-
},
188-
/** Call a POST endpoint */
189-
async POST(url, init) {
190-
return coreFetch(url, { ...init, method: "POST" });
191-
},
192-
/** Call a DELETE endpoint */
193-
async DELETE(url, init) {
194-
return coreFetch(url, { ...init, method: "DELETE" });
195-
},
196-
/** Call a OPTIONS endpoint */
197-
async OPTIONS(url, init) {
198-
return coreFetch(url, { ...init, method: "OPTIONS" });
199-
},
200-
/** Call a HEAD endpoint */
201-
async HEAD(url, init) {
202-
return coreFetch(url, { ...init, method: "HEAD" });
203-
},
204-
/** Call a PATCH endpoint */
205-
async PATCH(url, init) {
206-
return coreFetch(url, { ...init, method: "PATCH" });
207-
},
208-
/** Call a TRACE endpoint */
209-
async TRACE(url, init) {
210-
return coreFetch(url, { ...init, method: "TRACE" });
211-
},
179+
// Poor-man's set.
180+
const methods = { GET: 0, PUT: 0, POST: 0, DELETE: 0, OPTIONS: 0, HEAD: 0, PATCH: 0, TRACE: 0 };
181+
182+
const coreClient = {
212183
/** Register middleware */
213184
use(...middleware) {
214185
for (const m of middleware) {
@@ -231,6 +202,25 @@ export default function createClient(clientOptions) {
231202
}
232203
},
233204
};
205+
206+
const handler = {
207+
get: (coreClient, property) => {
208+
if (property in coreClient) {
209+
return coreClient[property];
210+
}
211+
212+
if (property in methods) {
213+
return (url, init) => coreFetch(url, { ...init, method: property });
214+
}
215+
216+
// Assume the property is a URL.
217+
return Object.fromEntries(
218+
Object.keys(methods).map((method) => [method, (init) => coreFetch(property, { ...init, method })]),
219+
);
220+
},
221+
};
222+
223+
return new Proxy(coreClient, handler);
234224
}
235225

236226
// utils

packages/openapi-fetch/test/index.test.ts

+13-13
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,6 @@ afterEach(() => server.resetHandlers());
2121
afterAll(() => server.close());
2222

2323
describe("client", () => {
24-
it("generates all proper functions", () => {
25-
const client = createClient<paths>();
26-
27-
expect(client).toHaveProperty("GET");
28-
expect(client).toHaveProperty("PUT");
29-
expect(client).toHaveProperty("POST");
30-
expect(client).toHaveProperty("DELETE");
31-
expect(client).toHaveProperty("OPTIONS");
32-
expect(client).toHaveProperty("HEAD");
33-
expect(client).toHaveProperty("PATCH");
34-
expect(client).toHaveProperty("TRACE");
35-
});
36-
3724
describe("TypeScript checks", () => {
3825
it("marks data or error as undefined, but never both", async () => {
3926
const client = createClient<paths>({
@@ -1843,6 +1830,19 @@ describe("client", () => {
18431830
);
18441831
});
18451832
});
1833+
1834+
describe("path style call", () => {
1835+
it("sends the correct method", async () => {
1836+
const client = createClient<paths>({ baseUrl });
1837+
const { getRequest } = useMockRequestHandler({
1838+
baseUrl,
1839+
method: "get",
1840+
path: "/anyMethod",
1841+
});
1842+
await client["/anyMethod"].GET();
1843+
expect(getRequest().method).toBe("GET");
1844+
});
1845+
});
18461846
});
18471847

18481848
// test that the library behaves as expected inside commonly-used patterns

0 commit comments

Comments
 (0)