<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: zahaar</title>
    <description>The latest articles on DEV Community by zahaar (@zahaar).</description>
    <link>https://dev.to/zahaar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F609030%2F84710290-bef1-4460-a439-b6157006decf.jpeg</url>
      <title>DEV Community: zahaar</title>
      <link>https://dev.to/zahaar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zahaar"/>
    <language>en</language>
    <item>
      <title>Generate PDFs from HTML via Puppeteer on AWS Lambda + API Gateway</title>
      <dc:creator>zahaar</dc:creator>
      <pubDate>Mon, 25 Apr 2022 01:45:00 +0000</pubDate>
      <link>https://dev.to/zahaar/generate-pdfs-from-html-via-puppeteer-on-aws-lambda-api-gateway-2h86</link>
      <guid>https://dev.to/zahaar/generate-pdfs-from-html-via-puppeteer-on-aws-lambda-api-gateway-2h86</guid>
      <description>&lt;p&gt;&lt;em&gt;“Evil cannot create anything new, they can only corrupt and ruin what good forces have invented or made.”&lt;/em&gt; - JRR Tolkien.&lt;/p&gt;

&lt;h1&gt;
  
  
  Preface
&lt;/h1&gt;

&lt;p&gt;Would it be great to have the functionality that would enable you to generate PDF files using HTML &amp;amp;&amp;amp; CSS capabilities without the need to rely on overly complex drivers that are dependent on a whole bunch of C &lt;a href="https://wkhtmltopdf.org/libwkhtmltox/" rel="noopener noreferrer"&gt;libraries&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;While also &lt;a href="https://github.com/wkhtmltopdf/wkhtmltopdf/issues/3661" rel="noopener noreferrer"&gt;supporting&lt;/a&gt; all the latest features of HTML5 &amp;amp;&amp;amp; CSS3?&lt;/p&gt;

&lt;p&gt;Well, we have great news. There is a framework called &lt;a href="https://github.com/puppeteer/puppeteer" rel="noopener noreferrer"&gt;Puppeteer&lt;/a&gt; that uses relatively new Chrome &lt;a href="https://developers.google.com/web/updates/2017/04/headless-chrome" rel="noopener noreferrer"&gt;feature&lt;/a&gt; and makes it accessible though a NodeJS based API.&lt;/p&gt;

&lt;p&gt;Essentially what &lt;strong&gt;Puppeteer&lt;/strong&gt; does, is: Launches a Chromium browser instance in a headless mode ( not actually opening it ), and allows us to manipulate the browser via set of API command to parse website, retrieve images and generate PDF as if you were actually opening an HTML file in the latest browser version, etc..&lt;/p&gt;

&lt;p&gt;While we can create a running &lt;a href="https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#running-puppeteer-in-docker" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; Puppeteer instance and deploy that on ECS or Heroku. The creation of stable &amp;amp;&amp;amp; optimized image can be quite challenging...&lt;/p&gt;

&lt;p&gt;Having a running instance in AWS Lambda &lt;em&gt;IHMO&lt;/em&gt; in contrast would be much simpler in terms of development speed, debug and monitoring. Besides, serverless, is a nice concept for POC ( you pay for what you use )&lt;/p&gt;

&lt;h1&gt;
  
  
  Repo -&amp;gt; End Result
&lt;/h1&gt;

&lt;p&gt;You can see the complete working example in this &lt;a href="https://github.com/zahaar/generate-pdf-lambda" rel="noopener noreferrer"&gt;repo&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/zahaar" rel="noopener noreferrer"&gt;
        zahaar
      &lt;/a&gt; / &lt;a href="https://github.com/zahaar/generate-pdf-lambda" rel="noopener noreferrer"&gt;
        generate-pdf-lambda
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a href="https://github.com/zahaar/generate-pdf-lambda./LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/6cd0120cc4c5ac11d28b2c60f76033b52db98dac641de3b2644bb054b449d60c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e737667" alt="License: MIT"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Generate PDF document via Puppeteer running on AWS Lambda&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;This repo contains a serverless application that takes a HTML template and return a PDF in form of a binary&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Diagram&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/zahaar/generate-pdf-lambda./docs/LambdaPdf.drawio.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fzahaar%2Fgenerate-pdf-lambda.%2Fdocs%2FLambdaPdf.drawio.png" alt="Diagram"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Requirements&lt;/h1&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/get-docker/" rel="nofollow noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html" rel="nofollow noopener noreferrer"&gt;AWS SAM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html" rel="nofollow noopener noreferrer"&gt;AWS Console Account &amp;amp;&amp;amp; API Keys&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;How to Run&lt;/h1&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Clone this repo &lt;code&gt;git clone https://github.com/zahaar/generate-pdf-lambda&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Import &lt;a href="https://github.com/zahaar/generate-pdf-lambda./cUrl.txt" rel="noopener noreferrer"&gt;cUrl&lt;/a&gt; to &lt;a href="https://insomnia.rest/" rel="nofollow noopener noreferrer"&gt;Insomnia&lt;/a&gt; ( Postman is not recommended, as it can't visualize Pdf ).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run &lt;code&gt;make api-local&lt;/code&gt; to have local API GW running.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Send &lt;code&gt;cUrl&lt;/code&gt; request via Insomnia.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;You can also invoke Lambda bypassing API GW, by supplying an example event in &lt;a href="https://github.com/zahaar/generate-pdf-lambda/events/api-gw-event.json" rel="noopener noreferrer"&gt;file&lt;/a&gt;, and running &lt;code&gt;make invokation-local&lt;/code&gt;. The response would be a base64 encoded PDF binary.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;How to Deploy&lt;/h1&gt;

