avatarJavier Perez

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

3376

Abstract

andler:</span> <span class="hljs-string">bootstrap</span> <span class="hljs-attr">package:</span> <span class="hljs-attr">artifact:</span> <span class="hljs-string">./bin/tracking-lambda/tracking-lambda.zip</span> <span class="hljs-attr">events:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">http:</span> <span class="hljs-comment">#v1 => REST API . v2 => HTTP API => httpApi</span> <span class="hljs-attr">path:</span> <span class="hljs-string">/pixel</span> <span class="hljs-attr">method:</span> <span class="hljs-string">get</span> <span class="hljs-attr">cors:</span> <span class="hljs-literal">true</span></pre></div><p id="1d96"><b>Lambda Function </b>The Lambda function is surprisingly concise, as its primary function is to return a 1x1 gif. However, even in its simplicity, there are essential considerations to ensure its seamless operation. Firstly, make sure to obtain the AWS Lambda packages for Go.</p><div id="a95d"><pre>go get github.com/aws/aws-lambda-go/events github.com/aws/aws-lambda-go/lambda</pre></div><p id="37d2">Second, make sure you send your response in base64 encoding and that you send the <b>IsBase64Encoded </b>key as true. That being said here is the code I used.</p><div id="4864"><pre><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> ( <span class="hljs-string">"bytes"</span> <span class="hljs-string">"context"</span> <span class="hljs-string">"encoding/base64"</span> <span class="hljs-string">"image"</span> <span class="hljs-string">"image/color"</span> <span class="hljs-string">"image/gif"</span> <span class="hljs-string">"os"</span> <span class="hljs-string">"time"</span>

<span class="hljs-string">"log/slog"</span>

<span class="hljs-string">"github.com/aws/aws-lambda-go/events"</span> <span class="hljs-string">"github.com/aws/aws-lambda-go/lambda"</span> )

<span class="hljs-keyword">var</span> logHandler = slog.NewTextHandler(os.Stdout, <span class="hljs-literal">nil</span>).WithAttrs([]slog.Attr{slog.String(<span class="hljs-string">"pkg"</span>, <span class="hljs-string">"main"</span>)}) <span class="hljs-keyword">var</span> logger = slog.New(logHandler)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> { ctx, c := context.WithTimeout(context.Background(), time.Second*<span class="hljs-number">30</span>) <span class="hljs-keyword">defer</span> c()

lambda.StartWithOptions(HandleRequest, lambda.WithContext(ctx)) }

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">HandleRequest</span><span class="hljs-params">(ctx context.Context, event events.APIGatewayProxyRequest)</span></span> (*events.APIGatewayProxyResponse, <span class="hljs-type">error</span>) { logger.Info(<span class="hljs-string">"An Email Was Opened"</span>) <span class="hljs-keyword">if</span> v, ok := event.Headers[<span class="hljs-string">"user-agent"</span>]; ok { logger.Info(<span class="hljs-string">"From:"</span>, slog.String(<span class="hljs-string">"user-agent"</span>, v)) }

<span class="hljs-comment">// Email ID</span> logger.Info(<span class="hljs-string">"Email ID:"</span>, slog.String(<span class="hljs-string">"email-id"</span>, event.Quer

Options

yStringParameters[<span class="hljs-string">"email-id"</span>])) logger.Info(<span class="hljs-string">"path:"</span>, slog.String(<span class="hljs-string">"path"</span>, event.Path))

<span class="hljs-comment">// 1 x 1 pixel</span> img := image.NewRGBA(image.Rect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>)) img.Set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, color.RGBA{<span class="hljs-number">255</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">255</span>}) <span class="hljs-keyword">var</span> buff bytes.Buffer

<span class="hljs-keyword">if</span> err := gif.Encode(&buff, img, <span class="hljs-literal">nil</span>); err != <span class="hljs-literal">nil</span> {

logger.Error(<span class="hljs-string">"failed to encode pixel."</span>, slog.String(<span class="hljs-string">"error"</span>, err.Error()))

<span class="hljs-keyword">return</span> &events.APIGatewayProxyResponse{ StatusCode: <span class="hljs-number">200</span>, Headers: <span class="hljs-keyword">map</span>[<span class="hljs-type">string</span>]<span class="hljs-type">string</span>{ <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>, <span class="hljs-string">"Access-Control-Allow-Origin"</span>: <span class="hljs-string">""</span>, }, IsBase64Encoded: <span class="hljs-literal">false</span>, }, <span class="hljs-literal">nil</span> } <span class="hljs-comment">// reason to send image as base64</span> <span class="hljs-comment">/ API Gateway typically expects data in a text format, and base64 encoding is a convenient way to represent binary data */</span> <span class="hljs-comment">// out 1x1 pixel as a base64 string.</span> response := base64.StdEncoding.EncodeToString(buff.Bytes())

