{"id":26422330,"url":"https://github.com/jollytoad/webhook-workshop","last_synced_at":"2026-04-20T09:06:39.833Z","repository":{"id":225988961,"uuid":"767426835","full_name":"jollytoad/webhook-workshop","owner":"jollytoad","description":"Build and deploy webhooks with Deno","archived":false,"fork":false,"pushed_at":"2025-09-08T14:27:04.000Z","size":127,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-08T16:31:44.548Z","etag":null,"topics":["adaptavist","deno","deno-deploy","gitlab","gitpod","typescript","webhooks"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jollytoad.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-03-05T09:21:32.000Z","updated_at":"2025-09-08T14:27:08.000Z","dependencies_parsed_at":"2024-12-09T15:31:45.583Z","dependency_job_id":"37cc366e-af37-4443-8f65-a425e0448a7c","html_url":"https://github.com/jollytoad/webhook-workshop","commit_stats":null,"previous_names":["jollytoad/webhook-workshop"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jollytoad/webhook-workshop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jollytoad%2Fwebhook-workshop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jollytoad%2Fwebhook-workshop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jollytoad%2Fwebhook-workshop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jollytoad%2Fwebhook-workshop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jollytoad","download_url":"https://codeload.github.com/jollytoad/webhook-workshop/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jollytoad%2Fwebhook-workshop/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32040371,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T00:18:06.643Z","status":"online","status_checked_at":"2026-04-20T02:00:06.527Z","response_time":94,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["adaptavist","deno","deno-deploy","gitlab","gitpod","typescript","webhooks"],"created_at":"2025-03-18T02:03:51.880Z","updated_at":"2026-04-20T09:06:39.798Z","avatar_url":"https://github.com/jollytoad.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Webhook Workshop\n\nThis is an example of building [GitLab] [webhook] handlers using [Gitpod] and\n[Deno], and deploying them to Deno [Deploy].\n\n[GitLab]: https://gitlab.com\n[webhook]: https://docs.gitlab.com/ee/user/project/integrations/webhooks.html\n[Gitpod]: https://gitpod.io\n[Deno]: https://deno.com\n[Deploy]: https://deno.com/deploy\n\n## What can you do with it?\n\nYou can quickly build, test and deploy webhooks for [GitLab] (either gitlab.com\nor your own hosted instance), or other SaaS platforms.\n\n[GitPod] can provide an environment to edit the code and run a development\nserver with a public URL, giving a realtime.\n\n[Deno] provides a modern Typescript runtime that avoids the large initial\ndownload of packages, and incorporates all the tooling (ie. formatting, linting,\ntype checking, task running, testing) you need.\n\nDeno [Deploy] provides a cloud edge hosting solution with a very simple\ndeployment mechanism, and support for a transactional distributed key-value\nstore, queueing, and scheduling out of the box, without having to provision\nadditional services.\n\n[GitLab] itself provides the git repo hosting for your webhook codebase, and\nCI/CD to test and deploy them to Deno Deploy.\n\nThe intention is that you should fork this project, and use it as a template\nfrom which to create your own webhooks. It contains a number of example\nwebhooks, including a hook to create an issue on a pipeline failure and use an\nOpenAI LLM to summarise the reason and suggest a remedy. The full list is\npresented further down.\n\n### Flexibility\n\nThe only hard requirement of this project is the Deno runtime, although the\noverall workflow demonstrated here could be adapted to an alternative runtime or\neven language.\n\nGitpod provides a convenience for an instant dev environment, and hosting a dev\nserver that is publicly accessible, but you could, if you prefer, just checkout\nthe code locally, install Deno, and use a HTTP tunnel.\n\nDeno Deploy provides a convenient platform for hosting your production server,\nbut again, you are not locked into this. You could bundle into a Docker\ncontainer or even compile to a single binary executable. You would have to deal\nwith provisioning of database, and queueing if you required that though.\n\nAlthough this project focuses on GitLab webhooks, but you could handle webhooks\nfrom other services too, even in the same server.\n\nAnd, as this is really just a web server, you could use it as a template to\nbuild other things that aren't even webhooks. A web app or site, an API, or\nschedule tasks (using [Deno Cron](https://docs.deno.com/deploy/kv/manual/cron)).\n\n## Prerequisites\n\nYou'll need accounts on:\n\n- [GitLab]\n- [Gitpod]\n- Deno [Deploy]\n- OpenAI - if you wish to use the `create-issue-on-fail` hook\n\n## How to get started\n\n1. **Fork** this repository in GitLab.\n2. Click **Edit in Gitpod**. If it's your first time using Gitpod you'll have to\n   go through a short onboarding process.\n3. Select your preferred IDE (VSCode in browser, if you aren't sure), you'll\n   only need a small instance.\n4. Wait for your workspace to be created, this may take a minute or two, but it\n   should re-open instantly at a later date.\n5. Check your local dev server is running and publicly available, the IDE will\n   prompt you to open the site in a browser.\n6. Copy the long cryptic URL it took you too, this will be the base URL of your\n   webhooks whilst testing.\n7. Create a `.env` file from the `.env.example` and populate with appropriate\n   tokens.\n8. Restart the server (hit `ctrl+c` in the terminal, and run `deno task start`),\n   most code changes will be picked up automatically, but you may need to do\n   this again for certain things.\n\nYou are now ready to start the development and testing cycle of a webhook\nagainst your development server running in Gitpod.\n\n## Quick introduction to the architecture\n\nThis project is a simple web-server, using a filesystem based convention to\norganise routing. All HTTP routes can be found in individual files under\n[`app/routes`](./app/routes/), and all webhooks can specifically be found there\nunder [`hook`](./app/routes/hook/).\n\nSo the URL https://something-cryptic.gitpod.io/hook/log will invoke the handler\nat [`app/routes/hook/log.ts`](./app/routes/hook/log.ts).\n\nThe main entry point of the server is [`app/main.ts`](./app/main.ts) for\nproduction, and [`scripts/dev.ts`](./scripts/dev.ts) for development, you can\nfollow everything the server does from these entry points, but for the sake of\ngetting started your main focus will be on the modules under\n[`app/routes/hook`](./app/routes/hook/).\n\n## Testing and working on a webhook\n\nFor this example we'll focus on the [`log`](./app/routes/hook/log.ts) hook.\n\nTo add the webhook in GitLab:\n\n1. Find or create a different project in GitLab in which you can test your\n   webhooks, one that already has CI/CD pipeline is good if you want to try out\n   the most complex example\n   ([`create-issue-on-fail`](./app/routes/hook/create-issue-on-fail.ts)).\n2. In that project go to **Settings** `\u003e` **Webhooks**.\n3. Click **Add new webhook**.\n4. Paste your base gitpod **URL** (obtained earlier) and append your hook route,\n   example:\n   `https://8000-jollytoad-webhookworksh-259mv5t6rzu.ws-eu108.gitpod.io/hook/log`\n5. If you generated and set `GITLAB_WEBHOOK_TOKEN` in your `.env`, then paste\n   this value into the **Secret Token**.\n6. Select the webhook events you are interested in under **Trigger**.\n7. (Leave **SSL verification** enabled).\n8. Finally click **Add webhook**.\n\nYou can now test your webhook via the **Test** menu, or go and perform a real\naction in GitLab that triggers the appropriate event, for example, creating an\nissue or running a pipeline.\n\nBack in your Gitpod IDE, keep an eye on your terminal running the development\nservice and watch the logs for the hook being called.\n\nOnce you are sure the dev server is handling the webhook events, go ahead an\nopen [`log.ts`](./app/routes/hook/log.ts) in your IDE and make some changes,\nmaybe add an extra `console.log`. These should save automatically, causing the\ndev server to restart making your changes immediately live.\n\nNow try triggering the webhook again within GitLab and confirm your changes are\nlive from the output within the IDE terminal.\n\nCongratulations you now have a realtime development environment running for your\nwebhook experiments.\n\nIf you want to create a new hook module, copying the `log.ts` is always a good\nplace to start.\n\n## Deploying the webhook for production usage\n\nThis project contains a GitLab CI/CD configuration to automatically deploy your\nwebhooks to production on Deno Deploy on every commit.\n\nBefore you commit, it's a good idea to run `deno task check` in a terminal,\nwhich will run the code formatter, linter and type checking for the project.\n\nFor your webhooks to be deployed to the production hosting you'll need to sign\ninto Deno Deploy and generate a deployment token.\n\n1. Sign into [Deno Deploy](https://dash.deno.com/signin).\n2. Visit your [Account Settings](https://dash.deno.com/account).\n3. Scroll down to **Access Tokens** and hit **+ New Access Token**.\n4. Give the token a good description (I like to use the GitLab URL of the\n   webhooks project) and hit **Generate**.\n5. Copy the newly generated token.\n6. Go back to your webhooks project in GitLab, and to: **Settings** _\u003e_\n   **CI/CD** _\u003e_ **Variables** (click **Expand**).\n7. Then **Add variable**.\n8. For this example, under **Flags**, check only **Mask variable**.\n9. Set the **Key** field to `DENO_DEPLOY_TOKEN`, and paste the copied token into\n   the **Value** field.\n\nThe pipeline should be ready to deploy now, but before you do, back in your IDE:\n\n1. Open the file [deno.json](./deno.json)\n2. Change the value of **deploy** \u003e **project**. This should be a unique name\n   within the `https://\u003cproject\u003e.deno.dev` domain, for example:\n   `https://my-webhooks.deno.dev`, so make sure your chosen project name doesn't\n   already exist.\n3. You can now commit and push your changes.\n\nThe GitLab CI/CD pipeline should kick-in, check your project, and then deploy to\nDeno Deploy. Watch the pipeline in GitLab, and check that the project has been\ncreated within your [Deploy dashboard](https://dash.deno.com).\n\nIf everything went well your webhooks will be live in production at the\n_deno.dev_ URL.\n\nNow you'll need to set up some environment variables within Deploy, you only\nneed to do this once.\n\n1. Visit your [Deploy dashboard](https://dash.deno.com).\n2. Click on your webhooks project, and then the **Settings** tab.\n3. Down to **Environment Variables** and hit **+ Add Variable**.\n4. Enter keys and values for each of your variables from `.env`, clicking **+\n   Add Variable** to add another.\n5. Only once you've added all variables, click **Save**.\n6. Now go to the **Logs** tab, where you can watch the logs of the hook\n   invocations.\n\nYou production webhooks are ready to test and use from within GitLab.\n\n1. Go back to your test project in GitLab, and **Settings** \u003e **Webhooks**.\n2. Find the test webhook you added earlier, **Edit** and replace the _gitpod_\n   **URL** with the new _deno_ **URL**, for example:\n   `https://my-webhooks.deno.dev/hook/log`.\n\nNow test your webhook as you did early and monitor the logs with the Deno Deploy\nproject (rather than Gitpod).\n\nYou should now have live production webhooks ready to handle your GitLab events.\n\n## Example Webhooks\n\nThis project contains several example webhooks that can provide a starting point\nfor your own...\n\n### [log](./app/routes/hook/log.ts)\n\nThis simply logs information from the webhook request and event data to the\nconsole. This is an ideal hook to copy as a starting point, as it allows you to\ninspect the event payload and find the details you need, and gradually remove\nthe logging that you no longer need.\n\n### [create-issue-on-fail](./app/routes/hook/create-issue-on-fail.ts)\n\nThis is a more complete example, that listens for a `pipeline`, specifically a\nfailure.\n\nIt demonstrates requests to the GitLab API, to a third-party API, in this case\nOpenAI, and use a queueing mechanism in Deno.\n\nIt will create a new GitLab issue within the same project as the pipeline with\nthe label `failed-pipeline`, or use an existing open one.\n\nThen it will fetch the logs of the failed jobs from the pipeline, and feed them\ninto the OpenAI GPT-4 LLM, asking for a summary of the failure and a potential\nremedy. It will add this as a comment to the issue.\n\nYou'll need to set the environment variables: `GITLAB_API_TOKEN` and optionally\nthe `OPENAI_API_KEY` (log summary comments are skipped if not set).\n\nThe whole handler is wrapped in `background(...)` function, this is a helper\nthat pushes entire Request onto the\n[Deno Kv Queue](https://docs.deno.com/deploy/kv/manual/queue_overview) and\nimmediately responds with `202 Accepted`. The queued request is then handled in\nthe background by the handler function passed to `background()`.\n\n### [count](./app/routes/hook/count.ts)\n\nThis demonstrates use of the [Deno KV](https://deno.com/kv), key-value database\nbuilt into Deno, and also available within Deno Deploy.\n\nA counter for each hook type is incremented when hit.\n\nThis is accompanied by a simple web page that reads the counts from Kv and\nrenders them in a table. You can open the `https://.../report/count` link of\nyour dev or production app to see this. See\n[count.tsx](./app/routes/report/count.tsx) (NOTE: this page makes use of JSX,\nbut it isn't React).\n\n### [queue](./app/routes/hook/queue.ts)\n\nThis is another, albeit simpler, demo of the queueing mechanism described above.\n\n### [slow](./app/routes/hook/slow.ts)\n\nDoes nothing more than sleep for some amount of time, which can be given in a\nparameter of the webhook URL. This can be useful to test what happens if a\nwebhook times out.\n\nIt also demonstrates how a search parameter can be useful as configuration for a\nwebhook.\n\n## Starting the dev server\n\nThis is done automatically when using Gitpod, but in other environments you may\nneed to start it manually:\n\n```sh\ndeno task start\n```\n\n## Adding and removing webhooks (and other routes)\n\nRoutes in the web server are represented by the filesystem structure, this needs\nto be discovered by a script to generate the [routes.ts](./app/routes.ts)\nmodule. This is done automatically when running the dev server, but sometimes\nyou may need to manually run the generation like so:\n\n```sh\ndeno task gen\n```\n\nThis may be necessary after adding or deleting a typescript module.\n\n## Other maintenance tasks\n\nA number of other tasks are defined in the [deno.json](./deno.json) file,\nincluding:\n\n- `deploy` - to manually deploy to Deno Deploy (this is used from the CI/CD\n  pipeline)\n- `ok` - to format, lint and typescript the project\n- `lock` - to delete and recreate a fresh [deno.lock](./deno.lock) file\n- `token` - to generate a random token that can be used as your webhook secret\n  token\n\n## Configurations\n\nThis project contains a number of configurations that you may want to custom\nalong your journey:\n\n- [.gitlab-ci.yml](./.gitlab-ci.yml) - the CI/CD pipeline for GitLab, that\n  deploys to Deno Deploy.\n- [.gitpod.yml](./.gitpod.yml) \u0026 [.gitpod.Dockerfile](./.gitpod.Dockerfile) -\n  configuration for Gitpod.\n- [deno.json](./deno.json) - configuration for the Deno runtime, and the target\n  Deno Deploy project.\n- [.env](./.env) - environment variables for the dev server (do not commit to\n  git).\n- [.env.example](./.env.example) - a template for your `.env` file.\n\n## Resources\n\nThis project makes use of a number of Deno libraries and npm packages..\n\n- [Deno Standard Library](https://jsr.io/@std)\n- [HTTP Functions](https://jsr.io/@http/fns)\n- [JSX Streaming](https://jsr.io/@http/jsx-stream)\n- [OpenAI API](https://github.com/openai/openai-node)\n- [GitLab Event Types](https://github.com/lawvs/gitlab-event-types)\n\n## GitLab Hackathon\n\nThis was originally submitted as an\n[entry for a GitLab hackathon](https://devpost.com/software/webhook-workshop).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjollytoad%2Fwebhook-workshop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjollytoad%2Fwebhook-workshop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjollytoad%2Fwebhook-workshop/lists"}