&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;A configured AWS CLI V2 is a must -&amp;gt; &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html" rel="nofollow noopener noreferrer"&gt;AWS Console Account &amp;amp;&amp;amp; API Keys&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;make deploy&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fetch AWS SAM deploy output URL &lt;code&gt;Value&lt;/code&gt;, and change the Url in Insomnia from &lt;code&gt;localhost&lt;/code&gt; to that value
&lt;em&gt;execution result in&lt;/em&gt;…&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/zahaar/generate-pdf-lambda" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Requirements and Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html" rel="noopener noreferrer"&gt;AWS SAM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html" rel="noopener noreferrer"&gt;AWS Console Account &amp;amp;&amp;amp; API Keys&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1. SetUp local AWS SAM Template with Chrome Lambda Layer
&lt;/h3&gt;

&lt;p&gt;In this step the local SAM execution setUp will be complete. Once this is done, we will have a strong reference point.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The end version of this step can be fetched from &lt;a href="https://github.com/zahaar/generate-pdf-lambda/commits/1_local-setup" rel="noopener noreferrer"&gt;&lt;code&gt;1_local-setup&lt;/code&gt;&lt;/a&gt; branch&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can create a basic SAM template by running &lt;code&gt;sam init&lt;/code&gt; or reference a &lt;a href="https://dev.to/zahaar/snss3api-aws-lambda-opinionated-boilerplate-cookicutter-8f3"&gt;guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;but our end goal should be a sophisticated structure like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

├── Makefile
├── VERSION -- for VERSION tracking, helpful for CI
├── envs.json -- to sep envs for local execution ( if necessary )
├── events
│   └── api-gw-event.json -- an example API GW event for local execution
├── src
│   └── app.js -- main source code file
└── template.yaml -- AWS SAM configuration template


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;code&gt;app.js&lt;/code&gt; contains simple code that will return the same &lt;code&gt;event.body&lt;/code&gt; that it receives from example event.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;while &lt;code&gt;template.yml&lt;/code&gt; has a resource configuration for API GW Service&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...
...
  ApiGatewayApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Staging
    BinaryMediaTypes:
      - application~1pdf  // Note the support for binary pdf media Type
...


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;and the Lambda. As per context of our goal, it's called &lt;code&gt;PdfFunction&lt;/code&gt;&lt;br&gt;
Take note of the &lt;code&gt;Layer&lt;/code&gt; being used in this config. By setting &lt;code&gt;chrome-aws-lambda&lt;/code&gt;, we have essentially ruled out the need to set &lt;code&gt;package.json&lt;/code&gt; dependencies for &lt;code&gt;puppeteer&lt;/code&gt; and &lt;code&gt;chrome&lt;/code&gt; on &lt;code&gt;Docker&lt;/code&gt; image thar AWS is using on EC2 for Lambdas, as this step can be quite challenging.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...
...
  PdfFunction:
    Type: AWS::Serverless::Function
    Description: Invoked by EventBridge scheduled rule
    Properties:
      CodeUri: src/
      Handler: app.handler
      Runtime: nodejs12.x
      Timeout: 15
      MemorySize: 3008

      Layers:
        - !Sub 'arn:aws:lambda:${AWS::Region}:764866452798:layer:chrome-aws-lambda:22'
      Environment:
        Variables:
          EXAMPLE_ENV: 'CHANGE_THIS'
      Events:
        ApiEvent:
          Type: Api
          Properties:
            Path: /pdf
            Method: post
            RestApiId:
              Ref: ApiGatewayApi
