Sendgrid-nodejs: Easy to understand example

Created on 28 Jun 2016  ·  75Comments  ·  Source: sendgrid/sendgrid-nodejs

Hello,

Would be great to provide an easy to understand example on your readme, something as simple as https://sendgrid.com/blog/migrating-app-sendgrids-template-engine/ for example, because tbh if I wanted to write 30 lines of javascript code to send a simple email I wouldn't be using sendgrid.

Your library is quite frustrating to use, even for an experienced developer.

Cheers,
and sorry for this angry rant

ps: this example is exactly what I'm looking for https://sendgrid.com/docs/Integrate/Code_Examples/v2_Mail/nodejs.html , so easy to use

help wanted community enhancement

Most helpful comment

@thinkingserious I finally came to understand what bothers me so much about all the Sendgrid helper classes introduced in v3... there's just too many of them and they make sending emails unnecessarily complicated :P

I didn't want to overhaul all of Sendgrid's code, so I decided to make a simple wrapper instead, sendgrid-mailer.

While Sendgrid's helper classes are useful behind the scenes, to create and validate email models, I don't think they should be forced upon us just to interact with the API. I find it makes writing Node apps more complicated than it should be, and the naming of the helpers can be quite confusing, e.g. Mail vs Email.

I think the basic example illustrates the problem well:

https://github.com/sendgrid/sendgrid-nodejs#with-mail-helper-class

This just seems way too convoluted for something as basic as "I want to send an email". It requires you to load 8 different dependencies and write 15 lines of code before you can send an email.

Not using the helper classes is arguably even worse though, as then you have to know the required structure of the email JSON request and populate it correctly yourself. One mistake will break the request easily.

So I abstracted all the helper classes logic away behind a simple, easy to use API helper, so that sending emails with Sendgrid becomes easy and fun again :)

The most basic example becomes:

//Load mailer and set API key (only needed once)
const mailer = require('sendgrid-mailer').config(API_KEY);

//Create email data 
const email = {
  to: '[email protected]',
  from: 'Someone <[email protected]>',
  subject: 'Hello world',
  text: 'Hello plain world!',
  html: '<p>Hello HTML world!</p>',
};

//Send away 
mailer.send(email); //Returns promise 

The request is constructed behind the scenes and uses Sendgrid's helpers transparently.

It also supports sending multiple emails, exposes the Sendgrid object in case further customisation is needed, accepts Sendgrid Mail instances as well, and accepts a wide variety of formats for the to/from fields, e.g. Name <[email protected]> or an object with name/email properties. See the README for more details.

There's probably still room for improvement, but I wanted to share this solution here for people looking to use the Sendgrid API without being overwhelmed by it.

Hope it comes in handy for someone, and maybe it can provide some direction for the future of this library, in exposing a simpler API :)

cc @julesbou a bit late, but I hope you can still find a use for it.

All 75 comments

Hello @julesbou,

Angry rants are fine with me, especially if they have solutions attached :)

Thank you for taking the time to let us know.

We are currently planning the improvements for the next iteration, so you had good timing!

If you have any other particular requests, please let us know either in this thread or please open additional issues.

Thanks!

How do we use the helper class? All I wanna do is set the from name??

@jsgandalf,

Easiest way is: https://github.com/sendgrid/sendgrid-nodejs#hello-email, specifically: https://github.com/sendgrid/sendgrid-nodejs/blob/master/examples/helpers/mail/example.js#L4

You can also just build your own json as follows: https://github.com/sendgrid/sendgrid-nodejs/blob/master/examples/mail/mail.js#L31 (here is an overview of the json: https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/index.html)

Thanks!

Very helpful! This should be on the readme!!

@jsgandalf @thinkingserious I too find the library quite frustrating to use. I am trying to send a template email. I found the examples here not very helpful. I am confused on how to set the "to" and "from" email addresses, and this code does not even send my email. I've been scratching my head for the past three days. Any help?

Thanks for the feedback @bcootner. Your request helps bump fixing this up the queue. This is an issue I'm itching to get done.

In the mean time, hopefully this example will help:

var sg = require('sendgrid').SendGrid(process.env.SENDGRID_API_KEY)

function helloTemplate(){
  var helper = require('sendgrid').mail

  from_email = new helper.Email("[email protected]")
  to_email = new helper.Email("[email protected]")
  subject = "Hello World from the SendGrid Node.js Library"
  content = new helper.Content("text/plain", "some text here")
  mail = new helper.Mail(from_email, subject, to_email, content)
  // The constructor above already creates our personalization object
  // -name- and -card- are substitutions in my template
  substitution = new helper.Substitution("-name-", "Example User")
  mail.personalizations[0].addSubstitution(substitution)
  substitution = new helper.Substitution("-card-", "Denver")
  mail.personalizations[0].addSubstitution(substitution)
  mail.setTemplateId("YOUR_TEMPLATE_ID")
  return mail.toJSON()
}

