Skip to content
Merged
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
18 changes: 9 additions & 9 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ The generated file contains:

```ts
// What gets generated (simplified)
import { createSdk as coreCreateSdk, ClientInstance, Request } from "@hyper-fetch/core";
import { createSdk as coreCreateSdk } from "@hyper-fetch/core";
import type { ClientInstance, RequestModel } from "@hyper-fetch/core";

// Schema types extracted from your OpenAPI document
export interface Components {
Expand All @@ -162,17 +163,16 @@ export interface Components {
};
}

// Typed request for each endpoint with operationId
export const getUsers = client.createRequest<{
response: Components["schemas"]["User"][];
}>()({
endpoint: "/users",
method: "GET",
});
// Request model for each endpoint with operationId
export type SdkSchema = {
users: {
$get: RequestModel<{ response: Components["schemas"]["User"][]; endpoint: "/users" }>;
};
};

// SDK tree you import and use
export const createSdk = <Client extends ClientInstance>(client: Client) =>
coreCreateSdk<Client, SdkSchema<Client>>(client);
coreCreateSdk<Client, SdkSchema>(client);
```

## ⚠️ Important Notes
Expand Down
7 changes: 4 additions & 3 deletions packages/cli/__tests__/codegen/openapi/v3/generator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,12 +246,13 @@ describe("Generator", () => {
const compact = sdkSchema.replaceAll(/\s+/g, " ");

expect(compact).toContain(
'users: { $userId: { postsList: { $get: Request<GetUsers$userIdPostsListResponseType, undefined, GetUsers$userIdPostsListQueryParams, GetUsers$userIdPostsListErrorType, "/users/:userId/posts-list", Client>;',
'users: { $userId: { postsList: { $get: RequestModel<{ response: GetUsers$userIdPostsListResponseType; queryParams: GetUsers$userIdPostsListQueryParams; error: GetUsers$userIdPostsListErrorType; endpoint: "/users/:userId/posts-list" }>;',
);
expect(compact).toContain(
'$post: Request<CreatePostResponseType, CreatePostRequestBody, undefined, CreatePostErrorType, "/users/:userId/posts-list", Client>;',
'$post: RequestModel<{ response: CreatePostResponseType; payload: CreatePostRequestBody; error: CreatePostErrorType; endpoint: "/users/:userId/posts-list" }>;',
);
expect(compact).toContain('$get: Request<GetResponseType, undefined, undefined, GetErrorType, "/", Client>;');
expect(compact).toContain('$get: RequestModel<{ response: GetResponseType; error: GetErrorType; endpoint: "/" }>;');
expect(compact).toContain("export type SdkSchema = {");
expect(generatedTypes.join("\n")).toContain(
"export type GetUsers$userIdPostsListResponseType = Paths.Users$UserIdPostsList.Get.Responses.$200",
);
Expand Down
31 changes: 23 additions & 8 deletions packages/cli/src/codegen/openapi/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ export class OpenapiRequestGenerator {
const defaultFileName = "openapi.client";
const { schemaTypes, generatedTypes, sdkSchema, createSdkFn } = await this.generateRequestsFromSchema();
const contents = [
`import { createSdk as coreCreateSdk, ClientInstance, Request } from "@hyper-fetch/core";`,
`import { createSdk as coreCreateSdk } from "@hyper-fetch/core";`,
"\n",
`import type { ClientInstance, RequestModel } from "@hyper-fetch/core";`,
"\n\n",
schemaTypes,
"\n\n",
Expand Down Expand Up @@ -111,7 +113,7 @@ export class OpenapiRequestGenerator {
getAvailableOperations(this.openapiDocument).forEach((operation) => {
const meta = OpenapiRequestGenerator.generateMethodMetadata(operation, exportedTypes);
const operationTypes = OpenapiRequestGenerator.generateTypes(meta);
const requestInstanceType = OpenapiRequestGenerator.generateRequestInstanceType(meta, operationTypes);
const requestModelType = OpenapiRequestGenerator.generateRequestModelType(meta, operationTypes);

generatedTypes.push(Object.values(operationTypes).join("\n"));

Expand All @@ -138,24 +140,24 @@ export class OpenapiRequestGenerator {
currentLevel = currentLevel[key];
}
// Prefix method names with $
currentLevel[`$${method.toLowerCase()}`] = requestInstanceType;
currentLevel[`$${method.toLowerCase()}`] = requestModelType;
});

const sdkSchema = `export type SdkSchema<Client extends ClientInstance> = {\n${formatSchema(schemaTree)}\n}`;
const sdkSchema = `export type SdkSchema = {\n${formatSchema(schemaTree)}\n}`;

const createSdkFn = `

export type { Components };

export const createSdk = <Client extends ClientInstance>(client: Client, options?: Parameters<typeof coreCreateSdk>[1] | undefined) => {
return coreCreateSdk<Client, SdkSchema<Client>>(client, options);
return coreCreateSdk<Client, SdkSchema>(client, options);
};
`;

return { schemaTypes, generatedTypes, sdkSchema, createSdkFn };
};

static generateRequestInstanceType(
static generateRequestModelType(
{ id, path: endpoint, queryParamsRequired }: { id: string; path: string; queryParamsRequired?: boolean },
types: Record<string, string>,
) {
Expand All @@ -168,9 +170,22 @@ export const createSdk = <Client extends ClientInstance>(client: Client, options
? `${createTypeBaseName(id)}QueryParams`
: undefined;

const QueryParamsGeneric = QueryParams && !queryParamsRequired ? `${QueryParams} | undefined` : QueryParams;
const fields: string[] = [];
if (Response) {
fields.push(`response: ${Response}`);
}
if (Payload) {
fields.push(`payload: ${Payload}`);
}
if (QueryParams) {
fields.push(`${queryParamsRequired ? "queryParams" : "queryParams?"}: ${QueryParams}`);
}
if (LocalError) {
fields.push(`error: ${LocalError}`);
}
fields.push(`endpoint: "${endpoint}"`);

return `Request<${Response}, ${Payload}, ${QueryParamsGeneric}, ${LocalError}, "${endpoint}", Client>`;
return `RequestModel<{ ${fields.join("; ")} }>`;
}

static generateHyperFetchRequest(
Expand Down
Loading