For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
Book a demoLog inStart for free
  • Overview
    • Introduction
    • How it works
    • Quickstart
    • Customer showcase
  • Working with SDKs
    • Project structure
    • Adding custom code
    • Migrating to Replay
    • Capabilities
  • Generators
      • Generating an SDK
      • Publishing to Maven Central
      • Configuration
      • Adding custom code
      • Changelog
      • Customer showcase
  • Reference
    • generators.yml
Checking status...
SOC2Soc 2 Type II
© 2026 Fern • Birch Solutions, Inc., a Postman company

Documentation

SDKsDocsAsk FernCLI Reference

API Definitions

OpenAPIAsyncAPIOpenRPCgRPC

Resources

BlogSupportPricing

Company

Brand KitPrivacy PolicyTerms of Service
LogoLogo
Book a demoLog inStart for free
On this page
  • Adding custom logic
  • Adding custom SDK methods
  • Adding custom client configuration
  • Default implementation
  • Method reference
  • Common patterns
  • Requirements
  • Adding custom dependencies
GeneratorsJava

Adding custom code

||View as Markdown|
Was this page helpful?
Edit this page
Previous

Java configuration

Next

Changelog

This page covers how to add custom logic, methods, and dependencies to your TypeScript SDK.

Adding custom logic

To get started adding custom code:

These steps cover adding a new custom file to the SDK. To preserve line-level edits to a generated file, use Replay instead.
1

Create a new file and add your custom logic

src/main/java/<package>/Helper.java
1package com.example.helper;
2
3public class Helper {
4
5public static void myHelper() {
6 System.out.println("Hello World!");
7}
8
9}
2

Add your file to .fernignore

.fernignore
1# Specify files that shouldn't be modified by Fern
2
3src/main/java/<package>/Helper.java
3

Consume the helper

Now your users can consume the helper function by importing it from the SDK.

1import com.example.helper.Helper;
2
3public class Main {
4
5public static void main(String[] args) {
6 Helper.myHelper();
7}
8
9}

Adding custom SDK methods

Fern also allows you to add custom methods to the SDK itself (e.g. client.my_method() ) by inheriting the Fern generated client and then extending it.

1

Update generators.yml configuration

Name your Fern-generated client something like BaseClient to reflect that this client will be extended.

generators.yml
1- name: fernapi/fern-java-sdk
2 version: "..."
3 config:
4 client-class-name: BaseClient
2

Import and extend the generated client

First, import the Fern generated base client and extend it. Then, add whatever methods you want.

src/main/java/com/example/MyClient.java
1package com.example;
2
3import com.example.client.BaseClient;
4
5public class MyClient extends BaseClient { // extend the Fern generated client
6
7 public void myHelper() {
8 System.out.println("Hello World!");
9 }
10
11}
3

Update .fernignore

Add the MyClient.java to .fernignore.

.fernignore
1+ src/main/java/com/example/MyClient.java
4

Consume the method

Now your users can consume the helper function by importing it from the SDK.

1client.myHelper();

Adding custom client configuration

The Java SDK generator supports builder extensibility through an opt-in self-type pattern. When enabled via the enable-extensible-builders flag, generated builders can be extended while maintaining type safety during method chaining.

