> For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://buildwithfern.com/learn/llms.txt. # Publishing to npm > Learn how to publish your Fern TypeScript SDK to npm using OIDC or token-based authentication. Complete guide with GitHub Actions setup. Publish your public-facing Fern TypeScript SDK to the [npmjs registry](https://www.npmjs.com/). After following the steps on this page, you'll have a versioned package published on npm. To distribute the SDK internally instead, generate to the local file system (optionally [self-hosted](/learn/sdks/deep-dives/self-hosted)) and build a tarball with [`fern generate --package`](/learn/cli-api-reference/cli-reference/sdk-commands#package). #### Already publishing to npm? If you're using token-based authentication, npm has deprecated long-lived classic tokens. See [Migrating from token-based to OpenID Connect (OIDC) publishing](#migrating-from-token-based-to-oidc-publishing) to upgrade to the more secure OIDC authentication. ![Versioned package published on npmjs.com](/learn/_fern-img/c1600e18a2ff4a80d7fa9edc67538035469a426ea7876f49bfe340ad01b92296.webp) This page assumes that you have: * An initialized `fern` folder, a GitHub repository for your TypeScript SDK, and a TypeScript generator group in `generators.yml`. See [Generating an SDK (TypeScript)](quickstart#add-the-sdk-generator). Before proceeding, verify the prerequisites are actually in place: 1. **Source repo with `fern/` folder**: The user should be working in their source repo, not the SDK repo. Confirm `generators.yml` exists and has a TypeScript generator group. 2. **TypeScript generator group**: If they skipped the [TypeScript quickstart](quickstart), they need to run `fern add fern-typescript-sdk --group ts-sdk` first. Default to OIDC authentication unless the user has a specific reason to use token-based auth. OIDC is more secure and avoids managing npm tokens. ## Configure SDK package settings Update your `generators.yml` file to configure the package name, output location, and client naming for npm publishing. Your `generators.yml` [should live in your source repository](/learn/sdks/overview/project-structure) (or on your local machine), not the repository that contains your TypeScript SDK code. #### Configure \`output\` location In the `group` for your TypeScript SDK, change the output location from `local-file-system` (the default) to `npm` to indicate that Fern should publish your package directly to the npmjs registry: **`generators.yml`** ```yaml {6-7} title="generators.yml" groups: ts-sdk: # Group name for your TypeScript SDK generators: - name: fern-typescript-sdk version: 3.95.1 output: location: npm ``` #### Add a unique package name Your package name must be unique in the npmjs registry, otherwise publishing your SDK will fail. **`generators.yml`** ```yaml {8} title="generators.yml" groups: ts-sdk: generators: - name: fern-typescript-sdk version: 3.95.1 output: location: npm package-name: your-package-name ``` #### Configure \`namespaceExport\` The `namespaceExport` option controls the name of the generated client. This is the name customers use to import your SDK (`import { your-client-name } from 'your-package-name';`). **`generators.yml`** ```yaml {9-10} title="generators.yml" groups: ts-sdk: generators: - name: fern-typescript-sdk version: 3.95.1 output: location: npm package-name: your-package-name config: namespaceExport: YourClientName # must be PascalCase ``` ## Configure GitHub publishing Fern can automatically publish your SDK to npmjs via GitHub Actions. Configure your GitHub repository and publishing mode: Optionally set the mode to control how Fern handles SDK publishing: * `mode: release` (default): Fern generates code, commits to the default branch (or the `branch` you specify), and tags a release automatically * `mode: pull-request` (recommended): Fern generates code and creates a PR for you to review before release * `mode: push`: Fern generates code and pushes to a branch you specify for you to review before release You can also configure other settings, like the reviewers or license. Refer to the [full `github` (`generators.yml`) reference](/learn/sdks/reference/generators-yml#github) for more information. **`generators.yml`** ```yaml title="generators.yml" {11-14} groups: ts-sdk: generators: - name: fern-typescript-sdk version: 3.95.1 output: location: npm package-name: your-package-name config: namespaceExport: YourClientName github: repository: your-org/your-repository mode: push # or "pull-request" branch: your-branch-name # Required for mode: push ``` ## Configure authentication Choose how you want to authenticate with npmjs when publishing. npm has deprecated long-lived classic tokens for publishing from CI/CD workflows. **OpenID Connect (OIDC) authentication is strongly recommended** for security. #### OIDC authentication (Recommended) OIDC-based publishing (also known as "trusted publishing") is the most secure way to publish. With OIDC, you don't need to manage authentication tokens - npmjs trusts your GitHub repository to publish directly. #### Prerequisites * Fern TypeScript SDK generator version `3.12.3` or later * Fern CLI version `0.94.0` or later (only required for local generation with `--local`) #### Add OIDC to generators.yml Add `token: OIDC` to the `output` section: **`generators.yml`** ```yaml title="generators.yml" {9} groups: ts-sdk: generators: - name: fern-typescript-sdk version: 3.95.1 # Must be 3.12.3 or later output: location: npm package-name: your-package-name token: OIDC config: namespaceExport: YourClientName github: repository: your-org/your-repository mode: push branch: your-branch-name ``` #### Publish a placeholder package OIDC can't perform a package's first publish: npm requires the package to exist before you can add a trusted publisher. Publish a placeholder version to create it: ```bash npm login PACKAGE_NAME="your-package-name" ACCESS="public" # or "restricted" for private packages dir="$(mktemp -d)" echo "{ \"name\": \"$PACKAGE_NAME\", \"version\": \"0.0.0\" }" > "$dir/package.json" npm publish "$dir" --access "$ACCESS" ``` Set the placeholder version to something below your first real release (for example, `0.0.0`). #### Authorize your repository on npmjs.com Configure [trusted publishing](https://docs.npmjs.com/trusted-publishers) on npmjs.com to allow your GitHub repository to publish: 1. Open the package page on npmjs.com and go to the **Settings** tab 2. Find the **Trusted Publisher** section and click **Add trusted publisher** 3. Select **GitHub Actions** as your provider 4. Fill in: * **Organization or user**: Your GitHub username or organization * **Repository**: Your TypeScript SDK repository name (e.g., `your-org/your-repository`) * **Workflow filename**: `ci.yml` * **Environment name**: Leave blank 5. Under **Allowed actions**, select **Allow `npm publish`** #### Generate your SDK Generate your SDK to create the GitHub Actions workflow with OIDC configuration: ```bash fern generate --group ts-sdk ``` This creates a `.github/workflows/ci.yml` file that's configured to use OIDC for npm publishing. Alternatively, you can push your `generators.yml` changes and let the Fern GitHub Action generate the workflow for you. #### Troubleshooting **"Unable to authenticate" error** Common causes: * Workflow filename doesn't match exactly (must be `ci.yml`) * Trusted publisher configuration on npmjs.com doesn't match your repository settings * Using self-hosted runners (not supported by npmjs.org) **Solution:** Double-check your trusted publisher configuration on npmjs.com matches your repository name and workflow filename exactly. **Private repository limitations** Provenance attestations aren't generated for packages published from private repositories, even when using trusted publishing. This is a [known limitation](https://github.blog/changelog/2023-07-25-publishing-with-npm-provenance-from-private-source-repositories-is-no-longer-supported/). #### Token-based authentication (Legacy) **npm has deprecated long-lived classic tokens.** Long-lived authentication tokens can be exposed in logs, compromised, and are difficult to manage and rotate. [OIDC-based authentication is strongly recommended instead](#migrating-from-token-based-to-oidc-publishing). #### Generate an npm token 1. Log into [npmjs.com](https://www.npmjs.com/) 2. Click on your profile picture and select **Edit Profile** 3. Select **Access Tokens** 4. Click **Generate New Token** and choose either **Classic Token** (select "Automation" type) or **Granular Access Token** 5. Save your token securely - it won't be displayed again For more information on access tokens, see npm's [About access tokens](https://docs.npmjs.com/about-access-tokens) documentation. #### Add token to generators.yml Add `token: ${NPM_TOKEN}` to the `output` section: **`generators.yml`** ```yaml title="generators.yml" {9} groups: ts-sdk: generators: - name: fern-typescript-sdk version: 3.95.1 output: location: npm package-name: your-package-name token: ${NPM_TOKEN} config: namespaceExport: YourClientName github: repository: your-org/your-repository mode: push branch: your-branch-name ``` #### Add NPM\_TOKEN as a GitHub Actions secret 1. Open your repository on GitHub and go to **Settings** 2. Navigate to **Secrets and variables** > **Actions** 3. Click **New repository secret** 4. Name it `NPM_TOKEN` and paste your npm token 5. Click **Add secret** ## Publish your SDK Your SDK will automatically be published to npmjs when you create a GitHub release with a version tag: 1. Create a GitHub release with a version tag (for example, `v1.0.0`) 2. The CI workflow will run automatically and publish to npm 3. View your package on npmjs.com to confirm the version #### Alternative: Manual workflow dispatch If you prefer to trigger publishes manually, create a `.github/workflows/publish.yml` file: **`.github/workflows/publish.yml`** ```yaml title=".github/workflows/publish.yml" name: Publish TypeScript SDK on: workflow_dispatch: inputs: version: description: "Version to publish (e.g., 1.0.0)" required: true type: string jobs: publish: runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@v4 - name: Install Fern run: npm install -g fern-api - name: Generate and publish SDK env: FERN_TOKEN: ${{ secrets.FERN_TOKEN }} run: fern generate --group ts-sdk --version ${{ inputs.version }} --log-level debug ``` Add your `FERN_TOKEN` as a repository secret (run `fern token` to generate one), then trigger the workflow from the **Actions** tab. --- ## Migrating from token-based to OIDC publishing If you're using token-based authentication and need to migrate to OIDC, follow these steps: ### Why migrate to OIDC npmjs is implementing trusted publishing to remove security risks associated with long-lived tokens, which can be: * Exposed in logs or configuration files * Compromised and used persistently until manually revoked * Difficult to manage and rotate OIDC-based publishing uses short-lived, cryptographically signed tokens that are specific to your workflow and can't be extracted or reused. ### Prerequisites Before migrating, ensure you have: * A package published to [npmjs.org](https://npmjs.org) * A GitHub repository with GitHub Actions configured * Access to your package settings on [npmjs.com](https://npmjs.com) * Fern CLI version `0.94.0` or later (for local generation) ### Choose your migration path Select the approach that fits your situation: #### Path 1: Upgrade your generator (Recommended) This is the easiest path if you can upgrade to version 3.12.3 or later of the TypeScript SDK generator. **When to use this path:** * You're able to upgrade to Fern TypeScript SDK generator version 3.12.3 or later * You haven't `.fernignore`'d your CI workflow file #### Configure trusted publishing on npmjs.com Follow npm's ["Add a trusted publisher on npmjs.com"](https://docs.npmjs.com/trusted-publishers#step-1-add-a-trusted-publisher-on-npmjscom) instructions: 1. Open the package page on [npmjs.com](https://npmjs.com) and go to the **Settings** tab 2. Find the **Trusted Publisher** section and click **Add trusted publisher** 3. Select **GitHub Actions** as your provider 4. Configure: * **Organization or user**: Your GitHub username or organization * **Repository**: Your TypeScript SDK repository name * **Workflow filename**: `ci.yml` (the default Fern workflow file) * **Environment name**: Leave blank (unless you use GitHub environments) 5. Under **Allowed actions**, select **Allow `npm publish`** #### Update your generators.yml Change the `output.token` field from `${NPM_TOKEN}` to `OIDC` and ensure you're using version `3.12.3` or later: **`generators.yml`** ```yaml title="generators.yml" groups: ts-sdk: generators: - name: fern-typescript-sdk version: 3.95.1 # Must be 3.12.3 or later output: location: npm package-name: your-package-name token: OIDC # Changed from ${NPM_TOKEN} config: namespaceExport: YourClientName github: repository: your-org/your-repository ``` #### Regenerate your SDK Regenerate your SDK with the updated CI configuration. You can do this either: **Locally:** ```bash fern generate --group ts-sdk ``` **Or via GitHub Actions:** If you use the Fern GitHub Action to generate your SDK, simply push your updated `generators.yml` file and let the workflow regenerate the SDK for you. This will update your `.github/workflows/ci.yml` file with the required OIDC permissions. #### Remove the NPM\_TOKEN secret After verifying the migration works, remove the `NPM_TOKEN` secret from your GitHub repository settings to prevent accidental use. #### Path 2: Manual CI workflow update Use this path if you can't upgrade the generator or have customized your CI workflow. **When to use this path:** * You can't upgrade due to breaking changes or bugs * You've customized your CI workflow and added it to `.fernignore` * Path 1 didn't update your workflow file #### Configure trusted publishing on npmjs.com Follow the same instructions as Path 1 to add your repository as a trusted publisher on npmjs.com. #### Update your CI workflow manually Open your `.github/workflows/ci.yml` file and make these changes to the `publish` job: **`.github/workflows/ci.yml`** ```yaml title=".github/workflows/ci.yml" publish: needs: [ compile, test ] if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') runs-on: ubuntu-latest permissions: contents: read # ADD THIS: Required for actions/checkout@v4 id-token: write # ADD THIS: Required for OIDC steps: - name: Checkout repo uses: actions/checkout@v4 - name: Set up node uses: actions/setup-node@v4 # ADD THIS: Ensure npm 11.5.1 or later is installed for OIDC support - name: Update npm run: npm install -g npm@latest - name: Install pnpm uses: pnpm/action-setup@v4 - name: Install dependencies run: pnpm install - name: Build run: pnpm build # MODIFY THIS: Remove npm config set and env block - name: Publish to npm run: | if [[ ${GITHUB_REF} == *alpha* ]]; then npm publish --access public --tag alpha elif [[ ${GITHUB_REF} == *beta* ]]; then npm publish --access public --tag beta else npm publish --access public fi # Previously had: # run: | # npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN} # if [[ ${GITHUB_REF} == *alpha* ]]; then # npm publish --access public --tag alpha # elif [[ ${GITHUB_REF} == *beta* ]]; then # npm publish --access public --tag beta # else # npm publish --access public # fi # env: # NPM_TOKEN: ${{ secrets.NPM_TOKEN }} ``` **Key changes:** * Add `permissions` block with `id-token: write` and `contents: read` to the publish job * Add step to update npm to version 11.5.1 or later * Remove the `npm config set` line from the publish step * Remove the `env` block with `NPM_TOKEN` from the publish step #### (Optional) Add ci.yml to .fernignore If you haven't already, add your CI workflow to `.fernignore` to prevent future generator updates from overwriting your manual changes: **`.fernignore`** ```text title=".fernignore" .github/workflows/ci.yml ``` #### Remove the NPM\_TOKEN secret After verifying the migration works, remove the `NPM_TOKEN` secret from your GitHub repository settings. ### Verify your migration After completing either migration path: 1. **Trigger a workflow run** by creating a GitHub release with an alpha tag (for example, `v1.0.0-alpha`) 2. **Check the workflow logs** to verify the publish step succeeds 3. **Verify provenance** by visiting your package on [npmjs.com](https://npmjs.com) - you should see a provenance badge ### Migration troubleshooting #### "Unable to authenticate" error **Common causes:** * Workflow filename doesn't match exactly (must be `ci.yml` with the `.yml` extension) * Missing `id-token: write` or `contents: read` permissions in workflow * npm CLI version is older than 11.5.1 * Using self-hosted runners (not supported) **Solution:** Double-check your trusted publisher configuration on npmjs.com matches your actual workflow file name and verify all requirements are met. #### Workflow still using NPM\_TOKEN If your workflow continues using token-based authentication: * Verify you've removed the `npm config set` line and the `env: NPM_TOKEN` block from the publish step * Check that npm CLI version 11.5.1+ is installed (add the update npm step) * Ensure you're using generator version 3.12.3 or later (if using Path 1) * When using `--local` generation, you need to use Fern CLI version 0.94.0 or later > Learn how to publish your Fern TypeScript SDK to npm using OIDC or token-based authentication. Complete guide with GitHub Actions setup.