...


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;To test that all requirements are met, let's run a local event.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

make invokation-local


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The output should be essentially the same as the execution logs in CloudWatch&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...
...
Mounting /Users/wparker/Dev/scheduled-website-screenshot-app/.aws-sam/build/PdfFunction as /var/task:ro,delegated inside runtime container
START RequestId: e4d7743d-5be2-4735-84c8-9d5160d9a750 Version: $LATEST
...


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  2. Configure Puppeteer in Lambda; Supply Template HTML
&lt;/h3&gt;

&lt;p&gt;Next step is to program &lt;code&gt;app.js&lt;/code&gt; to start &lt;code&gt;puppeteer&lt;/code&gt;, consume &lt;code&gt;HTML&lt;/code&gt; from an &lt;code&gt;API GW&lt;/code&gt; event and return a &lt;code&gt;base64&lt;/code&gt; encoded response that would be &lt;code&gt;decoded&lt;/code&gt; on &lt;code&gt;Response&lt;/code&gt; by &lt;code&gt;API GW&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The end version of this step can be fetched from &lt;a href="https://github.com/zahaar/generate-pdf-lambda/tree/2_generate-pdf" rel="noopener noreferrer"&gt;&lt;code&gt;2_generate-pdf&lt;/code&gt;&lt;/a&gt; branch&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We need to change the Lambda handler code to something like this. &lt;a href="https://github.com/zahaar/generate-pdf-lambda/blob/76f18b4e4973d47416ee06183309b80e34bdf3c9/src/app.js" rel="noopener noreferrer"&gt;&lt;code&gt;File&lt;/code&gt;&lt;/a&gt; ( File is too long to displayed here )&lt;/p&gt;

&lt;p&gt;Key takeaways are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Browser launch args parameters in this example are set specifically for AWS Lambda compatibility.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;defaultViewport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultViewport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ignoreHTTPSErrors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;The return format goal was set to mimic A4 document.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setViewport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;deviceScaleFactor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;isLandscape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;The response headers are set for &lt;code&gt;pdf&lt;/code&gt; file transfer. &lt;code&gt;isBase64Encoded&lt;/code&gt; flag is set to &lt;code&gt;true&lt;/code&gt; to inform &lt;code&gt;API GW&lt;/code&gt; that it needs to decode the file.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Methods&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET, POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Disposition&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attachment; filename="foo.pdf"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;isBase64Encoded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;To test this code, an &lt;code&gt;HTML&lt;/code&gt; template is needed. We will use this open-source &lt;a href="https://github.com/sparksuite/simple-html-invoice-template" rel="noopener noreferrer"&gt;one&lt;/a&gt; for demonstration.&lt;br&gt;
The document is being sent as body with &lt;code&gt;'Content-Type: text/html'&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please note &lt;code&gt;'Accept: application/pdf'&lt;/code&gt;, this is important.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The end result of &lt;code&gt;cUrl&lt;/code&gt; request is in this &lt;a href="https://github.com/zahaar/generate-pdf-lambda/blob/76f18b4e4973d47416ee06183309b80e34bdf3c9/cUrl.txt" rel="noopener noreferrer"&gt;file&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To test our result let's start local SAM in local &lt;code&gt;start-api&lt;/code&gt; mode. ( akin to a server, contrary to one time invokation)&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

make api-local


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Import &lt;code&gt;cUrl&lt;/code&gt; into &lt;code&gt;Insomnia&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;em&gt;&lt;strong&gt;Result&lt;/strong&gt;&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frnjmh22yf4363ml7mo9e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frnjmh22yf4363ml7mo9e.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Deploy Lambda + API GW via SAM
&lt;/h3&gt;

&lt;p&gt;Refers to step in &lt;a href="https://github.com/zahaar/generate-pdf-lambda/blob/e11413b680f4c86b45789e938c81402508478900/README.md" rel="noopener noreferrer"&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/a&gt; on &lt;code&gt;main&lt;/code&gt; branch&lt;/p&gt;
&lt;h1&gt;
  
  
  Tips
&lt;/h1&gt;
&lt;h5&gt;
  
  
  1. Insomnia vs Postman
&lt;/h5&gt;

