Configuring Fern for Multiple Languages
This guide configures TypeScript, Python, Java, and Go SDK generators in a single Fern generators.yml, all reading one shared API definition, each with its own language-specific options. It is part of the Fern SDK generation section and the broader SDK Generation & Changelog Automation framework. The goal is one source of truth for your API and one command, fern generate --group sdks, that produces four idiomatic clients.
Problem & Context
Maintaining a separate SDK repository per language drifts fast: a field added to the API ships in the TypeScript client weeks before it reaches Go, and the auth handling diverges because four codegen configs live in four places. Fern solves this by reading one API definition into a single intermediate representation and running every generator against it, so all four SDKs reflect the same API the moment you generate.
The configuration challenge is that each language wants different metadata — npm scope and namespace for TypeScript, a PEP 8 package name for Python, a Maven group/artifact coordinate for Java, an import path for Go. Fern keeps these isolated in a per-generator config block while sharing everything that matters (the API itself). This guide assumes you have run fern init and have a definition in fern/definition. Once you can generate every language, publishing them is covered in publishing SDKs to npm and PyPI with Fern.
Step-by-Step Solution
1. Install the Fern CLI
npm install -g [email protected]
fern --version
Expected output:
0.64.21
2. Confirm the shared API definition
All generators read the same definition. Verify it resolves before adding generators.
fern check
Expected output:
fern/definition: All checks passed
If you started from an OpenAPI spec, fern init --openapi ./openapi.yaml imports it into fern/openapi and Fern uses it as the shared source for every generator.
3. Add all four generators to generators.yml
Edit fern/generators.yml. List each generator under one group named sdks, with pinned versions and a local output.location so you can inspect results before publishing.
# fern/generators.yml
groups:
sdks:
generators:
- name: fernapi/fern-typescript-node-sdk
version: 0.50.0
output:
location: local-file-system
path: ../sdks/typescript
- name: fernapi/fern-python-sdk
version: 4.18.0
output:
location: local-file-system
path: ../sdks/python
- name: fernapi/fern-java-sdk
version: 2.30.0
output:
location: local-file-system
path: ../sdks/java
- name: fernapi/fern-go-sdk
version: 0.34.0
output:
location: local-file-system
path: ../sdks/go
4. Set per-generator options
Add a config block to each generator for language-specific metadata. These keys are scoped to their own generator and do not affect the others.
# fern/generators.yml (config blocks added)
groups:
sdks:
generators:
- name: fernapi/fern-typescript-node-sdk
version: 0.50.0
output:
location: local-file-system
path: ../sdks/typescript
config:
namespaceExport: AcmeApi
packageJson:
name: "@acme/acme-sdk"
- name: fernapi/fern-python-sdk
version: 4.18.0
output:
location: local-file-system
path: ../sdks/python
config:
package_name: acme_sdk
pydantic_config:
version: v2
- name: fernapi/fern-java-sdk
version: 2.30.0
output:
location: local-file-system
path: ../sdks/java
config:
group: com.acme
artifact: acme-sdk-java
- name: fernapi/fern-go-sdk
version: 0.34.0
output:
location: local-file-system
path: ../sdks/go
config:
packageName: acme
module:
path: github.com/acme/acme-go
5. Generate all languages
Run the group to produce every SDK from the one definition.
fern generate --group sdks
Expected output:
┌─
│ ✓ fernapi/fern-typescript-node-sdk ../sdks/typescript
│ ✓ fernapi/fern-python-sdk ../sdks/python
│ ✓ fernapi/fern-java-sdk ../sdks/java
│ ✓ fernapi/fern-go-sdk ../sdks/go
└─
Confirm the four output folders were written:
ls sdks
Expected output:
go java python typescript
Complete Working Example
A complete fern/generators.yml that generates all four languages locally, plus a CI step that fails the build if any generator errors. This is the file you commit.
# fern/generators.yml — full multi-language configuration
default-group: sdks
groups:
sdks:
generators:
- name: fernapi/fern-typescript-node-sdk
version: 0.50.0
output:
location: local-file-system
path: ../sdks/typescript
config:
namespaceExport: AcmeApi
packageJson:
name: "@acme/acme-sdk"
- name: fernapi/fern-python-sdk
version: 4.18.0
output:
location: local-file-system
path: ../sdks/python
config:
package_name: acme_sdk
pydantic_config:
version: v2
- name: fernapi/fern-java-sdk
version: 2.30.0
output:
location: local-file-system
path: ../sdks/java
config:
group: com.acme
artifact: acme-sdk-java
- name: fernapi/fern-go-sdk
version: 0.34.0
output:
location: local-file-system
path: ../sdks/go
config:
packageName: acme
module:
path: github.com/acme/acme-go
# .github/workflows/generate-sdks.yml
name: Generate SDKs
on:
pull_request:
paths:
- "fern/**"
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npm install -g [email protected]
- run: fern check
- run: fern generate --group sdks
With default-group: sdks set, contributors can also run a bare fern generate locally and get all four SDKs without remembering the group name.
Gotchas & Edge Cases
Generator versions are not interchangeable. Each fernapi/fern-*-sdk generator has its own release cadence, so fern-python-sdk 4.18.0 and fern-go-sdk 0.34.0 are unrelated numbers. Pin each one independently and bump them deliberately; copying one version number across generators will fail with generator version not found.
Go modules need a real import path. The Go generator’s module.path must match where the SDK will actually be imported from (github.com/acme/acme-go), because it is written verbatim into go.mod. A placeholder path compiles locally but breaks go get for your users.
Per-generator config keys are case- and style-specific. The TypeScript generator uses namespaceExport (camelCase), the Python generator uses package_name (snake_case), and the Java generator uses group/artifact. Fern silently ignores an unknown key inside a generator block, so a mistyped option fails quietly rather than loudly — run fern check and inspect the generated output to confirm an option took effect.
FAQ
Can all four generators share one API definition?
Yes. Fern reads a single API definition from the fern/definition folder (or an imported OpenAPI spec) and every generator listed in generators.yml consumes the same intermediate representation. You never duplicate the API per language.
Do per-generator config options conflict across languages?
No. Each generator has its own config block under its entry in generators.yml, so a TypeScript option like namespaceExport and a Java option like group are independent. Fern ignores any option that a given generator does not recognize within its own block.
How do I generate only some languages during local development?
Put the languages you want into a named group and run fern generate --group local. Generators in other groups are skipped, so you can keep a fast local group with one or two languages and a full release group with all four.
Related
- Fern SDK generation — parent section for all Fern configuration topics.
- Publishing SDKs to npm and PyPI with Fern — ship the SDKs you generate here to package registries.
- SDK Generation & Changelog Automation — the overarching framework for generating and releasing SDKs.
- Customizing OpenAPI Generator templates — a template-driven alternative for multi-language generation.