Skip to main content

Overview

Keq's command-line tool compiles swagger documents into typescript code, allowing you to send HTTP requests as if calling functions.

Node.js version must be >= 20.19.0

Installation

caution

It's recommended to lock the version of @keq-request/cli in package.json. Minor version upgrades of @keq-request/cli may modify code templates to fix bugs. There's a very small chance this could introduce backward-incompatible changes.

Usage

First, we need a swagger document. For example, the following cat-service-swagger.json file describes an HTTP endpoint for getting cat information.

cat-service-swagger.json
cat-service-swagger.json
{
  "openapi": "3.0.0",
  "info": {
    "title": "Cat Service",
    "version": "0.0.1"
  },
  "paths": {
    "/cats": {
      "get": {
        "operationId": "getCats",
        "parameters": [
          {
            "name": "breed",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Cat"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Cat": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "age": {
            "type": "number"
          }
        }
      }
    }
  }
}

Then, create the @keq-request/cli configuration file specifying the swagger document location and compilation parameters:

.keqrc.ts
import { defineKeqConfig, FileNamingStyle } from "@keq-request/cli"

export default defineKeqConfig({
  outdir: "./src/apis", // Output directory for compiled results
  rendering: {
    fileNamingStyle: FileNamingStyle.snakeCase,
  },
  modules: {
    catService: "./cat-service-swagger.json",
    // You can also fetch swagger documents from the network:
    // dogService: "http://dog.example.com/swagger.json"
  },
})
Recommendation

Prefer typescript or javascript configuration files. Some advanced features (like operationIdFactory) can only be implemented through code.

Next, run the following command in the terminal:

After execution, @keq-request/cli generates the following files under ./src/apis/cat_service/:

./src/apis/cat_service/get_cats.request.ts
import { Keq } from "keq"
import { request } from './request'
import type { GetCatsOperation, GetCatsResponseBodies, GetCatsRequestParameters } from "./types/operations/get_cats.type.js"
export type { GetCatsRequestQuery, GetCatsRequestHeaders, GetCatsRequestBodies } from "./types/operations/get_cats.type.js"


const method = "get"
const pathname = "/cats"

export function getCats<STATUS extends keyof GetCatsResponseBodies, CONTENT_TYPE extends never = never>(args?: GetCatsRequestParameters): Keq<GetCatsOperation<STATUS, CONTENT_TYPE>> {
  const req = request.get<GetCatsResponseBodies[STATUS]>("/cats")

  if (args && "breed" in args) req.query("breed", args["breed"], {"arrayFormat":"repeat"})

  return req
}

getCats.pathname = pathname
getCats.method = method

Now we can call the getCats function to send HTTP requests:

import { getCats } from "./src/apis/cat_service/operations"

const cats = await getCats({ breed: "siamese", unknownKey: 'value' }) // unknownKey is not in swagger, will be discarded
  .retry(3, 1000)
  .timeout(1000)
  // Append additional request parameters
  .query('extraQuery', 'extra')
  .set('Authorization', 'TOKEN')
  .send({ extraBody: 'extra' })

// Actual request URL: /cats?breed=siamese&extraQuery=extra

console.log(`My cats are: ${cats.map(cat => cat.name).join(', ')}`)
console.log(`Request path: ${getCats.pathname}`)
BodyFallbackPlugin can add undefined request parameters like unknownKey to the request body instead of discarding them.

Finally, we can add unified error handling for all endpoints in the catService module. Since each module has its own independent request instance, simply import it:

import { request as catServiceRequest } from './src/apis/cat_service/request'
import { validateStatusCode } from '@keq-request/exception'

// Mount middleware directly on the module's request instance
catServiceRequest.use(validateStatusCode())

If you want multiple modules to share global middleware (like auth, logging), create a core KeqRequest and let modules inherit from it:

import { KeqRequest } from 'keq'
import { validateStatusCode } from '@keq-request/exception'
import { request as catServiceRequest } from './src/apis/cat_service/request'
import { request as dogServiceRequest } from './src/apis/dog_service/request'

// Create core request with global middleware
const coreRequest = new KeqRequest()
coreRequest.use(validateStatusCode())
coreRequest
  .apply(setHeader('Authorization', 'Bearer TOKEN'))
  .forRoutes({ host: 'api.example.com' })

// Let modules inherit global middleware
catServiceRequest.inherit(coreRequest)
dogServiceRequest.inherit(coreRequest)
Why does Keq use a chainable API + middleware design pattern?

Keq was originally designed to generate request functions from swagger documents. However, relying solely on CLI-generated code leaves many problems unsolved:

  1. HTTP endpoint information defined in swagger documents may be incomplete or even incorrect — missing auth info, caching strategies, etc. External factors may prevent us from modifying the swagger document to fill in this information.
  2. Clients calling the same HTTP endpoint in different scenarios may have different needs — retry in some cases, not in others. Statically generated code cannot meet these dynamic requirements.
  3. Endpoints from the same swagger document often share similar auth, error handling, and logging needs. Generating these highly customized features through CLI Plugins would be very difficult to maintain.

Keq is an HTTP client designed to provide more powerful runtime APIs for statically generated code:

  • Chainable API allows dynamically modifying request parameters when calling request functions, even setting parameters that contradict the swagger definition. It perfectly avoids bloated configuration options.
  • Middleware provides an elegant way to add unified functionality to all endpoints in a module. Each module has its own KeqRequest instance — just import and mount middleware; use inherit to inherit global middleware for layered management. And through the chainable API, you can flexibly enable or disable middleware in different scenarios.

Configuration Files

.keqrc.(yml|json|js|ts)

@keq-request/cli automatically searches for configuration files named .keqrc.yml, .keqrc.json, .keqrc.js, or .keqrc.ts. You can specify the configuration file path with -c --config <config_file_path>.

ParameterRequiredDefaultDescription
outdirtrue-Output directory for compiled results
modulestrue-Swagger file paths and module names
renderingfalse{}Options controlling code generation output
rendering.fileNamingStylefalse-File naming style
rendering.esmfalsetrue if package.json has "type": "module"Whether to generate ESM-style code
rendering.additionalPropertiesTypefalse'unknown'Controls whether additionalProperties: true (or undeclared) renders as unknown or any in generated TypeScript types
rendering.entrypointfalsefalseWhen enabled, generates an index.ts entry file for each module that re-exports all generated files
rendering.v2Compatfalsefalse(Deprecated) When enabled, adds .option('module', { name, pathname, method }) to generated micro-functions for v2 compatibility
translatorsfalse[]Generated code style
pluginsfalse[]Plugins provide full customization of CLI behavior

rendering.fileNamingStyle

EnumExample
FileNamingStyle.rawKeeps original name (Default)
FileNamingStyle.camelCase"twoWords"
FileNamingStyle.capitalCase"Two Words"
FileNamingStyle.constantCase"TWO_WORDS"
FileNamingStyle.dotCase"two.words"
FileNamingStyle.headerCase"Tow-Words"
FileNamingStyle.noCase"two words"
FileNamingStyle.paramCase"two-words"
FileNamingStyle.pascalCase"TwoWords"
FileNamingStyle.pathCase"two/words"
FileNamingStyle.sentenceCase"Two words"
FileNamingStyle.snakeCase"two_words"

.keqfilter

You can also control which HTTP endpoints generate code via the .keqfilter file. The file uses INI Section format — [deny] blocks declare endpoints to ignore, [allow] blocks declare exceptions (higher priority than [deny]).

Each line follows the format METHOD module:/pathname, supporting glob wildcards.

[deny]
# Ignore all endpoints in all modules
*       *:/**

# Ignore GET /cats in all modules
*       *:/cats

# Ignore all GET endpoints
GET     *:/**

# Ignore /cats in catService module
*       catService:/cats

# Ignore GET /cats in catService module
GET     catService:/cats

[allow]
# Don't ignore POST /dogs in dogService module
POST    dogService:/dogs

Commands

keq build [options]

Generates code according to the configuration file.

OptionDescription
--module <moduleName...>Only generate modules matching the module name
-c --config <config_file_path>Path to the configuration file
-i --interactiveInteractively specify which HTTP endpoints to generate via CLI
--tolerantTolerant mode: ignores download failures and Swagger validation errors, generates code as much as possible. Without this flag, any error causes build failure (exit 1)
--freshClears output directory and download cache before building, suitable for full regeneration
--no-cacheDisables download cache, re-downloads OpenAPI documents on every build
--method <method...>Only generates HTTP endpoints matching the method ('get' | 'post' | 'put' | 'patch' | 'head' | 'options' | 'delete')
--pathname <pathname...>Only generates HTTP endpoints matching the pathname

keq filter <mode> [options]

Adds filter rules to the .keqfilter file. Use --interactive to interactively specify endpoints to filter.

OptionDescription
<mode>deny: add ignore rule; allow: add exception rule (higher priority than deny); all: ignore all endpoints
-c --config <config_file_path>Path to the configuration file
--moduleSet the module name for the filter rule
--method <method> Set the HTTP method for the filter rule ('get' | 'post' | 'put' | 'patch' | 'head' | 'options' | 'delete')
--pathname <pathname...>Set the HTTP endpoint path for the filter rule
-i --interactiveInteractively specify which HTTP endpoints to filter via CLI
--tolerantTolerant mode: ignores download failures and Swagger validation errors
--buildAutomatically runs keq build after filter rules are added

keq mock [options]

Starts a Mock HTTP server based on OpenAPI documents, automatically generating mock data conforming to Schema definitions. Suitable for frontend development when backend endpoints aren't ready yet.

OptionDescription
-c --config <config_file_path>Path to the configuration file
-p --port <port>Listen port (default: 3939)
--host <host>Bind host address (default: localhost)
--module <moduleName...>Only mock modules matching the module name
--corsEnable CORS response headers (default: true)
--delay <delay>Response delay (ms), supports range format like "100-500"
--max-depth <depth>Maximum Schema depth for mock data generation (default: 10)
--ref-depth <depth>Maximum recursion depth for circular $ref references (default: 5)
--debugPrint debug information
--tolerantTolerant mode: ignores invalid Swagger/OpenAPI document structure

Mock Data Generation Priority

The mock server generates response data in the following priority:

  1. example field defined in OpenAPI Media Type
  2. First available entry in examples
  3. Auto-generated based on Schema using json-schema-faker

keq search <query> [options]

Semantic search for API endpoints using natural language. Based on the intfloat/multilingual-e5-small embedding model, supports cross-language matching in Chinese and English.

OptionDescription
<query>Natural language search query (required)
-c --config <config_file_path>Path to the configuration file
--module <moduleName...>Only search within modules matching the module name
--limit <limit>Maximum number of results (default: 10)
--detailInclude full request/response Schema in results (parameters, requestBody, responses)
--format <format>Output format, options: json, compact (default: json)
--allIgnore .keqfilter rules, search all endpoints
--tolerantTolerant mode: ignores invalid Swagger/OpenAPI document structure
--debugPrint debug information
tip

On first run, the embedding model is automatically downloaded to ~/.cache/keq/models. Subsequent runs use the cache directly.

Output Examples

compact format:

[0.92] GET  /cats  Get all cat info  (catService#getCats)
[0.85] POST /cats  Create a new cat    (catService#createCat)

json format:

{
  "results": [
    {
      "score": 0.92,
      "module": "catService",
      "method": "GET",
      "pathname": "/cats",
      "operationId": "getCats",
      "summary": "Get all cat info",
      "description": "",
      "tags": ["cats"]
    }
  ],
  "total": 1
}

keq apis [options]

Lists all API endpoints and component information defined in OpenAPI documents, including endpoint summary/description and schema descriptions.

OptionDescription
-c --config <config_file_path>Path to the configuration file
--includes <includes...>Specify output content, options: operations, components
--module <moduleName...>Only list modules matching the module name
--method <method>Only list endpoints matching the HTTP method
--pathname <pathname>Only list endpoints matching the path
--format <format>Output format, options: compact, json
--jsonOutput in JSON format (equivalent to --format json)
--allIgnore .keqfilter rules, list all modules and endpoints. Cannot be used with --module, --method, --pathname
--tolerantTolerant mode: ignores invalid Swagger/OpenAPI document structure
--debugPrint debug information

Output Example

Module: catService
──────────────────────────────────────────────────────────────────────────

  APIs:
    GET     /cats
    Get all cat info
    POST    /cats
    Create a new cat

  Schemas:
    Cat
    Cat data model