&lt;p&gt;Instead of playing tricks with &lt;a href="https://github.com/postmanlabs/postman-app-support/issues/8040" rel="noopener noreferrer"&gt;Postman PDF Visualization&lt;/a&gt;. I highly recommend switching to &lt;a href="https://insomnia.rest/" rel="noopener noreferrer"&gt;Insomnia&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Insomnia Visualized Response&lt;/th&gt;
&lt;th&gt;Postman Visualization Response&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxa1ugbrjn96xittax7ro.png" alt="Insomnia Visualized Response"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F99y0polvmqzuif1pdox8.png" alt="Postman Visualization Response"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h5&gt;
  
  
  2. &lt;code&gt;Error: Error building docker image: pull access denied for&lt;/code&gt;
&lt;/h5&gt;


&lt;div class="ltag__stackexchange--container"&gt;
  &lt;div class="ltag__stackexchange--title-container"&gt;
    
      &lt;div class="ltag__stackexchange--title"&gt;
        &lt;div class="ltag__stackexchange--header"&gt;
          &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fstackoverflow-logo-b42691ae545e4810b105ee957979a853a696085e67e43ee14c5699cf3e890fb4.svg" alt=""&gt;
          &lt;a href="https://stackoverflow.com/questions/65720417/how-to-pull-aws-lambda-container-image/65721111#65721111" rel="noopener noreferrer"&gt;
            &lt;span class="title-flare"&gt;answer&lt;/span&gt; re: How to pull AWS Lambda container image
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="ltag__stackexchange--post-metadata"&gt;
          &lt;span&gt;Jan 14 '21&lt;/span&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;a class="ltag__stackexchange--score-container" href="https://stackoverflow.com/questions/65720417/how-to-pull-aws-lambda-container-image/65721111#65721111" rel="noopener noreferrer"&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fstackexchange-arrow-up-eff2e2849e67d156181d258e38802c0b57fa011f74164a7f97675ca3b6ab756b.svg" alt=""&gt;
        &lt;div class="ltag__stackexchange--score-number"&gt;
          52
        &lt;/div&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fstackexchange-arrow-down-4349fac0dd932d284fab7e4dd9846f19a3710558efde0d2dfd05897f3eeb9aba.svg" alt=""&gt;
      &lt;/a&gt;
    
  &lt;/div&gt;
  &lt;div class="ltag__stackexchange--body"&gt;
    
&lt;p&gt;Works fine here. You shouldn't need credentials for Public ECR (you &lt;em&gt;can&lt;/em&gt; use auth for &lt;a href="https://docs.aws.amazon.com/AmazonECR/latest/public/public-registries.html" rel="noreferrer noopener"&gt;specific cases&lt;/a&gt;) but if you just want to consume it, remove the existing credentials&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker logout public.ecr.aws
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and then try the build again.&lt;/p&gt;
&lt;p&gt;That said, if you still want to make use of the…&lt;/p&gt;
    
  &lt;/div&gt;
  &lt;div class="ltag__stackexchange--btn--container"&gt;
    &lt;a href="https://stackoverflow.com/questions/65720417/how-to-pull-aws-lambda-container-image/65721111#65721111" class="ltag__stackexchange--btn" rel="noopener noreferrer"&gt;Open Full Answer&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  3. Consider using latest AWS Lambda feature: &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/urls-configuration.html" rel="noopener noreferrer"&gt;Lambda URL&lt;/a&gt;
&lt;/h5&gt;

&lt;h5&gt;
  
  
  4. Be mindful of HTTP Headers, when dealing with API GW. As this can lead to a huge &lt;a href="https://github.com/twitchtv/twirp/issues/81" rel="noopener noreferrer"&gt;confusion&lt;/a&gt;.
&lt;/h5&gt;

</description>
      <category>lambda</category>
      <category>puppeteer</category>
      <category>pdf</category>
      <category>aws</category>
    </item>
    <item>
      <title>SNS/S3/API AWS Lambda opinionated boilerplate cookicutter</title>
      <dc:creator>zahaar</dc:creator>
      <pubDate>Fri, 31 Dec 2021 14:21:05 +0000</pubDate>
      <link>https://dev.to/zahaar/snss3api-aws-lambda-opinionated-boilerplate-cookicutter-8f3</link>
      <guid>https://dev.to/zahaar/snss3api-aws-lambda-opinionated-boilerplate-cookicutter-8f3</guid>
      <description>&lt;p&gt;The goal of this article is to share and explain, a very basic and straightforward, &lt;code&gt;AWS Lambda&lt;/code&gt; template for &lt;code&gt;GoLang&lt;/code&gt;, based on &lt;a href="https://github.com/cookiecutter/cookiecutter" rel="noopener noreferrer"&gt;&lt;code&gt;cookiecutter&lt;/code&gt;&lt;/a&gt; templating capabilities.&lt;/p&gt;

&lt;h1&gt;
  
  
  Table of content:
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;What it is&lt;/li&gt;
&lt;li&gt;How to use&lt;/li&gt;
&lt;li&gt;What it is Not&lt;/li&gt;
&lt;li&gt;Worth mentioning&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What this template &lt;strong&gt;is&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This is a so-called "tightly coupled" template, it was build with the purpose of mainly satisfying these goals:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Template that can be &lt;strong&gt;executed/tested&lt;/strong&gt; locally, withoud deploying the Lambda to the Cloud, by running Docker image with AWS &lt;a href="https://github.com/aws/serverless-application-model" rel="noopener noreferrer"&gt;SAM&lt;/a&gt; framework&lt;/li&gt;
&lt;li&gt;Abitlity to chose an example event, which can be: &lt;code&gt;SNS&lt;/code&gt;, &lt;code&gt;S3&lt;/code&gt; or &lt;code&gt;API Gateway&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Template that generates all scaffolding files: LICENSE, AUTHORS, Makefile, SAM template, pre-commit, VERSION, gitlab CI example jobs, envs.json&lt;/li&gt;
&lt;li&gt;An opionated template that ideally is meant to be testable locally in Docker encapsulated environment to provice the developer an ability to provide a test event ( &lt;code&gt;./events/event.json&lt;/code&gt; ), an used it to develop Lambda fast by executing it locally numerous times&lt;/li&gt;
&lt;li&gt;Minimalistic code example (aka "Hello Works"), with backpack full of batteries included, and minimise the need for an additional configuration as much as possible&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to use
&lt;/h2&gt;

&lt;p&gt;Here is the &lt;code&gt;repo&lt;/code&gt;: &lt;a href="https://github.com/zahaar/go-lambda-cookiecutter" rel="noopener noreferrer"&gt;https://github.com/zahaar/go-lambda-cookiecutter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/zahaar/go-lambda-cookiecutter" rel="noopener noreferrer"&gt;README.md&lt;/a&gt; contains all core instructions and prerequisites needed to use this cookicutter&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
...
First, get Cookiecutter. Trust me, it's awesome::

    $ pip install "cookiecutter&amp;gt;=1.7.0"

Now run it against this repo::
...
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbldiilgzxy5b7jqg1t73.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbldiilgzxy5b7jqg1t73.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What this template is &lt;strong&gt;Not&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;No deployment to AWS, leave it to Terraform, Claud Formation or other IaaC tool of your preference &lt;/li&gt;
&lt;li&gt;Not a complex example. A complex example would complicate things, and negatively influence initial understanding of the structure. Therefore SNS, S3 and API example, contain a simple print-out as an output.&lt;/li&gt;
&lt;li&gt;It's hard to simulate complex scenarios locally with AWS. P.e: the complexity of AWS SAM template will scale tremendously if we would include a complex chain of Lambdas that invoke one another. KISS.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Worth mentioning
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Don't treat &lt;code&gt;go&lt;/code&gt; serverless programm any different from other programs that you write
&amp;gt; CC: Go Time Podcast, Serverless and Go&lt;/li&gt;
&lt;li&gt;Take great care about business logic, and it's application. How it will shape Lambda code.&lt;/li&gt;
&lt;li&gt;Serverless code should be small, minimalistic. Something in a sense of 50 lines microservice&lt;/li&gt;
&lt;li&gt;Striving for Cloud Agnostic serverless code ( e.g something that will work on GPC, Azure and AWS simultaneously ) , &lt;strong&gt;Doesn't make sense&lt;/strong&gt;. That why we aim only for AWS Lambda
&amp;gt; CC: Go Time Podcast, Serverless and Go&lt;/li&gt;
&lt;li&gt;Why use GoLang for AWS Lambda: &lt;a href="https://github.com/nzoschke/gofaas/blob/master/docs/intro-go-faas.md#why-go" rel="noopener noreferrer"&gt;link&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Collection of cookiecutter from AWS SAM: &lt;a href="https://github.com/aws/aws-sam-cli-app-templates" rel="noopener noreferrer"&gt;link&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Testing, unit testing, integration testing with AWS SAM is at this moment the best things that industry can offer. IMHO/ &lt;/li&gt;
&lt;li&gt;In case of failing to pull/build Docker image, refer to this &lt;a href="https://stackoverflow.com/questions/41379808/authorization-token-has-expired-issue-aws-cli-on-macos-sierra" rel="noopener noreferrer"&gt;thread&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>lambda</category>
      <category>aws</category>
      <category>go</category>
    </item>
    <item>
      <title>pdb | ipdb in docker-compose, enable interactive debugging</title>
      <dc:creator>zahaar</dc:creator>
      <pubDate>Fri, 25 Jun 2021 08:29:44 +0000</pubDate>
      <link>https://dev.to/zahaar/pdb-ipdb-in-docker-compose-enable-interactive-debugging-1goj</link>
      <guid>https://dev.to/zahaar/pdb-ipdb-in-docker-compose-enable-interactive-debugging-1goj</guid>
      <description>&lt;p&gt;&lt;strong&gt;&lt;code&gt;pdb&lt;/code&gt;&lt;/strong&gt; is a powerful debugging tool that is included as a Python standard library. It can seriously speed up, our debug process, while resolving certain bugs&lt;/p&gt;

