Developer tools · Convex component
convex-github
Sync GitHub issues and pull requests into Convex reactively, and open issues, comment, and merge PRs from Convex functions.
npm install convex-githubDownloads
Download history
Build this
Build with convex-github
The install and setup steps from the package documentation.
- 1
Install
shnpm install convex-github - 2
Quick Start
1. Add the component
ts// convex/convex.config.ts import { defineApp } from "convex/server"; import convexGithub from "convex-github/convex.config"; const app = defineApp(); app.use(convexGithub); export default app;2. Set environment variables
shnpx convex env set GITHUB_TOKEN ghp_... npx convex env set GITHUB_WEBHOOK_SECRET whsec_...GITHUB_TOKENis a personal access token, a fine-grained token, or a GitHub App installation token withissuesandpull_requestsscopes on the repos you want to manage.GITHUB_WEBHOOK_SECRETis the secret you set on the webhook in step 4.3. Mount the webhook handler
ts// convex/http.ts import { httpRouter } from "convex/server"; import { components } from "./_generated/api"; import { GitHub } from "convex-github"; const github = new GitHub(components.convexGithub, { token: process.env.GITHUB_TOKEN!, webhookSecret: process.env.GITHUB_WEBHOOK_SECRET!, }); const http = httpRouter(); http.route({ path: "/webhooks/github", method: "POST", handler: github.webhookHandler, }); export default http;4. Register the webhook in GitHub
In your repo (or organization) settings, add a webhook pointing at
https://<your-deployment>.convex.site/webhooks/github, content typeapplication/json, with the same secret asGITHUB_WEBHOOK_SECRET. Subscribe to the Issues and Pull requests events.5. Initialize the client
ts// convex/example.ts import { action, query } from "./_generated/server"; import { components } from "./_generated/api"; import { GitHub } from "convex-github"; import { v } from "convex/values"; const github = new GitHub(components.convexGithub, { token: process.env.GITHUB_TOKEN!, webhookSecret: process.env.GITHUB_WEBHOOK_SECRET!, }); export const listIssuesByRepo = query({ args: { repo: v.string() }, handler: async (ctx, args) => { return await github.listIssuesByRepo(ctx, args); }, }); - 3
Setup
The component needs no schema changes in your app — its tables (
issues,pullRequests,webhookEvents) live entirely inside the component's own isolated schema. All you need is the webhook mounted (step 3 above) and aGitHubclient instance wherever you call its methods.Repos are identified throughout by their
"owner/repo"full name (e.g."sholajegede/the-convex-reactor"), matching GitHub's ownrepository.full_namefield, so there's no separate repo-registration step — the first webhook event orcreateIssuecall for a repo is enough for it to start showing up in queries. - 4
Usage
Create an issue
tsexport const fileIssue = action({ args: { owner: v.string(), repo: v.string(), title: v.string(), body: v.optional(v.string()) }, handler: async (ctx, args) => { return await github.createIssue(ctx, args); }, });Returns
{ number, url }and immediately records the issue in Convex — you don't have to wait for the webhook round-trip to see it in a query.Comment on an issue
tsexport const comment = action({ args: { owner: v.string(), repo: v.string(), issueNumber: v.number(), body: v.string() }, handler: async (ctx, args) => { return await github.createIssueComment(ctx, args); }, });Close an issue
tsexport const resolveIssue = action({ args: { owner: v.string(), repo: v.string(), issueNumber: v.number() }, handler: async (ctx, args) => { await github.closeIssue(ctx, args); return null; }, });closeIssueonly patches the issue'sstatein Convex — it never overwrites the issue's storedtitle,labels, orurlwith stale data, so it's safe to call even if your local copy of those fields is out of date.Merge a pull request
tsexport const merge = action({ args: { owner: v.string(), repo: v.string(), pullNumber: v.number() }, handler: async (ctx, args) => { await github.mergePullRequest(ctx, args); return null; }, });Pass
mergeMethod: "merge" | "squash" | "rebase"to control the merge strategy (defaults to"merge").mergePullRequestlooks the pull request up first to get its id (GitHub's merge response doesn't include it), merges it, then immediately patches itsmergedandstatefields in Convex — likecloseIssue, it doesn't wait for thepull_requestwebhook to arrive.Read issues and pull requests reactively
tsxconst issues = useQuery(api.example.listIssuesByRepo, { repo: "sholajegede/the-convex-reactor" });Every
issuesandpull_requestwebhook event patches or inserts a row, so this query re-renders live as issues are opened, labeled, assigned, or closed on GitHub — no polling.