<span class="hljs-keyword">return</span> &events.APIGatewayProxyResponse{ StatusCode: <span class="hljs-number">200</span>, Headers: <span class="hljs-keyword">map</span>[<span class="hljs-type">string</span>]<span class="hljs-type">string</span>{ <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"image/gif"</span>, }, Body: response, IsBase64Encoded: <span class="hljs-literal">true</span>, }, <span class="hljs-literal">nil</span> }</pre></div><p id="c8d3">I hope this helps. Bye 👋</p><p id="5412">Repository where you can find this code: <a href="https://github.com/japb1998/tracking-demo">https://github.com/japb1998/tracking-demo</a></p><h1 id="a505">Stackademic</h1><p id="3db4">Thank you for reading until the end. Before you go:</p><ul><li>Please consider <b>clapping</b> and <b>following</b> the writer! 👏</li><li>Follow us <a href="https://twitter.com/stackademichq"><b>X</b></a><b> | <a href="https://www.linkedin.com/company/stackademic">LinkedIn</a> | <a href="https://www.youtube.com/c/stackademic">YouTube</a> | <a href="https://discord.gg/in-plain-english-709094664682340443">Discord</a></b></li><li>Visit our other platforms: <a href="https://plainenglish.io"><b>In Plain English</b></a><b> | <a href="https://cofeed.app/">CoFeed</a> | <a href="https://venturemagazine.net/">Venture</a></b></li></ul></article></body>

Step-by-Step Guide: Creating a 1x1 Pixel with Golang, AWS Lambda, API Gateway

In my recent endeavor of developing a booking app using Lambda, Go, and the Serverless framework, I encountered the need to track when a reminder email was opened. After conducting some research, the most straightforward solution appeared to be utilizing a 1x1 pixel. Fortunately, implementing this solution was relatively simple, especially with Golang’s image package.

Allow me to share the code with you, along with some essential considerations you should keep in mind.

Serverless.yml

The Serverless framework has been a steadfast companion throughout my entire serverless journey, empowering me to craft infrastructure as code and deploy fully functional APIs within minutes. To achieve this seamlessly with the Serverless framework, there are a few key configurations to set up.

Firstly, it’s crucial to enable the binaryMediaTypes option for API Gateway. This ensures that the service treats specific media types as binaries, paving the way for smoother interactions.

Secondly, for those working with Golang, it’s essential to note that the go1.x runtime is deprecated. Instead, opt for the provided.al2 runtime. Additionally, to execute your Golang code successfully, ensure that you name your executable file "bootstrap."

With these considerations in mind, let me share my serverless.yml file.

service: tracking-pixel-golang

frameworkVersion: '3'

provider:
  name: aws
  runtime: provided.al2
  architecture: arm64
  versionFunctions: false
  stage: ${opt:stage, 'dev'}
  region: ${opt:region, 'us-east-1'}
  apiGateway:
    binaryMediaTypes:
      - "*/*"
  custom:
    deploymentBucket:
      name: ${self:service}-${self:provider.stage}-artifacts
      serverSideEncryption: AES256



package:
  individually: true

functions:
  tracking-lambda:
    handler: bootstrap
    package:
      artifact: ./bin/tracking-lambda/tracking-lambda.zip
    events:
      - http: #v1 => REST API . v2 => HTTP API => httpApi
          path: /pixel
          method: get
          cors: true

Lambda Function The Lambda function is surprisingly concise, as its primary function is to return a 1x1 gif. However, even in its simplicity, there are essential considerations to ensure its seamless operation. Firstly, make sure to obtain the AWS Lambda packages for Go.

go get github.com/aws/aws-lambda-go/events github.com/aws/aws-lambda-go/lambda

Second, make sure you send your response in base64 encoding and that you send the IsBase64Encoded key as true. That being said here is the code I used.

package main

import (
 "bytes"
 "context"
 "encoding/base64"
 "image"
 "image/color"
 "image/gif"
 "os"
 "time"

 "log/slog"

 "github.com/aws/aws-lambda-go/events"
 "github.com/aws/aws-lambda-go/lambda"
)

var logHandler = slog.NewTextHandler(os.Stdout, nil).WithAttrs([]slog.Attr{slog.String("pkg", "main")})
var logger = slog.New(logHandler)

func main() {
 ctx, c := context.WithTimeout(context.Background(), time.Second*30)
 defer c()

 lambda.StartWithOptions(HandleRequest, lambda.WithContext(ctx))
}

func HandleRequest(ctx context.Context, event events.APIGatewayProxyRequest) (*events.APIGatewayProxyResponse, error) {
 logger.Info("An Email Was Opened")
 if v, ok := event.Headers["user-agent"]; ok {
  logger.Info("From:", slog.String("user-agent", v))
 }

 // Email ID
 logger.Info("Email ID:", slog.String("email-id", event.QueryStringParameters["email-id"]))
 logger.Info("path:", slog.String("path", event.Path))

 // 1 x 1 pixel
 img := image.NewRGBA(image.Rect(0, 0, 1, 1))
 img.Set(0, 0, color.RGBA{255, 0, 0, 255})
 var buff bytes.Buffer

 if err := gif.Encode(&buff, img, nil); err != nil {

  logger.Error("failed to encode pixel.", slog.String("error", err.Error()))

  return &events.APIGatewayProxyResponse{
   StatusCode: 200,
   Headers: map[string]string{
    "Content-Type":                "application/json",
    "Access-Control-Allow-Origin": "*",
   },
   IsBase64Encoded: false,
  }, nil
 }
 // reason to send image as base64
 /*
  API Gateway typically expects data in a text format, and base64 encoding is a convenient way to represent binary data
 */
 // out 1x1 pixel as a base64 string.
 response := base64.StdEncoding.EncodeToString(buff.Bytes())

 return &events.APIGatewayProxyResponse{
  StatusCode: 200,
  Headers: map[string]string{
   "Content-Type": "image/gif",
  },
  Body:            response,
  IsBase64Encoded: true,
 }, nil
}

I hope this helps. Bye 👋

Repository where you can find this code: https://github.com/japb1998/tracking-demo

Stackademic

Thank you for reading until the end. Before you go:

AWS
Golang
Serverless Framework
Recommended from ReadMedium