Resources

Fern vs. OpenAPI Generator

View as Markdown

Fern generates production-ready SDKs with built-in OAuth token refresh, pagination, retries, typed errors, and comprehensive documentation. OpenAPI Generator produces basic API wrappers that require implementing these features yourself.

CapabilityFernOpenAPI Generator
DocumentationComprehensive README + endpoint docsMinimal (title only)
AuthenticationAutomatic OAuth token refreshManual token management
PaginationAsync iterators with for awaitManual cursor handling
Error handlingTyped error classes per status codeGeneric ResponseError
ResilienceBuilt-in retries, timeouts, abort signalsNone (requires custom code)

Documentation and onboarding

Fern SDKs include comprehensive documentation covering installation, authentication, types, errors, pagination, and retries. OpenAPI Generator provides minimal documentation.

FeatureFernOpenAPI Generator
READMEComprehensive guide covering installation, auth, types, errors, pagination, retries, timeouts, logging, and runtime compatibilityMinimal (often just a title)
Endpoint documentationGenerated reference.md with usage examples for every endpointNone
Code examplesInline examples in method documentationNone

Client ergonomics

Fern provides a single unified client with namespaced resources. OpenAPI Generator uses separate API classes per resource.

FeatureFernOpenAPI Generator
Client structureSingle unified client with namespaced resourcesSeparate API classes per resource
Method namingClean, idiomatic names (client.plant.add())Verbose names (plantApi.addPlant())
ConfigurationCentralized in client constructorPassed to each API class

Fern
1const client = new PlantStoreClient({ clientId, clientSecret });
2await client.plant.add({ name: "Fern", status: "available" });
3await client.user.login();
OpenAPI Generator
1const config = new Configuration({ accessToken: async () => token });
2const plantApi = new PlantApi(config);
3const userApi = new UserApi(config);
4await plantApi.addPlant({ plant: { name: "Fern", status: "available" } });
5await userApi.loginUser({ username, password });

Authentication and token lifecycle

Fern includes a built-in OAuth token provider that automatically retrieves and refreshes tokens with a configurable buffer before expiration. OpenAPI Generator requires manual token management.

FeatureFernOpenAPI Generator
OAuth token refreshAutomatic with OAuthTokenProviderManual implementation required
Token expiration handlingBuilt-in buffer before expirationDeveloper responsibility
Environment variablesAutomatic fallback to env varsManual configuration

Pagination

Fern provides async iterators for pagination with for await support. OpenAPI Generator returns raw responses with manual cursor tracking.

FeatureFernOpenAPI Generator
Async iteratorsBuilt-in for await supportNone
Page navigationhasNextPage() / getNextPage() methodsManual cursor tracking
Underlying responseAccessible via page.responseDirect return value

Fern
1const pageableResponse = await client.plant.list();
2for await (const item of pageableResponse) {
3 console.log(item);
4}
5
6// Or manually iterate pages
7let page = await client.plant.list();
8while (page.hasNextPage()) {
9 page = await page.getNextPage();
10}
OpenAPI Generator
1let cursor: string | undefined;
2do {
3 const response = await plantApi.listPlants({ cursor });
4 for (const plant of response.plants) {
5 console.log(plant);
6 }
7 cursor = response.pagination?.nextCursor;
8} while (cursor);

Error handling

Fern generates typed error classes for specific HTTP status codes. OpenAPI Generator uses a generic ResponseError class.

FeatureFernOpenAPI Generator
Typed errorsBadRequestError, NotFoundError, MethodNotAllowedErrorGeneric ResponseError
Error bodyParsed and typedRequires manual parsing
Raw response accesserr.rawResponseerr.response

Fern
1try {
2 await client.plant.add({ name: "Fern", status: "available" });
3} catch (err) {
4 if (err instanceof PlantStore.BadRequestError) {
5 console.log("Invalid request:", err.body);
6 } else if (err instanceof PlantStore.NotFoundError) {
7 console.log("Plant not found");
8 } else if (err instanceof PlantStoreError) {
9 console.log("Status:", err.statusCode, "Body:", err.body);
10 }
11}
OpenAPI Generator
1try {
2 await plantApi.addPlant({ plant });
3} catch (err) {
4 if (err instanceof ResponseError) {
5 const body = await err.response.json();
6 if (err.response.status === 400) {
7 console.log("Invalid request:", body);
8 } else if (err.response.status === 404) {
9 console.log("Plant not found");
10 }
11 }
12}

Resilience features

Fern includes automatic retries with exponential backoff, configurable timeouts, and request cancellation. OpenAPI Generator requires custom implementation of these features.

FeatureFernOpenAPI Generator
Automatic retriesExponential backoff with jitter for 408, 429, 5xxNone
Retry-After headerAutomatically respectedManual implementation
Configurable timeoutstimeoutInSeconds option (default 60s)None
Request cancellationabortSignal optionPossible via initOverrides
Max retriesConfigurable per-request or globallyNone

Fern
1const response = await client.plant.add(
2 { name: "Fern", status: "available" },
3 {
4 maxRetries: 3,
5 timeoutInSeconds: 30,
6 abortSignal: controller.signal
7 }
8);

Logging and observability

Fern includes configurable logging with automatic redaction of auth headers, tokens, and API keys. OpenAPI Generator has no built-in logging.

FeatureFernOpenAPI Generator
Built-in loggingConfigurable levels (Debug/Info/Warn/Error)None
Custom loggersPluggable (winston, pino, etc.)None
Sensitive data redactionAutomatic for auth headers, tokens, API keysNone
Request/response loggingDebug-level with redacted URLsManual implementation

Fern
1const client = new PlantStoreClient({
2 clientId,
3 clientSecret,
4 logging: {
5 level: logging.LogLevel.Debug,
6 logger: new logging.ConsoleLogger(),
7 silent: false
8 }
9});

Raw response access

Fern provides a .withRawResponse() method for accessing HTTP response details. OpenAPI Generator uses separate *Raw() methods.

FeatureFernOpenAPI Generator
Access patternSingle method with .withRawResponse()Separate *Raw() methods
Headers accessrawResponse.headersresponse.raw.headers
Response bodydata propertyvalue() method

Fern
1const { data, rawResponse } = await client.plant.add({
2 name: "Fern",
3 status: "available"
4}).withRawResponse();
5
6console.log(rawResponse.headers);
OpenAPI Generator
1const response = await plantApi.addPlantRaw({ plant });
2const data = await response.value();
3console.log(response.raw.headers);

Runtime compatibility and packaging

Fern supports multiple JavaScript runtimes with ESM/CJS dual builds and automatic runtime detection. OpenAPI Generator provides ESM/CJS support.

FeatureFernOpenAPI Generator
Node.js18+Supported
Denov1.25+Not supported
Bun1.0+Not supported
Cloudflare WorkersSupportedNot supported
React NativeSupportedNot supported
ESM/CJS buildsDual builds with proper exportsBasic ESM/CJS
Subpackage exportsTree-shakeable importsNone
Runtime detectionAutomaticNone

Testing and quality

Fern SDKs include unit and wire tests with linting enabled. OpenAPI Generator doesn’t include tests.

FeatureFernOpenAPI Generator
Unit testsYesNone
Wire testsYesNone
LintingBiome (enabled)Disabled (/* eslint-disable */)
Zero dependenciesYesYes