# Publish npm packages to CloudRepo

> Publish an npm package to a CloudRepo repository with a repository token in .npmrc, publish scoped packages, publish from CI, and fix a 401, 403, 404, 409 or 413.

npm publishes to CloudRepo the way it publishes to any registry: the registry’s address and your token go in `.npmrc`, and `npm publish` uploads the package. This page is for the person who publishes a package. To install it, see [npm repositories](/docs/formats/npm.html).

## Before you start

- An npm repository. If you have none, [create one](/docs/manage/repositories.html#creating-a-repository).
- A repository token that reaches it, with **Read + write**. A **Read only** token can install but not publish: CloudRepo answers its `npm publish` with `403`. See [Repository tokens](/docs/authenticate/repository-tokens.html) to create one. npm sends the token on its own, as `_authToken`. There is no username.
- Your repository’s registry URL, `https://<organization>.mycloudrepo.io/repositories/<repository>/`, with the trailing slash. Your organization’s name is the first part of your CloudRepo host, and the repository’s name is the one the admin portal shows.
- A package name in lowercase letters, digits, hyphens, dots and underscores, scoped (`@scope/name`) or not.

## Publish with npm publish

**npm**

**1. Put the registry and the credential in `~/.npmrc`.** npm replaces `${CLOUDREPO_TOKEN}` with the environment variable of that name, so the file holds no token and can be copied or committed. Export the variable in the shell that runs npm, or in your CI’s secret store.

`~/.npmrc`

```ini
registry=https://your-org.mycloudrepo.io/repositories/your-repo/
//your-org.mycloudrepo.io/repositories/your-repo/:_authToken=${CLOUDREPO_TOKEN}
```

Keep the trailing `/` on both lines, and keep the second line identical to the registry URL without `https:`. npm sends the token only to the registry that prefix names, so if the two differ, even by the slash, npm sends no token at all. This sets CloudRepo as your default registry. To keep npmjs.org as the default and publish only this package to CloudRepo, see [Publish to CloudRepo and install from elsewhere](#publish-to-cloudrepo-and-install-from-elsewhere).

**2. Check the connection.**

**Terminal**

```bash
npm whoami
```

Expected: npm prints your email address.

**3. Publish.** Run this in the directory of your package:

**Terminal**

```bash
npm publish
```

Expected: npm prints `+ <name>@<version>`.

**4. Check that it landed.**

**Terminal**

```bash
npm view docs-npm version
```

Expected: npm prints the version you published. The package is also listed in the repository in the [admin portal](https://admin.cloudrepo.io).

## Scoped packages

A scope routes a package name to a registry. Name your package with a scope, and give npm one line that sends that scope to CloudRepo, in `~/.npmrc` beside the token line from step 1:

`~/.npmrc`

```ini
@myscope:registry=https://your-org.mycloudrepo.io/repositories/your-repo/
//your-org.mycloudrepo.io/repositories/your-repo/:_authToken=${CLOUDREPO_TOKEN}
```

Then publish as usual. npm sends `@myscope/*` packages to CloudRepo, and every other package to its default registry. A scoped package needs this line to install, too: if `npm install @myscope/name` answers `404`, npm is asking the default registry, and the line is missing.

## Publish to CloudRepo and install from elsewhere

If npmjs.org stays your default registry, name CloudRepo in the package itself. `publishConfig` in `package.json` makes `npm publish` go there, whatever your default registry is. The token line stays in `~/.npmrc`:

`package.json`

```json
{
  "name": "my-package",
  "version": "1.0.0",
  "publishConfig": {
    "registry": "https://your-org.mycloudrepo.io/repositories/your-repo/"
  }
}
```

`~/.npmrc`

```ini
//your-org.mycloudrepo.io/repositories/your-repo/:_authToken=${CLOUDREPO_TOKEN}
```

## Publish from CI

Keep the token in your CI’s secret store, expose it as the environment variable `CLOUDREPO_TOKEN`, and let `.npmrc` name the variable, as in step 1. Then the build runs plain npm:

**Terminal**

```bash
npm publish
```

To write the `.npmrc` at build time, from the environment:

**Terminal**

```bash
echo '//your-org.mycloudrepo.io/repositories/your-repo/:_authToken=${CLOUDREPO_TOKEN}' >> .npmrc
```

The single quotes matter: they write the text `${CLOUDREPO_TOKEN}` into the file, and npm replaces it with the variable’s value when it runs, so the file never holds the token.

Never commit a token. Commit only the `.npmrc` that names the variable, and keep the variable itself in your CI platform’s secrets (GitHub Actions secrets, GitLab CI variables, Jenkins credentials).

> **An unset variable is not an error:** If `CLOUDREPO_TOKEN` is not set in the shell that runs npm, npm does not warn you. It sends the text `${CLOUDREPO_TOKEN}` as the token, and CloudRepo does not accept that. Check that the variable is set before you chase a `404`.

## Size

CloudRepo accepts an npm publish of up to 210 MiB (220,200,960 bytes), which holds a tarball of about 155 MiB: npm sends the tarball inside a JSON document, and base64 encoding adds about a third. A larger publish is refused with `413`. If you need to publish larger packages, split them into several.

## Publish a version once

Overwrite Protection is on by default for an npm repository. With it on, publishing a version that is already in the repository is refused with `409 Conflict`, and the version already there stays as it is. npm prints CloudRepo’s explanation: `Version already exists. Overwrites are disabled for this repository.`

Increment the version in `package.json` and publish again. If the repository is meant to accept republished versions, turn **Overwrite Protection** off in its settings instead; see [Overwrite Protection](/docs/manage/repositories.html#overwrite-protection). npm has no mutable-version convention, so there is no equivalent of Maven’s `-SNAPSHOT`: no version is exempt.

## When a publish is refused

Check these in order:

- **`ENEEDAUTH`, or “need auth”, and no request reached CloudRepo.** npm sent no token. The `//your-org.mycloudrepo.io/repositories/your-repo/:` prefix on the `_authToken` line must match the `registry=` line exactly, without `https:` and with the trailing slash.
- **404 Not Found.** npm sends `_authToken` as a Bearer token, and a Bearer that CloudRepo does not accept (mistyped, expired, or from another organization) answers `404`, not `401`. Check the variable is set (see above), that the token is whole, and that it has not expired: the **Repository Tokens** page in the admin portal shows its status.
- **403 Forbidden.** The token cannot publish here. A **Read only** token cannot publish at all, and a token reaches only the repositories ticked when it was created. Use a **Read + write** token that includes this repository. A token that `npm login` wrote is also read-only unless you chose **Read-write** when you authorized it.
- **409 Conflict.** The version is already in the repository. See above.
- **413.** The publish is over 210 MiB (220,200,960 bytes). See above.

More: [Repository tokens](/docs/authenticate/repository-tokens.html), for every client’s credential; and [npm repositories](/docs/formats/npm.html), for installing, `npm login`, and taking a version back.

---

The page: https://www.cloudrepo.io/docs/publish/npm.html
