mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 01:12: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
|
- abandoned cart
|
||||||
- reminder
|
- reminder
|
||||||
- automated
|
- automated
|
||||||
|
sidebar_position: 4
|
||||||
---
|
---
|
||||||
|
|
||||||
import Tabs from '@theme/Tabs';
|
import Tabs from '@theme/Tabs';
|
||||||
|
@ -10,6 +10,7 @@ keywords:
|
|||||||
- expiration
|
- expiration
|
||||||
- coupon
|
- coupon
|
||||||
- automated
|
- automated
|
||||||
|
sidebar_position: 2
|
||||||
---
|
---
|
||||||
|
|
||||||
import Tabs from '@theme/Tabs';
|
import Tabs from '@theme/Tabs';
|
||||||
|
@ -10,7 +10,7 @@ keywords:
|
|||||||
- daily
|
- daily
|
||||||
- summary
|
- summary
|
||||||
- automated
|
- automated
|
||||||
sidebar_position: 2
|
sidebar_position: 3
|
||||||
---
|
---
|
||||||
|
|
||||||
import Tabs from '@theme/Tabs';
|
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
|
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
|
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.
|
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
|
The principles used in this example can guide you to design your own custom logic for triggering any event based on
|
||||||
time interval. [Check it out](/scheduled-triggers/recipes/abandoned-cart.mdx)!
|
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