Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pluginClient: client=fetch does not work #1463

Closed
nimo23 opened this issue Dec 14, 2024 · 5 comments · Fixed by #1472
Closed

pluginClient: client=fetch does not work #1463

nimo23 opened this issue Dec 14, 2024 · 5 comments · Fixed by #1472
Labels
bug Something isn't working

Comments

@nimo23
Copy link

nimo23 commented Dec 14, 2024

What version of kubb is running?

3.3.0

What kind of platform do you use?

MacOS

How does your kubb.config.ts config look like

import { defineConfig } from '@kubb/core'
import { pluginOas } from '@kubb/plugin-oas'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginClient } from '@kubb/plugin-client'
import { pluginZod } from '@kubb/plugin-zod'

export default defineConfig(() => {
    return {
    ...

    pluginClient({
        output: {
            path: './clients/fetch'
    },
        client: 'fetch',
        parser: 'client', // TODO 'zod' or 'client'?
        paramsType: "inline",
        //dataReturnType: 'full',
        pathParamsType: "inline"
}),
..

Swagger/OpenAPI file?

No response

What version of external packages are you using(@tanstack-query, MSW, React, Vue, ...)

react 19.0.0

What steps can reproduce the bug?

  1. use client: 'fetch' instead of client: 'axios' in pluginClient
  2. generate the fetch resources and look into the generated clients folder

For example, this is the resource generated for:

clients/fetch/getUserByUserName.ts:

import client from '@kubb/plugin-client/clients/fetch'
import type {
  GetUserByUsernameQueryResponse,
  GetUserByUsernamePathParams,
  GetUserByUsernameQueryParams,
  GetUserByUsername404,
} from '../../../types/UserResource/GetUserByUsername.ts'
import type { RequestConfig } from '@kubb/plugin-client/clients/fetch'

/**
 * @description Get the user by username
 * @summary Get By Username
 * {@link /api/users/:username}
 */
export async function getUserByUsername(
  username: GetUserByUsernamePathParams['username'],
  params?: GetUserByUsernameQueryParams,
  config: Partial<RequestConfig> = {},
) {
  const res = await client<GetUserByUsernameQueryResponse, GetUserByUsername404, unknown>({ method: 'GET', url: `/api/users/${username}`, params, ...config })
  return res.data
}

Looking into @kubb/plugin-client/clients/fetch, it uses the axios client instead of fetch() api:

/**
 * Subset of AxiosRequestConfig
 */
type RequestConfig<TData = unknown> = {
    baseURL?: string;
    url?: string;
    method: 'GET' | 'PUT' | 'PATCH' | 'POST' | 'DELETE' | 'OPTIONS';
    params?: unknown;
    data?: TData | FormData;
    responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream';
    signal?: AbortSignal;
    headers?: [string, string][] | Record<string, string>;
};
/**
 * Subset of AxiosResponse
 */
type ResponseConfig<TData = unknown> = {
    data: TData;
    status: number;
    statusText: string;
    headers?: [string, string][] | Record<string, string>;
};
declare const fetchClient: {
    <TData, TError = unknown, TVariables = unknown>(config: RequestConfig<TVariables>): Promise<ResponseConfig<TData>>;
    getConfig(): never;
    setConfig(): never;
};

export { type RequestConfig, type ResponseConfig, fetchClient as default, fetchClient };

How often does this bug happen?

Every time

What is the expected behavior?

Something like the following file (which was previously needed (before kubb 3.3.3) to be included via importPath: 'src/fetchRequest.ts'):

/**
 * Subset of FetchRequestConfig
 */
export type RequestConfig<TData = unknown> = {
	baseURL?: string
	url?: string
	method: 'GET' | 'PUT' | 'PATCH' | 'POST' | 'DELETE'
	params?: object
	data?: TData | FormData
	responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'
	signal?: AbortSignal
	headers?: HeadersInit
}
/**
 * Subset of FetchResponse
 */
export type ResponseConfig<TData = unknown> = {
	data: TData
	status: number
	statusText: string
}

export const fetchClient = async <TData, TError = unknown, TVariables = unknown>(config: RequestConfig<TVariables>): Promise<ResponseConfig<TData>> => {
	const response = await fetch(`${config.baseURL}${config.url}`, {
		method: config.method.toUpperCase(),
		body: JSON.stringify(config.data),
		signal: config.signal,
		headers: config.headers,
	})

        // we use ECMAScript Safe Assignment Operator (?=) 
        // as there are cases where no data is sent from the server (e.g. 204 (No Content) status code)
        const data ?= await response.json()

	return {
		data,
		status: response.status,
		statusText: response.statusText,
	}
}

export default fetchClient

Additional information

No response

@nimo23 nimo23 added the bug Something isn't working label Dec 14, 2024
Copy link

linear bot commented Dec 14, 2024

@stijnvanhulle
Copy link
Collaborator

Are you sure about this because the fetch client is using fetch behind the scenes, see https://github.com/kubb-labs/kubb/blob/main/packages/plugin-client/src/clients/fetch.ts

@nimo23
Copy link
Author

nimo23 commented Dec 14, 2024

Are you sure about this because the fetch client is using fetch behind the scenes, see https://github.com/kubb-labs/kubb/blob/main/packages/plugin-client/src/clients/fetch.ts

I checked again: The generated import statement ('@kubb/plugin-client/clients/fetch') does not point to fetch.ts but to fetch.d.ts (located in: 'node_modules/@kubb/plugin-client/dist/clients/fetch.d.ts') - but I think the error is because the fetch URL which is hard-coded to "https://example.org/post":

const response = await fetch('https://example.org/post', {

I think this needs to be changed to make it work properly.

Other (minor) things:

  1. Especially, the @kubb/plugin-client/ needs to be installed as normal depencency instead of save-dev, or?
  2. the javascript doc for fetch.d.ts and fetch.ts should be changed. In
    * Subset of AxiosRequestConfig

should be changed to

/**
 * Subset of FetchRequestConfig
 */

and

* Subset of AxiosResponse

should be changed to

/**
 * Subset of FetchResponse
 */

@stijnvanhulle
Copy link
Collaborator

@nimo23 Could you have another look, I did some changes that should reflect more what was needed, see v.3.3.3

@nimo23
Copy link
Author

nimo23 commented Dec 15, 2024

@stijnvanhulle Thanks! I tried it and now it works almost correct. However, the default baseURL is aways undefined and therefore throws the exception:

SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data


var fetchClient = async (config) => {
	console.log("fetchClientURL from fetch.ts:");
        // the baseUrl is 'undefined'
	console.log(`${config.baseURL}${config.url}`);
	const response = await fetch(`${config.baseURL}${config.url}`, {
		method: config.method.toUpperCase(),
		body: JSON.stringify(config.data),
		signal: config.signal,
		headers: config.headers
	});
	const data = await response.json();
	return {
		data,
		status: response.status,
		statusText: response.statusText
	};
};

If I customize the baseUrl in

pluginClient({
    baseURL: '.',
    ..
}

then it works. However, according to https://www.kubb.dev/plugins/plugin-client/#baseurl, the default baseUrl should be used without the need to set it in the plugin configuration unless a custom baseUrl is defined, right?

I think it should set the default baseURL (and headers?) in fetch.js (similar to how axios.ts does):

// src/clients/axios.ts
var _config = {
  baseURL: typeof AXIOS_BASE !== "undefined" ? AXIOS_BASE : void 0,
  headers: typeof AXIOS_HEADERS !== "undefined" ? JSON.parse(AXIOS_HEADERS) : void 0
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants