> ## Content Index
> Fetch the complete content index at: https://www.bramadams.dev/llms.txt
> Use this file to discover other available public pages before exploring further.

# Writing OpenAPI with... OpenAI
- URL: https://www.bramadams.dev/202304021706/
- Published: 2023-04-02T21:17:26.000Z
- Updated: 2023-04-02T21:17:26.000Z
- Description: How can developers quickly and easily convert TypeScript handler functions into OpenAPI specs?
- Author: Bram Adams
- Tags: programming

With the announcement of ChatGPT Plugins using the OpenAPI spec, developers the world over simultaneously thought: "damn, maybe I should've made those Swagger docs after all, lmao".

For NextJS users (at least), the I wrote a program that automatically introspects the `/api` folder and finds endpoints within the handler method and uses GPT to infer an OpenAPI spec. Meta!!

How does this work?

First, we define two helper functions: `getApiRoutes` that uses glob to find all TypeScript files in the `pages/api` directory, and `getHandlerFunction` that extracts the handler function from each file using Babel to parse the code and returns an array of `{name, content}` objects with the name being the filename, and content being a string slice containing only the handler function code.

Then, we define a function called `inferOpenApiSpec`, which calls both previously defined helper functions to obtain an array of handlers with their respective contents, then loops over each item in this array.  
For each handler function **found**, it sends a message as input to GPT-3 along with some preamble text describing how to convert TS functions into JSDoc YAML.

Once it receives a response from GPT-3 it writes resulting contents into `./openapi/{handlerFunction.name}.js`.

```ts
/* 
1. get the handler function from files
2. use gpt to infer the openapi yaml from the spec
3. return the result as a string
*/
import { glob } from "glob";
import { readFileWrapper, writeFileWrapper } from "./helpers";
import { loadEnvConfig } from "@next/env";
loadEnvConfig(process.cwd());

import { Configuration, OpenAIApi } from "openai";
const configuration = new Configuration({
  organization: "…",
  apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);

const SKIP_FILES = []; // files to skip from api folder

import * as babel from "@babel/core";
// needs to be required instead of imported to work
const parser = require("@babel/parser");

const getApiRoutes = async () => {
  const apiRoutes = await glob("pages/api/**/*.ts");
  return apiRoutes;
};

const getHandlerFunction = async (apiRoutes: string[]) => {
  const handlerFunctions = [];
  for (const apiRoute of apiRoutes) {
    const file = await readFileWrapper(apiRoute);

    const ast = parser.parse(file, {
      sourceType: "module",
      plugins: ["typescript"],
    });

    babel.traverse(ast, {
      ExportDefaultDeclaration(path) {
        const declaration = path.node.declaration;
        if (
          declaration.type === "FunctionDeclaration" &&
          declaration.id.name === "handler"
        ) {

          handlerFunctions.push({ content: file.slice(declaration.start, declaration.end), name: apiRoute.slice(10, -3) });
        }
      },
    });
  }

  return handlerFunctions.filter((handlerFunction) => !SKIP_FILES.includes(handlerFunction.name));
};

const inferOpenApiSpec = async () => {
  const apiRoutes = await getApiRoutes();
  const handlerFunctions = await getHandlerFunction(apiRoutes);

  for (const handlerFunction of handlerFunctions) {
    console.log(`Reading ${handlerFunction.name}`);
    const completion = await openai.createChatCompletion({
      model: "gpt-3.5-turbo",
      messages: [
        {
          role: "system",
          content:
            "I am a bot that converts handler functions into OpenAPI specs (the path only). I only return YAML in JSDoc format.",
        },
        {
          role: "user",
          content:
            `I want to convert this handler function with path /api/${handlerFunction.name} into an OpenAPI spec: ${handlerFunction.content}`
        },
      ],
    });
    const res = completion.data.choices[0].message.content;
    const openApiSpec = res.slice(res.indexOf("```yaml") + 7, res.indexOf("```", res.indexOf("```yaml") + 7));
    let extractedJSDoc = openApiSpec.slice(openApiSpec.indexOf("/**") + 3, openApiSpec.indexOf("*/"))
    .replace("openapi", "swagger")
    
    extractedJSDoc = "/**\n" + extractedJSDoc + "\n*/\n";
    console.log("Full response:");
    console.log(res)
    console.log(`Writing ${handlerFunction.name}`);
    await writeFileWrapper(`./openapi/${handlerFunction.name}.js`, extractedJSDoc);
  }
};

inferOpenApiSpec();

```

**bramadams.dev is a reader-supported [published Zettelkasten](https://www.bramadams.dev/about/). [Both free and paid subscriptions are available](#/portal). If you want to support my work, the best way is by taking out a paid subscription.**