function send(toSend){
  //console.log(JSON.stringify(toSend, null, 2))
  //console.log(JSON.stringify(toSend))

  var sg = require('sendgrid').SendGrid(process.env.SENDGRID_API_KEY)

  var requestBody = toSend
  var emptyRequest = require('sendgrid-rest').request
  var requestPost = JSON.parse(JSON.stringify(emptyRequest))
  requestPost.method = 'POST'
  requestPost.path = '/v3/mail/send'
  requestPost.body = requestBody
  sg.API(requestPost, function (response) {
    console.log(response.statusCode)
    console.log(response.body)
    console.log(response.headers)
  })
}

send(helloTemplate())

Please reach out if you need additional help.

@thinkingserious Thanks for you time and thoughtful response. I have the following code:

    var  sg = require('sendgrid').SendGrid("SG.**** .. ***6o");

    function welcomeTemplate() {
         console.log("template")
         var helper = require('sendgrid').mail
         from_email = new helper.Email("[email protected]")
         to_email = new helper.Email("[email protected]")
         subject = "Hello World from the SendGrid Node.js Library"
         content = new helper.Content("text/plain", "some text here")
         mail = new helper.Mail(from_email, subject, to_email, content)
         substitution = new helper.Substitution("-Name-", "bcootner")
         mail.personalizations[0].addSubstitution(substitution)
         mail.setTemplateId("5a****-*******-******-9")    //The ID of my Transactional Template
         return mail.toJSON()
}

function sendMail(toSend) {
     console.log("Send")
     var sg = require('sendgrid').SendGrid("SG.***....****o")
     var requestBody = toSend
     var emptyRequest = require('sendgrid-rest').request
     var requestPost = JSON.parse(JSON.stringify(emptyRequest))
     requestPost.method = 'POST'
     requestPost.path = '/v3/mail/send'
     requestPost.body = requestBody
     sg.API(requestPost, function (response) {
            console.log(response.statusCode)
            console.log(response.body)
            console.log(response.headers)
       })
}

And then I call this code with sendMail(welcomeTemplate()) but that doesn't work. The "template" and "send" appear in the logs but the statusCode, body or headers do not show up, no emails are being received and nothing shows up on my dashboard.

After requestPost.body = requestBody could you please do console.log(JSON.stringify(requestBody)) and let me know what you get.

@thinkingserious After some debugging it seems like nothing gets printed to the log after the line var emptyRequest = require('sendgrid-rest').request. First "template" gets logged, then about 5 minutes later "send" gets printed (and if I uncomment the lines you commented in the send function those work fine). Is there something I'm missing for this require statement, I just did npm install sendgrid

Please try this:

A. Create a file called post.json with the following payload:

{"content":[{"type":"text","value":"Hello, World!"}],"from":{"email":"[email protected]"},"personalizations":[{"subject":"Hello, World!","to":[{"email":"[email protected]"}]}]}

B. Replace the example emails

C. Run the following command:

curl -X "POST" "https://api.sendgrid.com/v3/mail/send" \
        -H "Authorization: Bearer $SENDGRID_API_KEY"  \
        -H "Content-Type: application/json" \
        -d @post.json

D. Either replace $SENDGRID_API_KEY with your API KEY, or add your API Key to an environment variable called SENDGRID_API_KEY

@thinkingserious I received that test email, I was also able to get the Hello Test working fine, it's just these templates don't want to work.

EDIT: So I replaced var emptyRequest = require('sendgrid-rest').request with var emptyRequest = sg.emptyRequest() and that sends the email, there's just not any formatting from the template design or any images.

@bcootner,

Unfortunately, I can't reproduce your error, so I'm trying to help step through where there could be a problem. Since you can send an email via curl, that means your API key is good. Perhaps the Template ID has a problem?

If you remove mail.setTemplateId("5a****-*******-******-9") does the email send?

@thinkingserious So if I remove the setTemplateID it sends the test "Hello World from the SendGrid Node.js Library".

I'm not sure if you saw my edit, but by replacing var emptyRequest = require('sendgrid-rest').request with var emptyRequest = sg.emptyRequest() the text from my template does send, but there no formatting (no fonts, no images). I get the following print out now from the console.