&lt;p&gt;for this &lt;/p&gt;

&lt;h2&gt;
  
  
  Problematic
&lt;/h2&gt;

&lt;p&gt;Trying to initiate the &lt;code&gt;pdb&lt;/code&gt; in your &lt;code&gt;docker-compose&lt;/code&gt; container will result in the following error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;...
...
    &lt;span class="k"&gt;return &lt;/span&gt;self.view_functions[rule.endpoint]&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;**&lt;/span&gt;req.view_args&lt;span class="o"&gt;)&lt;/span&gt;
  File &lt;span class="s2"&gt;"/backend/app.py"&lt;/span&gt;, line 25, &lt;span class="k"&gt;in &lt;/span&gt;test_response
    &lt;span class="k"&gt;return &lt;/span&gt;response
  File &lt;span class="s2"&gt;"/backend/app.py"&lt;/span&gt;, line 25, &lt;span class="k"&gt;in &lt;/span&gt;test_response
    &lt;span class="k"&gt;return &lt;/span&gt;response
  File &lt;span class="s2"&gt;"/usr/local/lib/python3.9/bdb.py"&lt;/span&gt;, line 88, &lt;span class="k"&gt;in &lt;/span&gt;trace_dispatch
    &lt;span class="k"&gt;return &lt;/span&gt;self.dispatch_line&lt;span class="o"&gt;(&lt;/span&gt;frame&lt;span class="o"&gt;)&lt;/span&gt;
  File &lt;span class="s2"&gt;"/usr/local/lib/python3.9/bdb.py"&lt;/span&gt;, line 113, &lt;span class="k"&gt;in &lt;/span&gt;dispatch_line
    &lt;span class="k"&gt;if &lt;/span&gt;self.quitting: raise BdbQuit
bdb.BdbQuit

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;that happens only if you try to run &lt;code&gt;pdb&lt;/code&gt; in &lt;code&gt;docker-compose&lt;/code&gt; and not via &lt;code&gt;docker run ...&lt;/code&gt;, due to &lt;code&gt;up&lt;/code&gt; not being an interactive command by design.&lt;br&gt;&lt;br&gt;
more on this topic: &lt;a href="https://github.com/docker/compose/issues/4677"&gt;https://github.com/docker/compose/issues/4677&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;you can try in out yourself:&lt;/p&gt;

&lt;p&gt;step 1.  clone the repo setup repo with the minimal &lt;code&gt;Flask&lt;/code&gt; app in &lt;code&gt;docker-compose&lt;/code&gt;. &lt;br&gt;
&lt;code&gt;https://github.com/zahaar/docker-pdb-interactive-debugging/&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
step 2. &lt;code&gt;cd docker-pdb-interactive-debugging&lt;/code&gt;&lt;br&gt;
step 3. checkout on &lt;code&gt;docker-error&lt;/code&gt; branch&lt;br&gt;
&lt;code&gt;git checkout docker-error&lt;/code&gt;&lt;br&gt;
step 4. run &lt;code&gt;docker compose up&lt;/code&gt;, or &lt;code&gt;docker-compose up&lt;/code&gt; if you are using old docker CLI version&lt;br&gt;
step 5. in a separate shell run &lt;code&gt;curl&lt;/code&gt; command.&lt;br&gt;&lt;br&gt;
&lt;code&gt;curl -XGET 'http://localhost:5000/api/v1.0/the_answer'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;you would see the expected &lt;code&gt;pdb.DdbQuit&lt;/code&gt; exception error:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
...
  File "/usr/local/lib/python3.9/bdb.py", line 113, in dispatch_line
    if self.quitting: raise BdbQuit
