Skip to content

Support async createClientConfig for per-request client configuration#3445

Merged
mrlubos merged 6 commits intomainfrom
copilot/support-async-create-client-config
Feb 25, 2026
Merged

Support async createClientConfig for per-request client configuration#3445
mrlubos merged 6 commits intomainfrom
copilot/support-async-create-client-config

Conversation

Copy link
Contributor

Copilot AI commented Feb 25, 2026

  • Update CreateClientConfig type in packages/custom-client/src/types.ts to allow returning Promise<Config<...>>
  • Update all bundle types.ts files (client-fetch, client-axios, client-ky, client-next, client-nuxt, client-ofetch, client-angular) to allow returning Promise<Config<...>>
  • Update packages/openapi-ts/src/plugins/@hey-api/client-core/createClientConfig.ts to generate the new type with | Promise<...> return type
  • Update packages/openapi-ts/src/plugins/@hey-api/client-core/client.ts to use await when calling createClientConfig
  • Update all test snapshots that include CreateClientConfig type
  • Fix CI failure: update bundled client/client/types.gen.ts files in all examples
  • Fix CI failure: update client-angular bundle types.ts
  • Fix CI failure: update angular client types.gen.ts test snapshots (2.0.x, 3.0.x, 3.1.x) that were missed when the angular bundle was updated late
Original prompt

This section details on the original issue you should resolve

<issue_title>Support async createClientConfig for per-request client configuration (multi-tenant / SSR)</issue_title>
<issue_description>

Discussed in https://github.com/orgs/hey-api/discussions/3441

Originally posted by looloosha February 24, 2026

Problem

When using @hey-api/client-fetch with runtimeConfigPath, the exported createClientConfig function is synchronous:

export type CreateClientConfig<T extends ClientOptions = ClientOptions2> =
  (override?: Config<ClientOptions & T>) => Config<Required<ClientOptions> & T>;

This works well for simple cases like setting a static baseUrl. However, in server-side multi-tenant applications (e.g. a Next.js app serving multiple brands), the client configuration depends on per-request data that requires async operations:

  1. Base URL varies per brand — determined by the incoming request hostname (requires await headers() in Next.js App Router)
  2. Auth token varies per session — fetched from an auth provider at request time (inherently async)

Current workaround

Since createClientConfig can't be async, the runtimeConfigPath feature is unusable for this scenario. Instead, we create a fresh client per request in a manual wrapper:

import { headers } from 'next/headers'
import { createClient, createConfig } from './bff-client/client/index'

const brandBaseUrls: Record<Brand, string | undefined> = {
  af: process.env.BFF_BASE_URL_AF,
  otf: process.env.BFF_BASE_URL_OTF,
}

export async function getBffClient() {
  const reqHeaders = await headers()
  const brand = await getBrand(reqHeaders)

  const auth = await getAuth()
  const tokenResponse = await auth.api.getAccessToken({
    body: { providerId: 'microsoft' },
    headers: reqHeaders,
  })
  const accessToken = tokenResponse?.accessToken

  return createClient(
    createConfig({
      baseUrl: brandBaseUrls[brand],
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    })
  )
}

Then every SDK call needs the client passed explicitly:

const client = await getBffClient()
const { data } = await getCurrentUser({ client })

This works, but it means:

  • The singleton client exported from client.gen.ts is never used
  • The runtimeConfigPath feature provides no value
  • Every call site must remember to pass { client }

Proposal

It would be great if createClientConfig could optionally be async:

export type CreateClientConfig<T extends ClientOptions = ClientOptions2> =
  (override?: Config<ClientOptions & T>) =>
    Config<Required<ClientOptions> & T> | Promise<Config<Required<ClientOptions> & T>>;

Alternatively, a different mechanism that supports per-request configuration in server-side contexts would work too — for example, a clientProvider pattern or middleware-style hook that runs before each request.

Use case summary

Requirement Why async is needed
Multi-tenant base URL Brand is derived from request hostname (await headers() in Next.js)
Per-request auth token Token is fetched from auth provider at request time
SSR / Server Components Next.js App Router server components require async access to request context