2016-07-13T00:01:07.514157+00:00 app[web.1]:   date: 'Wed, 13 Jul 2016 00:01:07 GMT',
2016-07-13T00:01:07.514159+00:00 app[web.1]:   connection: 'close',
2016-07-13T00:01:07.514157+00:00 app[web.1]:   date: 'Wed, 13 Jul 2016 00:01:07 GMT',
2016-07-13T00:01:07.514158+00:00 app[web.1]:   'content-type': 'text/plain; charset=utf-8',
2016-07-13T00:01:07.502390+00:00 app[web.1]: 202
2016-07-13T00:01:07.502454+00:00 app[web.1]: 
2016-07-13T00:01:07.514155+00:00 app[web.1]: { server: 'nginx',
2016-07-13T00:01:07.514159+00:00 app[web.1]:   connection: 'close',
2016-07-13T00:01:07.514161+00:00 app[web.1]:   'x-frame-options': 'DENY' }

Something makes me think it's a problem with 'text/plain', but I'm not sure. Thanks again!

Hey @bcootner, I have just experienced the same issue and been scratching my head for a few hours, but managed to solve it. It feels that there is too much unnecessary stuff needed before you can actually send the email, but here's my helper function to do that:

_Params_

  • from (e.g '[email protected]')
  • to (e.g '[email protected]')
  • subject (e.g 'test subject')
  • message (e.g 'test message')
  • template (template id which you want to use)
  • templateVariables (object, where key - variable in the email, value - value to replace it with) { ":name": "someName", ":email": "someEmail" }. Note that :email and :name is how I have set my variables in the email template, it can be -email- or whatever you have
  • success callback
  • error callback
    let sendTemplateMail =  (from, to, subject, message, template, templateVariables, successCallback, errorCallback) => {
        var sg = require('sendgrid').SendGrid(process.env.SENDGRID_API_KEY),
            helper = require('sendgrid').mail,
            content_text = new helper.Content("text/plain", message),
            content_html = new helper.Content("text/html", message),
            request = sg.emptyRequest(),
            email_to = new helper.Email(to),
            email_from = new helper.Email(from),
            mail = new helper.Mail(email_from, subject, email_to, content_text),
            personalization = new helper.Personalization();

        personalization.addTo(email_to);
        personalization.setSubject(subject);

        mail.setTemplateId(template);
        mail.addContent(content_html);

        for(var key in templateVariables) {
            if(templateVariables.hasOwnProperty(key)) {
                var substitution = new helper.Substitution(key, templateVariables[key]);
                personalization.addSubstitution(substitution);
            }
        }

        mail.addPersonalization(personalization);

        request.method = 'POST';
        request.path = '/v3/mail/send';
        request.body = mail.toJSON();

        sg.API(request, (json) => {
            if(json.statusCode >= 300) {
                errorCallback(json);
            } else {
                successCallback(json);
            }
        });
    }

The documentation indeed needs some refactoring, with basic, simple examples. Hope my function will help you out :)

@ByEmke thanks for your support! Could you please email us at [email protected] with you T-shirt size and mailing address?

With regards to the documentation, a refactor is on it's way as well as for the mail helper itself. We've been collecting all of your feedback and are excited about the next iteration.

Also, a big confusion is that you still need set the content when using template. Fortunately, that will change very soon (days).

@bcootner I think you nailed it. You need content_html = new helper.Content("text/html", message). Fortunately, this requirement is going away in a few days, our team is currently changing this behavior so that when you specify a template, you will no longer need to set the content.

@ByEmke @thinkingserious thanks for your help on this issue, I managed to get it to work with the following code last night:

 var sg = require('sendgrid').SendGrid("SendGrid API Key" or <environment variable>)

function sendEmail(tempID){
    var helper = require('sendgrid').mail;
    from_email = new helper.Email("[email protected]")
    to_email = new helper.Email("[email protected]")
    subject = "Dummy Subject"
    content = new helper.Content("text/html", "dummy content")
    mail = new helper.Mail(from_email, subject, to_email, content)

    substitution = new helper.Substitution("-name-", "User's Name")
    mail.personalizations[0].addSubstitution(substitution)
    mail.setTemplateId(tempID)
    var requestBody = mail.toJSON()
    var requestPost = JSON.parse(JSON.stringify(sg.emptyRequest()))
    requestPost.method = 'POST'
    requestPost.path = '/v3/mail/send'
    requestPost.body = requestBody
    sg.API(requestPost, function (response) {
       console.log(response.statusCode)
       console.log(response.body)
       console.log(response.headers)
  })
}

Thanks again!

I also feel that the docs are quite hard to follow, especially when using templates and some other features.

I prefer the request.body = {} syntax over the helper() syntax as this makes things more verbose and less easy to read (for me at least).

I spent quite some time going through the docs, testing changes, looking at your examples and trying again only to come here and find out that you _do_ need to set content when using templates though the docs state otherwise. Argh. Stuff like this can drive you mad. Anyways kudos to @ByEmke for providing a working example.