bdb.BdbQuit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pdb&lt;/code&gt; expects an open TTY, so let's add it to our &lt;code&gt;compose&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
...
    ports:
      - '5000:5000'
    stdin_open: true # für pdb
    tty: true # für pdb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Let's try recreating the steps:&lt;/p&gt;

&lt;p&gt;Step 1. &lt;code&gt;git clone https://github.com/zahaar/docker-pdb-interactive-debugging/&lt;/code&gt;, but on a &lt;code&gt;main&lt;/code&gt; branch this time&lt;br&gt;
Step 2. &lt;code&gt;docker compose up&lt;/code&gt;&lt;br&gt;
Step 3. In a separate shell window attach local input to a running container, by running.&lt;br&gt;&lt;br&gt;
&lt;code&gt;docker attach docker-interractive-debugging_api_1 // container name&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note we have this capability only after opening an TTY for our container&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Step 4. In a separate shell window run.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -XGET 'http://localhost:5000/api/v1.0/the_answer'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Step 5. now in the window where container output was &lt;code&gt;attached&lt;/code&gt; you would see, &lt;code&gt;pdb&lt;/code&gt; waiting for commands.&lt;br&gt;&lt;br&gt;
you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;p true_response&lt;/code&gt; to print the value of the variable &lt;code&gt;true_response&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;c&lt;/code&gt; to resume normal flow&lt;/li&gt;
&lt;li&gt;to all other crazy things with &lt;code&gt;pdb&lt;/code&gt; !!!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it. Easy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important Note&lt;/strong&gt;: to stop attached window without stopping the &lt;strong&gt;main&lt;/strong&gt; running container, you have to use,&lt;br&gt;&lt;br&gt;
escape sequence: &lt;code&gt;CTRL + p -&amp;gt; CTRL + q&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Tip:
&lt;/h2&gt;

&lt;p&gt;checkout &lt;code&gt;pdb&lt;/code&gt; commands cheatsheet &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



</description>
      <category>pdb</category>
      <category>python</category>
      <category>docker</category>
    </item>
    <item>
      <title>git workree, quick intro ( simplify your git workflow! )</title>
      <dc:creator>zahaar</dc:creator>
      <pubDate>Sun, 30 May 2021 15:59:11 +0000</pubDate>
      <link>https://dev.to/zahaar/git-workree-quick-intro-simplify-your-git-workflow-j0k</link>
      <guid>https://dev.to/zahaar/git-workree-quick-intro-simplify-your-git-workflow-j0k</guid>
      <description>&lt;p&gt;Here is a fast example of how to use &lt;code&gt;git worktree&lt;/code&gt;, it's extremely helpful and at the same time extremely unpopular &lt;code&gt;git&lt;/code&gt; feature.&lt;/p&gt;

&lt;p&gt;Surprisingly it has been around for ages, but no one seems to know about, yet fall in love on first try!!!&lt;/p&gt;

