Substitutions not functional when using SendGrid in conjunction with Firebase functions

I'm encountering difficulties when trying to include substitutions data in emails sent from Sendgrid using Firebase Cloud Functions.

Here's my function

exports.firestoreEmail = functions.firestore
.document('users/{id}')
  .onCreate(snap => {
    const user = snap.data();
    const msg = {
      to: user.email,
      from: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4e2b362f233e222b602d2123">[email protected]</a>',
      subject: `${user.firstName}, please Verify Your Email Address`,
      templateId: 'templateID',
      substitutionWrappers: ['{{', '}}'],
      substitutions: {
        firstName: user.firstName,
        email: user.email,
        id: user.id
      }
    };
    return sgMail
      .send(msg)
      .then(() => console.log('email sent!'))
      .catch(err => console.log(err));
  });

and the transactional template for the templateId is

<html>
  <head></head>
  <body>{{firstName}} - {{email}} - {{id}}</body>
</html>

This results in an email being sent to the user.email as planned, but with empty spaces where the substitutions data should appear.

In line with the documentation and use cases provided here, I've also attempted to include

sgMail.setSubstitutionWrappers('{{', '}}');

to globally set the substitutionWrappers. However, it still doesn't seem to work.

I've also used console.log(user) which displays the data intended for the substitutions in the console.

What am I overlooking? The data is accessible, the email format is correct, and the function closely aligns with the examples provided by SendGrid.

Answer №1

After spending numerous hours, I finally cracked the code by understanding that substitutions and substitutionWrappers are meant for Legacy Transactional Templates.

For the v3 API, it's recommended to use dynamic_template_data instead of substitutions, with the handlebars {{ }} acting as the substitutionWrappers.

  dynamic_template_data: {
    firstName: user.firstName,
    email: user.email,
    id: user.id
  }

Lesson learned - always thoroughly read the documentation next time... well, most likely.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The middleware in Express.js is failing to execute the next function

I have been attempting to run a post function that contains a next(); within the code. To solve this issue, I have exported the function's definition from another file and am trying to call it through an express router. Unfortunately, the function is ...

Tips for updating the content of multiple tabs in a container with just one tab in Bootstrap 4.x

I am attempting to create two tab containers, where one is used to describe the content of a set of files and the other is used as a list of download links for the described files. Initially, I tried controlling the two containers using just one tab. I ca ...

Incorporating a radio button input into the parent component in a React application

The main component stores all the state data. Whenever there is a change in any input field, the function myChangeHandler is triggered and updates the state accordingly. myChangeHandler(event) { this.setState({ [event.target.name]: event.target.value }) ...

Extracting a nested property from a JSON object without relying on the lodash get method

We have a sample json data: { "name": "app", "version": "0.1.0", "description": "Sample description", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "so ...

What is the best method for retaining the complete YQL outcome within a JavaScript object?

I am trying to store the output of my YQL Query in a JavaScript object Here is my query: SELECT * FROM html WHERE url="http://myurl.com" and xpath="/html/body/center/table[1]/tr" Can someone guide me on how to proceed? I have gone through the YQL docu ...

Fill in input field based on choice from the drop-down menu - JavaScript

I am facing an issue where I cannot add text inside the text box based on the dropdown selection. For example, if I select option2 from the dropdown, the textbox should be populated with option2. (function() { 'use strict'; setInterva ...

Compilation unsuccessful. The LineGraph.js module could not be located due to recursion in resolving

After successfully installing react-chartjs-2 and chart.js using the command npm install --save react-chartjs-2 chart.js, I encountered an error when attempting to use LinkGraph: Failed to compile. ./src/LineGraph.js Module not found: Recursion in resolvi ...

Retrieve the element that was clicked by targeting its class name using JavaScript

Unfortunately, I couldn't create this example in JSFiddle as it's currently in read-only mode. My goal is to identify the specific element that was clicked based on a given class. var button = document.getElementsByClassName("mybutton"); button ...

Is it possible to utilize an await in an rxjs observable?

Implementing an interceptor for my HTTP requests requires the use of the access token from the user instance. Within my app component, I initialize the user: app.component.ts private async restoreUser(): Promise<UserModel | any> { // ... some vi ...

Showing the Datepicker from jQuery right in the middle of the screen

Here's the generated code as default: <div id="ui-datepicker-div" class="ui-datepicker ui-widget ui-widget-content ui-helper- clearfix ui-corner-all ui-datepicker-multi ui-datepicker-multi-2" style="width: 34em; position: absolute; left: ...

What could be the reason for my array being nested inside another array as a value in a dictionary

I am having some difficulty understanding how the selected array is being returned in my dictionary. Could you please provide me with an explanation? The language being used is javascript. I have constructed a dictionary with arrays as values. However, wh ...

Resize an image to fit perfectly within a material-ui grid

Is there a way to resize images within a grid layout without them getting cropped? Each image I try to fit into the top horizontal grid behaves differently due to varying dimensions. Instead of stretching and fitting into the container, the images end up b ...

AJAX request: No values are being returned by $_GET

After spending hours trying to figure this out... I've been working on using AJAX to grab values from a jQuery slider within an <input> tag. The AJAX request is not failing (see code below), and when I use console.log to check the variable I&ap ...

if the checkbox is selected, navigate to the displayed section

In my scenario, I have a total of 5 checkboxes and corresponding to each checkbox, there are 5 div elements. The functionality that I need is that when a checkbox is checked, the associated div should be displayed; otherwise, it should be hidden. The check ...

When using AutoComplete in MUI, I encountered an issue while attempting to assign a default value to the checkbox from an API. Instead of achieving the desired result, I received an error stating "(inter

Within this snippet, I am seeking to retrieve a default value from the API to populate a checkbox initially. I have employed the Material-UI Autocomplete component, which includes a defaultValue prop. Despite my efforts to utilize this prop, I am encounter ...

Eliminating the nested API call within a loop

After making an API call to retrieve a set of data such as a list of users, I noticed that I am implementing a for loop and within it, I am making another API call to obtain each user's profile details based on their ID. I understand that this approac ...

Refresh the redux state within the reactjs component and make use of it in the application

I am in need of assistance with opening a search aid, selecting a value, and retrieving it. After clicking on a button, I activate a search help where I choose data to store. However, I am unsure how to utilize this stored data upon returning. I aim to di ...

I encountered an issue where the error "data.map is not a function" occurs whenever I try to execute a React component

I have implemented the HorizontalScrollbar component and passed data as a prop. When I try to run the HorizontalScrollbar component, I encounter the following error in the console tab of Chrome: Error: Uncaught TypeError: data.map is not a function Horiz ...

Implementing a dynamic like functionality using Ajax in Django

I've created a new structure for the like button, but it's not functioning correctly. Here are the files I'm working with: models.py class Comment(models.Model): title = models.CharField(max_length=50) author = models.ForeignKey(Pr ...

Use jQuery to change the background color when clicked

Below is the HTML code with UL and LI elements: <UL> <LI><span id='select1'>Text</span></LI> <LI><span id='select2'>Text</span></LI> <LI><span id='select3'>Tex ...