@peterkuiper (or should I say Adam :))

Thanks for the feedback, we are very interested in how we can make the library docs better.

To be clear, are you referring to these? https://github.com/sendgrid/sendgrid-nodejs/blob/master/USAGE.md

With regard to the content issue (we heard all of you :)), this is just about fixed. We will deploying a release to the API that will not require the content to be set when using a template. We hope that change will make the template experience much better.

@thinkingserious I was checking out the docs on sendgrid.com (ie https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/index.html) while I should have gone straight to Github. I skimped through the README of this repo, checked out the examples and totally missed USAGE.md (my bad). The USAGE.md file should be the one that should be put on on sendgrid.com as it has my preferred format :) Still not sure about some best practices when using templates but I hope I can figure that out myself or find it in the docs.

I was browsing that file and just found a random example of something that I find confusing:

var request = sg.emptyRequest()
request.queryParams["start_time"] = '1'
request.queryParams["limit"] = '1'
request.queryParams["end_time"] = '1'
request.queryParams["offset"] = '1'
request.method = 'GET'
request.path = '/v3/suppression/blocks'
sg.API(request, function (response) {
    console.log(response.statusCode)
    console.log(response.body)
    console.log(response.headers)
  })

What types are start_time, limit, end_time and offset and what do these mean? Are there more queryParams? And why isn't it request.body?

There is a link to the User Guide (which does not help at all) while there should have been a link to the API docs which clears things up https://sendgrid.com/docs/API_Reference/Web_API_v3/blocks.html.

So you have to go back and forth trying to figure out where to find the information you need.

Excellent feedback @peterkuiper!

I'd like to swag you out as well. Please send us your T-shirt size and mailing address to [email protected].

We will update the USAGE.md docs to include descriptions of the query and response bodies.

Regarding templates, the plan there is to create a helper like we did for the mail endpoint, complete with examples of common use cases. Also, there is a fix coming where you will not have to specify the content when using templates. That is a big source of confusion.

@thinkingserious cool, thanks :) email sent!

While I am at it, I noticed something else which also refers to best practices (and is causing some confusion for my team). One of my teammates created templates for some features (ie verify email) and added verify-nl, verify-en etc. So every template was basically the same with the only difference being the text (different language). I have seen <%subject%> en <%body%> and couldn't find any reference to it (TL;DR from my part maybe) so I assume it is an internal substitution on your end which happens when using the API.

I'm curious what a "normal" practice might be:

  • Create a template for each feature in a different language and only do some minor substitutions (ie name)

It seems using this approach might cause a lot of work when you change parts of your template. Not sure if you would be able to create "template templates" (ie header/footer/whatever part). Also, the limit of 300 templates can easily be reached when you have a lot of different languages (this isn't an issue in our case).

  • Create a template for each feature and use <%body%> to set the body from your application and just have one "master" template (or use your on sub tag)

Hope the question makes sense :)

@peterkuiper,

I don't know, but I've put out some feelers. I'll respond with what I discover. Thanks!

@thinkingserious I ended up using version 2.0.0 which I really like how it's working:

var client = require("sendgrid")("key");

client.send({
  to: "[email protected]",
  from: "[email protected]",
  subject: "subject",
  text:  "hello"
  // Or using the html property:
  // html: "Hello <strong>World!</strong>!"
}, fn);

I'm looking to see how this would look like in version 3.x.x. I find the current examples really confusing.

Great feedback @IonicaBizau!

We will definitely be considering your thoughts for the next iteration.

@thinkingserious Great! Still, I'd like to know how this would look like in 3.x.x.

There is no one-to-one mapping as this client was recreated from scratch to decouple from the v2 mail endpoint and allow access to the v3 endpoints.

That said, the mail helper purpose is to get to a point where the call can be as easy as it was with v2. We're just not there yet, we back at the beginning and looking forward to many more iterations.

@thinkingserious I did see that, but I don't like the following points:

  1. It's confusing because there are global variables (declared without var or let or similar keyword). E.g.: from_email = new helper.Email("[email protected]") instead of var from_email = ...
  2. Using new helper.Email just for an email resource is very unfriendly. This can be wrapped probably in a lower-level functionality inside of the module. Same thing for Content, Mail etc.
  3. Setting manually the method, path and body is again quite unfriendly.
  4. The callback function should be in the err, data, ... format (instead of response).

In short, IMHO, the _quick start_ is a low level implementation for sending an email. It can be useful for deeper functionalities, but definitely not for a _quick start_. 😁


Thank you very much for the amazing service and module. ✨

@IonicaBizau,

Awesome feedback, thanks!

We will definitely be referring to your thoughts as we build out the next iteration of the mail helper.