&lt;p&gt;I, personally discovered it from one of Guido's, tweets:&lt;br&gt;
&lt;/p&gt;
&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--IB2812X9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/424495004/GuidoAvatar_normal.jpg" alt="Guido van Rossum profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Guido van Rossum
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @gvanrossum
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Why did I not know before about "git worktree"?  :-(  &lt;a href="https://t.co/wBM6uO2DX8"&gt;git-scm.com/docs/git-workt…&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      20:27 PM - 07 Apr 2021
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1379893622145871873" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1379893622145871873" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1379893622145871873" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Problematic
&lt;/h2&gt;

&lt;p&gt;to present a solution, we have to understand the problem it tries to solve, before jumping to a solution. Here is a simple scenario:&lt;/p&gt;

&lt;p&gt;You are trying to work on your own feature for some time. Meanwhile, a coworker writes to you, with a request to review his work, that he has done on a &lt;strong&gt;separate&lt;/strong&gt; branch of the same repo.&lt;/p&gt;

&lt;p&gt;Most likely, your next steps would be:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1&lt;/strong&gt; - save your work in &lt;code&gt;stash&lt;/code&gt; with command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git stash save &amp;lt;name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2&lt;/strong&gt; - &lt;code&gt;checkout&lt;/code&gt; to the branch that coworker wants you to review&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout feature-coworkers-branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3&lt;/strong&gt; - there might be a need to install the dependencies, with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; req.txt
// or 
yarn 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4&lt;/strong&gt; - after browsing though the branch, and finishing with the review, you would want to revert back to the branch that you have worker on&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout feature-your-old-branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5&lt;/strong&gt; - &lt;strong&gt;HIDERANCE&lt;/strong&gt; 1: you have to find &amp;amp;&amp;amp; &lt;strong&gt;unstash&lt;/strong&gt; your code. You can run, &lt;code&gt;git stash pop/apply&lt;/code&gt;, in case the last stash that you have saved, is the one you need&lt;/p&gt;

&lt;p&gt;you might need to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git stash list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git stash pop stash&lt;span class="o"&gt;{&lt;/span&gt;0&lt;span class="o"&gt;}&lt;/span&gt; 
// or 
git stash apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;please note that git stash apply won't delete the stashed item from the stash list&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;to look up for just the right &lt;code&gt;stash&lt;/code&gt;, saving stash with  helps a lot. But still sometimes, in a heat of the moment, while having multiple work processes saved in stash, we get lost, apply the the wrong &lt;code&gt;stash&lt;/code&gt; and then another one... &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6&lt;/strong&gt; - &lt;strong&gt;HINDERANCE&lt;/strong&gt; 2: you might encounter a problem of dependency conflict. The dependencies that were installed in &lt;code&gt;feature-coworkers-branch&lt;/code&gt;, might hinder your work, due to requiring different versions, demanding different configuration etc... The solution would be to delete/reinstall them, but that's once again an annoying process.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: A different workflow, might be cloning a repo in a different folder, doing the review there and discarding it afterwards. But what about the time it would take to install all the dependencies? &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All in all, that's an uncomfortable situation to be in. We have encountered it frequently, it consumes our time and mental power.&lt;/p&gt;

&lt;p&gt;Luckily, &lt;code&gt;git&lt;/code&gt; have a better way&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;worktree&lt;/code&gt; is wonderful alternative, let's try to follow the same steps using this feature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1&lt;/strong&gt; - While working on your feature, use &lt;code&gt;worktree&lt;/code&gt; command to create a new folder/directory of the same repo, and specify which branch you want to start with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree add ../repo_test feature-42-update-mat-view
&lt;span class="c"&gt;# git worktree add &amp;lt;path&amp;gt; &amp;lt;optional:starting-branch-name&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and BOOM, now you are in a separate folder/directory, on a &lt;code&gt;feature-42-update-mat-view&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2&lt;/strong&gt; - Install all the dependencies necessary to check the work&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; req.txt
// or 
yarn 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3&lt;/strong&gt; - Write to your colleague, remove &lt;code&gt;rm -rdf repo_test&lt;/code&gt; / &lt;code&gt;git worktree remove ../repo_test&lt;/code&gt; ( or keep it there ), and get back to folder containing your own work progress.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If a working tree is deleted without using &lt;code&gt;git worktree remove&lt;/code&gt;, then its associated administrative files, which reside in the repository (see "DETAILS" below), will eventually be removed automatically ...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's it!!!&lt;/p&gt;

&lt;p&gt;No need to &lt;code&gt;stash&lt;/code&gt; the code, revert installed dependencies and dig through &lt;code&gt;stash list&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This way is not only faster, but also, much less error prone! &lt;br&gt;
Having errorless &lt;code&gt;git&lt;/code&gt; workflow is especially important to keep the concentration on your work.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tips &amp;amp;&amp;amp; References
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1&lt;/strong&gt; - Official &lt;code&gt;git&lt;/code&gt; &lt;a href="https://git-scm.com/docs/git-worktree"&gt;documentation&lt;/a&gt; is very good&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2&lt;/strong&gt; - If you create a worktree with intention to make some experimental changes, pass &lt;code&gt;-d&lt;/code&gt; flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree add &lt;span class="nt"&gt;-d&lt;/span&gt; ../repo_test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this quote from &lt;code&gt;git worktree&lt;/code&gt; &lt;a href="https://git-scm.com/docs/git-clone#_description"&gt;docs&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For instance, &lt;code&gt;git worktree add -d &amp;lt;path&amp;gt;&lt;/code&gt; creates a new working tree with a detached HEAD at the same commit as the current branch.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;3&lt;/strong&gt; - Another good workflow would be is to start off by cloning a &lt;code&gt;bare&lt;/code&gt; repo. Then use &lt;code&gt;worktree add ...&lt;/code&gt; and have a list of your &lt;strong&gt;worktrees&lt;/strong&gt; in a &lt;strong&gt;bare&lt;/strong&gt; repo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--bare&lt;/span&gt; url...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;git &lt;a href="https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---bare"&gt;--bare&lt;/a&gt; docs&lt;/p&gt;

</description>
      <category>git</category>
      <category>worktree</category>
      <category>stash</category>
    </item>
  </channel>
</rss>
