https://github.com/gr2m/github-webhook-relay
Receive webhooks from a GitHub repository via WebSocket
https://github.com/gr2m/github-webhook-relay
Last synced: about 1 year ago
JSON representation
Receive webhooks from a GitHub repository via WebSocket
- Host: GitHub
- URL: https://github.com/gr2m/github-webhook-relay
- Owner: gr2m
- License: isc
- Created: 2022-12-03T00:39:03.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2025-04-01T22:40:45.000Z (over 1 year ago)
- Last Synced: 2025-04-10T10:42:21.187Z (about 1 year ago)
- Language: JavaScript
- Size: 41 KB
- Stars: 7
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# `github-webhook-relay`
> Receive webhooks from a GitHub repository via WebSocket
>
> **Warning**
> Receiving webhooks via websockets is currently in [private beta](https://github.blog/changelog/2022-11-16-webhook-forwarding-in-the-github-cli-public-beta/)
A Node.js library that uses the same APIs as the [`gh webhook` plugin](https://github.com/cli/gh-webhook) for the [GitHub CLI](https://cli.github.com/).
## Usage
The `createHookToken` option needs to be set to a [token with the `admin:repo_hook` and/or `admin:org_hook` scope](https://github.com/settings/tokens/new?scopes=admin:repo_hook,admin:org_hook&description=github-webhook-relay), depending on which you want to create.
### Minimal example
```js
import WebhookRelay from "github-webhook-relay";
const relay = new WebhookRelay({
owner: "gr2m",
repo: "github-webhooks-relay",
events: ["issues"],
createHookToken: process.env.GITHUB_TOKEN,
});
relay.on("webhook", ({ id, name, payload, signature, headers }) => {
console.log("received webhook: %s", name);
});
relay.on("error", (error) => {
console.log("error: %s", error);
});
relay.start();
```
### Use with Octokit
If you want to relay webhooks to a GitHub App, you can use [github-app-webhook-relay](https://github.com/gr2m/github-app-webhook-relay).
```js
import { App, Octokit } from "octokit";
import WebhookRelay from "github-webhook-relay";
const MyOctokit = Octokit.defaults({
userAgent: "my-app/1.2.3",
});
const app = new App({
appId: process.env.APP_ID,
privateKey: process.env.APP_PRIVATE_KEY,
webhooks: {
secret: process.env.APP_WEBHOOK_SECRET,
},
Octokit: MyOctokit,
});
app.webhooks.on("issues.opened", async ({ octokit }) => {
const { data: comment } = await octokit.request(
"POST /repos/{owner}/{repo}/issues/{issue_number}/comments",
{
owner: payload.repository.owner.login,
repo: payload.repository.name,
issue_number: payload.issue.number,
body: "Hello, world!",
}
);
console.log("[app] Comment created: %s", comment.html_url);
});
const relay = new WebhookRelay({
owner: "gr2m",
repo: "github-webhooks-relay",
events: ["issues"],
octokit: new MyOctokit({ auth: process.env.GITHUB_TOKEN }),
});
relay.on("webhook", app.webhooks.verifyAndReceive);
```
## API
### Constructor
```js
const relay = new WebhookRelay(options);
```
name
type
description
options.owner
string
**Required**. The account name of the GitHub user or organization.
options.repo
string
When set, the webhook will be created for the repository. When not set, the webhook will be created for the organization. Note that user-level webhooks are not supported by GitHub, so `options.owner` must be an organization.
options.events
string[]
**Required**. The list of events that the webhook should subscribe to. For a list of supported event names, see [the GitHub docs](https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads).
options.createHookToken
string
**Required unless `options.octokit` is set**. Access token to create the repository webhook. The token needs to have the `admin:repo_hook` scope. ([create a personal access token](https://github.com/settings/tokens/new?scopes=admin:repo_hook&description=github-webhook-relay)).
options.octokit
octokit
**Required unless `options.createHookToken` is set**. `octokit` is an instance of [`@octokit/core`](https://github.com/octokit/core.js/#readme) or a compatible constructor such as [`octokit`'s `Octokit`](https://github.com/octokit/octokit.js#octokit-api-client).
options.webhookSecret
string
The secret used to sign the webhook payloads. Defaults to no secret.
### `relay.on()`
```js
relay.on(eventName, callback);
```
name
type
description
eventName
string
**Required**. Supported events are
1. `webhook` - emitted when a webhook is received
1. `start` - emitted when the relay is started
1. `stop` - emitted when the relay is stopped
1. `error` - emitted when an error occurs
callback
function
**Required**. The event handler.
When `eventName` is `webhook`, the callback is called with an object with the following properties:
- `id` - the webhook delivery GUID
- `name` - the name of the event
- `body` - the webhook payload as string†
- `signature` - the signature of the webhook payload
- `headers` - the headers of the webhook request
No arguments are passed when `eventName` is set to `start` or `stop`.
When `eventName` is `error`, the callback is called with an error object.
†The webhook payload is passed as is in case the signature needs to be verified again. Parsing the JSON and later stringifying it again bight result in a signature mismatch.
### `relay.start()`
```js
relay.start();
```
Creates the repository hook and connects to the GitHub webhook forwarding service.
### `relay.stop()`
```js
relay.start();
```
Disconnects from the GitHub webhook forwarding service and deletes the repository hook.
## How it works
When creating a repository webhook using [the `POST /repos/{owner}/{repo}/hooks` endpoint](https://docs.github.com/en/rest/webhooks/repos?apiVersion=2022-11-28#create-a-repository-webhook) and you set `name` to `"cli"` then the response body will include a `ws_url` key.
You can connect to the `ws_url` using a WebSocket client. Note that the handshake request requires an `Authorization` header which will have to be set to a personal access token. Using [the `ws` npm package](https://github.com/websockets/ws#readme) the code looks like this:
```js
const ws = new WebSocket(webSocketUrl, {
headers: {
Authorization: process.env.GITHUB_TOKEN,
},
});
```
Once the websocket is connected, the hook can be activated by setting the `active` property to `true` using [the `PATCH /repos/{owner}/{repo}/hooks/{hook_id}` endpoint](https://docs.github.com/en/rest/webhooks/repos?apiVersion=2022-11-28#update-a-repository-webhook).
Each Webhook request is received with a separate message. The message is a JSON string with a `Header` and `Body` keys. The `Body` is base64 encoded.
A response has to be sent back within 10s, otherwise the webhook request will be canceled and time out. No further message will be sent until a response is sent back. The response has to be a JSON string as well with the keys `Status`, `Header`, and `Body`. The `Body` value needs to be base64 encoded.
Disconnecting from the websocket will automatically delete the repository webhook.
## Contributing
Please see [CONTRIBUTING.md](CONTRIBUTING.md).
## See also
- [`github-webhook-relay-cli`](https://github.com/gr2m/github-webhook-relay-cli/#readme) - CLI version of this library
- [`github-app-webhook-relay`](https://github.com/gr2m/github-app-webhook-relay/#readme) - Webhook relay for GitHub Apps
## License
[ISC](LICENSE)