{"id":16182081,"url":"https://github.com/joelhooks/inngest-resend-example","last_synced_at":"2025-03-19T02:30:34.627Z","repository":{"id":187580044,"uuid":"677175408","full_name":"joelhooks/inngest-resend-example","owner":"joelhooks","description":"  Transactional emails in Next.js using Resend and Inngest","archived":false,"fork":false,"pushed_at":"2023-08-18T21:13:26.000Z","size":100,"stargazers_count":27,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-08T16:35:51.118Z","etag":null,"topics":["email","example","inngest","nextjs","typescript"],"latest_commit_sha":null,"homepage":"https://inngest-resend.vercel.app/","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/joelhooks.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}},"created_at":"2023-08-10T23:40:45.000Z","updated_at":"2025-01-21T15:26:21.000Z","dependencies_parsed_at":"2024-10-27T19:24:00.860Z","dependency_job_id":"ae556030-0b9a-49fd-b7b8-e957bb2365fb","html_url":"https://github.com/joelhooks/inngest-resend-example","commit_stats":null,"previous_names":["joelhooks/inngest-resend-example"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joelhooks%2Finngest-resend-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joelhooks%2Finngest-resend-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joelhooks%2Finngest-resend-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joelhooks%2Finngest-resend-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joelhooks","download_url":"https://codeload.github.com/joelhooks/inngest-resend-example/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243960373,"owners_count":20375101,"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","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":["email","example","inngest","nextjs","typescript"],"created_at":"2024-10-10T06:29:16.347Z","updated_at":"2025-03-19T02:30:34.303Z","avatar_url":"https://github.com/joelhooks.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Inngest \u0026 Resend for Customer Lifecycle Emails\n\nThe purpose of this example is to show you how to get started sending customer lifecycle emails with [Resend](https://resend.com/) and [Inngest](https://inngest.com/).\n\nResend is a modern email service provider that play nicely with React components.\n\nInngest is a platform for easily scheduling queues, background jobs, and workflows.\n\nIf you get stuck, join the [Inngest Discord](https://www.inngest.com/discord) and we'll help you out!\n\n## How it works\n\nBefore we get into the technical details, it's important to understand the whats occurring in this example.\n\nThis is a relatively simple (😅) Next.js app that includes a connection to a database via Prisma, authentication using NextAuth, and a simple page to demonstrate functionality.\n\nWhen a new user joins the site for the first time, we will send them a welcome email. If they don't perform a specific action within a certain time period, we will send them a follow up email.\n\n### Beyond the basics\n\nThis is meant to be a starting point for you to build your own application. You can use this as a starting point to build your own application, or you can use it as a reference to add functionality to your existing application.\n\n## Setup\n\nThis example uses [next-auth](https://next-auth.js.org/) for authentication and [prisma](https://www.prisma.io/) for database access. The project is configured with GitHub for authentication and a Postgres database by default.\n\n### Database\n\n`next-auth` can be configured with [many different databases and adapters](https://next-auth.js.org/adapters). For this example, we are using Postgres via Prisma. You can find the configuration in `prisma/schema.prisma`.\n\nA quick way to get postgres up and running is using [Neon](https://neon.tech/), which has a free tier and provides you with a URL that you can use to connect to your database by adding it to the `DATABASE_URL` env var.\n\nTechnically the database is optional, but it's more useful to explore the example with a database in place. ALternatives to Neon include services like Heroku or similar that allow you to create a Postgres database.\n\n### GitHub OAuth\n\nYou'll need to setup a GitHub OAuth app to use this example. You can do that [here](https://github.com/settings/developers) for a personal account using `http://localhost:3000/api/auth/callback/github` as the callback URL by default. If you are configuring a different `localhost` port, you'll need to update the callback URL accordingly. Check [here for the GitHub provider configuration options](https://next-auth.js.org/providers/github).\n\n[`next-auth` supports **many** providers](https://next-auth.js.org/providers/), so if you'd like to explore more options to suit your needs.\n\n### Inngest\n\nInngest is a platform for easily scheduling queues, background jobs, and workflows in your applications. \n\nInngest makes distributed systems easy to build and maintain and takes the pain out of scheduling jobs and workflows by managing all of the associated infrastructure for you.\n\nYou can create a free account [here](https://inngest.com/).\n\nIn this example we are using Inngest to schedule a job to send a follow up email to users who have not performed a specific action within a certain time period.\n\nTo get started, you will run the local Inngest Dev Server:\n\n```shell\nnpx inngest-cli@latest dev\n```\n\nThis starts the dev server, which will automatically find the Next.js application and allow you to test your jobs locally.\n\nThe standard configuration for Inngest is to serve an API route at `/api/inngest` and in this application you can find that by navigating to `/src/app/api/inngest/route.ts` where the route is defined and exported.\n\n```typescript\nexport const { GET, POST, PUT } = serve(inngest, [\n  userCreated,\n]);\n```\n\nThe `{ GET, POST, PUT }` are the HTTP methods that Inngest will respond to. The `serve` function takes an array of functions that you want to expose to Inngest. In this case, we are exposing just one function: `userCreated`.\n\n```typescript\nexport const userCreated = inngest.createFunction(\n  { name: \"A User Was Created\" },\n  { event: \"user/created\" },\n  async ({ event, step }) =\u003e {\n    const { email } = event.user;\n\n    await step.run(\"send-welcome-email\", async () =\u003e {\n      return sendEmail({\n        to: email,\n        subject: \"Welcome to our app!\",\n        body: \"\u003cp\u003eThanks for signing up!\u003c/p\u003e\",\n      });\n    });\n\n    const completedAction = await step.waitForEvent(\"user/created.document\", {\n      timeout: \"1m\",\n      if: \"event.user.email == async.user.email\",\n    });\n\n    if (!completedAction) {\n      await step.run(\"send-nudge-email\", async () =\u003e {\n        return sendEmail({\n          to: email,\n          subject: \"How can we help!\",\n          body: \"\u003cp\u003eWhat can we do better? We are always here to help you suceed.\u003c/p\u003e\",\n        });\n      });\n    } else {\n      await step.run(\"send-congrats-email\", async () =\u003e {\n        return sendEmail({\n          to: email,\n          subject: \"You did it!\",\n          body: \"\u003cp\u003eWe are so glad figured it out! It's challenging to do anything in this rough and tumble world so congrats on that.\u003c/p\u003e\",\n        });\n      });\n    }\n  }\n);\n```\n\nThe `userCreated` function is a multi-step function that performs several actions.\n\n* Sends the user a welcome email\n* Waits 1 minute for the user to perform a specific action\n* If the user performs the action, sends a congratulations email\n* If the user does not perform the action, sends a nudge email\n\n\n\nThose functions are both located in `/src/inngest/functions`. Feel free to add your own functions but **remember to add any functions you create to the array that is passed to `serve`** so that Inngest can find them!\n\n👋 Inngest functions have to be added to the array passed to `serve` in order for Inngest to find them.\n\nThe other important thing to be able to locate is the Inngest client which is located in `/src/inngest/inngest.client.ts`. This is imported where you want to send events to Inngest.\n\n👋 Inngest is 100% server-side and is not imported for use in the browser!\n\nIn this example we import the client in two places.\n\nThe first is our simple form located `/src/app/page.tsx` where it is used in a Next.js server action to send an event when a button is pushed. This is the action that we are waiting for the user to perform in order to send the follow up email.\n\n```typescript\nasync function handleButtonOne() {\n  \"use server\";\n  await inngest.send({\n    name: \"user/email.reaction\",\n    user: session?.user,\n    data: {},\n  });\n}\n```\n\nThe other event is dispatched from the NextAuth config in the `createdUser` callback that library provide located in `/src/server/auth.ts`:\n\n```typescript\nevents: {\n  /**\n   * 👋 Internally next-auth creates event hooks to handle any application\n   * side effects that need to happen when an event occurs\n   * in our case we want to send an event to inngest when a user is created\n   * so that we can track user signups and other user related events.\n   * @see https://next-auth.js.org/configuration/events#createuser\n   * @param param0\n   */\n  createUser: async ({ user }) =\u003e {\n    inngest.send({ name: \"user/created\", user, data: {} });\n  },\n},\n```\n\nThis creates a complete workflow that reacts to the creation of a new user by sending them a welcome email and waits for a period of time to send a follow up if they don't perform a specific action OR sends them a congrats email if they do perform the action.\n\nSimple, but powerful!\n\n### Sending Emails with Resend\n\nResend makes it relatively easy to get started sending emails. You can create a free account [here](https://resend.com/).\n\nTo send emails, you'll also need to [verify a sending domain with Resend using DNS](https://resend.com/domains). This is a simple process that will allow you to send emails from your domain. It's required because Resend is built to send transactional emails.\n\n## Deploy the Application\n\nBefore you deploy you'll need to add the following environment variables to your deployment:\n\n- `GITHUB_CLIENT_ID`\n- `GITHUB_CLIENT_SECRET`\n- `DATABASE_URL`\n- `INNGEST_EVENT_KEY`\n- `INNGEST_SIGNING_KEY`\n- `RESEND_API_KEY`\n- `FROM_EMAIL`\n- `NEXTAUTH_SECRET`\n\nYou can get the `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` from your GitHub OAuth app. The `DATABASE_URL` is the URL to your Postgres database. The `NEXTAUTH_SECRET` is a random string that you can generate with `openssl rand -hex 32`.\n\nThe `INNGEST_EVENT_KEY` and the `INNGEST_SIGNING_KEY` can be found in the Inngest cloud dashboard for your application or applied to your application via the Inngest Vercel integration.\n\n👋 [Details on deploying your Inngest application to production can be found here](https://www.inngest.com/docs/deploy)\n\nThe `RESEND_API_KEY` is the API key that you can find in the Resend dashboard for your application.\n\nThe `FROM_EMAIL` is the email addresses that you want to send emails from and reply to. Note that this has to be verified with Resend if you use that service.\n\n[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fjoelhooks%2Finngest-resend-example%2Ftree%2Fmain\u0026env=INNGEST_EVENT_KEY,INNGEST_SIGNING_KEY,RESEND_API_KEY,GITHUB_CLIENT_ID,GITHUB_CLIENT_SECRET,DATABASE_URL,NEXTAUTH_SECRET,FROM_EMAIL\u0026project-name=inngest-resend-example\u0026repository-name=inngest-resend-example)\n\n## What's next? How do I make an app with this?\n\nWe try to keep this project as simple as possible, so you can start with just the scaffolding we set up for you, and add additional things later when they become necessary.\n\nIf you are not familiar with the different technologies used in this project, please refer to the respective docs.\n\n- [Next.js](https://nextjs.org)\n- [NextAuth.js](https://next-auth.js.org)\n- [Prisma](https://prisma.io)\n- [Tailwind CSS](https://tailwindcss.com)\n- [shadcn/ui](https://ui.shadcn.com/)\n- [Resend](https://resend.com/)\n- [Inngest](https://inngest.com/)\n\n## About the Stack\n\nThis is based on the [T3 Stack](https://create.t3.gg/) project bootstrapped with `create-t3-app`.\n\nIt's had some changes to migrate to the Next.js 13 App Router.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoelhooks%2Finngest-resend-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoelhooks%2Finngest-resend-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoelhooks%2Finngest-resend-example/lists"}