You are correct, this first version is a thin wrapper over the API. The plan is to gradually improve it's functionality based on community feedback, like what you have done!

For your support and contribution to the community, we would like to send you some swag. Please send us an email to [email protected] with your T-shirt size and mailing address.

Please feel free to add additional feedback as you see fit. Thanks again!

@IonicaBizau

You can also do something like this:

var sg = require('sendgrid').SendGrid('API_KEY');
var request = sg.emptyRequest()

request.body = {
  "content": [
    {
      "type": "text/html",
      "value": "<html><p>Hello, world!</p></html>"
    }
  ],
  "from": {
    "email": "[email protected]",
    "name": "My Name"
  },
  "personalizations": [
    {
      "to": [
        {
          "email": "[email protected]",
          "name": "John Doe"
        }
      ]
    }
  ],
  "template_id": "TEMPLATE_ID",
};

request.method = 'POST'
request.path = '/v3/mail/send'
sg.API(request, function (response) {
  console.log(response.statusCode)
  console.log(response.body)
  console.log(response.headers)
});

I prefer this syntax over the helper syntax. Take a look at the examples, they helped me quite a bit.

https://github.com/sendgrid/sendgrid-nodejs/blob/master/examples/mail/mail.js

HTH

@thinkingserious Wowowow! Will definitely do. Thanks—glad to help! 😁

@peterkuiper I did try that too, but I think I did the mistake to replace text/html with html and the content appeared as attachment. Then I just decided to downgrade the version.

But again, this still looks low-level (request.method = ..., sending the MIME type in the content array, response argument in the callback etc).

Thanks @peterkuiper!

I added that style of example to the README last week :)

@IonicaBizau,

Yes, that low-level stuff will be hidden soon.

@thinkingserious glad to hear that the request stuff is going to be hidden soon 👍

Following up on @IonicaBizau's great suggestions, another recommendation would be hide the implementation details of "personalizations," if possible. For example, doing something like:

var sg = require('sendgrid').SendGrid('API_KEY');
sg.addTo(x);
sg.addBcc(email);
sg.addBcc(email);
sg.addHeader(header);

It just seems unnecessary to expose this level of the detail through the SDK.

Also, not sure if this is already a feature, but it would be great to be able to chain these calls:

var sg = require('sendgrid').SendGrid('API_KEY');
sg.addTo(x)
  .addBcc(email)
  .addBcc(email)
  .addHeader(header)
  .send();

Thanks and keep up the good work 👍

@jamesdixon,

Thanks for the additional feedback!

Until we get to enhancing the mail helper to enable some more implementation hiding, check out @adambuczynski's awesome pull request: https://github.com/sendgrid/sendgrid-nodejs/pull/261. It does not directly address your specific idea, but it's a step in the right direction.

@thinkingserious you mention the low level request stuff will be hidden soon, have you guys got something planned for that already? I was thinking about it yesterday, and though that while it's nice to have access to the entire API, you can use sendgrid-rest for that as well. That way, all this library has to do, is allow you to send email, and as such only expose the email sending API.

That was you separate concerns and narrow the focus of this package a bit, allowing for a cleaner API with chaining etc. like @jamesdixon suggested.

@adambuczynski,

Our plan is to execute the abstraction layer through the helpers. Mail is the first, but in the near future we plan to add one for subuser management and templates. But first we need to polish the mail helper and make it the standard.

You are correct, this library is just a very thin layer over sendgrid-rest and simply adds examples, a USAGE file and unit tests (these are all automated [we are hoping to open source this too] using our Swagger definition). Long term, we envision implementing helper code for all endpoints, complete with data validation both ways, with the core of that functionality also automated. Workflows and use cases will be hand crafted.

Over the next few weeks/months we will be exposing the roadmap here on GitHub for further feedback.

Also, by the end of August we will be adding support for the Incoming Parse Webhook to this library.

Thanks for asking this question and hope my reply sparks further feedback from you and the community. We want this to be a community driven library, supported and led by SendGrid.

Thanks for the update @thinkingserious. I'll be happy to play a role in the sendgrid nodejs library going forward.

Why are you adding Parse support at this stage? I thought that the Parse service was being retired next January.

@thinkingserious I forgot to mention that if there is anything I can help with here, just ping me. I'd be happy to hack the things if I'm able to. :smile:

@adambuczynski that's great news! Also, your pull request is getting closer to the top. I'm thinking I'll be able to get it merged early next week.

With regards to Parse, we have not decided to officially retire it just yet. Adding support in the libraries is part of our test to determine whether we will keep it going or not.

@IonicaBizau,

Thank you! Your support is greatly appreciated!