Related

Thanks for the great library! Happy to discuss alternative approaches.

</issue_description>

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


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@bolt-new-by-stackblitz
Copy link

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@changeset-bot
Copy link

changeset-bot bot commented Feb 25, 2026

🦋 Changeset detected

Latest commit: 54a4c47

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@hey-api/openapi-ts Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Feb 25, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hey-api-docs Ready Ready Preview, Comment Feb 25, 2026 1:01pm

Request Review

Copilot AI changed the title [WIP] Support async createClientConfig for per-request client configuration Support async createClientConfig for per-request client configuration Feb 25, 2026
Copilot AI requested a review from mrlubos February 25, 2026 08:30
@mrlubos mrlubos marked this pull request as ready for review February 25, 2026 09:06
@pullfrog
Copy link

pullfrog bot commented Feb 25, 2026

Leaping into action...

Pullfrog  | View workflow run | Using OpenCode | Triggered by Pullfrogpullfrog.com𝕏

@dosubot dosubot bot added size:M This PR changes 30-99 lines, ignoring generated files. lgtm This PR has been approved by a maintainer feature 🚀 Feature request. labels Feb 25, 2026
@mrlubos
Copy link
Member

mrlubos commented Feb 25, 2026

@copilot CI fails, please fix.

…e return type

Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 25, 2026

@copilot CI fails, please fix.

Fixed in b02c4f6. The CI was failing because the bundled client/client/types.gen.ts files in the examples were out of sync — they still had the old synchronous-only CreateClientConfig return type. Updated all 14 example files to include | Promise<Config<Required<ClientOptions> & T>>.

@mrlubos
Copy link
Member

mrlubos commented Feb 25, 2026

@copilot CI is still failing, fix

…type in CreateClientConfig

Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 25, 2026

@copilot CI is still failing, fix

Fixed in 955a9fc. The client-angular bundle types.ts was missing the | Promise<...> update — only the other 6 clients (fetch, axios, ky, next, nuxt, ofetch) had been updated previously. The angular client has its own separate bundle source file that was overlooked.

@mrlubos
Copy link
Member

mrlubos commented Feb 25, 2026

@copilot now tests are failing, fix itttt

Copy link
Contributor Author

Copilot AI commented Feb 25, 2026

@copilot now tests are failing, fix itttt

Fixed in 54a4c47. The test snapshot files for the angular client (client/types.gen.ts in the 2.0.x, 3.0.x, and 3.1.x test directories) were still using the old synchronous-only CreateClientConfig return type. Updated all 18 snapshot files to match the newly generated output.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 25, 2026

Open in StackBlitz

@hey-api/codegen-core

npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/codegen-core@3445

@hey-api/json-schema-ref-parser

npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/json-schema-ref-parser@3445

@hey-api/nuxt

npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/nuxt@3445

@hey-api/openapi-ts

npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/openapi-ts@3445

@hey-api/shared

npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/shared@3445

@hey-api/types

npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/types@3445

@hey-api/vite-plugin

npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/vite-plugin@3445

commit: 54a4c47

@mrlubos mrlubos merged commit 320c30a into main Feb 25, 2026
11 of 16 checks passed
@mrlubos mrlubos deleted the copilot/support-async-create-client-config branch February 25, 2026 13:17
@hey-api hey-api bot mentioned this pull request Feb 25, 2026
@Bram-dc
Copy link

Bram-dc commented Feb 27, 2026

This should not have been released, you cannot expect all use cases to support top level await.

@Bram-dc
Copy link

Bram-dc commented Feb 27, 2026

I am also a bit shocked on how much copilot is used in this project.

@mrlubos
Copy link
Member

mrlubos commented Feb 27, 2026

This should not have been released, you cannot expect all use cases to support top level await.

Can you share your use case?

@mrlubos
Copy link
Member

mrlubos commented Feb 27, 2026

I am also a bit shocked on how much copilot is used in this project.

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature 🚀 Feature request. lgtm This PR has been approved by a maintainer size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support async createClientConfig for per-request client configuration (multi-tenant / SSR)

3 participants