{"id":22486390,"url":"https://github.com/gr2m/github-app-example","last_synced_at":"2025-08-02T19:31:38.176Z","repository":{"id":66069626,"uuid":"96368601","full_name":"gr2m/github-app-example","owner":"gr2m","description":null,"archived":false,"fork":true,"pushed_at":"2020-07-23T22:00:37.000Z","size":770,"stargazers_count":60,"open_issues_count":0,"forks_count":12,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-07-27T15:53:33.400Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"bkeepers/github-app-example","license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gr2m.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null},"funding":{"github":"gr2m"}},"created_at":"2017-07-05T23:16:41.000Z","updated_at":"2025-06-19T20:31:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/gr2m/github-app-example","commit_stats":{"total_commits":24,"total_committers":7,"mean_commits":"3.4285714285714284","dds":"0.45833333333333337","last_synced_commit":"7f99083fb45a7cac7cd3846b6bb036c10076a3af"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/gr2m/github-app-example","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gr2m%2Fgithub-app-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gr2m%2Fgithub-app-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gr2m%2Fgithub-app-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gr2m%2Fgithub-app-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gr2m","download_url":"https://codeload.github.com/gr2m/github-app-example/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gr2m%2Fgithub-app-example/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268440412,"owners_count":24250793,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"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":[],"created_at":"2024-12-06T17:14:29.430Z","updated_at":"2025-08-02T19:31:37.841Z","avatar_url":"https://github.com/gr2m.png","language":"JavaScript","funding_links":["https://github.com/sponsors/gr2m"],"categories":["JavaScript"],"sub_categories":[],"readme":"# Building a GitHub App in Node.js\n\nThis tutorial will walk you through creating and deploying a GitHub App that comments on any new issue that is opened on a GitHub repository.\n\nYou will learn how to create a GitHub app and how to utilize GitHub’s webhooks to run your own Node.js code each time something happens on a GitHub repository.\n\n1. [What is a GitHub App?](#what-is-a-github-app)\n2. [What is a Webhook?](#what-is-a-webhook)\n3. [Receive a Webhook?](#receive-a-webhook)\n4. [Create a GitHub app](#create-a-github-app)\n5. [Install your GitHub app.](#install-your-github-app)\n6. [Securing your server.](#securing-your-server)\n7. [Commenting with your GitHub App](#commenting-with-your-github-app)\n8. [Bonus: Probot!](#bonus-probot)\n8. [Questions? Feedback?](#questions-feedback)\n\n## What is a GitHub App?\n\n[GitHub Apps](https://developer.github.com/apps/) are a new way to extend GitHub. Every GitHub user can create one for free. They can be installed directly on organizations and user accounts and granted access to specific repositories. Apps are first class actors within GitHub. They come with granular permissions and built-in webhooks.\n\n## What are GitHub’s webhooks?\n\nWebhooks are requests that GitHub sends to a specified URL for almost every significant action that users take on GitHub, whether it's pushes to code, opening or closing issues, opening or merging pull requests, or commenting on a discussion.\n\nUsually a GitHub App will be responding to these webhooks. In our example, we will listen to the [issues event](https://developer.github.com/v3/activity/events/types/#issuesevent) to use it as a trigger to create our comment.\n\nFind a list of [all available webhooks on GitHub](https://developer.github.com/webhooks/).\n\n## Receive a webhook\n\nIn order to receive a webhook, you have to create a Node.js server and make it accessible at a URL that GitHub can reach.\n\nA very simple way to do that is to use [Glitch](https://glitch.com). With Glitch, you can create a Node.js application right in your browser and it will be executed for you each time you make a change. We will start out with this server:\n\n```js\n// http is a standard module that comes with Node.js\nconst http = require('http')\n\nhttp.createServer(handleRequest).listen(3000)\n\nfunction handleRequest (request, response) {\n\n  // log request method \u0026 URL\n  console.log(`${request.method} ${request.url}`)\n\n  // for GET (and other non-POST) requests show \"ok\" and stop here\n  if (request.method !== 'POST') return response.end('ok')\n\n  // for POST requests, read out the request body, log it, then show \"ok\" as response\n  let payload = ''\n  request.on('data', (data) =\u003e payload += data );\n  request.on('end', () =\u003e {\n    console.log(payload)\n    response.end('ok')\n  })\n}\n```\n\nWe prepared a template on Glitch that you can use as starting point: [node-server-example](https://glitch.com/edit/#!/remix/node-server-example/3204f092-a7d7-4e88-ba01-4213b9f7d0ec). Click on the \"Show\" button on top to see the 'ok'. Click on \"Logs\" in the side bar and you will see `received request: GET /`.\n\nCongratulations, you are all set to receive requests, which is exactly what a webhook sent by GitHub is. 🎉\n\n## Create a GitHub app\n\nLet’s [create a GitHub App](https://github.com/settings/apps/new) (the page will ask you to login to your GitHub account or confirm your password).\n\nSet the **GitHub App Name** to anything you like, e.g. \"welcome-bot\". Then set the **Webhook URL** to the URL of your glitch app when you click on \"show\", it will be something like https://adjective-noun.glitch.me. Set both **Hompage URL** and **User authorization callback URL** to \"https://example.com/\", it can be changed later.\n\n![](/../master/assets/app-register.png?raw=true \"Register a GitHub App\")\n\nScroll down to Permissions: Issues and change the drop-down from \"No access\" to \"Read \u0026 Write\" and check the \"Issues\" checkbox.\n\n![](/../master/assets/app-permissions.png?raw=true \"GitHub App Permissions\")\n\nYou can ignore all other fields. Scroll to the end of the page and click \"Create GitHub App\".\n\nCongratulations, you created a GitHub App. 🎊\n\n## Install your GitHub app.\n\nClick on the green \"install\" button, you will get redirected to a page where you can select a repository on which you want to enable your bot. Select \"Only select repositories\" and activate it on one of your repositories for testing. You can also [create a new repository](https://github.com/new) first.\n\n![](/../master/assets/app-install.png?raw=true \"Install GitHub App\")\n\nBefore pressing the green \"Install\" button, make sure you watch the logs of your Glitch app. Now press \"Install\". In the logs you should now see something like this (shortened for readability)\n\n```\nPOST /\n{\"action\":\"added\",\"installation\":{…}}\n```\n\nNow create an issue in your test repository. It should log something like this\n\n```\nPOST /\n{\"action\":\"opened\",\"issue\":{…}}\n```\n\nCongratulations, you installed a GitHub App. 🙌\n\n## Securing your server.\n\nBefore moving on, we have to make sure nobody else but our GitHub App can send us requests. If the server as is, everybody who finds out about its URL can send forge fake requests and inject malicious code.\n\nIn order to secure our server we can set a secret that only our GitHub app and our server know. Open the settings page for your GitHub App (Settings → GitHub Apps → _Select your bot_). Now enter a secure, random text into the field **Webhook secret**. Remember it.\n\nOn your Glitch app, open the `.env` file. This is where we can put secrets without others having access to it. Remove all the content and replice it with the line below (make sure to replace `yoursecrethere` with your own secret)\n\n```\nWEBHOOK_SECRET=yoursecrethere\n```\n\nThis secret is used by GitHub to calculate a signature which your server can use for verification. This is not overly complex, but we will use a library for it: [github-webhook-handler](https://github.com/rvagg/github-webhook-handler). Besides, the library makes it simpler to handle hook events, as you will see in a moment.\n\nOpen the `package.json` file in your glitch app. Click on the \"Add package\" drop and enter \"github-webhook-handler\". Click on the first result to add it as a dependency of your server. You are now ready to use it.\n\nNow open the `server.js` file and change its content\n\n```js\n// values for the enviroment variables set in the .env file can be accesed at proces.env.VARIABLE_NAME\nconst secret = process.env.WEBHOOK_SECRET\n\nconst http = require('http')\nconst webHookHandler = require('github-webhook-handler')({\n  path: '/',\n  secret: secret\n})\nhttp.createServer(handleRequest).listen(3000)\n\nwebHookHandler.on('issues', (event) =\u003e {\n  console.log(`Received issue event for \"${event.payload.issue.title}\"`)\n})\n\nfunction handleRequest (request, response) {\n  // ignore all requests that aren’t POST requests\n  if (request.method !== 'POST') return response.end('ok')\n\n  // here we pass the current request \u0026 response to the webHookHandler we created\n  // on top. If the request is valid, then the \"issue\" above handler is called\n  webHookHandler(request, response, () =\u003e response.end('ok'))\n}\n```\n\nNot only is the code much more readable now, it is also secure and ready to go.\n\nCreate another issue on the test repository you activated your GitHub app for. You should see this in the logs\n\n```\nReceived issue event for \"…\"\n```\n\nIf something doesn’t work as expected, compare it to our [github-webhook-handler-example](https://glitch.com/edit/#!/github-webhook-handler-example). Click on \"Remix this\" to use it as a template for your own app. Make sure to change the **Webhook URL** in your GitHub App settings to the URL of the new Glitch app and to add the `WEBHOOK_SECRET=` to your new app’s `.env` file.\n\n## Commenting with your GitHub App\n\nA GitHub App is a first-class actor on GitHub. Just like a GitHub user (e.g. [@defunkt](https://github.com/defunkt)), it can be given access to repositories and perform actions through [GitHub’s API](https://developer.github.com/v3/).\n\nUnlike a user, a GitHub App does not sign in through the website (it is a robot, after all). Instead, it authenticates by signing a token with a private key, and then requesting an access token to perform actions on behalf of a specific installation. You don’t need to worry about all that, we will use another module which does the hard lifting for us, but in case you do care, learn all [about authentication options for GitHub Apps](https://developer.github.com/apps/building-integrations/setting-up-and-registering-github-apps/about-authentication-options-for-github-apps/).\n\nFirst, you need to generate a private key for your GitHub App. Open the Settings page (Settings → GitHub Apps → _Select your bot_), scroll down to \"Private key\" and press the \"Generate private key\" button. Open the downloaded `.pem` file with a text editor. Copy its content (including the first and last line starting with `-----`).\n\nOn your Glitch app, press the \"+ New file\" button and enter \".data/private-key.pem\" as its path (the \".\" at the beginning is important! All files in the \".data\" folder cannot be seen by others and are therefore secure). Paste the content from the text editor.\n\nNow you need to add your GitHub App’s id to the `.env` file, you find the id on your GitHub app’s settings page (replace `123` below with the actual id)\n\n```\nAPP_ID=123\n```\n\nNow we need to install two other libraries: [github-app](https://github.com/probot/github-app) and [github rest client](https://www.npmjs.com/package/@octokit/rest). Open `package.json` and add them as dependencies. The `\"dependencies\"` key should now look something like this\n\n```json\n  \"dependencies\": {\n    \"github-webhook-handler\": \"^0.6.0\",\n    \"github-app\": \"^3.0.0\",\n    \"@octokit/rest\": \"^18.0.2\"\n  },\n```\n\nNow open the `server.js` file and change its content\n\n```js\nconst secret = process.env.WEBHOOK_SECRET\n\nconst http = require('http')\nconst webHookHandler = require('github-webhook-handler')({\n  path: '/',\n  secret: secret\n})\nconst app = require('github-app')({\n  id: process.env.APP_ID,\n  cert: require('fs').readFileSync('.data/private-key.pem')\n})\n\nhttp.createServer(handleRequest).listen(3000)\n\nwebHookHandler.on('issues', (event) =\u003e {\n  // ignore all issue events other than new issue opened\n  if (event.payload.action !== 'opened') return\n\n  const {installation, repository, issue} = event.payload\n  app.asInstallation(installation.id).then((github) =\u003e {\n    github.issues.createComment({\n      owner: repository.owner.login,\n      repo: repository.name,\n      number: issue.number,\n      body: 'Welcome to the robot uprising.'\n    })\n  })\n})\n\nfunction handleRequest (request, response) {\n  if (request.method !== 'POST') return response.end('ok')\n  webHookHandler(request, response, () =\u003e response.end('ok'))\n}\n```\n\nCreate another issue on your test repository. Refresh the issue page if neccessary and you should see the \"Welcome to the robot uprising.\" comment 🤖\n\nIf something doesn’t work as expected, compare it to our [github-app-example](https://glitch.com/edit/#!/github-app-example). Click on \"Remix this\" to use it as a template for your own app. Make sure to change the **Webhook URL** in your GitHub App settings to the URL of the new Glitch app and to add `WEBHOOK_SECRET=` \u0026 `APP_ID` to your new app’s `.env` file. You also need to create the `.data/private-key.pem` file as described above.\n\n## Bonus: Probot!\n\nWe created a framework called [probot](https://probot.github.io/) which combines [github-webhook-handler](https://github.com/rvagg/github-webhook-handler) and [github](https://github.com/mikedeboer/node-github). Using `probot` you can\nsimplify the code to just this:\n\n```js\nmodule.exports = function (robot) {\n  robot.on('issues', handleIssue.bind(null, robot))\n}\n\nfunction handleIssue (robot, context) {\n  const api = context.github\n  const {installation, repository, issue} = context.payload\n\n  api.issues.createComment({\n    owner: repository.owner.login,\n    repo: repository.name,\n    number: issue.number,\n    body: 'Welcome to the robot uprising.'\n  })\n}\n```\n\nIsn’t that cool? Make sure to create the `.data/private-key.pem` file as described above and update the `.env` file (replace values for `WEBHOOK_SECRET` and `APP_ID`)\n\n```\n# you need to set NODE_ENV=production, otherwise probot will try to start localtunnel\nNODE_ENV=production\n# By default, proobot is looking for the file at /private-key.pem. We need to set it to our custom location\nPRIVATE_KEY_PATH=.data/private-key.pem\n\nWEBHOOK_SECRET=yoursecrethere\nAPP_ID=123\n```\n\nThen update your `package.json`. You only need `probot` as dependency, the others can be removed. You also need to update the \"start\" script to `probot run ./server.js`.\n\nCompare your Glitch app to our [probot-example](https://glitch.com/edit/#!/probot-example) if you run into any trouble.\n\n## Questions? Feedback?\n\nIf there is anything you think could be improved in this tutorial, please send a pull request. You can do so right from github.com by [editing this README.md file](https://github.com/gr2m/github-app-example/blob/master/README.md).\n\nIf you have any questions, please [create an issue](https://github.com/gr2m/github-app-example/issues/new)\n\nHappy coding!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgr2m%2Fgithub-app-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgr2m%2Fgithub-app-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgr2m%2Fgithub-app-example/lists"}