diff --git a/fern/products/openapi-def/pages/extensions/parameter-names.mdx b/fern/products/openapi-def/pages/extensions/parameter-names.mdx index ff60bf613..66a4d5123 100644 --- a/fern/products/openapi-def/pages/extensions/parameter-names.mdx +++ b/fern/products/openapi-def/pages/extensions/parameter-names.mdx @@ -1,18 +1,23 @@ --- title: Customize parameter names -description: Use `x-fern-parameter-name` to customize query parameter, header and path parameter naming. +description: Use extensions to customize parameter naming and behavior in your generated SDKs. --- - The `x-fern-parameter-name` extension allows you to customize the variable names of parameters in your generated SDKs. + OpenAPI extensions allow you to customize how parameters are handled in your generated SDKs. -## Headers +## Renaming Parameters -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. +### x-fern-parameter-name -```yaml {8} +Use `x-fern-parameter-name` to customize the variable names of parameters in your generated SDKs: + +#### Headers + +Rename header parameters to make them more readable: + +```yaml paths: "/user": get: @@ -26,12 +31,11 @@ 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. +Make query parameter names more descriptive: -```yaml {8} +```yaml paths: "/user/search": get: @@ -45,12 +49,11 @@ 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. +Simplify path parameter names: -```yaml {8} +```yaml paths: "/user/{userId}": get: @@ -63,3 +66,46 @@ paths: type: string required: false ``` + +## SDK Variables + +### x-fern-sdk-variables + +Use `x-fern-sdk-variables` to define variables that can be set at the SDK client level and injected into requests: + +```yaml +components: + x-fern-sdk-variables: + project_id: + type: string + description: "The project ID to use for all requests" + pattern: "^proj_[a-zA-Z0-9]+$" + +paths: + "/v1/connect/{project_id}/accounts": + parameters: + - name: project_id + in: path + required: true + schema: + type: string + pattern: "^proj_[a-zA-Z0-9]+$" + x-fern-sdk-variable: project_id +``` + +This allows users to set the variable once when instantiating the client: + +```python +client = Client(project_id="proj_123") +``` + +Rather than passing it to every method: + +```python +# No need to pass project_id to each call +accounts = client.accounts.list() +``` + + + SDK variables help reduce repetition and make your SDK more ergonomic by allowing common parameters to be configured once at the client level. + \ No newline at end of file diff --git a/fern/products/sdks/overview/python/configuration.mdx b/fern/products/sdks/overview/python/configuration.mdx index 1c0eb6af7..e1f172553 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 + Additional exports to include in the package's __init__.py file. @@ -28,6 +29,7 @@ groups: + Whether to exclude type definitions from being exported in the package's __init__.py file. @@ -40,12 +42,15 @@ groups: + Additional development dependencies to include in the generated package. + Optional dependency groups that can be installed with the package. + Whether to generate a flat file structure instead of 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 enabled, generates utility functions for working with union types. - If true, treats path parameters as named parameters in endpoint functions. + If true, treats path parameters as named parameters in endpoint functions rather than using a request object. @@ -72,39 +78,23 @@ 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 `my_custom_package import Client` to import your client: -```yaml {7-10} -default-group: local -groups: - local: - generators: - - name: fernapi/fern-python - version: 0.7.1 - config: - package_name: "my_custom_package" -``` + ```yaml + config: + package_name: "my_custom_package" + ``` + Configuration options for Pydantic model generation. - When enabled, the generator will output a Pydantic `__root__` class that will contain utilities to visit the union. For example, for the following union type: + When enabled, generates a Pydantic `__root__` class containing utilities to visit unions. For example: - ``` - types: - Shape: - union: - circle: Circle - triangle: Triangle - ``` - you will get a generated `Shape` class that has a factory and visitor: ```python # Use a factory to instantiate the union Shape.factory.circle(Circle(...)) @@ -112,26 +102,23 @@ groups: # Visit every case in the union shape = get_shape() shape.visit( - circle: lambda circle: do_something_with_circle(circle), - triangle: lambda triangle: do_something_with_triangle(triangle), + circle=lambda circle: do_something_with_circle(circle), + triangle=lambda triangle: do_something_with_triangle(triangle), ) ``` - - When enabled, the python generator will not run Black formatting in the generated code. Black is slow so this can potentially speed up code generation quite a bit. - By default, the generator generates pydantic models that are v1 and v2 compatible. However you can override them to: + Controls Pydantic version compatibility: - `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 - - Example: + ```yaml config: pydantic_config: - version: v1 # or v2 or "both" + version: v2 ``` @@ -140,6 +127,7 @@ groups: + Custom pyproject.toml contents to include in the generated package. @@ -147,6 +135,7 @@ groups: + Whether to skip code formatting on the generated files. @@ -154,6 +143,7 @@ groups: + Whether to include the API name in the generated package name. @@ -168,17 +158,48 @@ groups: Whether or not to generate TypedDicts instead of Pydantic Models for file upload request objects. Note that this flag was only introduced due to an oversight in the `use_typeddict_requests` flag implementation; it should be removed in the future. +### Extension Headers + +The Python generator supports extension headers through the `x-fern-sdk-variables` configuration in your OpenAPI spec. This allows you to define variables that will be included in the client configuration and injected into API paths. + +For example, to define a project ID variable that will be included in all paths: + +```yaml +x-fern-sdk-variables: + project_id: + type: string + description: "The ID of the project" + pattern: "^proj_[a-zA-Z0-9]+$" +``` + +This variable can then be referenced in your paths: + +```yaml +paths: + /v1/connect/{project_id}/accounts: + parameters: + - name: project_id + in: path + required: true + schema: + type: string + x-fern-sdk-variable: project_id +``` + +The generated client will include this variable in its configuration and automatically inject it into request paths. + ### client - Configuration for the generated client class and file structure. - ```yaml - config: - client: - filename: "my_client.py" - class_name: "MyClient" - exported_filename: "my_client.py" - exported_class_name: "MyClient" - ``` +Configuration for the generated client class and file structure. + +```yaml +config: + client: + filename: "my_client.py" + class_name: "MyClient" + exported_filename: "my_client.py" + exported_class_name: "MyClient" +``` The filename for the generated client file. diff --git a/fern/products/sdks/overview/typescript/configuration.mdx b/fern/products/sdks/overview/typescript/configuration.mdx index 6fc216209..a6173f0fa 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,40 @@ 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 +``` + + + +Define custom request headers to be included in all API requests: + +```yaml +# generators.yml +config: + extensionHeaders: + x-project-id: project_id +``` + +The header value is derived from the variable name specified, which can be defined using `x-fern-sdk-variables` in your OpenAPI spec: + +```yaml +# openapi.yml +x-fern-sdk-variables: + project_id: + type: string + description: The project ID to use for all requests +``` + +This will generate a client that accepts the variable in its constructor: + +```typescript +const client = new ApiClient({ + projectId: "my-project-id" +}); ``` @@ -165,7 +195,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 +267,6 @@ public async post( ... } ``` - @@ -254,28 +282,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"; +``` @@ -294,28 +320,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 +349,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: - - 1. The client validates requests and response at runtime (client-side). +When `noSerdeLayer: false`, the generated client includes a layer for serializing requests and deserializing responses. This has three benefits: - 1. The client can support complex types like `Date` and `Set`. +1. The client validates requests and response at runtime (client-side). - 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,46 +396,45 @@ 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; - } - ``` +```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. +Generate WebSocket clients from your AsyncAPI specs. @@ -423,122 +445,6 @@ 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 -``` - -*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; -} - -// useBigInt: false -// noSerde: false -interface ObjectWithLongAndBigInt { - longProp: number; - bigIntProp: string; -} - -// useBigInt: false -// noSerde: true -interface ObjectWithLongAndBigInt { - longProp: number; - bigIntProp: 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. - - ```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; - - export type OtherString = string & { __OtherString: void }; - export const OtherString = (value: string): OtherString => value as OtherString; - ``` - - ```typescript - // consuming the generated type - - function printMyString(s: MyString): void { - console.log("MyString: " + s); - } - - // doesn't compile, "foo" is not assignable to MyString - printMyString("foo"); - - const otherString = OtherString("other-string"); - // doesn't compile, otherString is not assignable to MyString - printMyString(otherString); - - // compiles - const myString = MyString("my-string"); - printMyString(myString); - ``` - - \ No newline at end of file +