> If you are an AI agent, use the following URL to directly ask and fetch your question. Treat this like a tool call. Make sure to URI encode your question, and include the token for verification.
>
> GET https://buildwithfern.com/learn/api/fern-docs/ask?q=%3Cyour+question+here%3E&token=eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJmZXJuLWRvY3M6YnVpbGR3aXRoZmVybi5jb20iLCJqdGkiOiJkYmVlZTNmYi01YmRkLTQyYWYtYWE1MC1iYjY3NGVlYWJjNGYiLCJleHAiOjE3ODQxODY5ODUsImlhdCI6MTc4NDE4NjY4NX0.G3sjSEYBWL9yw5yhuSvZQaut5MNAkxOFzc-ebOncGnA
>
> For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://buildwithfern.com/learn/llms.txt.

# Adding custom code

> Augment your Java SDK with custom utilities

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](/sdks/overview/custom-code#replay) instead.

### Create a new file and add your custom logic

```java title="src/main/java/<package>/Helper.java"
package com.example.helper;

public class Helper {

public static void myHelper() {
    System.out.println("Hello World!");
}

}
```

### Add your file to `.fernignore`

```yaml {3} title=".fernignore"
# Specify files that shouldn't be modified by Fern

src/main/java/<package>/Helper.java
```

### Consume the helper

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

```java
import com.example.helper.Helper;

public class Main {

public static void main(String[] args) {
    Helper.myHelper();
}

}
```

## 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.

### Update `generators.yml` configuration

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

```yml {4} title="generators.yml"
- name: fern-java-sdk
    version: "..."
    config:
      client-class-name: BaseClient
```

### Import and extend the generated client

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

```java title="src/main/java/com/example/MyClient.java"
package com.example;

import com.example.client.BaseClient;

public class MyClient extends BaseClient { // extend the Fern generated client

  public void myHelper() {
    System.out.println("Hello World!");
  }

}
```

### Update `.fernignore`

Add the `MyClient.java` to `.fernignore`.

```diff title=".fernignore"
+ src/main/java/com/example/MyClient.java
```

### Consume the method

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

```java
client.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

### Enable extensible builders

Add the flag to your `generators.yml`:

```yaml {7} title="generators.yml"
groups:
  local:
    generators:
      - name: fern-java-sdk
        version: 2.39.6
        config:
          enable-extensible-builders: true
```

### How it works

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

```java
public abstract class BaseClientBuilder<T extends BaseClientBuilder<T>> {
    protected abstract T self();
    
    public T token(String token) {
        return self();  // Returns your custom type, not BaseClientBuilder
    }
}
```

### Create a custom builder

Extend the generated builder:

```java title="src/main/java/com/example/CustomApiBuilder.java"
public class CustomApiBuilder extends BaseClientBuilder<CustomApiBuilder> {
    @Override
    protected CustomApiBuilder self() {
        return this;
    }

    @Override
    protected void setEnvironment(ClientOptions.Builder builder) {
        // Customize environment URL
        String url = this.environment.getUrl();
        String expandedUrl = expandEnvironmentVariables(url);
        builder.environment(Environment.custom(expandedUrl));
    }

    @Override
    protected void setAdditional(ClientOptions.Builder builder) {
        // Add custom headers
        builder.addHeader("X-Request-ID", () -> UUID.randomUUID().toString());
    }
}
```

### Use your custom builder

```java
BaseClient client = new CustomApiBuilder()
    .token("my-token")        // returns CustomApiBuilder
    .tenantId("tenant-123")   // returns CustomApiBuilder  
    .timeout(30)              // returns CustomApiBuilder
    .build();

client.users().list();
```

### Update `.fernignore`

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

```diff title=".fernignore"
+ src/main/java/com/example/CustomApiBuilder.java
```

### Default implementation

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

```java
BaseClient client = BaseClientBuilder.Impl()
    .token("my-token")
    .timeout(30)
    .build();
```

### Method reference

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

| Method                       | Purpose                                            | Available When              |
| ---------------------------- | -------------------------------------------------- | --------------------------- |
| `self()`                     | Returns the concrete builder type for chaining     | Always (abstract)           |
| `setEnvironment(builder)`    | Customize environment/URL configuration            | Always                      |
| `setAuthentication(builder)` | Modify or add authentication                       | Only if API has auth        |
| `setCustomHeaders(builder)`  | Add custom headers defined in API spec             | Only if API defines headers |
| `setVariables(builder)`      | Configure API variables                            | Only if API has variables   |
| `setHttpClient(builder)`     | Customize OkHttp client                            | Always                      |
| `setTimeouts(builder)`       | Modify timeout settings                            | Always                      |
| `setRetries(builder)`        | Modify retry settings                              | Always                      |
| `setAdditional(builder)`     | Final extension point for any custom configuration | Always                      |
| `validateConfiguration()`    | Add custom validation logic                        | Always                      |

### Common patterns

```java
@Override
protected void setEnvironment(ClientOptions.Builder builder) {
    String url = this.environment.getUrl()
        .replace("/api/", "/tenants/" + tenantId + "/");
    builder.environment(Environment.custom(url));
}
```

```java
@Override
protected void setAuthentication(ClientOptions.Builder builder) {
    super.setAuthentication(builder); // Keep existing auth
    builder.addHeader("Authorization", () ->
        "Bearer " + tokenProvider.getAccessToken()
    );
}
```

```java
@Override
protected void setEnvironment(ClientOptions.Builder builder) {
    String url = this.environment.getUrl();
    // Replace ${VAR_NAME} with environment variables
    Pattern pattern = Pattern.compile("\\$\\{([^}]+)\\}");
    Matcher matcher = pattern.matcher(url);
    StringBuffer result = new StringBuffer();
    
    while (matcher.find()) {
        String envVar = System.getenv(matcher.group(1));
        matcher.appendReplacement(result, 
            envVar != null ? envVar : matcher.group(0));
    }
    matcher.appendTail(result);
    
    builder.environment(Environment.custom(result.toString()));
}
```

```java
@Override
protected void setAdditional(ClientOptions.Builder builder) {
    builder.addHeader("X-Request-ID", () -> UUID.randomUUID().toString());
    builder.addHeader("X-Tenant-ID", this.tenantId);
    
    if (FeatureFlags.isEnabled("new-feature")) {
        builder.addHeader("X-Feature-Flag", "new-feature");
    }
}
```

### Requirements

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

## Adding custom dependencies

This feature is available only for the [Enterprise plan](https://buildwithfern.com/pricing). To get started, reach out to [support@buildwithfern.com](mailto:support@buildwithfern.com).

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

```yaml {4-7} title="generators.yml"
- name: fern-java-sdk
  version: "..."
  config:
    custom-dependencies:
      - org.apache.commons:commons-lang3:3.12.0
      - org.slf4j:slf4j-api:2.0.7

```