node.jsazuresendgridazure-functions-node.js

Using SendGrid with Azure node.js programming model v4


My goal is to send email from an Azure Static Web App contact page using an Azure Function. I have managed to get that working as described below, so my question is "how can this be done better in Azure Functions?"

Here is my working code:

const { app } = require("@azure/functions");
const mta = require("@sendgrid/mail");

app.http("sendGrid", {
  methods: ["GET", "POST"],
  authLevel: "anonymous",
  handler: async (request, context) => {
    const key = process.env.SENDGRID_API_KEY;
    var response = 'unexpected state'
    var code = 200;

    // Authenticate
    mta.setApiKey(key);

    // Timestamp for this invocation
    const timestamp = new Date(Date.now());

    // Build a message
    const content = {
      to: "user@domain", /* <== replace with your target email address */
      from: "user@domain", /* <== replace with authenticated sender address */
      subject: `SendGrid message ${timestamp}`,
      text: `SendGrid message body text ${timestamp}`,
      html: `SendGrid message body html <strong>${timestamp}</strong>`
    };

    await mta.send(content)
            .then(() => { 
                response = `message ${timestamp} sent`;
                code = 200;
            })
            .catch((thrown) => { 
                response = `message not sent ${thrown}`;
                code = 500;
            });
    
    context.log(response);

    return { status: code, body: response };
  }
});

Solution

  • To send email from an Azure Static Web App using an Azure Function of v4 you have to add frontend app to call the API endpoint. I used _src/index.html_ file to fetch the text from the API function and display it on the screen which acts like no framework .

    I used this MSDOC to add an API to Azure Static Web Apps with Azure Functions .

    Below simple HTML code is used to trigger/display the results Azure Function for sending an email using SendGrid.

    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>API Response Display</title>
        <style>
            body {
                font-family: Arial, sans-serif;
                margin: 20px;
            }
            .response {
                margin-top: 20px;
                padding: 20px;
                border: 1px solid #ccc;
                border-radius: 5px;
            }
            .success {
                background-color: #e6ffed;
                border-color: #a3d9a5;
            }
            .failure {
                background-color: #ffe6e6;
                border-color: #d9a5a5;
            }
        </style>
    </head>
    <body>
        <h1>API Response Display</h1>
        <button id="fetchButton">Fetch API Response</button>
        <div id="responseContainer" class="response"></div>
    
        <script>
            document.getElementById('fetchButton').addEventListener('click', async () => {
                const responseContainer = document.getElementById('responseContainer');
                responseContainer.innerHTML = 'Fetching...';
    
                try {
                    const response = await fetch('/api/httpTrigger');
                    const data = await response.json();
    
                    if (response.ok) {
                        responseContainer.innerHTML = `
                            <div class="success">
                                <h2>Success</h2>
                                <pre>${JSON.stringify(data, null, 2)}</pre>
                            </div>
                        `;
                    } else {
                        responseContainer.innerHTML = `
                            <div class="failure">
                                <h2>Failure</h2>
                                <pre>${JSON.stringify(data, null, 2)}</pre>
                            </div>
                        `;
                    }
                } catch (error) {
                    responseContainer.innerHTML = `
                        <div class="failure">
                            <h2>Error</h2>
                            <pre>${error}</pre>
                        </div>
                    `;
                }
            });
        </script>
    </body>
    </html>
    
    

    Git Folder Strucure:

    enter image description here

     app_location to  "./src" 
      api_location to  "./api1"
     output_location to  "." 
    

    Refer to this SO for yml workflow configuration file in Static Web App. Deployment Status

    enter image description here

    The endpoint of the function app must have the /api prefix, since Static Web Apps matches requests made to /api. The existing Azure Functions app exposes an endpoint via the following sample example Url https:/nameofazuresataticwebapp.azurewebsites.net/api/functionname as show in the below image.

    Azure Functions app with Azure Static Output:

    enter image description here

    enter image description here

    Azure Static Output: enter image description here

    enter image description here