openapi-ts icon indicating copy to clipboard operation
openapi-ts copied to clipboard

[nextjs]can you create a interceptor param in createClientConfig?

Open recvexi opened this issue 5 months ago • 8 comments

Description

because i need create a interceptor before use clinet,so I need to add the configuration when creating the client ,like this

export const createClientConfig: CreateClientConfig = (config) => ({
  ...config,
  baseUrl: 'https://example.com',
  requestInterceptor:()=>{},
  responserequestInterceptor:()=>{}
});

The generated code

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

export const client = createClient(
  createClientConfig(
    createConfig<ClientOptions>({
      baseUrl: "https://127.0.0.1:3000",
    }),
  ),
)

client.interceptors.request.use(()=>{})

I'm not sure if this is a good issue. Is there a better way to get it?

recvexi avatar Jul 23 '25 04:07 recvexi

@recvexi what did you end up doing?

mrlubos avatar Aug 17 '25 04:08 mrlubos

@recvexi what did you end up doing?

I wrote a script to do this temporarily

# openapi.sh
# 运行 openapi-ts
pnpm run openapi-ts

# 在 client.gen.ts 末尾追加内容
echo '

import { requestInterceptor } from "../interceptors"
client.interceptors.request.use(requestInterceptor)
' >> ../service/gen/client.gen.ts

recvexi avatar Sep 07 '25 13:09 recvexi

this is much needed

edward-meister avatar Oct 07 '25 21:10 edward-meister

这是非常需要的

我应该重新打开 issue 吗?

recvexi avatar Oct 09 '25 06:10 recvexi

这是非常需要的

我应该重新打开 issue 吗?

I am using nextjs and i haven't found the right place for the middlewares. based on your response, i ended up writing a custom script that injects middleware into client.ge.ts, but i can see it is not the safest option

// This file is auto-generated by @hey-api/openapi-ts

import { createClientConfig, setFetchClientMiddleware } from '@/utils/fetch-client';  // <-- MY CHANGE

import { type ClientOptions, type Config, createClient, createConfig } from './client';
import type { ClientOptions as ClientOptions2 } from './types.gen';

/**
 * The `createClientConfig()` function will be called on client initialization
 * and the returned object will become the client's initial configuration.
 *
 * You may want to initialize your client this way instead of calling
 * `setConfig()`. This is useful for example if you're using Next.js
 * to ensure your client always has the correct values.
 */
export type CreateClientConfig<T extends ClientOptions = ClientOptions2> = (override?: Config<ClientOptions & T>) => Config<Required<ClientOptions> & T>;

const client = createClient(createClientConfig(createConfig<ClientOptions2>())); // <-- MY CHANGE
setFetchClientMiddleware(client); // <-- MY CHANGE
export { client }; // <-- MY CHANGE

edward-meister avatar Oct 09 '25 06:10 edward-meister

same issue, much needed

joernroeder avatar Oct 31 '25 09:10 joernroeder

much needed

sahandsn avatar Oct 31 '25 10:10 sahandsn

Claude wrote this script, adding an entrypoint addInterceptors(client) to customise the client before returning it. The added import is coming from @/api/common/interceptors in my case, you'd need to adapt this path to your needs. I also export the createClientConfig from @/api/common/client which you would need to adapt.

#!/usr/bin/env node
/**
 * Patch Generated API Client
 *
 * This script performs 4 transformations on the auto-generated client.gen.ts:
 * 1. Add import for addInterceptors
 * 2. Remove export from client const declaration (if present)
 * 3. Add addInterceptors(client) call after client creation
 * 4. Ensure client is exported separately at the end
 */

const fs = require('fs');
const path = require('path');

const CLIENT_FILE = path.join(
  __dirname,
  '../src/api/generated/client.gen.ts' // <---- ADAPT THIS TO YOUR NEEDS
);

function patchGeneratedClient() {
  console.log('🔧 Patching generated client...');

  if (!fs.existsSync(CLIENT_FILE)) {
    console.error('❌ Generated client file not found:', CLIENT_FILE);
    process.exit(1);
  }

  let content = fs.readFileSync(CLIENT_FILE, 'utf8');

  // Check if already patched
  if (content.includes('addInterceptors(client)')) {
    console.log('✅ Client already patched');
    return;
  }

  // Step 1: Add addInterceptors import after createClientConfig import
  if (!content.includes("from '@/api/common/interceptors'")) {
    const createClientConfigImport = "import { createClientConfig } from '@/api/common/client';";
    const importIndex = content.indexOf(createClientConfigImport);

    if (importIndex === -1) {
      console.error('❌ Could not find createClientConfig import');
      process.exit(1);
    }

    const afterImport = importIndex + createClientConfigImport.length;
    const interceptorImport = "\nimport { addInterceptors } from '@/api/common/interceptors';";
    content = content.slice(0, afterImport) + interceptorImport + content.slice(afterImport);
  }

  // Step 2: Remove export from client const declaration (if present)
  content = content.replace(/export const client = /, 'const client = ');

  // Step 3: Add addInterceptors(client) call after client creation
  const clientPattern = /(const client = createClient\([\s\S]*?\);)/;
  const match = content.match(clientPattern);

  if (!match) {
    console.error('❌ Could not find client creation statement');
    process.exit(1);
  }

  const replacement = `${match[1]}\n\naddInterceptors(client);`;
  content = content.replace(clientPattern, replacement);

  // Step 4: Ensure client is exported separately (if not already present)
  if (!content.includes('export { client }')) {
    content = content.trimEnd() + '\n\nexport { client };\n';
  }

  // Write patched file
  fs.writeFileSync(CLIENT_FILE, content, 'utf8');
  console.log('✅ Generated client patched successfully');
}

try {
  patchGeneratedClient();
  process.exit(0);
} catch (error) {
  console.error('❌ Failed to patch generated client:', error.message);
  process.exit(1);
}

joernroeder avatar Oct 31 '25 10:10 joernroeder