Skip to content
FeedbackDashboard

Migrate from Fern

Learn how to migrate your SDK generation from Fern to Stainless, including exporting your OpenAPI spec and configuring SDK generation.

This guide walks you through migrating from Fern to Stainless for SDK generation. You will export your OpenAPI specification from Fern, create a new Stainless project and configure SDK generation to match your existing SDKs.

Stainless and Fern both generate SDKs from OpenAPI specifications, but they differ in their approach to configuration and SDK output. This guide helps you migrate your existing Fern setup to Stainless while maintaining feature parity with your existing SDKs.

  • OpenAPI specification (exported from Fern)
  • SDK generation configuration
  • Documentation content and structure
  • CI/CD workflows

Ensure you have:

  • An existing Fern project with configured SDK generation
  • The Fern CLI installed
  • A GitHub account for authenticating with Stainless
  • Access to your Fern configuration files (fern.config.json, generators.yml, docs.yml)

If your Fern project uses the Fern Definition format, export it to OpenAPI 3.1 format using the fern export command.

Navigate to your Fern project directory and run:

Terminal window
fern export openapi.yml

This command creates an openapi.yml file in your current directory containing your API definition in OpenAPI format.

If you have multiple APIs defined in your fern/apis/ folder, specify which API to export:

Terminal window
fern export --api public-api openapi.yml
fern export --api internal-api internal-openapi.yml
  1. Go to app.stainless.com and sign in with your GitHub account
  2. Create a new Stainless organization when prompted
  3. Create your first project by uploading your exported OpenAPI specification

Alternatively, use the Stainless CLI:

Terminal window
# Install the CLI
brew install stainless-api/tap/stl
# Authenticate
stl auth login
# Initialize a new project
stl init

Follow the prompts to upload your OpenAPI spec and configure your project.

When creating your project, Stainless generates an initial stainless.yml configuration file to interpret your OpenAPI spec. This config defines the SDK structure, pagination, error handling, and publishing settings.

This section maps Fern’s generators.yml configuration to Stainless’s stainless.yml configuration.

Both platforms support the following languages for SDK generation:

  • TypeScript
  • Python
  • Go
  • Java
  • C# (.NET)
  • PHP
  • Ruby
  • Swift (Stainless - coming soon), Swift (Fern - beta)
  • Rust (Stainless - coming soon), Rust (Fern - beta)
  • Kotlin (Stainless only)
  • Terraform (Stainless only)
  • CLI (Stainless only)

Here’s how Fern’s generators.yml maps to Stainless’s stainless.yml:

generators.yml

api:
specs:
- openapi: "./openapi.yml"
origin: "https://api.example.com/openapi.json"
overrides: "./openapi-overrides.yml"
namespace: "v1"
settings:
title-as-schema-name: true
inline-path-parameters: false
inline-all-of-schemas: true
prefer-undiscriminated-unions-with-literals: true
filter:
endpoints: ["POST /users", "GET /users/{id}"]
example-generation:
request:
max-depth: 2

stainless.yml

# yaml-language-server: $schema=https://app.stainless.com/config.schema.json
organization:
name: example
docs: 'docs.example.com'
contact: 'support@example.com'
targets:
typescript:
package_name: example
production_repo: example/example-typescript
publish:
npm: true
environments:
production: https://example.com/api/v1
client_settings:
opts:
api_key:
read_env: API_KEY
auth:
security_scheme: api_key
resources:
activities:
methods:
list: get /activities
# ...
pagination:
- name: limit_start_pagination
type: offset
request:
# ...

See the Stainless config reference for complete configuration options.

Both Fern and Stainless support publishing to package managers and GitHub:

