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 |
Beta Was this translation helpful? Give feedback.
-
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 😅 |
Beta Was this translation helpful? Give feedback.
-
I think we should just lean into option 1, using |
Beta Was this translation helpful? Give feedback.
-
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 |
Beta Was this translation helpful? Give feedback.
-
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. |
Beta Was this translation helpful? Give feedback.
-
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?
Beta Was this translation helpful? Give feedback.
All reactions