Is there a way to set multiple recipients using the mail helper?
I noticehelper.Mail(from_email, subject, to_email, content) uses personalization.addTo to set the recipient.. I believe this only works with 1 recipient not an array of recipients (maybe i'm wrong) and the helper doesn't provide a setTo function (ideal way to set the tos).

I see that if I were setting the personalization myself with let personalization = new helper.Personalization(); I could do personalization.tos = [email, email]//email=new helper.Email(email_address, name) is it safe? Is it likely to change in future releases?

@albert-the-creator I use it as follows:

  //Helpers
  const Mail = sendgrid.mail.Mail;
  const Email = sendgrid.mail.Email;
  const Personalization = sendgrid.mail.Personalization;

  //Create new mail object
  let mail = new Mail();

  //Create recipients
  let recipients = new Personalization();
  recipients.addTo(new Email(data.to, data.toname));

  //Set recipients
  mail.addPersonalization(recipients);

I think you can add multiple recipients by repeating the recipients.addTo line, for example in a loop.

I do find this approach quite verbose, with the splitting up into Email and Personalization classes. Sometimes you just want to add an email address and send it away, and this feels like you need a lot of lines of code to accomplish that.

@albert-the-creator,

That constructor was made to help people send a single email easier, basically it's a "hello email" function.

For multiple emails, you probably want to follow this example (as you have already started down this path by setting the personalization yourself): https://github.com/sendgrid/sendgrid-nodejs/blob/master/examples/helpers/mail/example.js#L15

It may also be useful to read about personalizations: https://sendgrid.com/docs/Classroom/Send/v3_Mail_Send/personalizations.html

The helper will definitely evolve, but any breaking changes will be signified with a major point release. I don't expect the API to change much though.

@adambuczynski,

Thanks for pitching in! We need to update the helper to account for additional common use cases like this one and sending emails with templates for example.

@thinkingserious the helpers API was something I am also interested in possibly refactoring/improving when I have time again :)

@adambuczynski,

That's awesome, when you decide to start on that, please ping me. Or if we start on it before you, I'll ping you.

I used the code by @bcootner and it worked fine. However It would be great to have a proper and official example of how to use a template with this node plugin.

@otmezger,

Thanks for the follow up!

I am working on that example this week, you will find it linked in the README.

I don't use the Template helper, but I will share my approach to loading both HTML and text format templates and partials via a helper load method:

  /**
   * Load an email (both plain text and html)
   */
  load(email, data, req) {

    //Get filenames
    const PARTIAL_HTML = path.resolve('./app/components/' + email + '.html');
    const PARTIAL_TEXT = path.resolve('./app/components/' + email + '.txt');

    //Append some globals
    if (req) {
      Object.assign(data, {
        app: {
          title: req.app.locals.APP_TITLE,
          version: req.app.locals.APP_VERSION,
          url: req.app.locals.APP_BASE_URL,
        },
        date: {
          year: moment().format('YYYY'),
          date: moment().format('DD-MM-YYYY'),
        },
      });
    }

    //Return promise
    return Promise.all([
      Promise.all([
        readFile(TEMPLATE_TEXT, 'utf8'),
        readFile(PARTIAL_TEXT, 'utf8'),
      ]).spread((template, partial) => {
        return template.replace('{{partial}}', partial);
      }),
      Promise.all([
        readFile(TEMPLATE_HTML, 'utf8'),
        readFile(PARTIAL_HTML, 'utf8'),
      ]).spread((template, partial) => {
        return template.replace('{{partial}}', partial);
      }),
    ]).then(result => result.map(contents => replaceData(contents, data)));
  },

This can then be used as follows to obtain an object ready for use with Sendgrid:

  let email = mailer.load('user/emails/verify-email-address', data, req)
    .spread((text, html) => ({
      to: user.email,
      from: req.app.locals.EMAIL_IDENTITY_NOREPLY,
      subject: 'Verify your email address',
      text, html,
    }));

You could easily adapt the example to use something like Handlebars templating as well. In the above example the replaceData helper is a simple function that replaces handlebars-like tags with data provided.

I'm not sure if this is relevant or not, but by NOT using the "mail helper" I saved myself an immeasurable amount of time. This is after getting so frustrated I downgraded to 2.0 on purpose. I'm just using a factory-type function to generate valid V3 JSON and am now very excited at the improvements in the V3 API (since I can actually use them now).

This is something that I struggled with at first as well. I think the
helpers could probably be made more efficient, and easier to use. I would
even argue that maybe they shouldn't be part of the core sendgrid module,
in order to separate concerns and allow people to opt in to use them.

I'll have another look at them and see if I can come up with some concrete
suggestions.

On Fri, Aug 26, 2016, 05:48 Sean Lindo [email protected] wrote:

