Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

OpenApi: optional query and header parameters are documented as nullable

オープン
#9,461 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@Marve10s がすでに取り組んでいます。

2026年10月17日 から。

  • #9464 @Marve10s による — オープン

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
57/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
typescript
領域
api

調査の方向性

Start in OpenApi.ts at processParameters, the entry point named in the issue, and trace how optional query and header parameter schemas pass through the representation pipeline. Compare those parameter schemas with the body handling described in the issue, then inspect or add focused tests for OpenApi.fromApi query and header output. Done means optional parameters omit JSON null while preserving the documented string forms, with body behavior unchanged.

索引モデルが issue の本文から書いたものです。

説明

What version of Effect is running?

4.0.5. Also seen on 4.0.0. OpenApi.ts on main is the same as in 4.0.5.

What steps can reproduce the bug?

OpenApi.fromApi documents a query parameter declared with Schema.optional as anyOf: [T, null]. A query string has no way to send null, and the server's query decoder doesn't accept one.

import { Schema } from "effect"
import { HttpApi, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/http-api"

const Query = Schema.Struct({
  limit: Schema.optional(Schema.FiniteFromString),
  cursor: Schema.optional(Schema.NullOr(Schema.String))
})

const Api = HttpApi.make("api").add(
  HttpApiGroup.make("items").add(
    HttpApiEndpoint.get("list", "/items", { query: Query, success: Schema.String })
  )
)

console.log(JSON.stringify(OpenApi.fromApi(Api).paths["/items"].get.parameters, null, 2))

// What the server does with the `null` the document allows
const decodeQuery = Schema.decodeUnknownExit(Schema.toCodecStringTree(Query))
console.log(decodeQuery({ limit: "null" })._tag) // "Failure"
What is the expected behavior?

limit documented as { "type": "string" } with required: false. required: false already says the parameter can be left out, so the undefined that Schema.optional adds has nothing left to describe.

cursor documented with only the string form of its NullOr, anyOf: [{ "type": "string", "enum": ["null"] }, { "type": "string" }], and no JSON null around it.

What do you see instead?
[
  {
    "name": "limit",
    "in": "query",
    "schema": { "anyOf": [{ "type": "string" }, { "type": "null" }] },
    "required": false
  },
  {
    "name": "cursor",
    "in": "query",
    "schema": {
      "anyOf": [
        { "anyOf": [{ "type": "string", "enum": ["null"] }, { "type": "string" }] },
        { "type": "null" }
      ]
    },
    "required": false
  }
]

cursor shows the mismatch most clearly. Its NullOr gets the query codec's representation, the string "null". The undefined from Schema.optional gets the JSON codec's, where Undefined encodes as null. One parameter ends up described by two codecs.

Additional information
  • Headers have the same problem: headers: Schema.Struct({ "x-trace": Schema.optional(Schema.String) }) comes out as anyOf: [{ "type": "string" }, { "type": "null" }].
  • Bodies are fine. A JSON body does decode null to undefined, so T | null is the right description there.
  • Effect's own HttpApiClient is unaffected, since it drops undefined before building the URL. Clients generated from the document are the ones that break. We generate ours with orval, which types limit as string | null and sends ?limit=null when given null. The service then rejects the request, or, for a plain Schema.String, reads "null" as a real value.
  • Switching to Schema.optionalKey fixes the document. It also makes { limit: undefined } fail to encode, so every caller that builds a query from a possibly-undefined value has to leave the key out by hand, which is what Schema.optional exists to avoid.
  • A possible workaround until it's fixed: an OpenApi.Transform that drops the null member from every parameter where required is false.
  • My guess at the cause, from reading the source: processParameters queues parameter schemas as "properties" ops, and those go through the same representation pipeline as bodies, so an optional property's undefined gets the JSON rule.
主要言語
TypeScript
スター
16.7k
フォーク
808
平均マージ
10時間 7分
マージ済み PR(30日)
808

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

Effect-TS/effect のほかの issue

Effect-TS/effect の issue をすべて見る

似ている issue

TypeScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。