zod-codegen 1.0.3 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/CONTRIBUTING.md +1 -1
- package/EXAMPLES.md +730 -0
- package/PERFORMANCE.md +59 -0
- package/README.md +272 -58
- package/dist/src/services/code-generator.service.js +249 -45
- package/dist/src/types/openapi.js +1 -1
- package/dist/tests/unit/code-generator.test.js +290 -0
- package/dist/tests/unit/file-reader.test.js +110 -0
- package/dist/tests/unit/generator.test.js +77 -7
- package/eslint.config.mjs +1 -0
- package/examples/.gitkeep +3 -0
- package/examples/README.md +74 -0
- package/examples/petstore/README.md +121 -0
- package/examples/petstore/authenticated-usage.ts +60 -0
- package/examples/petstore/basic-usage.ts +51 -0
- package/examples/petstore/server-variables-usage.ts +63 -0
- package/examples/petstore/type.ts +217 -0
- package/examples/pokeapi/README.md +105 -0
- package/examples/pokeapi/basic-usage.ts +57 -0
- package/examples/pokeapi/custom-client.ts +56 -0
- package/examples/pokeapi/type.ts +109 -0
- package/package.json +4 -2
- package/samples/pokeapi-openapi.json +212 -0
- package/samples/server-variables-example.yaml +49 -0
- package/src/services/code-generator.service.ts +707 -88
- package/src/types/openapi.ts +1 -1
- package/tests/unit/code-generator.test.ts +321 -0
- package/tests/unit/file-reader.test.ts +134 -0
- package/tests/unit/generator.test.ts +99 -7
- package/tsconfig.examples.json +17 -0
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Petstore API Example
|
|
2
|
+
|
|
3
|
+
This example demonstrates how to use `zod-codegen` with the Swagger Petstore OpenAPI specification.
|
|
4
|
+
|
|
5
|
+
## Generated Files
|
|
6
|
+
|
|
7
|
+
After running `zod-codegen`, you'll get:
|
|
8
|
+
|
|
9
|
+
- `type.ts` - Contains all Zod schemas and the generated API client
|
|
10
|
+
|
|
11
|
+
## Basic Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import {SwaggerPetstoreOpenAPI30} from './type.js';
|
|
15
|
+
|
|
16
|
+
// Create a client instance using default server from OpenAPI spec
|
|
17
|
+
const client = new SwaggerPetstoreOpenAPI30({});
|
|
18
|
+
|
|
19
|
+
// Use the generated methods
|
|
20
|
+
const pets = await client.findPetsByStatus('available');
|
|
21
|
+
console.log('Available pets:', pets);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Server Configuration Options
|
|
25
|
+
|
|
26
|
+
The generated client supports flexible server configuration:
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './type.js';
|
|
30
|
+
|
|
31
|
+
// Option 1: Use default server (first server from OpenAPI spec)
|
|
32
|
+
const defaultClient = new SwaggerPetstoreOpenAPI30({});
|
|
33
|
+
|
|
34
|
+
// Option 2: Override with custom base URL
|
|
35
|
+
const customClient = new SwaggerPetstoreOpenAPI30({
|
|
36
|
+
baseUrl: 'https://custom-api.example.com/v3',
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Option 3: Select server by index (if multiple servers exist)
|
|
40
|
+
const indexedClient = new SwaggerPetstoreOpenAPI30({
|
|
41
|
+
serverIndex: 0,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Option 4: Use server variables (if your OpenAPI spec has templated URLs)
|
|
45
|
+
// Example spec: https://{environment}.example.com:{port}/v{version}
|
|
46
|
+
const variableClient = new SwaggerPetstoreOpenAPI30({
|
|
47
|
+
serverIndex: 0,
|
|
48
|
+
serverVariables: {
|
|
49
|
+
environment: 'api.staging',
|
|
50
|
+
port: '8443',
|
|
51
|
+
version: '2',
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Example: Finding Pets
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import {SwaggerPetstoreOpenAPI30, defaultBaseUrl} from './type.js';
|
|
60
|
+
|
|
61
|
+
async function findAvailablePets() {
|
|
62
|
+
const client = new SwaggerPetstoreOpenAPI30(defaultBaseUrl);
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
// Find pets by status
|
|
66
|
+
const availablePets = await client.findPetsByStatus('available');
|
|
67
|
+
console.log(`Found ${availablePets.length} available pets`);
|
|
68
|
+
|
|
69
|
+
// Find pets by tags
|
|
70
|
+
const taggedPets = await client.findPetsByTags(['friendly', 'cute']);
|
|
71
|
+
console.log(`Found ${taggedPets.length} tagged pets`);
|
|
72
|
+
|
|
73
|
+
// Get a specific pet by ID
|
|
74
|
+
const pet = await client.getPetById(1);
|
|
75
|
+
console.log('Pet details:', pet);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('Error fetching pets:', error);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
findAvailablePets();
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Example: Adding a Pet
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import {SwaggerPetstoreOpenAPI30, Pet, PetStatus, defaultBaseUrl} from './type.js';
|
|
88
|
+
import {z} from 'zod';
|
|
89
|
+
|
|
90
|
+
async function addNewPet() {
|
|
91
|
+
const client = new SwaggerPetstoreOpenAPI30(defaultBaseUrl);
|
|
92
|
+
|
|
93
|
+
const newPet: z.infer<typeof Pet> = {
|
|
94
|
+
id: 12345,
|
|
95
|
+
name: 'Fluffy',
|
|
96
|
+
status: PetStatus.enum.available,
|
|
97
|
+
category: {
|
|
98
|
+
id: 1,
|
|
99
|
+
name: 'Dogs',
|
|
100
|
+
},
|
|
101
|
+
photoUrls: ['https://example.com/fluffy.jpg'],
|
|
102
|
+
tags: [
|
|
103
|
+
{id: 1, name: 'friendly'},
|
|
104
|
+
{id: 2, name: 'cute'},
|
|
105
|
+
],
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
const addedPet = await client.addPet(newPet);
|
|
110
|
+
console.log('Pet added:', addedPet);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error('Error adding pet:', error);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
addNewPet();
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Example: Extending the Client
|
|
120
|
+
|
|
121
|
+
See the main [EXAMPLES.md](../../EXAMPLES.md) for comprehensive examples on extending the client for authentication, CORS, and custom headers.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example showing how to extend the generated client for authentication
|
|
3
|
+
*
|
|
4
|
+
* Run with: npx ts-node examples/petstore/authenticated-usage.ts
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './type.js';
|
|
8
|
+
|
|
9
|
+
class AuthenticatedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
10
|
+
private apiKey: string | null = null;
|
|
11
|
+
|
|
12
|
+
constructor(options: ClientOptions = {}) {
|
|
13
|
+
super(options);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
|
|
17
|
+
const options = super.getBaseRequestOptions();
|
|
18
|
+
return {
|
|
19
|
+
...options,
|
|
20
|
+
headers: {
|
|
21
|
+
...((options.headers as Record<string, string>) || {}),
|
|
22
|
+
...(this.apiKey ? {api_key: this.apiKey} : {}),
|
|
23
|
+
'User-Agent': 'PetstoreClient/1.0.0',
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
setApiKey(key: string): void {
|
|
29
|
+
this.apiKey = key;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
clearApiKey(): void {
|
|
33
|
+
this.apiKey = null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function main() {
|
|
38
|
+
const client = new AuthenticatedPetstoreAPI({});
|
|
39
|
+
|
|
40
|
+
// Set API key for authenticated requests
|
|
41
|
+
client.setApiKey('your-api-key-here');
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
console.log('🔐 Making authenticated request...\n');
|
|
45
|
+
|
|
46
|
+
// This endpoint typically requires authentication
|
|
47
|
+
const inventory = await client.getInventory();
|
|
48
|
+
console.log('✅ Inventory retrieved:');
|
|
49
|
+
console.log(JSON.stringify(inventory, null, 2));
|
|
50
|
+
} catch (error) {
|
|
51
|
+
if (error instanceof Error) {
|
|
52
|
+
console.error('❌ Error:', error.message);
|
|
53
|
+
} else {
|
|
54
|
+
console.error('❌ Unknown error:', error);
|
|
55
|
+
}
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
void main();
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Basic usage example for the generated Petstore API client
|
|
3
|
+
*
|
|
4
|
+
* Run with: npx ts-node examples/petstore/basic-usage.ts
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {SwaggerPetstoreOpenAPI30} from './type.js';
|
|
8
|
+
|
|
9
|
+
async function main() {
|
|
10
|
+
// Use default server (first server from OpenAPI spec)
|
|
11
|
+
const client = new SwaggerPetstoreOpenAPI30({});
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
console.log('🔍 Finding available pets...\n');
|
|
15
|
+
|
|
16
|
+
// Find pets by status
|
|
17
|
+
const availablePets = await client.findPetsByStatus('available');
|
|
18
|
+
console.log(`✅ Found ${availablePets.length} available pets`);
|
|
19
|
+
|
|
20
|
+
if (availablePets.length > 0) {
|
|
21
|
+
console.log('\n📋 First pet details:');
|
|
22
|
+
console.log(JSON.stringify(availablePets[0], null, 2));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Find pets by tags (if available)
|
|
26
|
+
try {
|
|
27
|
+
const taggedPets = await client.findPetsByTags(['friendly']);
|
|
28
|
+
console.log(`\n🏷️ Found ${taggedPets.length} pets with tags`);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.log('\n⚠️ Tags endpoint may not be available');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Get store inventory
|
|
34
|
+
try {
|
|
35
|
+
const inventory = await client.getInventory();
|
|
36
|
+
console.log('\n📦 Store inventory:');
|
|
37
|
+
console.log(JSON.stringify(inventory, null, 2));
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.log('\n⚠️ Inventory endpoint may require authentication');
|
|
40
|
+
}
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error instanceof Error) {
|
|
43
|
+
console.error('❌ Error:', error.message);
|
|
44
|
+
} else {
|
|
45
|
+
console.error('❌ Unknown error:', error);
|
|
46
|
+
}
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
void main();
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example showing how to use server variables for different environments
|
|
3
|
+
*
|
|
4
|
+
* Run with: npx ts-node examples/petstore/server-variables-usage.ts
|
|
5
|
+
*
|
|
6
|
+
* Note: This example uses a hypothetical API with server variables.
|
|
7
|
+
* For a real example, generate a client from an OpenAPI spec with server variables.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './type.js';
|
|
11
|
+
|
|
12
|
+
async function main() {
|
|
13
|
+
// Example 1: Use default server (first server from OpenAPI spec)
|
|
14
|
+
console.log('📡 Example 1: Using default server\n');
|
|
15
|
+
const defaultClient = new SwaggerPetstoreOpenAPI30({});
|
|
16
|
+
console.log('Default client created');
|
|
17
|
+
|
|
18
|
+
// Example 2: Override with custom baseUrl
|
|
19
|
+
console.log('\n📡 Example 2: Overriding with custom baseUrl\n');
|
|
20
|
+
const customClient = new SwaggerPetstoreOpenAPI30({
|
|
21
|
+
baseUrl: 'https://custom-api.example.com/v3',
|
|
22
|
+
});
|
|
23
|
+
console.log('Custom client created');
|
|
24
|
+
|
|
25
|
+
// Example 3: Select different server by index (if multiple servers exist)
|
|
26
|
+
console.log('\n📡 Example 3: Selecting server by index\n');
|
|
27
|
+
const indexedClient = new SwaggerPetstoreOpenAPI30({
|
|
28
|
+
serverIndex: 0, // Use first server
|
|
29
|
+
});
|
|
30
|
+
console.log('Indexed client created');
|
|
31
|
+
|
|
32
|
+
// Example 4: Using server variables (if your OpenAPI spec has templated URLs)
|
|
33
|
+
// For example: https://{environment}.example.com:{port}/v{version}
|
|
34
|
+
console.log('\n📡 Example 4: Using server variables\n');
|
|
35
|
+
const variableClient = new SwaggerPetstoreOpenAPI30({
|
|
36
|
+
serverIndex: 0,
|
|
37
|
+
serverVariables: {
|
|
38
|
+
// environment: 'api.staging', // Uncomment if your spec has these variables
|
|
39
|
+
// port: '8443',
|
|
40
|
+
// version: '2',
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
console.log('Variable client created');
|
|
44
|
+
|
|
45
|
+
// Example 5: Combining server selection with custom baseUrl override
|
|
46
|
+
console.log('\n📡 Example 5: Combining options\n');
|
|
47
|
+
const combinedClient = new SwaggerPetstoreOpenAPI30({
|
|
48
|
+
serverIndex: 0,
|
|
49
|
+
serverVariables: {
|
|
50
|
+
// Add variables here if your spec supports them
|
|
51
|
+
},
|
|
52
|
+
// baseUrl takes precedence if provided
|
|
53
|
+
// baseUrl: 'https://override.example.com',
|
|
54
|
+
});
|
|
55
|
+
console.log('Combined client created');
|
|
56
|
+
|
|
57
|
+
console.log('\n✅ All examples completed!');
|
|
58
|
+
console.log("\n💡 Tip: Check your OpenAPI spec's servers array to see available options.");
|
|
59
|
+
console.log(' Server variables allow you to switch between environments (dev, staging, prod)');
|
|
60
|
+
console.log(' without changing code!');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
void main();
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
|
2
|
+
// Built with zod-codegen@1.0.1
|
|
3
|
+
// Latest edit: Thu, 13 Nov 2025 13:36:35 GMT
|
|
4
|
+
// Source file: ./samples/swagger-petstore.yaml
|
|
5
|
+
/* eslint-disable */
|
|
6
|
+
// @ts-nocheck
|
|
7
|
+
|
|
8
|
+
// Imports
|
|
9
|
+
import {z} from 'zod';
|
|
10
|
+
|
|
11
|
+
// Components schemas
|
|
12
|
+
export const Order = z.object({
|
|
13
|
+
id: z.number().int().optional(),
|
|
14
|
+
petId: z.number().int().optional(),
|
|
15
|
+
quantity: z.number().int().optional(),
|
|
16
|
+
shipDate: z.string().datetime().optional(),
|
|
17
|
+
status: z.enum(['placed', 'approved', 'delivered']).optional(),
|
|
18
|
+
complete: z.boolean().optional(),
|
|
19
|
+
});
|
|
20
|
+
export const Address = z.object({
|
|
21
|
+
street: z.string().optional(),
|
|
22
|
+
city: z.string().optional(),
|
|
23
|
+
state: z.string().optional(),
|
|
24
|
+
zip: z.string().optional(),
|
|
25
|
+
});
|
|
26
|
+
export const Customer = z.object({
|
|
27
|
+
id: z.number().int().optional(),
|
|
28
|
+
username: z.string().optional(),
|
|
29
|
+
address: z.array(Address).optional(),
|
|
30
|
+
});
|
|
31
|
+
export const Category = z.object({
|
|
32
|
+
id: z.number().int().optional(),
|
|
33
|
+
name: z.string().optional(),
|
|
34
|
+
});
|
|
35
|
+
export const User = z.object({
|
|
36
|
+
id: z.number().int().optional(),
|
|
37
|
+
username: z.string().optional(),
|
|
38
|
+
firstName: z.string().optional(),
|
|
39
|
+
lastName: z.string().optional(),
|
|
40
|
+
email: z.string().optional(),
|
|
41
|
+
password: z.string().optional(),
|
|
42
|
+
phone: z.string().optional(),
|
|
43
|
+
userStatus: z.number().int().optional(),
|
|
44
|
+
});
|
|
45
|
+
export const Tag = z.object({
|
|
46
|
+
id: z.number().int().optional(),
|
|
47
|
+
name: z.string().optional(),
|
|
48
|
+
});
|
|
49
|
+
export const Pet = z.object({
|
|
50
|
+
id: z.number().int().optional(),
|
|
51
|
+
name: z.string(),
|
|
52
|
+
category: Category.optional(),
|
|
53
|
+
photoUrls: z.array(z.string()),
|
|
54
|
+
tags: z.array(Tag).optional(),
|
|
55
|
+
status: z.enum(['available', 'pending', 'sold']).optional(),
|
|
56
|
+
});
|
|
57
|
+
export const ApiResponse = z.object({
|
|
58
|
+
code: z.number().int().optional(),
|
|
59
|
+
type: z.string().optional(),
|
|
60
|
+
message: z.string().optional(),
|
|
61
|
+
});
|
|
62
|
+
export const serverConfigurations = [
|
|
63
|
+
{
|
|
64
|
+
url: 'https://petstore3.swagger.io/api/v3',
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
export const defaultBaseUrl = 'https://petstore3.swagger.io/api/v3';
|
|
68
|
+
export type ClientOptions = {
|
|
69
|
+
baseUrl?: string;
|
|
70
|
+
serverIndex?: number;
|
|
71
|
+
serverVariables?: Record<string, string>;
|
|
72
|
+
};
|
|
73
|
+
function resolveServerUrl(serverIndex?: number | undefined, serverVariables?: Record<string, string> = {}): string {
|
|
74
|
+
const configs = [
|
|
75
|
+
{
|
|
76
|
+
url: 'https://petstore3.swagger.io/api/v3',
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
const idx = serverIndex ?? 0;
|
|
80
|
+
if (idx < configs.length) {
|
|
81
|
+
const config = configs[idx];
|
|
82
|
+
let url = config.url;
|
|
83
|
+
if (config.variables && serverVariables) {
|
|
84
|
+
for (const [key, value] of Object.entries(serverVariables)) {
|
|
85
|
+
url = url.replace(new RegExp('\\{' + key + '\\}', 'g'), value);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return url;
|
|
89
|
+
}
|
|
90
|
+
return 'https://petstore3.swagger.io/api/v3';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Client class
|
|
94
|
+
export class SwaggerPetstoreOpenAPI30 {
|
|
95
|
+
readonly #baseUrl: string;
|
|
96
|
+
constructor(options: ClientOptions) {
|
|
97
|
+
const resolvedUrl =
|
|
98
|
+
options.baseUrl !== null ? options.baseUrl : resolveServerUrl(options.serverIndex, options.serverVariables);
|
|
99
|
+
this.#baseUrl = resolvedUrl;
|
|
100
|
+
}
|
|
101
|
+
protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
|
|
102
|
+
return {};
|
|
103
|
+
}
|
|
104
|
+
async #makeRequest<T>(
|
|
105
|
+
method: string,
|
|
106
|
+
path: string,
|
|
107
|
+
options: _params___Record_string__string___number___boolean___data___unknown__contentType___string__headers___Record_string__string__ = {},
|
|
108
|
+
): Promise<T> {
|
|
109
|
+
const baseUrl = `${this.#baseUrl}${path}`;
|
|
110
|
+
const url =
|
|
111
|
+
options.params && Object.keys(options.params).length > 0
|
|
112
|
+
? (() => {
|
|
113
|
+
Object.entries(options.params).forEach(([key, value]) => {
|
|
114
|
+
new URL(baseUrl).searchParams.set(key, String(value));
|
|
115
|
+
});
|
|
116
|
+
return new URL(baseUrl).toString();
|
|
117
|
+
})()
|
|
118
|
+
: new URL(baseUrl).toString();
|
|
119
|
+
const baseOptions = this.getBaseRequestOptions();
|
|
120
|
+
const contentType =
|
|
121
|
+
options.contentType === 'application/x-www-form-urlencoded'
|
|
122
|
+
? 'application/x-www-form-urlencoded'
|
|
123
|
+
: 'application/json';
|
|
124
|
+
const baseHeaders = baseOptions.headers !== undefined ? baseOptions.headers : {};
|
|
125
|
+
const headers = Object.assign(
|
|
126
|
+
{},
|
|
127
|
+
baseHeaders,
|
|
128
|
+
{'Content-Type': contentType},
|
|
129
|
+
options.headers !== undefined ? options.headers : {},
|
|
130
|
+
);
|
|
131
|
+
const body =
|
|
132
|
+
options.data !== undefined
|
|
133
|
+
? options.contentType === 'application/x-www-form-urlencoded'
|
|
134
|
+
? (() => {
|
|
135
|
+
const params = new URLSearchParams();
|
|
136
|
+
Object.entries(options.data).forEach(([key, value]) => {
|
|
137
|
+
params.set(key, String(value));
|
|
138
|
+
});
|
|
139
|
+
return params.toString();
|
|
140
|
+
})()
|
|
141
|
+
: JSON.stringify(options.data)
|
|
142
|
+
: null;
|
|
143
|
+
const response = await fetch(url, Object.assign({}, baseOptions, {method, headers: headers, body: body}));
|
|
144
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
145
|
+
return await response.json();
|
|
146
|
+
}
|
|
147
|
+
async addPet(body: Pet): Promise<Pet> {
|
|
148
|
+
return Pet.parse(
|
|
149
|
+
await this.#makeRequest('POST', '/pet', {data: body, contentType: 'application/x-www-form-urlencoded'}),
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
async updatePet(body: Pet): Promise<Pet> {
|
|
153
|
+
return Pet.parse(
|
|
154
|
+
await this.#makeRequest('PUT', '/pet', {data: body, contentType: 'application/x-www-form-urlencoded'}),
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
async findPetsByStatus(status?: string): Promise<Pet[]> {
|
|
158
|
+
return await this.#makeRequest('GET', '/pet/findByStatus', {params: {status: status}});
|
|
159
|
+
}
|
|
160
|
+
async findPetsByTags(tags?: string[]): Promise<Pet[]> {
|
|
161
|
+
return await this.#makeRequest('GET', '/pet/findByTags', {params: {tags: tags}});
|
|
162
|
+
}
|
|
163
|
+
async getPetById(petId: number): Promise<Pet> {
|
|
164
|
+
return Pet.parse(await this.#makeRequest('GET', `/pet/${petId}`, {}));
|
|
165
|
+
}
|
|
166
|
+
async updatePetWithForm(petId: number, name?: string, status?: string): Promise<void> {
|
|
167
|
+
return await this.#makeRequest('POST', `/pet/${petId}`, {params: {name: name, status: status}});
|
|
168
|
+
}
|
|
169
|
+
async deletePet(petId: number): Promise<void> {
|
|
170
|
+
return await this.#makeRequest('DELETE', `/pet/${petId}`, {});
|
|
171
|
+
}
|
|
172
|
+
async uploadFile(petId: number, additionalMetadata?: string): Promise<ApiResponse> {
|
|
173
|
+
return ApiResponse.parse(
|
|
174
|
+
await this.#makeRequest('POST', `/pet/${petId}/uploadImage`, {params: {additionalMetadata: additionalMetadata}}),
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
async getInventory(): Promise<Record<string, unknown>> {
|
|
178
|
+
return await this.#makeRequest('GET', '/store/inventory', {});
|
|
179
|
+
}
|
|
180
|
+
async placeOrder(body?: Order): Promise<Order> {
|
|
181
|
+
return Order.parse(
|
|
182
|
+
await this.#makeRequest('POST', '/store/order', {data: body, contentType: 'application/x-www-form-urlencoded'}),
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
async getOrderById(orderId: number): Promise<Order> {
|
|
186
|
+
return Order.parse(await this.#makeRequest('GET', `/store/order/${orderId}`, {}));
|
|
187
|
+
}
|
|
188
|
+
async deleteOrder(orderId: number): Promise<void> {
|
|
189
|
+
return await this.#makeRequest('DELETE', `/store/order/${orderId}`, {});
|
|
190
|
+
}
|
|
191
|
+
async createUser(body?: User): Promise<User> {
|
|
192
|
+
return User.parse(
|
|
193
|
+
await this.#makeRequest('POST', '/user', {data: body, contentType: 'application/x-www-form-urlencoded'}),
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
async createUsersWithListInput(body?: User[]): Promise<User> {
|
|
197
|
+
return User.parse(await this.#makeRequest('POST', '/user/createWithList', {data: body}));
|
|
198
|
+
}
|
|
199
|
+
async loginUser(username?: string, password?: string): Promise<string> {
|
|
200
|
+
return await this.#makeRequest('GET', '/user/login', {params: {username: username, password: password}});
|
|
201
|
+
}
|
|
202
|
+
async logoutUser(): Promise<void> {
|
|
203
|
+
return await this.#makeRequest('GET', '/user/logout', {});
|
|
204
|
+
}
|
|
205
|
+
async getUserByName(username: string): Promise<User> {
|
|
206
|
+
return User.parse(await this.#makeRequest('GET', `/user/${username}`, {}));
|
|
207
|
+
}
|
|
208
|
+
async updateUser(username: string, body?: User): Promise<void> {
|
|
209
|
+
return await this.#makeRequest('PUT', `/user/${username}`, {
|
|
210
|
+
data: body,
|
|
211
|
+
contentType: 'application/x-www-form-urlencoded',
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
async deleteUser(username: string): Promise<void> {
|
|
215
|
+
return await this.#makeRequest('DELETE', `/user/${username}`, {});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# PokéAPI Example
|
|
2
|
+
|
|
3
|
+
This example demonstrates how to use `zod-codegen` with the PokéAPI OpenAPI specification.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
First, generate the client from the PokéAPI OpenAPI spec:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
zod-codegen --input ./samples/pokeapi-openapi.json --output ./examples/pokeapi
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**Note**: PokéAPI doesn't provide an official OpenAPI specification, so we've created a simplified OpenAPI spec based on their API structure. For production use, you may want to create a more complete specification based on the [PokéAPI documentation](https://pokeapi.co/docs/v2).
|
|
14
|
+
|
|
15
|
+
## Basic Usage
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import {PokAPI, defaultBaseUrl} from './type.js';
|
|
19
|
+
|
|
20
|
+
// Create a client instance
|
|
21
|
+
const client = new PokAPI(defaultBaseUrl);
|
|
22
|
+
|
|
23
|
+
// Use the generated methods
|
|
24
|
+
const pokemon = await client.getPokemonById('pikachu');
|
|
25
|
+
console.log('Pokemon:', pokemon);
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Example: Fetching Pokémon Data
|
|
29
|
+
|
|
30
|
+
See [basic-usage.ts](./basic-usage.ts) for a complete example:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import {PokAPI, defaultBaseUrl} from './type.js';
|
|
34
|
+
|
|
35
|
+
async function getPokemonInfo() {
|
|
36
|
+
const client = new PokAPI(defaultBaseUrl);
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
// Get a specific Pokémon by ID or name
|
|
40
|
+
const pikachu = await client.getPokemonById('pikachu');
|
|
41
|
+
console.log(`Name: ${pikachu.name}`);
|
|
42
|
+
console.log(`Height: ${pikachu.height} dm`);
|
|
43
|
+
console.log(`Weight: ${pikachu.weight} hg`);
|
|
44
|
+
|
|
45
|
+
if (pikachu.types && pikachu.types.length > 0) {
|
|
46
|
+
const types = pikachu.types
|
|
47
|
+
.map((t) => t.type?.name)
|
|
48
|
+
.filter(Boolean)
|
|
49
|
+
.join(', ');
|
|
50
|
+
console.log(`Types: ${types}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Get Pokémon list with pagination
|
|
54
|
+
const pokemonList = await client.getPokemonList(20, 0);
|
|
55
|
+
console.log(`\nTotal Pokémon: ${pokemonList.count}`);
|
|
56
|
+
console.log(`Showing: ${pokemonList.results.length} results`);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error('Error fetching Pokémon:', error);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getPokemonInfo();
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Run the example:**
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npx ts-node examples/pokeapi/basic-usage.ts
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Example: Extending for Custom Headers
|
|
72
|
+
|
|
73
|
+
See [custom-client.ts](./custom-client.ts) for a complete example:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import {PokAPI, defaultBaseUrl} from './type.js';
|
|
77
|
+
|
|
78
|
+
class CustomPokeAPI extends PokAPI {
|
|
79
|
+
protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
|
|
80
|
+
const options = super.getBaseRequestOptions();
|
|
81
|
+
return {
|
|
82
|
+
...options,
|
|
83
|
+
headers: {
|
|
84
|
+
...((options.headers as Record<string, string>) || {}),
|
|
85
|
+
'User-Agent': 'MyPokemonApp/1.0.0',
|
|
86
|
+
Accept: 'application/json',
|
|
87
|
+
},
|
|
88
|
+
mode: 'cors',
|
|
89
|
+
cache: 'default',
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const client = new CustomPokeAPI(defaultBaseUrl);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Run the example:**
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
npx ts-node examples/pokeapi/custom-client.ts
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Note
|
|
104
|
+
|
|
105
|
+
PokéAPI is a public API that doesn't require authentication, making it perfect for testing and learning. The generated client will work out of the box without any additional configuration.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Basic usage example for the generated PokéAPI client
|
|
3
|
+
*
|
|
4
|
+
* Run with: npx ts-node examples/pokeapi/basic-usage.ts
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {PokAPI, defaultBaseUrl} from './type.js';
|
|
8
|
+
|
|
9
|
+
async function main() {
|
|
10
|
+
const client = new PokAPI(defaultBaseUrl);
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
console.log('🔍 Fetching Pokémon data...\n');
|
|
14
|
+
|
|
15
|
+
// Get a specific Pokémon by ID or name
|
|
16
|
+
console.log('📖 Getting Pikachu...');
|
|
17
|
+
const pikachu = await client.getPokemonById('pikachu');
|
|
18
|
+
console.log(`✅ Found: ${pikachu.name}`);
|
|
19
|
+
console.log(` ID: ${pikachu.id}`);
|
|
20
|
+
console.log(` Height: ${pikachu.height} dm`);
|
|
21
|
+
console.log(` Weight: ${pikachu.weight} hg`);
|
|
22
|
+
|
|
23
|
+
if (pikachu.types && pikachu.types.length > 0) {
|
|
24
|
+
const types = pikachu.types
|
|
25
|
+
.map((t) => t.type?.name)
|
|
26
|
+
.filter(Boolean)
|
|
27
|
+
.join(', ');
|
|
28
|
+
console.log(` Types: ${types}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (pikachu.sprites?.front_default) {
|
|
32
|
+
console.log(` Sprite: ${pikachu.sprites.front_default}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Get Pokémon list
|
|
36
|
+
console.log('\n📋 Getting Pokémon list...');
|
|
37
|
+
const pokemonList = await client.getPokemonList(10, 0);
|
|
38
|
+
console.log(`✅ Found ${pokemonList.count} total Pokémon`);
|
|
39
|
+
console.log(` Showing first ${pokemonList.results.length} results:`);
|
|
40
|
+
|
|
41
|
+
pokemonList.results.slice(0, 5).forEach((pokemon, index) => {
|
|
42
|
+
console.log(` ${index + 1}. ${pokemon.name}`);
|
|
43
|
+
});
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if (error instanceof Error) {
|
|
46
|
+
console.error('❌ Error:', error.message);
|
|
47
|
+
if (error.message.includes('HTTP')) {
|
|
48
|
+
console.error(' This might be a network issue or the API endpoint may have changed.');
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
console.error('❌ Unknown error:', error);
|
|
52
|
+
}
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
void main();
|