Replies: 5 comments 5 replies
|
I don't think Option 2 is a good option, you'd have to redefine the type signature unnecessarily it seems and the signature can still drift away from the actual types. Is there anything in openapi-fetch that we directly benefit from ? Is it feasible to use generics to build up the simpler |
|
I agree, I'm not fully comfortable with any option right now, that is why I value your comments and opinions. We could avoid signature drift using something like this: import { client, ToolShedApiPaths } from "@/schema"
type MaliciousPathParams =
ToolShedApiPaths["/api/repositories/{encoded_repository_id}/revisions/{changeset_revision}/malicious"]["put"]["parameters"]["path"]
const setMaliciousFetcher = (params: MaliciousPathParams) =>
client.PUT("/api/repositories/{encoded_repository_id}/revisions/{changeset_revision}/malicious", {
params: {
path: params,
},
})But it is still very verbose and ugly to me. I guess Option 3 could be to stick with
So it seems there is no huge difference between them, other than choosing one verbosity over the other 😅 |
|
I think we should just lean into option 1, using |
|
This is how it looks so far in the Toolshed #18532 I must say that it feels a little better to write even with the extra explicitness. Another point in favor is that the response "data" can be |
|
The first migration phase (convert all fetcher calls to Now comes the second phase, converting the The recommended library for mocking API responses by openapi-fetch is Mock Service Worker which seems pretty powerful and future-proof. I have experimented a bit with it in combination with openapi-msw and it looks very promising if we want to go this route. Here is an example of how mocking the API will look with this new approach: import { client, type HistoryDetailed, type HistorySummary, type MessageException } from "@/api";
import { clientMock } from "@/api/__mocks__";
const TEST_HISTORY_SUMMARY: HistorySummary = {
model_class: "History",
id: "test",
name: "Test History",
archived: false,
deleted: false,
purged: false,
published: false,
update_time: "2021-09-01T00:00:00",
count: 0,
annotation: "Test History Annotation",
tags: [],
url: "/api/histories/test",
};
const TEST_HISTORY_DETAILED: HistoryDetailed = {
...TEST_HISTORY_SUMMARY,
create_time: "2021-09-01T00:00:00",
contents_url: "/api/histories/test/contents",
importable: false,
slug: "testSlug",
size: 0,
user_id: "userID",
username_and_slug: "username/slug",
state: "ok",
empty: true,
hid_counter: 0,
genome_build: null,
state_ids: {},
state_details: {},
};
const EXPECTED_500_ERROR: MessageException = { err_code: 500, err_msg: "Internal Server Error" };
// Mock the API client
// You can do whatever you want with the parameters and return values
// All API schema types must be strictly respected and will be up to date with the OpenAPI schema
clientMock.get("/api/histories/{history_id}", ({ params, query, response }) => {
if (query.get("view") === "detailed") {
return response(200).json(TEST_HISTORY_DETAILED);
}
if (params.history_id === "must-fail") {
return response("5XX").json(EXPECTED_500_ERROR, { status: 500 });
}
return response(200).json(TEST_HISTORY_SUMMARY);
});
describe("clientMock", () => {
it("mocks the API client", async () => {
{
const { data, error } = await client.GET("/api/histories/{history_id}", {
params: {
path: { history_id: "test" },
query: { view: "summary" },
},
});
expect(error).toBeUndefined();
expect(data).toBeDefined();
expect(data).toEqual(TEST_HISTORY_SUMMARY);
}
{
const { data, error } = await client.GET("/api/histories/{history_id}", {
params: {
path: { history_id: "test" },
query: { view: "detailed" },
},
});
expect(error).toBeUndefined();
expect(data).toBeDefined();
expect(data).toEqual(TEST_HISTORY_DETAILED);
}
{
const { data, error } = await client.GET("/api/histories/{history_id}", {
params: {
path: { history_id: "must-fail" },
},
});
expect(error).toBeDefined();
expect(error).toEqual(EXPECTED_500_ERROR);
expect(data).toBeUndefined();
}
});
});Notice how all the types are strictly enforced and are in sync with the current OpenAPI schema. I still need to figure out an issue with Webpack trying to import the MSW library. |
Uh oh!
There was an error while loading. Please reload this page.
I'm migrating the code from openapi-typescript-fetch to openapi-fetch. And I would like to take this opportunity to decide on a convention we all are comfortable with.
A typical call using the previous openapi-typescript-fetch looks like this:
The new openapi-fetch is more explicit about path, query, and body parameters and has different syntax for calling the API.
See documentation here
I'm considering the following conventions:
Option 1: Use the new openapi-fetch as is everywhere
It may be harder to swap to a different framework later but it should be more straightforward.
Option 2: Create a fetcher function that wraps the new openapi-fetch
It creates a facade to have a single point of change in case we want to migrate to a different framework in the future. On the other side, it is kind of redundant to handle the parameters and types in the function when the client already does it.
Option 3: Your suggestion
Please post your suggestions in the comments.
Testing / Mocking
The openapi-fetch documentation has some recommendations for testing. Should we follow these recommendations? Like installing and using Mock Service Worker for mocking responses?
All reactions