Common use cases include:

  • Dynamic URL construction: Replace placeholders with runtime values (e.g., https://api.${TENANT}.example.com)
  • Custom authentication: Implement complex auth flows beyond basic token authentication
  • Request transformation: Add custom headers or modify requests globally
  • Multi-tenant support: Add tenant-specific configuration and headers
1

Enable extensible builders

Add the flag to your generators.yml:

generators.yml
1groups:
2 local:
3 generators:
4 - name: fernapi/fern-java-sdk
5 version: 2.39.6
6 config:
7 enable-extensible-builders: true
2

How it works

Generated builders use the self-type pattern for type-safe method chaining:

1public abstract class BaseClientBuilder<T extends BaseClientBuilder<T>> {
2 protected abstract T self();
3
4 public T token(String token) {
5 return self(); // Returns your custom type, not BaseClientBuilder
6 }
7}
3

Create a custom builder

Extend the generated builder:

src/main/java/com/example/CustomApiBuilder.java
1public class CustomApiBuilder extends BaseClientBuilder<CustomApiBuilder> {
2 @Override
3 protected CustomApiBuilder self() {
4 return this;
5 }
6
7 @Override
8 protected void setEnvironment(ClientOptions.Builder builder) {
9 // Customize environment URL
10 String url = this.environment.getUrl();
11 String expandedUrl = expandEnvironmentVariables(url);
12 builder.environment(Environment.custom(expandedUrl));
13 }
14
15 @Override
16 protected void setAdditional(ClientOptions.Builder builder) {
17 // Add custom headers
18 builder.addHeader("X-Request-ID", () -> UUID.randomUUID().toString());
19 }
20}
4

Use your custom builder

1BaseClient client = new CustomApiBuilder()
2 .token("my-token") // returns CustomApiBuilder
3 .tenantId("tenant-123") // returns CustomApiBuilder
4 .timeout(30) // returns CustomApiBuilder
5 .build();
6
7client.users().list();
5

Update .fernignore

Add your custom builder to .fernignore so Fern won’t overwrite it:

.fernignore
1+ src/main/java/com/example/CustomApiBuilder.java

Default implementation

If you don’t need to extend the builder, use the provided Impl class:

1BaseClient client = BaseClientBuilder.Impl()
2 .token("my-token")
3 .timeout(30)
4 .build();

Method reference

Each method serves a specific purpose and is only generated when needed:

MethodPurposeAvailable When
self()Returns the concrete builder type for chainingAlways (abstract)
setEnvironment(builder)Customize environment/URL configurationAlways
setAuthentication(builder)Modify or add authenticationOnly if API has auth
setCustomHeaders(builder)Add custom headers defined in API specOnly if API defines headers
setVariables(builder)Configure API variablesOnly if API has variables
setHttpClient(builder)Customize OkHttp clientAlways
setTimeouts(builder)Modify timeout settingsAlways
setRetries(builder)Modify retry settingsAlways
setAdditional(builder)Final extension point for any custom configurationAlways
validateConfiguration()Add custom validation logicAlways

Common patterns

Multi-tenant URLs
1@Override
2protected void setEnvironment(ClientOptions.Builder builder) {
3 String url = this.environment.getUrl()
4 .replace("/api/", "/tenants/" + tenantId + "/");
5 builder.environment(Environment.custom(url));
6}
Dynamic authentication
1@Override
2protected void setAuthentication(ClientOptions.Builder builder) {
3 super.setAuthentication(builder); // Keep existing auth
4 builder.addHeader("Authorization", () ->
5 "Bearer " + tokenProvider.getAccessToken()
6 );
7}
Environment variable expansion
1@Override
2protected void setEnvironment(ClientOptions.Builder builder) {
3 String url = this.environment.getUrl();
4 // Replace ${VAR_NAME} with environment variables
5 Pattern pattern = Pattern.compile("\\$\\{([^}]+)\\}");
6 Matcher matcher = pattern.matcher(url);
7 StringBuffer result = new StringBuffer();
8
9 while (matcher.find()) {
10 String envVar = System.getenv(matcher.group(1));
11 matcher.appendReplacement(result,
12 envVar != null ? envVar : matcher.group(0));
13 }
14 matcher.appendTail(result);
15
16 builder.environment(Environment.custom(result.toString()));
17}
Request tracking
1@Override
2protected void setAdditional(ClientOptions.Builder builder) {
3 builder.addHeader("X-Request-ID", () -> UUID.randomUUID().toString());
4 builder.addHeader("X-Tenant-ID", this.tenantId);
5
6 if (FeatureFlags.isEnabled("new-feature")) {
7 builder.addHeader("X-Feature-Flag", "new-feature");
8 }
9}

Requirements

  • Fern Java SDK version: 2.39.6 or later
  • Configuration: enable-extensible-builders: true in generators.yml

Adding custom dependencies

Enterprise feature

This feature is available only for the Enterprise plan. To get started, reach out to support@buildwithfern.com.

To add packages that your custom code requires, update your generators.yml.

generators.yml
1- name: fernapi/fern-java-sdk
2 version: "..."
3 config:
4 custom-dependencies:
5 - org.apache.commons:commons-lang3:3.12.0
6 - org.slf4j:slf4j-api:2.0.7