---
title: "Work with several forges"
description: "Register several providers and route calls by ref or by URL."
canonical_url: "https://forges.link/guides/multiple-forges"
---
# Work with several forges

> Register several providers and route calls by ref or by URL.

Tools that work across forges, such as an inbox or a bot, shouldn't need to know which forge a thread lives on. `createForges()` makes that possible. It takes a list of factories, or of providers you have already created, builds the providers and returns one object that routes your calls to the right one:

```ts
import { createForges, github, gitlab } from 'forges'

const forges = createForges([
  github({ auth: { type: 'token', token: process.env.GITHUB_TOKEN! } }),
  gitlab({ auth: { type: 'token', token: process.env.GITLAB_TOKEN! } }),
])
```

## Route by ref

Every ref carries a `forge` and an `instance`, and that is all `forges` needs to decide where a call goes. When you call something like `forges.threads.get(ref)` or `forges.repos.get(ref)`, the call is sent to the provider that matches the ref. If you pass a ref for a forge that you haven't registered, the call throws `UnknownForgeError`.

If you need the provider itself, for instance to check what it supports, ask for it with `forges.for(ref)`, `forges.get('github')` or `forges.all('gitlab')`:

```ts
const provider = forges.for(thread.ref)
if (provider?.can('threads.close', thread.ref.kind)) {
  await provider.threads.close(thread.ref)
}
```

## Read a pasted URL

Users often paste a link rather than a ref. `forges.parseUrl()` turns a web URL on any registered instance into refs, and it returns `undefined` when none of your providers serves that URL:

```ts
const parsed = forges.parseUrl('https://github.com/nuxt/nuxt/issues/1')

if (parsed?.thread) {
  const thread = await parsed.provider.threads.get(parsed.thread)
}
```

It also understands clone URLs, such as the output of `git remote get-url origin`, as long as the host is the same as the instance's web host:

```ts
const { repo } = forges.parseUrl('git@github.com:nuxt/nuxt.git') ?? {}
```

The same goes for the value of a `repository` field in `package.json`, such as `git+https://github.com/nuxt/nuxt.git` or `git://github.com/nuxt/nuxt.git`. The `github:`, `gitlab:` and `bitbucket:` shorthands refer to `github.com`, `gitlab.com` and `bitbucket.org`, so a self-hosted instance doesn't match them:

```ts
const { repo } = forges.parseUrl('github:nuxt/nuxt') ?? {}
```

A file URL sets `file`, with the repository-relative `path`, the `at` revision and the first selected `line`:

```ts
const parsed = forges.parseUrl('https://github.com/nuxt/nuxt/blob/main/README.md#L3')

if (parsed?.file) {
  const readme = await parsed.provider.contents.file(parsed.repo, parsed.file.path, { ref: parsed.file.at })
}
```

The revision is the first path segment after the file route, so in `blob/feat/a/README.md` it is `feat` and the path is `a/README.md`. `at` is unset when the URL leaves the revision to the default branch, as an Azure DevOps file URL without a `version` does. Some forges, such as Forgejo, Bitbucket and Azure DevOps, use the same URL for a folder, so `file` can name a folder there.

To go the other way, `provider.urlFor()` builds the web URL for a ref without sending a request.

## Fan out reads

Some calls make sense across all your forges at once, so `createForges()` runs them on every provider that supports them:

- `forges.notifications.list()` lists the notifications of one provider after another.
- `forges.search.threads()` and `forges.search.repos()` search every provider that supports search and merge the results, with the most recently updated first. To merge by creation time instead, pass `sort: 'created'`, and to start with the oldest, pass `direction: 'asc'`.
- `forges.threads.getMany()` groups the refs by provider, fetches them and returns the threads in the order you passed the refs.

Searches only read the next page when you iterate that far, so you can `break` out of the loop as soon as you have enough results. Two details are worth knowing. A provider that can't sort the way you asked only keeps its results in order within each page. And if a provider fails, you get a warning and no further items from it, while the other providers carry on.

## Run two instances of one forge

A provider takes its instance from the host of its `baseUrl`. This means you can register github.com and a GitHub Enterprise Server side by side:

```ts
const forges = createForges([
  github({ auth: personal }),
  github({ baseUrl: 'https://ghe.example.com/api/v3', auth: work }),
])
```

Since refs are routed by forge and instance, two providers can't share both, and `createForges()` throws if they do. If you need two providers for the same host, for example with different credentials, give one of them its own name by passing a different `instance`.


## Sitemap

See the full [sitemap](https://forges.link/sitemap.md) for all pages.
