
Product
Introducing Repository Access Permissions and Custom Roles
Socket now supports Custom Roles and Repository Access Permissions so organizations can control who can access specific repositories and actions.
@cerios/openapi-to-zod-playwright
Advanced tools
Generate type-safe Playwright API clients from OpenAPI specifications with Zod validation
Generate type-safe Playwright API clients from OpenAPI specifications with Zod validation.
ApiRequestContext for API testingexpect() for status checksincludeStatusCodes: ["2xx"])ignoreHeaders: ["Authorization"])basePath: "/api/v1")validateServiceRequest)For complete Zod schema generation features, see the @cerios/openapi-to-zod README:
z.inferz.discriminatedUnion()npm install @cerios/openapi-to-zod-playwright
Note: Peer dependencies (
@cerios/openapi-to-zod,@playwright/test,zod) are automatically installed by npm v7+. If you're using an older npm version, install them manually.
npx openapi-to-zod-playwright init
This will guide you through creating a configuration file:
? Input OpenAPI file path: openapi.yaml
? Output file path for schemas and types: tests/schemas.ts
? Output file path for client class: tests/client.ts
? Config file format: TypeScript (recommended)
? Include commonly-used defaults? Yes
Creates openapi-to-zod-playwright.config.ts:
import { defineConfig } from "@cerios/openapi-to-zod-playwright";
export default defineConfig({
defaults: {
mode: "normal",
validateServiceRequest: false,
},
specs: [
{
input: "openapi.yaml",
outputTypes: "tests/schemas.ts",
outputClient: "tests/client.ts",
},
],
});
npx openapi-to-zod-playwright
import { test, expect } from "@playwright/test";
import { ApiClient, ApiService } from "./api-client";
test("create and get user", async ({ request }) => {
const client = new ApiClient(request);
const service = new ApiService(client);
// Create user - validates request and response
const user = await service.postUsers201({
data: {
email: "test@example.com",
name: "Test User",
age: 25,
},
});
expect(user.email).toBe("test@example.com");
// Get user by ID
const fetchedUser = await service.getUsersByUserId(user.id);
expect(fetchedUser.id).toBe(user.id);
});
test("handle error response", async ({ request }) => {
const client = new ApiClient(request);
const service = new ApiService(client);
// Test error scenario
const response = await service.postUsersError({
data: { email: "invalid" }, // Missing required 'name'
});
expect(response.status()).toBe(400);
});
This package generates up to three separate files:
z.inferoutputTypes file// Generated schemas
export const userSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string(),
});
export type User = z.infer<typeof userSchema>;
Generated in the outputClient file:
ApiRequestContextPartial<> for flexibilityAPIResponseconst client = new ApiClient(request);
// Can send invalid data for testing
const response = await client.postUsers({
data: { invalid: "data" },
});
Generated when outputService is specified:
validateServiceRequest: true)expect()const service = new ApiService(client);
// Validates request and response - throws on invalid data
const user = await service.postUsers201({
data: { email: "test@example.com", name: "Test" },
});
Methods are named using the pattern: {httpMethod}{PascalCasePath}{StatusCode?}
Examples:
GET /users → getUsers() (single 200 response)POST /users → postUsers201() (201 response)GET /users/{userId} → getUsersByUserId(userId: string) (single 200 response)DELETE /users/{userId} → deleteUsersByUserId(userId: string) (204 response)POST /users → postUsersError() (error testing method)getUsers())postUsers200(), postUsers201())Error suffix for testing failures (e.g., getUsersByUserIdError())The stripPathPrefix option removes common prefixes from API paths before generating method names and documentation. This creates cleaner, more readable method names while keeping the actual HTTP requests intact when combined with basePath.
export default defineConfig({
specs: [
{
input: "openapi.yaml",
outputTypes: "schemas.ts",
outputClient: "client.ts",
stripPathPrefix: "/api/v1.0", // Strip this prefix from all paths
basePath: "/api/v1.0", // Add it back for HTTP requests
},
],
});
OpenAPI Spec:
paths:
/api/v1.0/users:
get:
summary: Get all users
/api/v1.0/posts:
get:
summary: Get all posts
Without stripPathPrefix:
// Method names generated from full path
getApiV10Users(); // GET /api/v1.0/users
getApiV10Posts(); // GET /api/v1.0/posts
With stripPathPrefix: '/api/v1.0':
// Method names generated from stripped path (cleaner)
getUsers(); // GET /users (shown in JSDoc)
getPosts(); // GET /posts (shown in JSDoc)
// Actual HTTP requests use basePath
// GET {baseURL}/api/v1.0/users
// GET {baseURL}/api/v1.0/posts
Use glob patterns to strip dynamic prefixes (e.g., version numbers):
export default defineConfig({
specs: [
{
input: "openapi.yaml",
outputTypes: "schemas.ts",
outputClient: "client.ts",
// Strip any versioned API prefix using wildcards
stripPathPrefix: "/api/v*",
},
],
});
Matches:
/api/v1.0/users → /users/api/v2.5/posts → /posts/api/v10.3/products → /productsGlob Pattern Syntax:
Glob patterns support powerful matching using minimatch:
* matches any characters within a single path segment (stops at /)** matches any characters across multiple path segments (crosses / boundaries)? matches a single character[abc] matches any character in the set{a,b} matches any of the alternatives!(pattern) matches anything except the pattern// Examples of glob patterns:
stripPathPrefix: "/api/v*"; // Matches /api/v1, /api/v2, /api/v10
stripPathPrefix: "/api/**/v1"; // Matches /api/v1, /api/internal/v1, /api/public/v1
stripPathPrefix: "/api/v*.*"; // Matches /api/v1.0, /api/v2.5
stripPathPrefix: "/api/v[0-9]"; // Matches /api/v1, /api/v2
stripPathPrefix: "/api/{v1,v2}"; // Matches /api/v1 or /api/v2
stripPathPrefix: "/!(internal)/**"; // Matches any path except those starting with /internal/
stripPathPrefix handles various input formats:
// All of these work the same:
stripPathPrefix: "/api/v1"; // Preferred
stripPathPrefix: "api/v1"; // Normalized to /api/v1
stripPathPrefix: "/api/v1/"; // Trailing slash removed
Pattern 1: Clean Version Prefixes
{
stripPathPrefix: '/api/v1.0',
basePath: '/api/v1.0'
}
// Paths: /api/v1.0/users → getUsers() → GET /api/v1.0/users
Pattern 2: Multiple API Versions with Wildcard
{
stripPathPrefix: '/api/v*',
basePath: '/api/v2' // Or determined at runtime
}
// All versions stripped, base path can vary
Pattern 3: Versioned Paths with Dots
{
stripPathPrefix: '/api/v*.*',
basePath: '/api/v2.0'
}
// Matches: /api/v1.0, /api/v2.5, etc.
Pattern 4: Organization Prefix
{
stripPathPrefix: '/myorg/api',
basePath: '/myorg/api'
}
// Paths: /myorg/api/users → getUsers() → GET /myorg/api/users
getUsers() from path /users instead of getApiV10Users() from /api/v1.0/users/users instead of /api/v1.0/users in documentationbasePath for requestsnpx openapi-to-zod-playwright init
Interactive prompts guide you through:
# Auto-discovers openapi-to-zod-playwright.config.{ts,json}
npx openapi-to-zod-playwright
# Or specify config file explicitly
npx openapi-to-zod-playwright --config path/to/config.ts
# Display version
npx openapi-to-zod-playwright --version
# Display help
npx openapi-to-zod-playwright --help
# Display help for init command
npx openapi-to-zod-playwright init --help
outputTypesis the preferred config key for schemas/types output. Deprecated alias:outputis still supported for backward compatibility. You must provide one ofoutputTypesoroutputper spec. If both are provided, they must have the same value.
openapi-to-zod-playwright.config.ts:
import { defineConfig } from "@cerios/openapi-to-zod-playwright";
export default defineConfig({
defaults: {
mode: "strict",
includeDescriptions: true,
showStats: false,
validateServiceRequest: false, // Optional request validation
},
specs: [
{
input: "specs/api-v1.yaml",
outputTypes: "src/generated/api-v1.ts",
outputClient: "src/generated/api-v1-client.ts",
},
{
input: "specs/api-v2.yaml",
outputTypes: "src/generated/api-v2.ts",
outputClient: "src/generated/api-v2-client.ts",
outputService: "src/generated/api-v2-service.ts",
stripPathPrefix: "/api/v2", // Strip prefix from paths for cleaner method names
basePath: "/api/v2", // Prepend base path to all endpoints
mode: "normal",
prefix: "v2",
ignoreHeaders: ["Authorization", "X-*"], // Ignore specific headers
operationFilters: {
includeTags: ["public"], // Only include operations with 'public' tag
includeStatusCodes: ["2xx", "4xx"], // Only generate for success and client errors
},
useOperationId: false, // Use path-based method names (default)
},
],
executionMode: "parallel", // or 'serial'
});
openapi-to-zod-playwright.config.json:
{
"defaults": {
"mode": "strict",
"includeDescriptions": true,
"showStats": false
},
"specs": [
{
"input": "specs/api-v1.yaml",
"outputTypes": "src/generated/api-v1.ts",
"outputClient": "src/generated/api-v1-client.ts"
},
{
"input": "specs/api-v2.yaml",
"outputTypes": "src/generated/api-v2.ts",
"outputClient": "src/generated/api-v2-client.ts",
"mode": "normal",
"prefix": "v2"
}
],
"executionMode": "parallel"
}
| Option | Type | Description | Default |
|---|---|---|---|
outputTypes | string | Preferred output path for generated schemas/types (required unless deprecated output is set) | undefined |
output | string | Deprecated alias for outputTypes; allowed for backward compatibility | undefined |
outputClient | string | Optional path for client class file | undefined |
outputService | string | Optional path for service class file (requires outputClient) | undefined |
validateServiceRequest | boolean | Enable Zod validation for request bodies in service methods | false |
stripPathPrefix | string | Strip prefix from paths before generating method names using glob patterns (literal string or glob pattern) | undefined |
ignoreHeaders | string[] | Header patterns to ignore (supports glob patterns like "X-*", "*") | undefined |
basePath | string | Base path to prepend to all endpoints (e.g., "/api/v1") | undefined |
useOperationId | boolean | Use operationId from spec for method names | false |
operationFilters | object | Filter operations (see below) | undefined |
If outputTypes and output are both set with different values, configuration validation fails.
| Filter | Type | Description |
|---|---|---|
includeTags | string[] | Include only operations with these tags |
excludeTags | string[] | Exclude operations with these tags |
includePaths | string[] | Include only these paths (supports glob patterns) |
excludePaths | string[] | Exclude these paths (supports glob patterns) |
includeMethods | string[] | Include only these HTTP methods |
excludeMethods | string[] | Exclude these HTTP methods |
includeOperationIds | string[] | Include only these operationIds |
excludeOperationIds | string[] | Exclude these operationIds |
excludeDeprecated | boolean | Exclude deprecated operations |
includeStatusCodes | string[] | Include only these status codes (e.g., ["2xx", "404"]) |
excludeStatusCodes | string[] | Exclude these status codes (e.g., ["5xx"]) |
For schema generation options inherited from @cerios/openapi-to-zod (like mode, includeDescriptions, prefix, suffix, etc.), see the @cerios/openapi-to-zod Configuration.
Common inherited options:
mode: "strict" | "normal" | "loose" - Validation strictnessincludeDescriptions: Include JSDoc comments in generated schemasshowStats: Include generation statistics in outputprefix/suffix: Add prefixes/suffixes to schema namesdefaultNullable: Treat properties as nullable by default when not explicitly specified (default: false)customDateTimeFormatRegex: Custom regex pattern for date-time validation (see below)uuidFormat: UUID/GUID validation format — "uuid" (default), "guid", or "uuidv1" through "uuidv8" (see below)By default, date-time format fields use z.iso.datetime(), which requires timezone suffix (Z). If your API returns date-times without the Z suffix (e.g., 2026-01-07T14:30:00), you can override this:
import { defineConfig } from "@cerios/openapi-to-zod-playwright";
export default defineConfig({
defaults: {
// For date-times without Z suffix (JSON/YAML config)
customDateTimeFormatRegex: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
},
specs: [
{
input: "openapi.yaml",
outputTypes: "src/schemas.ts",
outputClient: "src/client.ts",
},
],
});
Or using RegExp literal in TypeScript config:
export default defineConfig({
defaults: {
// TypeScript config - RegExp literal (single escaping)
customDateTimeFormatRegex: /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/,
},
specs: [{ input: "openapi.yaml", outputTypes: "src/schemas.ts", outputClient: "src/client.ts" }],
});
See the @cerios/openapi-to-zod Custom Date-Time Format documentation for more examples and patterns.
By default, uuid and guid format fields use z.uuid(). You can change this with the uuidFormat option:
import { defineConfig } from "@cerios/openapi-to-zod-playwright";
export default defineConfig({
defaults: {
uuidFormat: "uuidv4",
},
specs: [
{
input: "openapi.yaml",
outputTypes: "src/schemas.ts",
outputClient: "src/client.ts",
},
],
});
See the @cerios/openapi-to-zod UUID Format documentation for all available values.
// 200 OK with body
const users = await service.getUsers(); // Promise<User[]>
// 201 Created with body
const user = await service.postUsers201({ data: userData }); // Promise<User>
// 204 No Content
const result = await service.deleteUsersByUserId(userId); // Promise<null>
// Test error scenarios without validation
const response = await service.getUsersByUserIdError("invalid-id");
expect(response.status()).toBe(404);
const errorBody = await response.json();
expect(errorBody.message).toContain("not found");
Path parameters are extracted and become required method arguments in path order:
paths:
/orgs/{orgId}/repos/{repoId}:
get:
# ...
Generated:
// Path params in order
async getOrgsByOrgIdReposByRepoId(
orgId: string,
repoId: string,
options?: { query?: Record<string, any> }
): Promise<Repo>
All optional request properties are grouped in an options parameter:
interface RequestOptions {
query?: Record<string, any>; // Query parameters
headers?: Record<string, string>; // Request headers
data?: T; // Request body (JSON)
form?: Record<string, any>; // Form data
multipart?: Record<string, any>; // Multipart form data
}
await service.postUsers201({
data: { email: "test@example.com", name: "Test User" }, // Validated with Zod
headers: { "X-Custom": "value" },
});
// All properties are Partial - allows invalid data
await client.postUsers({
data: { invalid: "data" }, // No validation
});
test("successful user creation", async ({ request }) => {
const service = new ApiService(new ApiClient(request));
const user = await service.postUsers201({
data: { email: "test@example.com", name: "Test" },
});
// Automatically validated: status 201, response matches User schema
expect(user.id).toBeTruthy();
});
test("handle validation errors", async ({ request }) => {
const service = new ApiService(new ApiClient(request));
const response = await service.postUsersError({
data: { email: "invalid" }, // Missing required 'name'
});
expect(response.status()).toBe(400);
const error = await response.json();
expect(error.message).toContain("validation");
});
test("send invalid data", async ({ request }) => {
const client = new ApiClient(request);
// Use client directly to bypass validation
const response = await client.postUsers({
data: { completely: "wrong" },
});
expect(response.status()).toBe(400);
});
OpenApiPlaywrightGenerator - Main generator class for creating Playwright API clientsdefineConfig - Type-safe helper for creating configuration filesOpenApiPlaywrightGeneratorOptions - Options interface for the generatorPlaywrightConfigFile - Config file schema typePlaywrightOperationFilters - Playwright-specific operation filters (extends base filters with status code filtering)ZodErrorFormat - Zod error formatting options: "standard" | "prettify" | "prettifyWithValues"These utilities are exported for use in generated code:
serializeParams() - Serialize query/path parameters for HTTP requestsformatZodErrorPath() - Format Zod validation error path for readable messagesformatZodErrorWithValues() - Format Zod errors including the actual values that failedparseWithPrettifyError() - Parse with Zod and throw prettified errors on failureparseWithPrettifyErrorWithValues() - Parse with Zod including failed values in error messagesApiRequestContextOptions - Options for Playwright API request contextHttpHeaders - Type for HTTP header objectsMultipartFormData - Type for multipart form dataMultipartFormValue - Type for multipart form valuesQueryParams - Type for query string parametersRequestBody - Type for request body dataUrlEncodedFormData - Type for URL-encoded form dataClientGenerationError - Error thrown during client generation@cerios/openapi-core: CircularReferenceError, CliOptionsError, ConfigValidationError, FileOperationError, GeneratorError, SchemaGenerationError, SpecValidationErrorMIT © Ronald Veth - Cerios
Contributions are welcome! Please feel free to submit a Pull Request.
For issues and questions, please use the GitHub issues page.
FAQs
Generate type-safe Playwright API clients from OpenAPI specifications with Zod validation
The npm package @cerios/openapi-to-zod-playwright receives a total of 66 weekly downloads. As such, @cerios/openapi-to-zod-playwright popularity was classified as not popular.
We found that @cerios/openapi-to-zod-playwright demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Socket now supports Custom Roles and Repository Access Permissions so organizations can control who can access specific repositories and actions.

Product
Socket MCP now lets AI assistants review org alerts, investigate threats using the Socket threat feed, and inspect package files in addition to dependency scoring.

Product
Socket Firewall blocks malicious VS Code and Open VSX extensions before install, protecting developers from compromised editor marketplaces.