Darian Hickman
Darian Hickman

Reputation: 909

How to use SendGrid from Google App Engine Golang?

The example code at: https://sendgrid.com/blog/send-email-go-google-app-engine/

My guess this is very old sample code to use sendgrid-go on Google App Engine.

I've attempted 4 permutations and failed each time with:

https://api.sendgrid.com/v3/mail/send: http.DefaultTransport and http.DefaultClient are not available in App Engine. See https://cloud.google.com/appengine/docs/go/urlfetch/

Here is a minimum hardcoded attempt with some logging:

package sendgridgo


import(
    "github.com/sendgrid/sendgrid-go"
    "fmt"
    _"google.golang.org/appengine"
    "net/http"
    "google.golang.org/appengine/log"
    "google.golang.org/appengine"
    "google.golang.org/appengine/urlfetch"
     _ "github.com/sendgrid/sendgrid-go/helpers/mail"
)

func init(){
    http.HandleFunc("/", IndexHandler)
    appengine.Main()
}

func IndexHandler (w http.ResponseWriter, r *http.Request){
    ctx := appengine.NewContext(r)

    log.Infof(ctx, "IndexHandler")
    sg := sendgrid.NewSendClient("SENDGRID_API_KEY")
    log.Infof(ctx, "%v", sg)
    bob := urlfetch.Client(ctx)

    log.Infof(ctx, "UrlFetchClient %v", bob)
    //resp, err := sg.Send(m)
    request := sendgrid.GetRequest("SENDGRID_API_KEY", "/v3/mail/send", "https://api.sendgrid.com")
    request.Method = "POST"

    request.Body = []byte(` {
    "personalizations": [
        {
            "to": [
                {
                    "email": "[email protected]"
                }
            ],
            "subject": "Sending with SendGrid is Fun"
        }
    ],
    "from": {
        "email": "[email protected]"
    },
    "content": [
        {
            "type": "text/plain",
            "value": "and easy to do anywhere, even with Go"
        }
    ]
}`)
    resp, err := sendgrid.API(request)

    if err != nil{
        log.Errorf(ctx, "Failed %v", err)
    }

    fmt.Fprint(w, resp)


}

Upvotes: 1

Views: 912

Answers (4)

Jack
Jack

Reputation: 10613

SendGrid document the minimum needed code to send an email with Go on AppEngine in How to send email using Go. It differs from @birju-prajapati's answer; the client is created with sendgrid.NewSendClient.

An API key must be generated and your development environment updated with your SENDGRID_API_KEY. Then install sendgrid-go and its dependencies with go get github.com/sendgrid/sendgrid-go.

// using SendGrid's Go Library
// https://github.com/sendgrid/sendgrid-go
package main

import (
    "fmt"
    "log"
    "os"

    "github.com/sendgrid/sendgrid-go"
    "github.com/sendgrid/sendgrid-go/helpers/mail"
)

func main() {
    from := mail.NewEmail("Example User", "[email protected]")
    subject := "Sending with SendGrid is Fun"
    to := mail.NewEmail("Example User", "[email protected]")
    plainTextContent := "and easy to do anywhere, even with Go"
    htmlContent := "<strong>and easy to do anywhere, even with Go</strong>"
    message := mail.NewSingleEmail(from, subject, to, plainTextContent, htmlContent)
    client := sendgrid.NewSendClient(os.Getenv("SENDGRID_API_KEY"))
    response, err := client.Send(message)
    if err != nil {
        log.Println(err)
    } else {
        fmt.Println(response.StatusCode)
        fmt.Println(response.Body)
        fmt.Println(response.Headers)
    }
}

Upvotes: 0

Birju Prajapati
Birju Prajapati

Reputation: 11

The solution is documented in sendgrid.go:

// DefaultClient is used if no custom HTTP client is defined
var DefaultClient = rest.DefaultClient

So just do this at the beginning of your send, where ctx is appengine.NewContext(req):

sendgrid.DefaultClient = &rest.Client{HTTPClient: urlfetch.Client(ctx)}

Upvotes: 1

Darian Hickman
Darian Hickman

Reputation: 909

After 8 different attempts, including trying an example published in Google Cloud docs for using Sendgrid, an example from Sendgrid blog, and trying to use deprecated versions of Sendgrid api, I found Sendgrid curl examples at:

https://sendgrid.com/docs/Classroom/Send/v3_Mail_Send/curl_examples.html

curl --request POST \
  --url https://api.sendgrid.com/v3/mail/send \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"personalizations": [{"to": [{"email": "[email protected]"}]}],"from": {"email": "[email protected]"},"subject": "Hello, World!","content": [{"type": "text/plain", "value": "Heya!"}]}'

I then translated the HelloWorld example to into URLFetch usage

client := urlfetch.Client(ctx)

request, err := http.NewRequest("POST", "https://api.sendgrid.com/v3/mail/send", buf)
    if err != nil {
        log.Errorf(ctx, "Failed Request %v", request)
    }
    request.Header.Set("Authorization", "Bearer SENDGRID_API_KEY")
    request.Header.Set("Content-Type", "application/json")
    resp, err := client.Do(request)

One Easter weekend, later, it works!

Upvotes: 2

vishnu narayanan
vishnu narayanan

Reputation: 3923

You were on the right track, but skipped overriding the default sendgrid client with urlfetch client.

.
.
.
func IndexHandler (w http.ResponseWriter, r *http.Request){
    ctx := appengine.NewContext(r)    
    sg := sendgrid.NewSendClient("REPLACE_WITH_API_KEY")
    bob := urlfetch.Client(ctx)

    sg.Client = bob

    request := sendgrid.GetRequest("REPLACE_WITH_API_KEY", "/v3/mail/send", "https://api.sendgrid.com")
    request.Method = "POST"
.
.
.

Explanation

The error occurs as sendgrid tries to fetch a url with the default net/http method.

Quoting AppEngine Documentation

App Engine uses the URL Fetch service to issue outbound HTTP(S) requests. To issue an outbound HTTP request, use the http package as usual, but create your client using urlfetch.Client. urlfetch.Client returns an *http.Client that uses urlfetch.Transport, which is an implementation of the http.RoundTripper interface that makes requests using the URL Fetch API.

The workaround is to override the Sendgrid client to use urlfetch

        context := appengine.NewContext(r)
        sg := sendgrid.NewSendClient(os.Getenv("SENDGRID_API_KEY"))
        sg.Client = urlfetch.Client(context)

References

  1. GCloud Documentation- Issuing HTTP(S) Requests in App Engine Golang

Upvotes: 1

Related Questions