FeatureFernStainless
npm and JSR (TypeScript)
PyPI (Python)
GitHub (Go)
Maven Central (Java)
Maven Central (Kotlin)
RubyGems (Ruby)
NuGet (C#)
Packagist (PHP)
Crates.io (Rust)
GitHub (Swift)
npm (MCP server)
Homebrew (CLI)
Hashicorp (Terraform)

Configure publishing in your stainless.yml:

targets:
typescript:
package_name: @my-org/my-package
publish:
npm:
auth_method: oidc

Both platforms support:

  • Type-safe request/response handling
  • Automatic retries with exponential backoff
  • Configurable timeouts
  • Authentication (API keys, custom headers)
  • Pagination helpers

Fern defines pagination in your Fern Definition or via OpenAPI extensions:

# Fern Definition
service:
endpoints:
list:
pagination:
cursor: $response.next_cursor
results: $response.results

Stainless uses the pagination section in stainless.yml:

pagination:
- name: my_cursor_page
type: cursor
request:
next:
type: string
x-stainless-pagination-property:
purpose: next_cursor_param
limit:
type: integer
response:
my_data:
type: array
items:
type: object
next:
type: string
x-stainless-pagination-property:
purpose: next_cursor_field

See the pagination configuration guide for details.

Fern configures retries at the SDK level with defaults:

const client = new APIClient({
maxRetries: 3,
timeout: 60000,
});

Stainless generates similar retry configuration:

const client = new APIClient({
maxRetries: 3,
timeout: 60 * 1000,
});

Configure default retry behavior in stainless.yml:

client_settings:
default_retries:
# 5 retries are made, with the interval [1, 2, 4, 8, 10 (capped by the max)]
# not accounting for jitter
max_retries: 5
initial_delay_seconds: 1
max_delay_seconds: 10

Replace Fern GitHub Actions with Stainless workflows for automated SDK generation.

.github/workflows/fern-generate.yml

name: Generate SDKs
on:
push:
branches: [main]
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: fern-api/fern-action@v1
with:
command: generate
token: ${{ secrets.FERN_TOKEN }}

The recommended approach to keeping your OpenAPI spec updated in Stainless is a two step process that provides you with SDK preview builds. As your OpenAPI spec evolves, you can see how those changes are reflected in SDK diffs.

.github/workflows/stainless-generate.yml

name: Build SDKs for pull request
on:
pull_request:
types:
- opened
- synchronize
- reopened
- closed
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
env:
STAINLESS_ORG: YOUR_ORG
STAINLESS_PROJECT: YOUR_PROJECT
OAS_PATH: YOUR_OAS_PATH
jobs:
preview:
if: github.event.action != 'closed'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Run preview builds
uses: stainless-api/upload-openapi-spec-action/preview@v1
with:
org: ${{ env.STAINLESS_ORG }}
project: ${{ env.STAINLESS_PROJECT }}
oas_path: ${{ env.OAS_PATH }}
merge:
if: github.event.action == 'closed' && github.event.pull_request.merged == true
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Run merge build
uses: stainless-api/upload-openapi-spec-action/merge@v1
with:
org: ${{ env.STAINLESS_ORG }}
project: ${{ env.STAINLESS_PROJECT }}
oas_path: ${{ env.OAS_PATH }}

You can also trigger builds via the Stainless API or a persistent URL that is polled hourly.

After configuring SDK generation, verify that the generated SDKs match your expectations:

  1. Review the generated code: Compare SDK structure, method names, and types with your Fern-generated SDKs
  2. Test example usage: Ensure code snippets and examples work as expected
  3. Check diagnostics: Review any warnings or notes in the Stainless Studio
  4. Run SDK tests: Use the generated test scripts in each SDK repository

If your Stainless-generated SDKs don’t match your Fern SDKs:

  1. Review the configuration mapping examples in this guide
  2. Use the Stainless Studio to iterate on your stainless.yml configuration using transforms
  3. Check the config reference for advanced options
  4. Review the diagnostics reference to understand error messages

Both Fern and Stainless support custom code in generated SDKs:

Fern avoids overwriting your custom code changes by you listing out which files should not be generated in a .fernignore file.

Stainless allows you to edit code in your SDK repositories just like you would any other repository, and your edits persist on top of successive runs of codegen.

After completing your migration:

  1. Polish your SDKs: Use the SDK Studio to refine SDK structure and naming
  2. Configure production repositories: Connect your own GitHub repositories for publishing
  3. Set up automated builds: Configure GitHub Actions for continuous SDK generation
  4. Publish to package managers: Configure publishing credentials for npm, PyPI, Maven, etc.
  5. Migrate existing SDK users: Publish migration guides for your SDK users explaining any breaking changes