From 881cd640cdeb976da12c482fb06874c7da5b4bc3 Mon Sep 17 00:00:00 2001 From: Kapil Gowru Date: Thu, 31 Jul 2025 14:11:30 -0600 Subject: [PATCH 1/5] Create others.mdx based on issue #266 --- .../pages/extensions/others.mdx | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 fern/products/openapi-definition/pages/extensions/others.mdx diff --git a/fern/products/openapi-definition/pages/extensions/others.mdx b/fern/products/openapi-definition/pages/extensions/others.mdx new file mode 100644 index 000000000..ae7a8d69e --- /dev/null +++ b/fern/products/openapi-definition/pages/extensions/others.mdx @@ -0,0 +1,83 @@ +--- +title: "Other Extensions" +description: "Details on other common OpenAPI extensions used with Fern" +--- + +Fern supports several OpenAPI extensions that provide additional functionality when generating SDKs and documentation. Here is a comprehensive list of extension headers: + +## x-fern-sdk-variables + +Allows you to define variables that can be passed to the generated SDK client constructor. These variables will be automatically injected into API paths. + +```yaml +components: + x-fern-sdk-variables: + project_id: + type: string + description: "The project ID for the API" + pattern: "^proj_[a-zA-Z0-9]+$" +``` + +## x-fern-sdk-group-name + +Groups related endpoints together in the generated SDK under a single namespace/class. + +```yaml +paths: + /users: + get: + x-fern-sdk-group-name: "users" +``` + +## x-fern-sdk-method-name + +Specifies a custom method name to use in the generated SDK instead of the default operation ID. + +```yaml +paths: + /users: + get: + x-fern-sdk-method-name: "list" +``` + +## x-fern-pagination + +Configures pagination handling for list endpoints. + +```yaml +paths: + /users: + get: + x-fern-pagination: + cursor: "$request.after" + next_cursor: "$response.page_info.end_cursor" + results: "$response.data" +``` + +## x-fern-sdk-return-value + +Specifies which part of the response should be returned by the SDK method. + +```yaml +paths: + /users/{id}: + get: + x-fern-sdk-return-value: "data" +``` + +## x-fern-ignore + +Excludes an endpoint or schema from SDK generation. + +```yaml +paths: + /internal/metrics: + get: + x-fern-ignore: true +``` + +These extensions help customize how Fern generates SDKs from your OpenAPI specification while maintaining standard OpenAPI compatibility. + + +When using these extensions, make sure to validate that your OpenAPI specification remains valid according to the OpenAPI specification. + \ No newline at end of file From eaf4c6c9055321eb2f09c126495261563c265f9c Mon Sep 17 00:00:00 2001 From: Kapil Gowru Date: Thu, 31 Jul 2025 14:11:30 -0600 Subject: [PATCH 2/5] Add Other page to navigation --- fern/products/openapi-def/openapi-def.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fern/products/openapi-def/openapi-def.yml b/fern/products/openapi-def/openapi-def.yml index 419d75e5b..7870dc7ee 100644 --- a/fern/products/openapi-def/openapi-def.yml +++ b/fern/products/openapi-def/openapi-def.yml @@ -4,7 +4,7 @@ navigation: - page: Authentication path: ./pages/auth.mdx - page: Servers - path: ./pages/servers.mdx + path: ./pages/servers.mdx - section: Endpoints slug: endpoints contents: @@ -17,6 +17,9 @@ navigation: - page: Server-Sent Events path: ./pages/endpoints/sse.mdx slug: sse + - page: others + title: Other + path: pages/extensions/others.mdx - section: Extensions slug: extensions contents: @@ -38,7 +41,7 @@ navigation: - page: Overlay Customizations path: ./pages/overrides.mdx - page: Sync your OpenAPI Specification - path: ./pages/automation.mdx + path: ./pages/automation.mdx - section: Integrate your Server Framework slug: frameworks contents: From b5108e5570efcee4fdf8ce0739eb2dc986ee7292 Mon Sep 17 00:00:00 2001 From: Kapil Gowru Date: Thu, 31 Jul 2025 14:11:31 -0600 Subject: [PATCH 3/5] Update parameter-names.mdx based on issue #266 --- .../pages/extensions/parameter-names.mdx | 64 +++++++++++++++---- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/fern/products/openapi-def/pages/extensions/parameter-names.mdx b/fern/products/openapi-def/pages/extensions/parameter-names.mdx index ff60bf613..0a30caf07 100644 --- a/fern/products/openapi-def/pages/extensions/parameter-names.mdx +++ b/fern/products/openapi-def/pages/extensions/parameter-names.mdx @@ -1,16 +1,19 @@ --- title: Customize parameter names -description: Use `x-fern-parameter-name` to customize query parameter, header and path parameter naming. +description: Use extensions like x-fern-parameter-name and x-fern-sdk-variables to customize parameter naming and inject variables into generated SDKs. --- - The `x-fern-parameter-name` extension allows you to customize the variable names of parameters in your generated SDKs. + The `x-fern-parameter-name` and `x-fern-sdk-variables` extensions allow you to customize how parameters are named and injected in your generated SDKs. -## Headers +## Parameter Renaming with x-fern-parameter-name -In the example below, the header `X-API-Version` is renamed to `version` in the -generated SDK. The rename makes the SDK more human readable. +Use `x-fern-parameter-name` to rename parameters in your generated SDK for improved readability and usability. + +### Headers + +In the example below, the header `X-API-Version` is renamed to `version` in the generated SDK for better readability: ```yaml {8} paths: @@ -26,10 +29,9 @@ paths: required: true ``` -## Query parameters +### Query parameters -In the example below, the query parameter `q` is renamed to `search_terms` in the -generated SDK. The rename makes the parameter more approachable for end users. +Here, the query parameter `q` is renamed to `search_terms` for clearer intent: ```yaml {8} paths: @@ -45,10 +47,9 @@ paths: required: false ``` -## Path parameters +### Path parameters -In the example below, the path parameter `userId` is renamed to `id` in the -generated SDK. The rename makes the SDK less verbose. +The path parameter `userId` is simplified to `id` to reduce verbosity: ```yaml {8} paths: @@ -63,3 +64,44 @@ paths: type: string required: false ``` + +## SDK Variables with x-fern-sdk-variables + +Use `x-fern-sdk-variables` to define variables that will be injected into your SDK paths. This is especially useful for common path parameters that you want to set once during SDK initialization. + +```yaml +components: + # Other components... +x-fern-sdk-variables: + project_id: + type: string + description: "The ID of the project" + pattern: "^proj_[a-zA-Z0-9]+$" +``` + +Then reference this variable in your paths: + +```yaml +paths: + "/v1/connect/{project_id}/accounts": + parameters: + - name: project_id + in: path + required: true + description: "The project ID" + schema: + type: string + pattern: "^proj_[a-zA-Z0-9]+$" + x-fern-sdk-variable: project_id +``` + +When using the generated SDK, instead of passing the project_id parameter for each API call, you can set it once during client initialization: + +```python +# Python example +client = YourSDK(project_id="proj_123") +``` + + + The x-fern-sdk-variables feature requires Fern generator version 4.24.0 or higher. + \ No newline at end of file From f90b594ff637710056b402985933322872004dc9 Mon Sep 17 00:00:00 2001 From: Kapil Gowru Date: Thu, 31 Jul 2025 14:11:32 -0600 Subject: [PATCH 4/5] Update configuration.mdx based on issue #266 --- .../sdks/overview/python/configuration.mdx | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/fern/products/sdks/overview/python/configuration.mdx b/fern/products/sdks/overview/python/configuration.mdx index 1c0eb6af7..b95083d7e 100644 --- a/fern/products/sdks/overview/python/configuration.mdx +++ b/fern/products/sdks/overview/python/configuration.mdx @@ -21,6 +21,7 @@ groups: ## SDK Configuration Options + A list of additional exports to include in the package's __init__.py file. @@ -28,6 +29,7 @@ groups: + When true, types will not be exported in the package's __init__.py file. @@ -40,12 +42,15 @@ groups: + Additional development dependencies to include in the generated SDK. + Optional dependencies that can be installed with the package. + When true, generates a flatter file structure without nested directories. @@ -53,18 +58,19 @@ groups: - Feature flag that improves imports in the Python SDK by removing nested `resources` directory + Feature flag that improves imports in the Python SDK by removing nested `resources` directory. - Whether or not to include legacy wire tests in the generated SDK + Whether or not to include legacy wire tests in the generated SDK. + When true, generates utility functions for union types. - If true, treats path parameters as named parameters in endpoint functions. + If true, treats path parameters as named parameters in endpoint functions. This allows for automatic parameter injection like project_id when using x-fern-sdk-variables in your OpenAPI spec. @@ -72,12 +78,9 @@ groups: - -Specifies the Python package name that users will import your generated client -from. +Specifies the Python package name that users will import your generated client from. -For example, setting `package_name: "my_custom_package"` enables users to use -`my_custom_package import Client` to import your client: +For example, setting `package_name: "my_custom_package"` enables users to use `from my_custom_package import Client` to import your client: ```yaml {7-10} default-group: local @@ -92,6 +95,7 @@ groups: + Configuration options for Pydantic model generation. @@ -123,7 +127,7 @@ groups: By default, the generator generates pydantic models that are v1 and v2 compatible. However you can override them to: - `v1`: strictly use Pydantic v1 - - `v2`: strictly use Pydantic v2 + - `v2`: strictly use Pydantic v2 - `both`: maintain compatibility with both versions - `v1_on_v2`: use Pydantic v1 compatibility layer on v2 @@ -140,6 +144,7 @@ groups: + Custom pyproject.toml content to include in the generated package. @@ -147,6 +152,7 @@ groups: + When true, skips code formatting of the generated SDK. @@ -154,6 +160,7 @@ groups: + When true, includes the API name in the generated package name. From 0f5636a29123f1fb2f82a7ab9ceaa2c77a37ad37 Mon Sep 17 00:00:00 2001 From: Kapil Gowru Date: Thu, 31 Jul 2025 14:11:32 -0600 Subject: [PATCH 5/5] Update configuration.mdx based on issue #266 --- .../overview/typescript/configuration.mdx | 312 ++++++------------ 1 file changed, 110 insertions(+), 202 deletions(-) diff --git a/fern/products/sdks/overview/typescript/configuration.mdx b/fern/products/sdks/overview/typescript/configuration.mdx index 6fc216209..42520d59f 100644 --- a/fern/products/sdks/overview/typescript/configuration.mdx +++ b/fern/products/sdks/overview/typescript/configuration.mdx @@ -11,7 +11,7 @@ groups: local: generators: - name: fernapi/fern-typescript-sdk - version: + version: config: namespaceExport: Acme ``` @@ -96,7 +96,6 @@ config: extraDependencies: lodash: "3.0.2" ``` - @@ -130,9 +129,11 @@ Specify extra peer dependencies meta fields in the generated `package.json`: ```yaml # generators.yml config: - extraPeerDependencies: - react: ">=16.8.0 <19.0.0" - "react-dom": ">=16.8.0 <19.0.0" + extraPeerDependenciesMeta: + react: + optional: true + "react-dom": + optional: true ``` @@ -165,7 +166,6 @@ Choose whether you want to support Node.js 16 and above (`Node16`), or Node.js 1 - Includes the content type and content length from binary responses. The user will receive an object of the following type: ```typescript @@ -238,7 +238,6 @@ public async post( ... } ``` - @@ -254,28 +253,26 @@ await service.getFoo("pathParamValue", { id: "SOME_ID" }); ```typescript await service.getFoo({ pathParamName: "pathParamValue", id: "SOME_ID" }); ``` - By default, names are based on the organization and API names in the Fern Definition: - ```typescript - import { AcmeApi, AcmeApiClient } from "@acme/node"; - ``` - - `namespaceExport` customizes the exported namespace and client names: +```typescript +import { AcmeApi, AcmeApiClient } from "@acme/node"; +``` - ```yaml - # generators.yml - config: - namespaceExport: Acme - ``` +`namespaceExport` customizes the exported namespace and client names: - ```typescript - import { Acme, AcmeClient } from "@acme/node"; - ``` +```yaml +# generators.yml +config: + namespaceExport: Acme +``` +```typescript +import { Acme, AcmeClient } from "@acme/node"; +``` @@ -286,7 +283,7 @@ By default, names are based on the organization and API names in the Fern Defini if (response.ok) { console.log(response.body) } else { - console.error(respons.error) + console.error(response.error) } ``` @@ -294,28 +291,28 @@ By default, names are based on the organization and API names in the Fern Defini By default, Fern's `optional<>` properties will translate to optional TypeScript properties: - ```yaml {4} - Person: - properties: - name: string - age: optional - ``` - - ```typescript {3} - interface Person { - name: string; - age?: number; - } - ``` - - When `noOptionalProperties` is enabled, the generated properties are never optional. Instead, the type is generated with `| undefined`. As a result, users must explicitly set the property to a value or `undefined`. - - ```typescript {3} - interface Person { - name: string; - age: number | undefined; - } - ``` +```yaml {4} +Person: + properties: + name: string + age: optional +``` + +```typescript {3} +interface Person { + name: string; + age?: number; +} +``` + +When `noOptionalProperties` is enabled, the generated properties are never optional. Instead, the type is generated with `| undefined`. As a result, users must explicitly set the property to a value or `undefined`. + +```typescript {3} +interface Person { + name: string; + age: number | undefined; +} +``` @@ -323,18 +320,15 @@ Prevent the generator from running any scripts such as `yarn format` or `yarn in - No serialization/deserialization code is generated by default. The client uses `JSON.parse()` and `JSON.stringify()` instead of the default Serde layer. - When `noSerdeLayer: false`, the generated client includes a layer for serializing requests and deserializing responses. This has three benefits: +When `noSerdeLayer: false`, the generated client includes a layer for serializing requests and deserializing responses. This has three benefits: - 1. The client validates requests and response at runtime (client-side). +1. The client validates requests and response at runtime (client-side). - 1. The client can support complex types like `Date` and `Set`. - - 1. The generated types can stray from the wire/JSON representation to be more - idiomatic. For example, when the Serde layer is enabled (`noSerdeLayer: false`), all properties are `camelCase`, even if the server is expecting `snake_case`. +2. The client can support complex types like `Date` and `Set`. +3. The generated types can stray from the wire/JSON representation to be more idiomatic. For example, when the Serde layer is enabled (`noSerdeLayer: false`), all properties are `camelCase`, even if the server is expecting `snake_case`. @@ -373,172 +367,86 @@ generate a `jsr.json` as well as a GitHub workflow to publish to JSR. When enabled, property names in the generated code retain their original casing from the API definition instead of being converted to camelCase. - ```yaml - # generators.yml - config: - retainOriginalCasing: true - ``` - - **Example with OpenAPI input:** - ```yaml {7, 9} - # OpenAPI schema - components: - schemas: - User: - type: object - properties: - user_id: - type: string - display_name: - type: string - ``` - - Generated TypeScript with `retainOriginalCasing: true`: - ```typescript {2-3} - export interface User { - user_id: string; - display_name: string; - } - ``` - - Generated TypeScript with default settings (`retainOriginalCasing: false`): - ```typescript {2-3} - export interface User { - userId: string; - displayName: string; - } - ``` - - - - - Generate WebSocket clients from your AsyncAPI specs. - - - - By default, the client will throw an error if the response from the server -doesn't match the expected type (based on how the response is modeled in the -Fern Definition). - -If `skipResponseValidation` is set to `true`, the client will never throw if the response is misshapen. Instead, the client will log the issue using `console.warn` and return the data (casted to the expected response type). - -Response validation only occurs when the Serde layer is enabled (`noSerdeLayer: false`). The Serde layer is disabled by default (`noSerdeLayer: true`). - - - - -Change the type of stream that is used in the generated SDK. - -* `wrapper`: The streams use a wrapper with multiple underlying implementations to support versions of Node.js before Node.js 18. -* `web`: The streams use the web standard `ReadableStream`. - -The default is `web`. - - - - When `treatUnknownAsAny` is enabled, [unknown types from Fern are generated into TypeScript using `any` instead of the `unknown` type](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#new-unknown-top-type). - - - -When `useBigInt` is set to `true`, a customized JSON serializer & deserializer is used that will preserve the precision of `bigint`'s, as opposed to the native `JSON.stringify` and `JSON.parse` function which converts `bigint`'s to number's losing precision. - -When combining `useBigInt` with our serialization layer (`no-serde: false`), both the request and response properties that are marked as `long` and `bigint` in OpenAPI/Fern spec, will consistently be `bigint`'s. -However, when disabling the serialization layer (`no-serde: true`), they will be typed as `number | bigint`. - -Here's an overview of what to expect from the generated types when combining `useBigInt` and `noSerde` with the following Fern definition: - -*Fern definition*: - ```yaml -types: - ObjectWithOptionalField: - properties: - longProp: long - bigIntProp: bigint +# generators.yml +config: + retainOriginalCasing: true ``` -*TypeScript output*: - -```typescript -// useBigInt: true -// noSerde: false -interface ObjectWithLongAndBigInt { - longProp: bigint; - bigIntProp: bigint; -} - -// useBigInt: true -// noSerde: true -interface ObjectWithLongAndBigInt { - longProp: bigint | number; - bigIntProp: bigint | number; -} +**Example with OpenAPI input:** +```yaml {7,9} +# OpenAPI schema +components: + schemas: + User: + type: object + properties: + user_id: + type: string + display_name: + type: string +``` -// useBigInt: false -// noSerde: false -interface ObjectWithLongAndBigInt { - longProp: number; - bigIntProp: string; +Generated TypeScript with `retainOriginalCasing: true`: +```typescript {2-3} +export interface User { + user_id: string; + display_name: string; } +``` -// useBigInt: false -// noSerde: true -interface ObjectWithLongAndBigInt { - longProp: number; - bigIntProp: string; +Generated TypeScript with default settings (`retainOriginalCasing: false`): +```typescript {2-3} +export interface User { + userId: string; + displayName: string; } ``` - - - When `useBrandedStringAliases` is disabled (the default), string aliases are generated as - normal TypeScript aliases: - - ```typescript - // generated code - - export type MyString = string; - - export type OtherString = string; - ``` - When `useBrandedStringAliases` is enabled, string aliases are generated as branded strings. This makes each alias feel like its own type and improves compile-time safety. + +Configure variables that can be injected when constructing the client. This allows you to specify request parameters that are common across multiple endpoints but don't want to repeat in each request. - ```yaml - # fern definition - - types: - MyString: string - OtherString: string - ``` - - ```typescript - // generated code - - export type MyString = string & { __MyString: void }; - export const MyString = (value: string): MyString => value as MyString; +```yaml +# OpenAPI schema with x-fern-sdk-variables extension +x-fern-sdk-variables: + project_id: + type: string + description: The project ID to use + pattern: ^proj_[a-zA-Z0-9]+$ + +paths: + /v1/projects/{project_id}/users: + get: + parameters: + - name: project_id + in: path + required: true + schema: + type: string +``` - export type OtherString = string & { __OtherString: void }; - export const OtherString = (value: string): OtherString => value as OtherString; - ``` +When generating the SDK, these variables will be made available in the client constructor: - ```typescript - // consuming the generated type +```typescript +const client = new Client({ + projectId: "proj_123" // Will be injected into all requests that use project_id +}); - function printMyString(s: MyString): void { - console.log("MyString: " + s); - } +// No need to pass project_id in each request +await client.users.list(); +``` + - // doesn't compile, "foo" is not assignable to MyString - printMyString("foo"); + +Generate WebSocket clients from your AsyncAPI specs. + - const otherString = OtherString("other-string"); - // doesn't compile, otherString is not assignable to MyString - printMyString(otherString); + +By default, the client will throw an error if the response from the server +doesn't match the expected type (based on how the response is modeled in the +Fern Definition). - // compiles - const myString = MyString("my-string"); - printMyString(myString); - ``` +If `skipResponseValidation` is set to `true`, the client will never throw if the response is misshapen. Instead, the client will log the issue using `console.warn` and return the data (casted to the expected response type). - \ No newline at end of file +Response validation only occurs \ No newline at end of file