AutoDisco is a tool for automatic discovery of REST APIs that do not provide an OpenAPI specification themselves. It generates an OpenAPI schema, TypeScript types, JSON schemas, Zod schemas, or Markdown documentation by inferring request and response structures from user-defined probes, i.e. test traffic.
You can use it to quickly generate documentation and types for existing APIs so your LLM agents can understand how to interact with them.
You can use AutoDisco directly via the command line interface (CLI).
npx autodisco https://jsonplaceholder.typicode.com/postsThis will create an OpenAPI schema in autodisco/openapi/schema.json based on the response from the provided endpoint.
By default, the CLI will send a GET request to the provided endpoint, but you can customize the HTTP method, request body, headers and query parameters using the available CLI options.
To also generate TypeScript types, Zod schemas or JSON schemas, you can use the --generate flag:
npx autodisco https://jsonplaceholder.typicode.com/posts --generate typescriptThis will create TypeScript types in the autodisco/typescript directory:
autodisco/
βββ typescript/
β βββ get/
β βββ Posts.ts
βββ API.md
Available generate options are: openapi, openapi-typescript, typescript, zod, json, and markdown.
The default is openapi, if no options are provided. Markdown is always provided.
To run the discovery process for multiple endpoints, you can provide a configuration file, e.g. autodisco.config.{js,ts,mjs,cjs}:
export default {
baseUrl: 'https://jsonplaceholder.typicode.com',
probes: {
get: {
'/todos': {},
'/posts': {},
},
post: {
'/users': {
body: {
name: 'John Doe',
username: 'johndoe',
email: 'johndoe@example.com',
},
},
},
},
}Then run the autodisco command in your terminal:
npx autodiscoThis will create an OpenAPI schema in autodisco/openapi/schema.json for the specified endpoints.
Markdown is always provided, except when explicitly set to false in the generate configuration.
The autodisco CLI accepts only one argument, which is the base URL for the API you want to probe,
if the path is a local file, it will be treated as a configuration file and the base URL will be read from the configuration instead.
When running for a single endpoint, you can also specify the endpoint directly in the command:
npx autodisco https://jsonplaceholder.typicode.com/posts --method POST --body '{"userId: 1, "title": "foo", "body": "bar"}'This will create an OpenAPI schema in autodisco/openapi/schema.json based on the response from the provided endpoint using the specified HTTP method and request body.
Available CLI options are:
--method: The HTTP method to use for the probe (default:GET).--headers: Headers to include in the request (in JSON format).--query: Query parameters to include in the request (in JSON format).--body: The request body to include in the request (in JSON format).--generate: Options to customize code generation (available values:openapi,typescript,json,zod,markdown).
You can also use AutoDisco programmatically in your code. First, install the package:
npm install autodiscoThen, import the discover function and call it with your configuration:
import discover from 'autodisco'
await discover({
baseUrl: 'https://jsonplaceholder.typicode.com',
probes: {
get: {
'/todos': {},
'/posts': {},
// You can also define multiple probes for the same endpoint
'/posts/{id}': [
{
params: {
id: 1,
},
},
{
params: {
id: 2,
},
},
],
'/users/{id}': {
params: {
id: 1,
},
},
'/comments': {
query: {
postId: 1,
},
},
},
post: {
'/users': {
body: {
name: 'John Doe',
username: 'johndoe',
email: 'johndoe@example.com',
},
},
},
},
})This will create an OpenAPI schema for all configured endpoints in autodisco/openapi/schema.json.
If you want to generate TypeScript types from your endpoints you can enable the typescript option in the generate configuration:
import discover from 'autodisco'
await discover({
baseUrl: 'https://jsonplaceholder.typicode.com',
probes: {
get: {
'/todos': {},
},
post: {
'/users': {
body: {
name: 'John Doe',
username: 'johndoe',
email: 'johndoe@example.com',
},
},
},
},
generate: {
typescript: true,
},
})This will create TypeScript types in the autodisco/typescript directory.
autodisco/
βββ typescript/
β βββ get/
β β βββ Todos.ts
β βββ post/
β βββ Users.ts
βββ API.md
If you also want to generate Zod schemas for the probed endpoints, you can enable the zod option in the generate configuration:
import discover from 'autodisco'
await discover({
baseUrl: 'https://jsonplaceholder.typicode.com',
probes: {
get: {
'/todos': {},
},
post: {
'/users': {
body: {
name: 'John Doe',
username: 'johndoe',
email: 'johndoe@example.com',
},
},
},
},
generate: {
zod: true,
},
})This will create Zod schemas in the autodisco/zod directory.
autodisco/
βββ zod/
β βββ get/
β β βββ Todos.ts
β βββ post/
β βββ Users.ts
βββ API.md
If you want to generate JSON Schemas for the probed endpoints, you can enable the json option in the generate configuration:
import discover from 'autodisco'
await discover({
baseUrl: 'https://jsonplaceholder.typicode.com',
probes: {
get: {
'/todos': {},
},
post: {
'/users': {
body: {
name: 'John Doe',
username: 'johndoe',
email: 'johndoe@example.com',
},
},
},
},
generate: {
json: true,
},
})This will create JSON schemas in the autodisco/json directory.
autodisco/
βββ json/
β βββ get/
β β βββ Todos.json
β βββ post/
β βββ Users.json
βββ API.md
If you also want to generate TypeScript types for the probed endpoints using openapi-typescript, you can enable the typescript option in the generate configuration:
import discover from 'autodisco'
await discover({
baseUrl: 'https://jsonplaceholder.typicode.com',
probes: {
get: { '/todos': {} },
},
generate: {
openapi: {
typescript: true,
},
},
})This will create TypeScript types in the autodisco/openapi directory in addition to the OpenAPI schema:
autodisco/
βββ openapi/
β βββ schema.json
β βββ types.d.ts
βββ API.md
Note
Make sure to install openapi-typescript if you want to use TypeScript type generation:
npm install openapi-typescript
The discover function accepts a configuration object with the following values:
baseUrl: The base URL for the API (string, optional).outputDir: The directory to output the generated files (string, default:autodisco).probes: An object containing endpoints to probe (ProbeConfig, required).headers: An object containing headers to include in all requests (Record<string, string>, optional).minify: Whether to minify the generated OpenAPI schema (boolean, default:false).clear: Whether to clear the output directory before generating files (boolean, default:true).generate: Options to customize code generation (optional)markdown: Whether to generate Markdown documentation (boolean, default:true).openapi: Whether to generate the OpenAPI schema (boolean, default:true).typescript: Whether to generate TypeScript types (boolean | generateTypescriptOptions, optional).zod: Whether to generate Zod schemas (boolean, optional).json: Whether to generate JSON schemas (boolean, optional).
hooks: Hooks to customize the discovery process (optional).logger: Custom configuration for the logger (Consola options, optional).
Each probe can call an endpoint in multiple ways by specifying different combinations of params, query, and body.
Probes supports the following options:
params: An object containing path parameters (optional).query: An object containing query parameters (optional).body: An object containing the request body (for POST, PUT, PATCH requests, optional).headers: An object containing headers to include in the request (optional, overrides default headers).
The discovery process involves sending HTTP requests to the specified endpoints using the provided probes. The responses are analyzed to infer the structure of the API, which is then used to generate an OpenAPI schema. If enabled, TypeScript types and Zod schemas are also generated based on the inferred structures.
When all probes are completed, their responses will be converted to Zod schemas at runtime to ensure an accurate representation of the data structures and to circumvent any serialization issues.
After that, the OpenAPI schema will be generated using the inferred runtime schemas with zod-openapi
in the ${outputDir}/openapi directory.
If OpenAPI TypeScript type generation is enabled, the OpenAPI schema will be converted to TypeScript types
using openapi-typescript in the ${outputDir}/typescript directory.
If Zod schema generation is enabled, Zod schemas will be generated as files in the ${outputDir}/zod directory.
The same applies to JSON schema generation and TypeScript types in their respective ${outputDir}/json and ${outputDir}/typescript directories.
A Markdown based API documentation will be generated in the ${outputDir}/API.md file, summarizing the discovered endpoints and their inferred schemas.
The schema inference process breaks a response down by first identifying the primitive data types (string, number, boolean, null) and then combining them into more complex structures such as arrays and objects.
Arrays are inferred by examining the elements within the array and determining a common schema that encompasses all elements. If the elements have varying structures, all possible schemas are searched for a common discriminator, such as a shared property name with different values. If a common discriminator is found and the number of unique schemas matches the number of available discriminators, the array will be typed as a discriminated union. When no common structure can be found, the array is inferred to contain a single object with optional properties representing all possible fields.
Objects are inferred by analyzing each property and determining its type based on the values present in the responses. If a property is missing in some responses, it is marked as optional.
Given the following responses from probing an endpoint:
{
"users": [
{ "id": 1, "name": "Alice", "role": "admin", "extra": "data" },
{ "id": 2, "name": "Bob", "role": "user" },
{ "id": 3, "name": "Charlie", "role": "guest" }
]
}The inferred schema would be:
type Response = {
id: number
name: string
role: string
extra: string | undefined
}[]If the responses were more varied and provide a unique key for each unique kind of schema, the inference will detect it as a discriminated union such as:
{
"users": [
{ "id": 1, "name": "Alice", "role": "admin" },
{ "id": 2, "name": "Bob", "role": "editor", "permissions": ["read", "write"] },
{ "id": 3, "name": "Charlie", "role": "guest", "extra": "data" }
]
}With the inferred schema being:
type Response = ({
id: number
name: string
role: 'admin'
} | {
id: number
name: string
role: 'editor'
permissions: string[]
} | {
id: number
name: string
role: 'guest'
extra: string
})[]The hooks configuration allows you to customize the discovery process by providing functions that are called at specific points during execution.
| Hook Name | Props | Description |
|---|---|---|
discovery:start |
config |
Called when the discovery process begins |
discovery:completed |
config, totalTime, totalProbingTime |
Called when the entire discovery process is completed |
probe:request |
method, path, probeConfig |
Called before each API probe request is made |
probe:response |
method, path, probeConfig, response |
Called after each API probe response is received |
probes:completed |
config, results |
Called when all API probing is complete |
zod:runtime:generate |
config, method, path, schemaConfig, sample |
Called before generating runtime Zod schemas |
zod:runtime:generated |
config, results |
Called after runtime Zod schemas have been generated |
zod:generate |
config, method, name, schema |
Called before generating Zod schema files |
zod:generated |
config, result |
Called after Zod schema files have been generated |
json:generate |
config, method, name, schema |
Called before generating JSON schema files |
json:generated |
config, result |
Called after JSON schema files have been generated |
typescript:generate |
config, method, name, schema |
Called before generating TypeScript type files |
typescript:generated |
config, result |
Called after TypeScript type files have been generated |
markdown:generate |
config, nodes, separator |
Called before generating Markdown documentation |
markdown:generated |
config, result |
Called after Markdown documentation has been generated |
openapi:generate |
config, components, paths |
Called before generating the OpenAPI schema |
openapi:generated |
config, result |
Called after the OpenAPI schema has been generated |
openapi:typescript:generate |
config, openapiTSOptions |
Called before generating OpenAPI TypeScript types |
openapi:typescript:generated |
config, result |
Called after OpenAPI TypeScript types have been generated |
To run the project locally, you need to have Bun installed. You can use the provided nix-shell for a consistent development environment.
Install dependencies:
bun installRun tests:
bun testRun linter:
bun lintRun typechecks:
bun typecheckTo run the CLI locally, you can use bun run:
bun run ./src/cli/index.ts https://jsonplaceholder.typicode.com/posts
# Or
bun run ./src/cli/index.ts ./path/to/configAutoDisco is designed to be a powerful tool for discovering and documenting REST APIs, but it has some limitations to be aware of:
- It relies on the responses from the probes to infer the schema, so if the responses are not representative of the actual API, the generated schema may be inaccurate.
- It may not be able to infer certain complex data structures or relationships between endpoints, especially if the responses do not provide enough information.
- It does not currently support authentication or other advanced features that may be required to access certain APIs.
- It currently only works with JSON based API responses and may not be suitable for APIs that use other formats such as XML or GraphQL.
This project is heavily inspired by and built with the following libraries:
Published under the MIT License.