mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 17:31:56 +03:00
docs: add ST recipe for user re-engagement email
[DOCS-1127]: https://hasurahq.atlassian.net/browse/DOCS-1127?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ PR-URL: https://github.com/hasura/graphql-engine-mono/pull/10376 GitOrigin-RevId: 8ef692770f58ebb8e719d3f5a5bbd9b073e0022a
This commit is contained in:
parent
e9206818bb
commit
de0aa6b988
@ -9,6 +9,7 @@ keywords:
|
||||
- abandoned cart
|
||||
- reminder
|
||||
- automated
|
||||
sidebar_position: 4
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
|
@ -10,6 +10,7 @@ keywords:
|
||||
- expiration
|
||||
- coupon
|
||||
- automated
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
|
@ -10,7 +10,7 @@ keywords:
|
||||
- daily
|
||||
- summary
|
||||
- automated
|
||||
sidebar_position: 2
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
|
@ -44,7 +44,13 @@ will help you to implement your own custom logic for triggering any event which
|
||||
|
||||
In e-commerce applications, it's a common practice to remind customers about the items they may have added to
|
||||
their carts, but then left without checking out. These are generally known as "abandoned carts". Recalling
|
||||
customers in this eay can help to improve egangement and conversion rates. This recipe demonstrates how to send a
|
||||
customers in this eay can help to improve engagement and conversion rates. This recipe demonstrates how to send a
|
||||
gentle email reminder to customers about their carts 24 hours after they added items but didn't complete the purchase.
|
||||
The principles used in this example can guide you to design your own custom logic for triggering any event based on a
|
||||
time interval. [Check it out](/scheduled-triggers/recipes/abandoned-cart.mdx)!
|
||||
The principles used in this example can guide you to design your own custom logic for triggering any event based on
|
||||
a time interval. [Check it out](/scheduled-triggers/recipes/abandoned-cart.mdx)!
|
||||
|
||||
### User re-engagement email
|
||||
|
||||
In applications with a notification system, it's common to send a re-engagement email to users who have not logged in
|
||||
for a certain period of time. This recipe shows how to send a re-engagement email to users who have not logged in for
|
||||
over a week. [Check it out](/scheduled-triggers/recipes/user-reengagement-email.mdx)!
|
||||
|
302
docs/docs/scheduled-triggers/recipes/user-reengagement-email.mdx
Normal file
302
docs/docs/scheduled-triggers/recipes/user-reengagement-email.mdx
Normal file
@ -0,0 +1,302 @@
|
||||
---
|
||||
title: Using Schedule Triggers for Sending a user re-engagement email
|
||||
description: Succinct, tested, and reusable code recipes for common use cases in Hasura.
|
||||
sidebar_label: User re-engagement Email
|
||||
keywords:
|
||||
- hasura
|
||||
- docs
|
||||
- recipes
|
||||
- scheduled triggers
|
||||
- re-engagement
|
||||
- automated
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import Thumbnail from '@site/src/components/Thumbnail';
|
||||
import SampleAppBlock from '@site/src/components/SampleAppBlock';
|
||||
|
||||
# Send a User Re-engagement Email
|
||||
|
||||
## Introduction
|
||||
|
||||
Scheduled Triggers allows you to schedule business, or other logic to occur at specific times or intervals. In this
|
||||
guide, we'll explore how to use Scheduled Triggers to send an email to users if they haven't been active in over a week.
|
||||
We'll do this by executing this trigger every morning and seeing which users' `last_seen` property is older than a week.
|
||||
If it is, we'll send them an email persuading them to come back to our app.
|
||||
|
||||
<SampleAppBlock dependent />
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before getting started, ensure that you have the following in place:
|
||||
|
||||
- The docs e-commerce sample app deployed to Hasura Cloud.
|
||||
- A working SMTP server or email-sending service that you can integrate with to send emails.
|
||||
|
||||
:::info Tunneling your webhook endpoint from your local machine
|
||||
|
||||
If you plan on using a webhook endpoint hosted on your own machine, ensure that you have a tunneling service such as
|
||||
[ngrok](https://ngrok.com/) set up so that your Cloud Project can communicate with your local machine.
|
||||
|
||||
:::
|
||||
|
||||
## Our model
|
||||
|
||||
When sending transactional emails such as this, there are three fundamental components to consider:
|
||||
|
||||
- **Your data source**: In your database, which table contains the value that you want to use to determine whether or
|
||||
not to send the email?
|
||||
- **Your querying logic**: In your webhook, how will you query your database to determine whether or not to send the
|
||||
email? How will you return information so that you have the correct data to include in the email?
|
||||
- **Your email templating**: How will you generate and send the email containing the information you want to send?
|
||||
|
||||
Our sample app's database contains, among others, a `users` table that contains a `last_seen` property. We'll use this
|
||||
property to determine whether or not to send the email. We'll query the database to find all users whose `last_seen`
|
||||
property is older than a week. Then, we'll send an email to each of those users.
|
||||
|
||||
## Step 1: Create the Scheduled Event
|
||||
|
||||
Head to the Hasura Console of your project and navigate to the "Events" tab. From there, click on the `Cron Triggers`
|
||||
item in the sidebar. Then, click `Create`:
|
||||
|
||||
<Thumbnail
|
||||
src="/img/scheduled-triggers/scheduled-triggers_getting-started-guide_2.18.0_click-create.png"
|
||||
alt="Hasura Scheduled Trigger architecture"
|
||||
width="1000"
|
||||
/>
|
||||
|
||||
## Step 2: Configure the Scheduled Event
|
||||
|
||||
First, provide a name for your trigger, e.g., `user_reengagement_email`. Then, enter a webhook URL that will be called
|
||||
when the event is triggered. This webhook will be responsible for sending the re-engagement emails and can be hosted
|
||||
anywhere, and written in any language, you like.
|
||||
|
||||
The route on our webhook we'll use is `/user-reengagement-email`. Below, we'll see what this looks like with a service
|
||||
like [ngrok](https://ngrok.com/), but the format will follow this template:
|
||||
|
||||
```text
|
||||
https://<your-webhook-url>/user-reengagement-email
|
||||
```
|
||||
|
||||
:::info Tunneling your webhook endpoint to your local machine
|
||||
|
||||
You'll need to use a tunneling service such as [ngrok](https://ngrok.com/) to expose a webhook endpoint running on your
|
||||
local machine to the internet and Hasura Cloud. This will give you a public URL that will forward requests to your local
|
||||
machine and the server which we'll configure below.
|
||||
|
||||
You'll need to modify your webhook URL to use the public URL provided by ngrok.
|
||||
|
||||
After installing ngrok and
|
||||
[authenticating](https://ngrok.com/docs/secure-tunnels/ngrok-agent/tunnel-authtokens/#:~:text=Once%20you've%20signed%20up,make%20installing%20the%20authtoken%20simple.),
|
||||
you can do this by running:
|
||||
|
||||
```bash
|
||||
ngrok http 4000
|
||||
```
|
||||
|
||||
Then, copy the `Forwarding` value for use in our webhook 🎉
|
||||
|
||||
:::
|
||||
|
||||
Next, we'll configure the cron expression that will trigger the event. In this example, we want to send requests at 9:00
|
||||
AM every morning. We can do that with the following cron expression:
|
||||
|
||||
```
|
||||
0 9 * * *
|
||||
```
|
||||
|
||||
Our trigger must also have a payload. This payload will be sent to the webhook endpoint when the event is triggered. We
|
||||
don't have to include any data in the payload, but we can if we want to. In this example, we'll simply send a
|
||||
`trigger_type` property categorizing the event as a `daily_recap_email`. In the `Payload` section, enter the following:
|
||||
|
||||
```json
|
||||
{
|
||||
"trigger_type": "user_reengagement_email"
|
||||
}
|
||||
```
|
||||
|
||||
Under `Advanced Settings`, we can configure the headers that will be sent with the request. We'll add an
|
||||
`authentication` header to prevent abuse of the endpoint and ensure that only Hasura can trigger the event. Set the
|
||||
`Key` as `secret-authorization-string` and the `Value` as `super_secret_string_123`.
|
||||
|
||||
<Thumbnail
|
||||
src="/img/scheduled-triggers/scheduled-triggers_recipes_review-request_auth-header.png"
|
||||
alt="Hasura Scheduled Trigger architecture"
|
||||
width="1000"
|
||||
/>
|
||||
|
||||
Also, change the `Request Transform Options` to `POST` so that the payload is sent in the request body.
|
||||
|
||||
Finally, click the `Add Cron Trigger` button to create the Scheduled Event.
|
||||
|
||||
## Step 3: Create a webhook to handle the request
|
||||
|
||||
Whenever a cron job is triggered, Hasura will send a request to the webhook URL you provided. In this example, we're
|
||||
simply going to send a `POST` request. Our webhook will parse the request, ensure the header is correct, and then query
|
||||
the database to determine which users will receive an email.
|
||||
|
||||
Below, we've written an example of webhook in JavaScript. As we established earlier, this runs on port `4000`. If you're
|
||||
attempting to run this locally, follow the instructions below. If you're running this in a hosted environment, use this
|
||||
code as a guide to write your own webhook.
|
||||
|
||||
Init a new project with `npm init` and install the following dependencies:
|
||||
|
||||
```bash
|
||||
npm install express nodemailer
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Then, create a new file called <code>index.js</code> and add the following code:
|
||||
</summary>
|
||||
|
||||
```javascript
|
||||
const express = require('express');
|
||||
const nodemailer = require('nodemailer');
|
||||
|
||||
const app = express();
|
||||
|
||||
// Create a Nodemailer transporter using Ethereal email service
|
||||
// Ideally, this configuration would be stored somewhere else
|
||||
nodemailer.createTestAccount((err, account) => {
|
||||
if (err) {
|
||||
console.error('Failed to create a testing account. ' + err.message);
|
||||
return process.exit(1);
|
||||
}
|
||||
|
||||
// If all goes as planned, here's the console telling us we're 👍
|
||||
console.log('Credentials obtained, listening on the webhook...');
|
||||
|
||||
// Create a transporter object for nodemailer
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: 'smtp.ethereal.email',
|
||||
port: 587,
|
||||
secure: false,
|
||||
auth: {
|
||||
user: account.user,
|
||||
pass: account.pass,
|
||||
},
|
||||
});
|
||||
|
||||
// Function to fetch inactive users
|
||||
async function getInactiveUsers(oneWeekAgo) {
|
||||
const response = await fetch('<YOUR_CLOUD_PROJECT_ENDPOINT>', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-hasura-admin-secret': '<YOUR_ADMIN_SECRET>',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query: `
|
||||
query InactiveUsersQuery($oneWeekAgo: timestamptz!) {
|
||||
users(where: {last_seen: {_lt: $oneWeekAgo}}) {
|
||||
id
|
||||
name
|
||||
email
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
oneWeekAgo: oneWeekAgo.toISOString(),
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const { data } = await response.json();
|
||||
return data.users;
|
||||
}
|
||||
|
||||
// Function to send emails to inactive users
|
||||
async function sendInactiveUserEmails(transporter, inactiveUsers) {
|
||||
for (const user of inactiveUsers) {
|
||||
// Create a message object
|
||||
const message = {
|
||||
from: 'SuperStore.com <sender@SuperStore.com>',
|
||||
to: `${user.name} <${user.email}>`,
|
||||
subject: `We miss you, ${user.name.split(' ')[0]}!`,
|
||||
text: `Hi ${
|
||||
user.name.split(' ')[0]
|
||||
}, it's been over one week since you last logged in. Come back and do things!`,
|
||||
};
|
||||
|
||||
// Send the message using the Nodemailer transporter
|
||||
const info = await transporter.sendMail(message);
|
||||
|
||||
// Log the message info
|
||||
console.log(`\nMessage sent to ${user.name}: ${nodemailer.getTestMessageUrl(info)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Our route for the webhook
|
||||
app.post('/user-reengagement-email', async (req, res) => {
|
||||
// confirm the auth header is correct — ideally, you'd keep the secret in an environment variable
|
||||
const authHeader = req.headers['secret-authorization-string'];
|
||||
if (authHeader !== 'super_secret_string_123') {
|
||||
return res.status(401).json({
|
||||
message: 'Unauthorized',
|
||||
});
|
||||
}
|
||||
|
||||
// Calculate the date one week ago
|
||||
const oneWeekAgo = new Date();
|
||||
oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
|
||||
|
||||
// Fetch the list of users who haven't logged in for over one week
|
||||
const inactiveUsers = await getInactiveUsers(oneWeekAgo);
|
||||
|
||||
// Send emails to inactive users
|
||||
await sendInactiveUserEmails(transporter, inactiveUsers);
|
||||
|
||||
// Return a JSON response to the client
|
||||
res.json({
|
||||
message: 'Inactive user emails sent!',
|
||||
});
|
||||
});
|
||||
|
||||
// Start the server
|
||||
app.listen(4000, () => {
|
||||
console.log('Server started on port 4000');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
You can run the server by running `node index.js` in your terminal. If you see the message
|
||||
`Webhook server is running on port 4000`, you're good to go!
|
||||
|
||||
## Step 4: Test the setup
|
||||
|
||||
First, let's update one of the users in our database to have a `last_seen` property that is older than a week. In the
|
||||
Hasura Console, navigate to the `Data` tab and click on the `users` table. Then, click on the `Modify` tab and update
|
||||
the `last_seen` property of one of the users to be older than a week:
|
||||
|
||||
```plaintext
|
||||
YYYY-MM-DDTHH:MM:SS.SSSZ
|
||||
```
|
||||
|
||||
:::info Timestamp formatting
|
||||
|
||||
The format above is the [ISO 8601 format](https://www.iso.org/iso-8601-date-and-time-format.html). Enter the year,
|
||||
month, day, hour, minute, second, and millisecond of the date you want to set. For example, if you want to set the date
|
||||
to `2023-10-10`, you would enter `2023-10-10T00:00:00.000Z`.
|
||||
|
||||
:::
|
||||
|
||||
With your server running, Hasura should start hitting our endpoint. As we set our cron expression to `0 9 * * *`, the
|
||||
webhook will be triggered at 9:00 AM every day. We don't want to wait that long to test it. For now, update the
|
||||
expression to `* * * * *` to trigger the webhook every minute. Then, check out your invocation logs in the Hasura
|
||||
Console to verify that the webhook was triggered successfully and your terminal to see the outputted information and
|
||||
link to a handy email 🎉
|
||||
|
||||
<Thumbnail
|
||||
src="/img/scheduled-triggers/scheduled-triggers_recipes_review-terminal.png"
|
||||
alt="Hasura Scheduled Trigger architecture"
|
||||
width="1000"
|
||||
/>
|
||||
|
||||
Feel free to customize the webhook implementation based on your specific requirements and chosen email sending service.
|
||||
Remember to handle error scenarios, implement necessary validations, and add appropriate security measures to your
|
||||
webhook endpoint.
|
Loading…
Reference in New Issue
Block a user