I'm not sure if this by example or not, but by NOT using the "mail helper"
I saved myself an immeasurable amount of time. This is after getting so
frustrated I downgraded to 2.0 on purpose.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/sendgrid/sendgrid-nodejs/issues/252#issuecomment-242479491,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAd8Qu5iRLFQwoKlRd2fP5oioT1uLqB6ks5qjdV2gaJpZM4JAiac
.

@seanlindo,

Thanks for taking the time to provide some feedback!

Here is an example of using this library without the Mail Helper: https://github.com/sendgrid/sendgrid-nodejs#without-mail-helper-class

The Mail Helper is a first version and we are dependent on feedback like yours to help us improve the next version. Do you mind sharing the specific issues you ran into so we can create tickets to fix them?

Also, I'm adding your vote to our backlog task to improve the Mail Helper to help move it up the queue. Thanks!

@adambuczynski,

I see that your awesomeness continues :)

I'm looking forward to your feedback, I know it will be great!

@julesbou,

We have started a USE_CASES.md file where we will provide examples for common use cases. The first was transaction templates :)

I'm looking forward to your feedback, I know it will be great!

👻

@thinkingserious Sure! Give me a day or so and I'll provide you a concrete example.

This is one of my favorite threads here :)

I think your feedback is going to be critical for the next iteration of this library, which will likely be a breaking change. If you have a moment, could you please take a look at our proposal? https://github.com/sendgrid/sendgrid-nodejs/issues/290

Thanks!

@thinkingserious I finally came to understand what bothers me so much about all the Sendgrid helper classes introduced in v3... there's just too many of them and they make sending emails unnecessarily complicated :P

I didn't want to overhaul all of Sendgrid's code, so I decided to make a simple wrapper instead, sendgrid-mailer.

While Sendgrid's helper classes are useful behind the scenes, to create and validate email models, I don't think they should be forced upon us just to interact with the API. I find it makes writing Node apps more complicated than it should be, and the naming of the helpers can be quite confusing, e.g. Mail vs Email.

I think the basic example illustrates the problem well:

https://github.com/sendgrid/sendgrid-nodejs#with-mail-helper-class

This just seems way too convoluted for something as basic as "I want to send an email". It requires you to load 8 different dependencies and write 15 lines of code before you can send an email.

Not using the helper classes is arguably even worse though, as then you have to know the required structure of the email JSON request and populate it correctly yourself. One mistake will break the request easily.

So I abstracted all the helper classes logic away behind a simple, easy to use API helper, so that sending emails with Sendgrid becomes easy and fun again :)

The most basic example becomes:

//Load mailer and set API key (only needed once)
const mailer = require('sendgrid-mailer').config(API_KEY);

//Create email data 
const email = {
  to: '[email protected]',
  from: 'Someone <[email protected]>',
  subject: 'Hello world',
  text: 'Hello plain world!',
  html: '<p>Hello HTML world!</p>',
};

//Send away 
mailer.send(email); //Returns promise 

The request is constructed behind the scenes and uses Sendgrid's helpers transparently.

It also supports sending multiple emails, exposes the Sendgrid object in case further customisation is needed, accepts Sendgrid Mail instances as well, and accepts a wide variety of formats for the to/from fields, e.g. Name <[email protected]> or an object with name/email properties. See the README for more details.

There's probably still room for improvement, but I wanted to share this solution here for people looking to use the Sendgrid API without being overwhelmed by it.

Hope it comes in handy for someone, and maybe it can provide some direction for the future of this library, in exposing a simpler API :)

cc @julesbou a bit late, but I hope you can still find a use for it.

@adamreisnz seriously, thank you.

My background job that sends emails via sendgrid was using v4.0.7, which was already complicated enough. I upgraded to the latest version and sadly everything broke. I was so frustrated with how complex it was to send an email that I didn't even bother updating.

All that said, the simplicity of this is exactly what I was looking for. I'll give it a shot over the weekend.

Cheers!

@jamesdixon I was still stuck on v3.x.x and also didn't want to upgrade :)

Hopefully you will find it useful. Let me know how it works for you and if there's anything critical missing, thanks!

@adamreisnz,

This is AWESOME and thanks for sharing it with our community :)

tldr; our helper classes are not finished and you already beat us to where we are headed :)

We totally agree with your thoughts and this is the phase of the v3 library update we are currently working on across all 7 of our SDKs. Please see the Mail Helper Enhancement project here for more detail on the Node.js project.

Currently, we are finishing up the C# library. To get a concrete example of what we plan for this Node.js library, please see this. We are hoping to release that one within days.

So, I have a proposal:

Would you mind adding your solution into the Mail Helper here (similar to what we have done in C#)? I think your work would achieve a greater reach in that case. If not, no problem at all. Since you have posted your project under the MIT license I would hope you would forgive us if we borrowed from your good work a bit ;) And of course, we will make sure you get credit for your contributions.

If you decide to keep your work separate, could you please add a link here under the Community Libraries section?

Thanks for your amazing continued support. You rock!

@thinkingserious glad to hear you're thinking of heading the same way. The C# example looks better, but I think still somewhat convoluted for a JS implementation. Making everything classes is not necessarily always the best way to go in JS.

As for your proposal, since I'm still using the helper classes under the hood, I don't know how much of it would actually be useful to port into the official library. I would probably be more inclined to help with a major rewrite (e.g. v5) and start from fresh, taking input from users and the Node community and come up with an awesome API that is both easy to use yet flexible if needed.

But if in the mean time you want to borrow some of the code from my wrapper to use in the official library, by all means go for it :) I'll add the link to my wrapper in that page 🎉

I actually think that it might be a good idea to split the project into two packages, one lower level that includes the request logic for interacting with the API, and one higher level just for sending emails, similar to what I've created.

The lower level package could be used for people needing full access to the API, while the mailer package would be suitable for people just wanting to send emails. And of course under the hood, the mailer would use the API package.

@thinkingserious what are your thoughts on that?

@adamreisnz,

Thank you for the thoughtful feedback, as always.

I'm not sure if we need to split the project at this point. I think we can achieve similar results with the current helper model. That said, I'm open to exploring a new architecture in a future version and I would love to have you on board for that.

Hopefully some others in the community will weigh in.

I will think deeper on this and re-engage when it comes time to implement the updated Mail Helper.

Thanks again for your support and for adding the link to our docs!

It's a matter of separating concerns. You could include a helper/wrapper for easy mailing inside the main library, but in my opinion that's mixing two separate concerns to the likely detriment or needless complication of both. Keeping them separate will allow consumers to pick the library they need (or both), and would probably make development of both libraries easier because of the clearly defined boundaries.

I think it's the nature of Node and npm modules to have as much separation of concerns as possible, and have each package do a specific task (and be good at it!). It's something I've had to get used to as well when I first started with Node, but once I did, I liked the freedom and ability to develop clean, simple packages that it provides.

Look forward to hear some comments from the community.

Well, you are definitely persuasive with a solid argument :)

I really appreciate you taking the time to elaborate.

I was already looking forward to coming back to working on this library and now you've got me really excited to get back to working on this SDK with you and community.

Great, let me know when you're going to work on it :)
I'm on leave for 4 weeks so that means I'll finally have some spare time to do some work on open source projects as well 🌴

@adamreisnz,

Congrats!

A much higher level "mailer package" is exactly what we'd want. Just need to send some emails.

With the last version it was dead simple, and now it's an unholy mess. "Basic" example currently has 8 vars defined, requiring special constructors. Huge step backward in terms of ease of use.

Should be as simple as

sendgrid.send({
  from: fromEmail,
  fromName: fromName,
  to: toEmail,
  subject: 'Hello'
  ...
})
.then(...)
.catch(...)
.finally(...)

Zero docs are needed, just an example we can copy paste and on our way. That's the primary value of SendGrid to our team, and unfortunately it is now missing.

@adamreisnz Looks great; very obvious design decisions. I hope you are on the SendGrid payroll!

@thinkingserious please seriously consider pointing the main docs to @adamreisnz project, as it simplifies everything significantly, and if I had known about it earlier today, it would've saved me hours of headache trying to use what imho is an overengineered, Java-inspired api in JS. Please give your users what they want, not what you think they want :-)

Also, just to note that I wouldn't be surprised if other devs start ending up here more frequently now that the V2 API has been sunset, and new accounts will be forced to use the V3 API. Personally, the first thing I tried was using nodemailer (whose sendgrid transport still works w/ v2) which has made sense for dozens of projects in the past but took forever to get working this time around. If I had to guess given the popularity of nodemailer in the node community, I won't be the only one who ends up feeling this way in the coming months.

@fisch0920 thanks for your feedback, I'll be working together with @thinkingserious in the coming weeks to improve the Sendgrid API. Hopefully we'll come up with something that is easy to use but still flexible enough when needed.

Hello Everyone,

I thought you might be interested in the re-design on the SendGrid Node.js Mail Helper: https://github.com/sendgrid/sendgrid-nodejs/pull/378

Your feedback would be greatly appreciated!

With Best Regards,

Elmer

Please move to #378 for updates, thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Loriot-n picture Loriot-n  ·  4Comments

metalshan picture metalshan  ·  3Comments

danielflippance picture danielflippance  ·  4Comments

kiranshashiny picture kiranshashiny  ·  4Comments

umarhussain15 picture umarhussain15  ·  3Comments