Sending Email Continued

So I ran through the wonderful cookbook for Sending Emails using SendInBlue and everything went pretty flawless. This enabled the feature of sending an email from the admin interface. However, I’m struggling to integrate the new function built in email.ts to send an email from the Forgot Password page and adding the necessary code in auth.ts similar to the code added in user.ts

I also reviewed the post dbAuth - Fail to reset password (email) which was helpful but doesn’t use the reusable function email.ts like in the cookbook. Does someone have a working example to reference or any tips? Seems like functions like sending an email for password reset (when using dbAuth) would be one of the core features so I’m hoping there is a short guide that could extend the cookbook for working examples.

1 Like

Hey Justin! So the code in api/src/lib/email.js still just exports regular JS functions you can use elsewhere. In this case, you could import your email sending function from api/src/lib/email.js into api/src/functions/auth.js. Then update the forgotPasswordOptions.handler() function to call your email sending function, passing in the user or email address, or whatever else your function expects to receive. You’ll want to include the resetToken in the URL as shown in the comments above the handler definition.

Does that help? :slight_smile:

That’s what I tried but must be doing something wrong as I cannot get it to work. I’ll pull my stash and post the code I had.

Okay, I guess fresh eyes help. Here is what worked for me in case it helps others (this is based on the cookbook Sending Email and the relevant files in place):

import { Prisma } from '@prisma/client'

import { DbAuthHandler } from '@redwoodjs/api'

import { db } from 'src/lib/db'
import { sendEmail } from 'src/lib/email'

import { createAudit } from '../services/audits/audits'

export const handler = async (event, context) => {
  const forgotPasswordOptions = {
    handler: async ({ id }: Prisma.UserWhereUniqueInput) => {
      const user = await db.user.findUnique({
        where: { id },
      function sendForgotPasswordEmail(emailAddress: string) {
        const resetLink = `${process.env.APP_URL}/reset-password?resetToken=${user.resetToken}`
        const subject = 'Reset your forgotten password'
        const text = `Here is a link reset your password.  It will expire after 4hrs. <a href="${resetLink}">Reset my Password</>`
        const html = `Here is a link reset your password.  It will expire after 4hrs. <a href="${resetLink}">Reset my Password</>`
        return sendEmail({ to: emailAddress, subject, text, html })
      await sendForgotPasswordEmail(
      await createAudit({
        input: {
          user: { connect: { id } },
          log: 'Reset password email was sent to user.',
      return user

    expires: 60 * 60 * 24,

    errors: {
      usernameNotFound: 'Username not found',
      usernameRequired: 'Username is required',

Given the .env variable for APP_URL is set.

@rob does this all look correct? Also, thanks for jumping on this post!

However I should mention the Audit feature is not working so I need to do some digging into that.

Any reason why I’m getting this error? Everything seems to work now but I still have the dreaded red squiggle.

I haven’t changed the audits.sdl.tx from the cookbook:

export const schema = gql`
  type Audit {
    id: String!
    createdAt: DateTime!
    updatedAt: DateTime!
    userId: String!
    user: User!
    log: String!

  type Query {
    audits: [Audit!]! @requireAuth
    audit(id: String!): Audit @requireAuth

  input CreateAuditInput {
    userId: String!
    log: String!

  input UpdateAuditInput {
    userId: String
    log: String

  type Mutation {
    createAudit(input: CreateAuditInput!): Audit! @requireAuth
    updateAudit(id: String!, input: UpdateAuditInput!): Audit! @requireAuth
    deleteAudit(id: String!): Audit! @requireAuth

Looks like I’m receiving the same error for users.ts from the cookbook.

Looks good to me!

1 Like

Not sure about that, I avoid Typescript like the plague! @Tobbe updated the cookbook article for TS, maybe he knows? I’d just rename the file to .js to get rid of the squiggles :slight_smile:

You are mixing up the SDL types that say to create a log send a userId and a log for the Create input and the Prisma create and connect nested.

Your service should do the connect — but when calling the service with the input From the dbAuth handler you need to pass the userId and log.

Check out the staff maintained Superstore where I think a working example exists:
Superstore | Superstore (