Skip to content

Fix endpoint type inference for OpenAPI server variable templates#3664

Merged
ardatan merged 2 commits intomasterfrom
copilot/fix-server-variable-typecheck
Mar 23, 2026
Merged

Fix endpoint type inference for OpenAPI server variable templates#3664
ardatan merged 2 commits intomasterfrom
copilot/fix-server-variable-typecheck

Conversation

Copy link
Contributor

Copilot AI commented Mar 20, 2026

When an OAS document uses server variable templates (e.g. https://{username}.server.com:{port}/{version}), the endpoint option on createClient was typed as the raw literal string "https://{username}.server.com:{port}/{version}" — which no real URL can satisfy, forcing users to use type assertions.

Changes

  • ClientOptionsWithStrictEndpoint — updated the server object pattern to also infer the variables? field alongside url, then applies ResolveServerUrl to produce a proper template literal type instead of the raw URL template string.

  • ServerVariableType<TVarName, TVariables> — new utility type that maps a variable name to its TypeScript type: a union of its enum values if present, string otherwise.

  • ResolveServerUrl<TUrl, TVariables> — new utility type that recursively replaces {varName} tokens in a URL string with the corresponding ServerVariableType.

Result

// servers: [{ url: "https://{username}.server.com:{port}/{version}", variables: { port: { enum: ["8443", "443"] } } }]

createClient<NormalizeOAS<typeof oas>>({
  endpoint: 'https://me.server.com:443/v1',   // ✅ valid
  endpoint: 'https://me.server.com:8443/v2',  // ✅ valid
});

// endpoint type becomes: `https://${string}.server.com:${"8443" | "443"}/${string}`
// ClientOptionsWithStrictEndpoint rejects ports outside the enum:
const opts: ClientOptionsWithStrictEndpoint<NormOAS> = {
  endpoint: 'https://me.server.com:444/v1',  // ✅ type error — 444 not in enum
};
Original prompt

This section details on the original issue you should resolve

<issue_title>Server variables in the open api spec fail to typecheck on createClient with endpoint</issue_title>
<issue_description>Describe the bug

if the open api spec contains server variables as described here: https://learn.openapis.org/specification/servers.html#server-variables

servers:
- url: https://{username}.server.com:{port}/{version}
  variables:
    username:
      default: demo
      description: This value is assigned by the service provider.
    port:
      enum:
        - "8443"
        - "443"
      default: "8443"
    version:
      default: v1

The inferred type of as const on that schema gives us the string literal: "https://{username}.server.com:{port}/{version}" which no real endpoint can match against. Ideally the type inference would read the variables syntax and produce a typescript string literal type like:

type TEndpoint = `https://${string}.server:com:${"8443" | "443"}/${string}`

as it is I have to type assert when using

createClient<typeof theAboveSchema>({
  endpoint: `http://me.server.com:443/v1`, // won't typecheck
});

</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.

Add utility types ServerVariableType and ResolveServerUrl that transform
OpenAPI server URL templates with {variable} patterns into proper TypeScript
template literal types. Variables with enum constraints produce union types,
while unconstrained variables become string.

Update ClientOptionsWithStrictEndpoint to infer server variables alongside
the URL and apply ResolveServerUrl to produce the correct endpoint type.

Add test fixture and type test for the server variables scenario.

Co-authored-by: ardatan <20847995+ardatan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix server variable typecheck in open api client Fix endpoint type inference for OpenAPI server variable templates Mar 20, 2026
Copilot AI requested a review from ardatan March 20, 2026 13:48
@github-actions
Copy link
Contributor

💻 Website Preview

The latest changes are available as preview in: https://abaef2e4.fets.pages.dev

@github-actions
Copy link
Contributor

✅ Benchmark Results

     ✓ no_errors{server:node-http,mode:no-schema}
     ✓ expected_result{server:node-http,mode:no-schema}
     ✓ no_errors{server:node-http,mode:json-schema}
     ✓ expected_result{server:node-http,mode:json-schema}
     ✓ no_errors{server:uWebSockets,mode:no-schema}
     ✓ expected_result{server:uWebSockets,mode:no-schema}
     ✓ no_errors{server:uWebSockets,mode:json-schema}
     ✓ expected_result{server:uWebSockets,mode:json-schema}

     checks......................................: 100.00% ✓ 822776      ✗ 0     
     data_received...............................: 75 MB   622 kB/s
     data_sent...................................: 52 MB   435 kB/s
     http_req_blocked............................: avg=1.63µs   min=921ns    med=1.47µs   max=311.34µs p(90)=2.05µs   p(95)=2.29µs  
     http_req_connecting.........................: avg=1ns      min=0s       med=0s       max=165.19µs p(90)=0s       p(95)=0s      
     http_req_duration...........................: avg=204.74µs min=134.84µs med=194.98µs max=13.84ms  p(90)=221.35µs p(95)=230.44µs
       { expected_response:true }................: avg=204.74µs min=134.84µs med=194.98µs max=13.84ms  p(90)=221.35µs p(95)=230.44µs
     ✓ { server:node-http,mode:json-schema }.....: avg=217.3µs  min=163.79µs med=206.71µs max=13.84ms  p(90)=230.48µs p(95)=238.51µs
     ✓ { server:node-http,mode:no-schema }.......: avg=211.54µs min=150.97µs med=199.05µs max=13.83ms  p(90)=225.29µs p(95)=236.16µs
     ✓ { server:uWebSockets,mode:json-schema }...: avg=199.45µs min=144.07µs med=191.13µs max=12.31ms  p(90)=213.3µs  p(95)=220.74µs
     ✓ { server:uWebSockets,mode:no-schema }.....: avg=192.22µs min=134.84µs med=184.11µs max=12.19ms  p(90)=206.29µs p(95)=213.81µs
     http_req_failed.............................: 0.00%   ✓ 0           ✗ 411388
     http_req_receiving..........................: avg=26.57µs  min=13.95µs  med=25.56µs  max=3.07ms   p(90)=31.65µs  p(95)=34.19µs 
     http_req_sending............................: avg=9.81µs   min=5.54µs   med=9.69µs   max=729.34µs p(90)=13.32µs  p(95)=14.93µs 
     http_req_tls_handshaking....................: avg=0s       min=0s       med=0s       max=0s       p(90)=0s       p(95)=0s      
     http_req_waiting............................: avg=168.36µs min=109.02µs med=159.31µs max=13.76ms  p(90)=183µs    p(95)=191.26µs
     http_reqs...................................: 411388  3428.192882/s
     iteration_duration..........................: avg=286.45µs min=203.19µs med=275.91µs max=14.01ms  p(90)=308.09µs p(95)=320.14µs
     iterations..................................: 411388  3428.192882/s
     vus.........................................: 1       min=1         max=1   
     vus_max.....................................: 2       min=2         max=2   

@ardatan ardatan marked this pull request as ready for review March 23, 2026 08:33
@ardatan ardatan merged commit 9eccf34 into master Mar 23, 2026
17 of 18 checks passed
@ardatan ardatan deleted the copilot/fix-server-variable-typecheck branch March 23, 2026 08:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Server variables in the open api spec fail to typecheck on createClient with endpoint

2 participants