mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 17:02:49 +03:00
docs: add recipe for daily summary email
[DOCS-1124]: https://hasurahq.atlassian.net/browse/DOCS-1124?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ PR-URL: https://github.com/hasura/graphql-engine-mono/pull/9926 GitOrigin-RevId: 32821023c9f01e34e7b9e65830b00f389c3915d3
This commit is contained in:
parent
ecfec66e41
commit
a95a3222f5
329
docs/docs/scheduled-triggers/recipes/daily-summary-email.mdx
Normal file
329
docs/docs/scheduled-triggers/recipes/daily-summary-email.mdx
Normal file
@ -0,0 +1,329 @@
|
||||
---
|
||||
title: Using Schedule Triggers for Sending a Daily Summary Email
|
||||
description: Succinct, tested, and reusable code recipes for common use cases in Hasura.
|
||||
sidebar_label: Daily summary email
|
||||
keywords:
|
||||
- hasura
|
||||
- docs
|
||||
- recipes
|
||||
- scheduled triggers
|
||||
- daily
|
||||
- summary
|
||||
- automated
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import Thumbnail from '@site/src/components/Thumbnail';
|
||||
|
||||
# Send a Daily Summary Email
|
||||
|
||||
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 each user a daily summary of recent notifications which they
|
||||
have received. We'll do this by executing this trigger every morning and seeing what new notifications have come through
|
||||
in the last twenty-four hours. If a user has new notifications, they'll get an email listing them all.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before getting started, ensure that you have the following in place:
|
||||
|
||||
- A Hasura project, either locally or using [Hasura Cloud](https://cloud.hasura.io/?skip_onboarding=true).
|
||||
- A working SMTP server or email-sending service that you can integrate with to send emails.
|
||||
- If you plan on using a webhook endpoint hosted on your own machine with a Hasura project hosted elsewhere, ensure that
|
||||
you have a tunneling service such as [ngrok](https://ngrok.com/) set up so that a remotely hosted instance can
|
||||
communicate with your local machine.
|
||||
|
||||
## Our model
|
||||
|
||||
When sending transactionary 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?
|
||||
|
||||
For simplicity, we're assuming there are two tables in our database: `users` and `notifications`. The `users` table
|
||||
contains the details of all users, including the user's email address. The `notifications` table contains all
|
||||
notifications sent to users. Each notification has a `user_id` property that references the user to whom the
|
||||
notification was sent.
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Click here for the SQL to generate these tables and some seed data.
|
||||
</summary>
|
||||
|
||||
```sql
|
||||
-- create the users table
|
||||
CREATE TABLE public.users (
|
||||
id uuid PRIMARY KEY,
|
||||
email varchar(255) NOT NULL,
|
||||
name varchar(100) NOT NULL
|
||||
);
|
||||
|
||||
-- create the notifications table
|
||||
CREATE TABLE public.notifications (
|
||||
id serial PRIMARY KEY,
|
||||
user_id uuid NOT NULL REFERENCES public.users(id),
|
||||
message text NOT NULL,
|
||||
created_at timestamp DEFAULT now()
|
||||
);
|
||||
|
||||
-- seed data for the users table
|
||||
INSERT INTO public.users (id, email, name) VALUES
|
||||
('6f809f39-07a1-4c3b-a5e5-8e6d905366f1', 'user1@example.com', 'Daniel Ricciardo'),
|
||||
('d0f29c98-c789-4ec0-b84c-1593a6a8d0c2', 'user2@example.com', 'Charles Leclerc'),
|
||||
('acde545e-32d3-4f28-9475-61ab26a555d9', 'user3@example.com', 'Carlos Sainz');
|
||||
|
||||
-- seed data for the notifications table
|
||||
INSERT INTO public.notifications (user_id, message, created_at) VALUES
|
||||
('6f809f39-07a1-4c3b-a5e5-8e6d905366f1', 'Your order has been shipped!', NOW() - INTERVAL '30 hours'),
|
||||
('d0f29c98-c789-4ec0-b84c-1593a6a8d0c2', 'New product added: Smartphone XYZ is now available!', NOW() - INTERVAL '30 hours'),
|
||||
('acde545e-32d3-4f28-9475-61ab26a555d9', 'Special offer: Get 20% off on all electronics!', NOW() - INTERVAL '30 hours');
|
||||
```
|
||||
|
||||
You can copy / paste this into the `RUN SQL` tab in the Hasura Console on the `Data` page. Then, track all relationships
|
||||
under the `Public` schema on the `Data` page.
|
||||
|
||||
</details>
|
||||
|
||||
## Step 1: Create the Scheduled Event
|
||||
|
||||
Head to your 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., `daily_recap_email`. Then, enter a webhook URL that will be called when
|
||||
the event is triggered. This webhook will be responsible for sending the summary email and can be hosted anywhere, and
|
||||
written in any language, you like.
|
||||
|
||||
In the example below, if we're using Docker, we'll use a webhook endpoint hosted on our own machine running on port
|
||||
`4000`. Let's enter the following URL to allow Docker to communicate with the host machine:
|
||||
|
||||
```
|
||||
http://host.docker.internal:4000/review-request
|
||||
```
|
||||
|
||||
:::info Tunneling your webhook endpoint
|
||||
|
||||
If you're not running your Hasura instance on the same machine as your webhook endpoint, you'll need to use a tunneling
|
||||
service such as [ngrok](https://ngrok.com/) to expose your webhook endpoint to the internet. This will allow you to
|
||||
expose a public URL that will forward requests to your local machine and the server we'll configure below.
|
||||
|
||||
You'll need to modify your webhook URL to use the public URL provided by ngrok.
|
||||
|
||||
:::
|
||||
|
||||
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": "daily_recap_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 customers should receive a summary of their new notifications.
|
||||
|
||||
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,
|
||||
},
|
||||
});
|
||||
|
||||
// Our route for the webhook
|
||||
app.post('/review-request', 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',
|
||||
});
|
||||
}
|
||||
|
||||
// get our date ready for the query
|
||||
const today = new Date();
|
||||
const twentyFourHoursPrior = new Date(today.setDate(today.getDate() - 1));
|
||||
const twentyFourHoursPriorAsTimestamp = twentyFourHoursPrior.toISOString().split('T')[0];
|
||||
|
||||
// Fetch the data from our Hasura instance
|
||||
async function getRecentNotifications() {
|
||||
const response = await fetch('http://localhost:8080/v1/graphql', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
// "x-hasura-admin-secret": "<YOUR-ADMIN-KEY>",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query: `
|
||||
query DailyNotificationsQuery($start_time: timestamp!) {
|
||||
users {
|
||||
id
|
||||
email
|
||||
name
|
||||
notifications(where: {created_at: {_gte: $start_time}}) {
|
||||
id
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
start_time: twentyFourHoursPriorAsTimestamp,
|
||||
},
|
||||
}),
|
||||
});
|
||||
const { data } = await response.json();
|
||||
return data.users;
|
||||
}
|
||||
|
||||
// get our users and filter out the ones with no notifications
|
||||
let users = await getRecentNotifications();
|
||||
users = users.filter(user => user.notifications.length > 0);
|
||||
|
||||
// helper function to list the notifications in the email
|
||||
function listNotifications(notifications) {
|
||||
let list = '';
|
||||
notifications.map(notification => {
|
||||
list += `- ${notification.message}\n`;
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
// map over the data and send an email to each user
|
||||
async function sendReviewRequests() {
|
||||
let outcomes = [];
|
||||
users.map(async user => {
|
||||
// Create a message object
|
||||
const message = {
|
||||
from: 'SuperStore.com <sender@SuperStore.com>',
|
||||
to: `${user.name} <${user.email}>`,
|
||||
subject: `You've got new notifications, ${user.name.split(' ')[0]}!`,
|
||||
text: `Hi ${user.name.split(' ')[0]},\n\nCheck out your recent notifications:\n\n${listNotifications(
|
||||
user.notifications
|
||||
)}`,
|
||||
};
|
||||
|
||||
// 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)}`);
|
||||
|
||||
// add the info to the outcomes array
|
||||
outcomes.push({
|
||||
messageId: info.messageId,
|
||||
previewUrl: nodemailer.getTestMessageUrl(info),
|
||||
});
|
||||
return outcomes;
|
||||
});
|
||||
}
|
||||
|
||||
await sendReviewRequests(users);
|
||||
|
||||
// Return a JSON response to the client
|
||||
res.json({
|
||||
message: 'Review requests 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
|
||||
|
||||
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.
|
@ -19,8 +19,16 @@ can use them as a starting point to build your own custom logic.
|
||||
### Send a request for a product review
|
||||
|
||||
In e-commerce applications, it's common to send a request for a product review a certain period of time after the
|
||||
product has been delivered. This is great for improving trust on an e-commerce site, increasing SEO, and encouraging customers to return. This recipe shows how to send a request for a product review 7 days after the product is
|
||||
delivered. However, understanding the principles of this example will help you to implement your own custom logic for
|
||||
triggering any event which is based on a time interval. [Check it out](/scheduled-triggers/recipes/product-review.mdx)!
|
||||
product has been delivered. This is great for improving trust on an e-commerce site, increasing SEO, and encouraging
|
||||
customers to return. This recipe shows how to send a request for a product review 7 days after the product is delivered.
|
||||
However, understanding the principles of this example will help you to implement your own custom logic for triggering
|
||||
any event which is based on a time interval. [Check it out](/scheduled-triggers/recipes/product-review.mdx)!
|
||||
|
||||
### Send a daily summary email for all notifications
|
||||
|
||||
In applications with a notification system, it's common to send a daily summary email for all notifications. This recipe
|
||||
shows how to send a daily summary email for all notifications. However, understanding the principles of this example
|
||||
will help you to implement your own custom logic for triggering any event which is based on a time interval.
|
||||
[Check it out](/scheduled-triggers/recipes/daily-summary-email.mdx)!
|
||||
|
||||
<!-- TODO: ## Recipes for other features -->
|
||||
|
@ -10,6 +10,7 @@ keywords:
|
||||
- review
|
||||
- request
|
||||
- automated
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
@ -18,9 +19,11 @@ import Thumbnail from '@site/src/components/Thumbnail';
|
||||
|
||||
# Send a Product Review Request Email
|
||||
|
||||
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 a review request email for a product seven days after the order is
|
||||
delivered. We'll do this by executing this trigger every day at midnight to check whether it has been seven days since an order has been completed, if it has, and the user has not already received a reminder and has not left a review for their purchase, we'll send them an email asking them to do so.
|
||||
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 a review request email for a product seven days after the
|
||||
order is delivered. We'll do this by executing this trigger every day at midnight to check whether it has been seven
|
||||
days since an order has been completed, if it has, and the user has not already received a reminder and has not left a
|
||||
review for their purchase, we'll send them an email asking them to do so.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@ -29,7 +32,8 @@ Before getting started, ensure that you have the following in place:
|
||||
- A Hasura project, either locally or using [Hasura Cloud](https://cloud.hasura.io/?skip_onboarding=true).
|
||||
- A working SMTP server or email-sending service that you can integrate with to send emails.
|
||||
- If you plan on using a webhook endpoint hosted on your own machine with a Hasura project hosted elsewhere, ensure that
|
||||
you have a tunneling service such as [ngrok](https://ngrok.com/) set up so that a remotely hosted instance can communicate with your local machine.
|
||||
you have a tunneling service such as [ngrok](https://ngrok.com/) set up so that a remotely hosted instance can
|
||||
communicate with your local machine.
|
||||
|
||||
## Our model
|
||||
|
||||
@ -121,7 +125,8 @@ First, provide a name for your trigger, e.g., `send_review_request_email`. Then,
|
||||
when the event is triggered. This webhook will be responsible for sending the review request email and can be hosted
|
||||
anywhere, and written in any language, you like.
|
||||
|
||||
In the example below, if we're using Docker, we'll use a webhook endpoint hosted on our own machine running on port `4000`. Let's enter the following URL to allow Docker to communicate with the host machine:
|
||||
In the example below, if we're using Docker, we'll use a webhook endpoint hosted on our own machine running on port
|
||||
`4000`. Let's enter the following URL to allow Docker to communicate with the host machine:
|
||||
|
||||
```
|
||||
http://host.docker.internal:4000/review-request
|
||||
@ -164,6 +169,8 @@ Under `Advanced Settings`, we can configure the headers that will be sent with t
|
||||
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
|
||||
@ -321,10 +328,10 @@ You can run the server by running `node index.js` in your terminal. If you see t
|
||||
## Step 4: Test the setup
|
||||
|
||||
With your server running, Hasura should start hitting our endpoint. As we set our cron expression to `0 0 * * *`, the
|
||||
webhook will be triggered at midnight 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
|
||||
🎉
|
||||
webhook will be triggered at midnight 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"
|
||||
|
Loading…
Reference in New Issue
Block a user