0.2.3 • Published 9 months ago

openapi2zod v0.2.3

Weekly downloads
-
License
MIT
Repository
github
Last release
9 months ago

openapi2zod

Convert OpenAPI schemas to Zod schemas effortlessly.

Description

openapi2zod is a utility library that converts OpenAPI JSON schemas into Zod schemas. This allows developers to leverage the power of Zod for schema validation and parsing in their TypeScript projects.

Whether you're working with APIs and need to validate incoming data against your OpenAPI definitions, or you're integrating with tools like Vercel AI, openapi2zod simplifies the process by generating Zod schemas directly from your OpenAPI JSON.

Features

  • Easy Conversion: Convert OpenAPI JSON schemas to Zod schemas with minimal effort.
  • TypeScript Support: Strongly typed schemas for better developer experience.
  • Flexible Integration: Use the generated Zod schemas in any context where Zod is supported.
  • Supports OpenAPI 3.x: Compatible with OpenAPI 3.x specifications.

Installation

Install openapi2zod using npm:

npm i openapi2zod

or with yarn

yarn add openapi2zod

or with pnpm

pnpm i openapi2zod

Usage

You can import the generated bundle to use the whole library generated by this starter:

import { parseOpenApiToZod } from "openapi2zod";

// Convert OpenAPI properties to Zod schema
const zodRecords = parseOpenApiToZod(openapiSchema);

for (const [name, schema] of Object.entries(zodRecords)) {
  // Current schema metadata, if defined.
  const metadata = {};

  try {
    Object.assign(metadata, JSON.parse(schema._def.description || ''));
  } catch (e) {
    console.log(`[Dynamic Tool Generation] :: Error parsing metadata for ${name}. Skipping...`);
    continue;
  }

  // Current schema parameters, see our test-cases for various HTTP verbs both with requestBody and parameters.
  const requestArgs = schema,
    { description, operationId, path, method } = metadata;

  console.log({ 
    name,
    description,
    operationId,
    path,
    method,
    requestArgs 
  });

  // Do the rest of operation you needed with fetch etc..
}

Example with Vercel AI

Here's an example of how to use openapi2zod with Vercel AI to dynamically generate tools from an OpenAPI URL:

'use server';
import { CoreTool, tool } from 'ai';
import { unstable_noStore as noStore } from 'next/cache';
import { parseOpenApiToZod } from "openapi2zod";

// Define a type for tools with execute defined
type ExecutableTool = CoreTool<any, any> & {
  execute: (args: any) => PromiseLike<any>;
};

export async function generateAIToolByOpenApiUrl(openapiUrl: string, headers?: Record<string, string>) {
  /**
   * Deal with nextjs static caching
   * @see {@link https://github.com/vercel/storage/issues/510}
   */
  noStore();

  console.log(`[Dynamic Tool Generation] :: Fetching OpenAPI schema from ${openapiUrl}`);

  // Fetch OpenAPI schema from the given URL
  const openapiSchema = await fetch(openapiUrl, {
    cache: "no-cache",
    headers,
  }).then((res) => res.json());

  // Convert OpenAPI properties to Zod schema
  const zodRecords = parseOpenApiToZod(openapiSchema);

  // Output result
  const result: Record<string, ExecutableTool> = {};

  for ( const [name, schema] of Object.entries(zodRecords) ) {
    // Current schema description, if defined.
    const metadata: Record<string, string | undefined> = {};

    try {
      Object.assign(metadata, JSON.parse(schema._def.description || ''));
    }
    catch (e) {
      console.log(`[Dynamic Tool Generation] :: Error parsing metadata for ${name}. Skipping...`);
      continue;
    }

    if ( !metadata ) {
      console.log(`[Dynamic Tool Generation] :: No metadata found for ${name}. Skipping...`);
      continue;
    }

    // Current schema parameters, see our test-cases for various http verbs both with requestBody and parameters.
    const requestArgs = schema,
      { description, operationId, path, method } = metadata,
      serverUrl = openapiSchema.servers?.[0]?.url || ( new URL(openapiUrl) ).origin;

    if ( !operationId || !description || !path || !method ) {
      console.log(`[Dynamic Tool Generation] :: Missing required metadata for ${name}. Skipping...`);
      continue;
    }

    const extTool = tool({
      description: description,
      parameters: requestArgs,
      execute: async (params) => {
        // Determine the request options
        const options: RequestInit = {
          method,
          headers: {
            'Content-Type': 'application/json',
            ...headers,
          },
        };

        // For GET and DELETE, include params as query string instead of request body
        let url = `${serverUrl}${path}`;
        if (["GET", "DELETE"].includes(method.toUpperCase())) {
          const queryParams = new URLSearchParams(params).toString();
          url = queryParams ? `${url}?${queryParams}` : url;
        } else {
          // Only add `body` for methods that support it (POST, PUT, PATCH)
          options.body = JSON.stringify(params);
        }

        // Make the request using fetch
        const response = await fetch(url, options);
        console.log(`[Dynamic Tool Call] :: Request to ${url} returned status ${response.status}`);

        const result = await response.json();
        
        // Return response in a standardized format
        return result;
      },
    });

    // Add the tool to the result.
    result[operationId] = extTool;
  }

  return result;
}

export default generateAIToolByOpenApiUrl;

This example demonstrates how to fetch an OpenAPI schema, convert it to Zod schemas, and dynamically generate tools that can be used with Vercel AI.

Contributing

Contributions are welcome! If you'd like to contribute to openapi2zod, please open an issue or submit a pull request on GitHub.

NPM scripts

  • npm t: Run test suite
  • npm start: Run npm run build in watch mode
  • npm run test:watch: Run test suite in interactive watch mode
  • npm run test:prod: Run linting and generate coverage
  • npm run build: Generate bundles and typings, create docs
  • npm run lint: Lints code
  • npm run commit: Commit using conventional commit style (husky will tell you to use it if you haven't :wink:)

License

This project is licensed under the MIT License.

Acknowledgments

This project was created with TypeScript Library Starter.

Contact

If you have any questions or issues, please open an issue on the GitHub repository.

0.2.1

9 months ago

0.2.0

9 months ago

0.2.3

9 months ago

0.2.2

9 months ago

0.1.0

10 months ago