<?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: Gabi Dobocan</title>
    <description>The latest articles on DEV Community by Gabi Dobocan (@gabidobo).</description>
    <link>https://dev.to/gabidobo</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%2F342265%2F90d76537-5e55-4049-a0be-d6d0d1ea4ca4.jpeg</url>
      <title>DEV Community: Gabi Dobocan</title>
      <link>https://dev.to/gabidobo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gabidobo"/>
    <language>en</language>
    <item>
      <title>The Better Npm Audit 🪱</title>
      <dc:creator>Gabi Dobocan</dc:creator>
      <pubDate>Tue, 21 Mar 2023 12:20:57 +0000</pubDate>
      <link>https://dev.to/sandworm/the-better-npm-audit-g03</link>
      <guid>https://dev.to/sandworm/the-better-npm-audit-g03</guid>
      <description>&lt;p&gt;It's been almost two years since Dan Abramov wrote his controversial article on how &lt;code&gt;npm audit&lt;/code&gt; is &lt;a href="https://overreacted.io/npm-audit-broken-by-design/"&gt;broken by design&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;It makes experienced app developers miserable&lt;/strong&gt; because they have to either waste time doing obviously unnecessary work, or fight with their security departments trying to explain how &lt;code&gt;npm audit&lt;/code&gt; is a broken tool unsuitable for real security audits &lt;em&gt;by design&lt;/em&gt;.&lt;br&gt;
Two years - yet not much has changed when it comes to the number of false positives you get from &lt;code&gt;npm audit&lt;/code&gt; - false positives that have made JavaScript developers learn to ignore all security warnings in the console when installing new packages. Ironically, making poor audit results part of the default output when installing packages has had a reverse effect, by effectively training developers to ignore security warnings they can't easily understand or address.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  It's hard to enforce security rules
&lt;/h2&gt;

&lt;p&gt;So let's say you're not that guy. You care about security and see the threat in blindly relying on hundreds - often thousands - of packages with 3rd party code. You don't currently have the budget for the better-known services out there, but you still want to build security into the core of your application and your workflows as much as possible.&lt;/p&gt;

&lt;p&gt;So you add &lt;code&gt;npm audit --audit-level=high&lt;/code&gt; to your CI pipeline to enforce "no builds and/or deploys when there are high or critical vulnerabilities". Then you install some dependencies, write some code, and push an update. The build fails because &lt;a href="https://github.com/advisories/GHSA-36jr-mh4h-2g58"&gt;&lt;code&gt;d3-color&amp;lt;3.1.0&lt;/code&gt; is vulnerable to ReDoS&lt;/a&gt;. It's high severity so you have a look, and it turns out you're not using any of the options that can allow malicious input to &lt;a href="https://github.com/d3/d3-color/pull/100"&gt;make some RegExp explode&lt;/a&gt;. Great, there's nothing to address here! But what about fixing the build now? 🤔&lt;/p&gt;

&lt;h2&gt;
  
  
  Not all issues are resolved with code
&lt;/h2&gt;

&lt;p&gt;Ok, so maybe you remove the audit step from the CI for now but start using &lt;a href="https://github.com/dependabot"&gt;Dependabot&lt;/a&gt;. It surfaces the same vulnerabilities that your package manager would, directly from &lt;a href="https://github.com/advisories"&gt;GitHub's Advisory Database&lt;/a&gt;. It makes PRs with fixes, when available, and you can dismiss alerts that don't apply by selecting one of a few possible reasons:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--skXRt6pz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1679094191780/d00069d8-e907-4a18-a26e-834cd18fd911.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--skXRt6pz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1679094191780/d00069d8-e907-4a18-a26e-834cd18fd911.png" alt="Dependabot dismiss options. You can't specify more than this." width="880" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You quickly realize, though, that &lt;code&gt;npm audit&lt;/code&gt; will still display issues you've dismissed with Dependabot. Devs in your team installing new dependencies will still be ignoring the console warnings, and will probably not realize they're bringing in new vulnerabilities until the next Dependabot scan.&lt;/p&gt;

&lt;p&gt;The summary of the security research you're doing with validating all issues before dismissing them is now hopefully stored in a separate documentation repository that people in your team can access - because if it's not, people in the future won't know how or why a specific issue has been dismissed as "risk is tolerable". How has the risk been assessed, and what does "tolerable" mean? That knowledge might be siloed.&lt;/p&gt;

&lt;p&gt;Also, anyone who depends on your code is now seeing the same new vulnerability reports from Dependabot that you are getting. Every one of your users who cares about security is now re-doing your research, looking at your code and how you interact with the vulnerable library, before hopefully choosing to dismiss the issue from their repository. Some might just switch to a different library along the way. There's no way to inform the people that care about your security findings &amp;amp; resolutions.&lt;/p&gt;

&lt;p&gt;And yeah, those automated fixes aren't always great... 😅&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hICDoSGa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://programmerhumor.io/wp-content/uploads/2022/11/programmerhumor-io-debugging-memes-javascript-memes-68768ffceb17351.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hICDoSGa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://programmerhumor.io/wp-content/uploads/2022/11/programmerhumor-io-debugging-memes-javascript-memes-68768ffceb17351.png" alt="Thanks npm" width="862" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Not all issues are security advisories
&lt;/h2&gt;

&lt;p&gt;Ok, you're now ready to raise some money and take your app to the next level! As part of the due diligence process, the lawyers ask you for a list of all your dependencies, including license info for each. This is when you realize you should have been paying attention to licenses all along. You scramble to figure out what SPDX and OSI are. You think your backend repositories are mostly safe since you don't distribute that code, but then you find out about &lt;a href="https://www.fsf.org/bulletin/2021/fall/the-fundamentals-of-the-agplv3"&gt;Network Protective licenses&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A tool to automate and enforce license compliance for your entire team would be nice!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;At this point, you've maybe found IBM's &lt;a href="https://github.com/IBM/audit-ci"&gt;audit-ci&lt;/a&gt;, which can help with allowlisting advisory IDs, but doesn't audit for any other types of issues except reported security vulnerabilities (same for &lt;a href="https://github.com/jeemok/better-npm-audit"&gt;better-npm-audit&lt;/a&gt;, or &lt;a href="https://github.com/naugtur/npm-audit-resolver"&gt;npm-audit-resolver&lt;/a&gt;);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/davglass/license-checker"&gt;license-checker&lt;/a&gt; is pretty popular, but only scans licenses and is &lt;a href="https://github.com/davglass/license-checker/issues/245"&gt;not maintained anymore&lt;/a&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/oss-review-toolkit/ort"&gt;ort&lt;/a&gt; scans for both vulnerabilities &amp;amp; licenses, but it's heavy and intimidating (Kotlin knowledge required), and doesn't include a way to dismiss alerts.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Meanwhile, you're stuck explaining to everyone on your team about this new license compliance thing they need to look out for.&lt;/p&gt;

&lt;p&gt;Plus, have you recently tried getting &lt;code&gt;npm&lt;/code&gt; to tell you which of your dependencies is &lt;em&gt;deprecated&lt;/em&gt;? It only gives off warnings about that when you install new packages and you probably ignore it then. But you should care about deprecations too, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Sandworm 🪱
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/sandworm-hq"&gt;
        sandworm-hq
      &lt;/a&gt; / &lt;a href="https://github.com/sandworm-hq/sandworm-audit"&gt;
        sandworm-audit
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Security &amp;amp; License Compliance For Your App's Dependencies 🪱
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;

  
  
  &lt;img alt="Sandworm Audit" src="https://res.cloudinary.com/practicaldev/image/fetch/s--BJZi_YcP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/sandworm-hq/sandworm-auditlogo-dark.png" width="478"&gt;

&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Beautiful Security &amp;amp; License Compliance Reports For Your App's Dependencies 🪱&lt;/p&gt;
&lt;h2&gt;
Summary&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Free &amp;amp; open source command-line tool&lt;/li&gt;
&lt;li&gt;Works with any modern JavaScript package manager&lt;/li&gt;
&lt;li&gt;Scans your project &amp;amp; dependencies for vulnerabilities, license, and misc issues&lt;/li&gt;
&lt;li&gt;Supports &lt;a href="https://docs.sandworm.dev/audit/resolving-issues" rel="nofollow"&gt;marking issues as resolved&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Supports &lt;a href="https://docs.sandworm.dev/audit/license-policies" rel="nofollow"&gt;custom license policies&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.sandworm.dev/audit/fail-policies" rel="nofollow"&gt;Configurable fail conditions&lt;/a&gt; for CI / GIT hook workflows&lt;/li&gt;
&lt;li&gt;Outputs
&lt;ul&gt;
&lt;li&gt;JSON issue &amp;amp; license usage reports&lt;/li&gt;
&lt;li&gt;Easy to grok SVG dependency tree &amp;amp; treemap visualizations
&lt;ul&gt;
&lt;li&gt;Powered by D3&lt;/li&gt;
&lt;li&gt;Overlays security vulnerabilities&lt;/li&gt;
&lt;li&gt;Overlays package license info&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CSV of all dependencies &amp;amp; license info&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
Generate a report&lt;/h3&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/3314b677529b1332702a9fd237c25f1caeebf26455bf27daa59d753ddb68407a/68747470733a2f2f6173736574732e73616e64776f726d2e6465762f73686f77636173652f61756469742d7465726d696e616c2d6f75747075742e676966"&gt;&lt;img src="https://camo.githubusercontent.com/3314b677529b1332702a9fd237c25f1caeebf26455bf27daa59d753ddb68407a/68747470733a2f2f6173736574732e73616e64776f726d2e6465762f73686f77636173652f61756469742d7465726d696e616c2d6f75747075742e676966" alt="Running Sandworm Audit"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
Navigate charts&lt;/h3&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/50f8594d5b8e88fa09908cb9b85a9e4c4657f6a87175b3e9f28c57418aad3570/68747470733a2f2f6173736574732e73616e64776f726d2e6465762f73686f77636173652f747265656d61702d616e642d747265652e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/50f8594d5b8e88fa09908cb9b85a9e4c4657f6a87175b3e9f28c57418aad3570/68747470733a2f2f6173736574732e73616e64776f726d2e6465762f73686f77636173652f747265656d61702d616e642d747265652e706e67" alt="Sandworm treemap and tree dependency charts"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
CSV output&lt;/h3&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/51d66d4f0e37b4f00950dced01e527ec3065a351fe2620201a10abd9f9469e2f/68747470733a2f2f6173736574732e73616e64776f726d2e6465762f73686f77636173652f6373762d736e69702e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/51d66d4f0e37b4f00950dced01e527ec3065a351fe2620201a10abd9f9469e2f/68747470733a2f2f6173736574732e73616e64776f726d2e6465762f73686f77636173652f6373762d736e69702e706e67" alt="Sandworm dependency CSV"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
JSON output&lt;/h3&gt;
&lt;div class="highlight highlight-source-json notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;{
  &lt;span class="pl-ent"&gt;"createdAt"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;...&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
  &lt;span class="pl-ent"&gt;"packageManager"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;...&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
  &lt;span class="pl-ent"&gt;"name"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;...&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
  &lt;span class="pl-ent"&gt;"version"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;...&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
  &lt;span class="pl-ent"&gt;"rootVulnerabilities"&lt;/span&gt;: [&lt;span class="pl-ii"&gt;...&lt;/span&gt;],
  &lt;span class="pl-ent"&gt;"dependencyVulnerabilities"&lt;/span&gt;: [&lt;span class="pl-ii"&gt;...&lt;/span&gt;],
  &lt;span class="pl-ent"&gt;"licenseUsage"&lt;/span&gt;: {&lt;span class="pl-ii"&gt;...&lt;/span&gt;},
  &lt;span class="pl-ent"&gt;"licenseIssues"&lt;/span&gt;: [&lt;span class="pl-ii"&gt;...&lt;/span&gt;],
  &lt;span class="pl-ent"&gt;"metaIssues"&lt;/span&gt;: [&lt;span class="pl-ii"&gt;...&lt;/span&gt;],
  &lt;span class="pl-ent"&gt;"errors"&lt;/span&gt;: [&lt;span class="pl-ii"&gt;...&lt;/span&gt;],&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/sandworm-hq/sandworm-audit"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Sandworm Audit is a command-line tool designed to help with all of your auditing woes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It's free &amp;amp; open source!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It lets you customize and own your security workflow&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It works with any modern JavaScript package manager&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It scans your project &amp;amp; dependencies for &lt;a href="https://docs.sandworm.dev/audit/issue-types"&gt;vulnerabilities, license, and misc issues&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It supports &lt;a href="https://docs.sandworm.dev/audit/resolving-issues"&gt;marking issues as resolved&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It supports &lt;a href="https://docs.sandworm.dev/audit/license-policies"&gt;custom license policies&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It has &lt;a href="https://docs.sandworm.dev/audit/fail-policies"&gt;configurable fail conditions&lt;/a&gt; for running in CI / GIT hooks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Product of the day on Product Hunt!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.producthunt.com/posts/sandworm?utm_source=badge-top-post-badge&amp;amp;utm_medium=badge&amp;amp;utm_souce=badge-sandworm"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8t-HjQb2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://api.producthunt.com/widgets/embed-image/v1/top-post-badge.svg%3Fpost_id%3D380800%26theme%3Dneutral%26period%3Ddaily" alt="Sandworm - Keep your JavaScript code secure and compliant with Sandworm | Product Hunt" width="250" height="54"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Running a Sandworm audit will generate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A JSON report with all issue &amp;amp; license usage information;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easy to grok SVG dependency tree &amp;amp; treemap visualizations;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A CSV list of all project dependencies &amp;amp; license info.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3U4-X00u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://sandworm.dev/images/audit-terminal-output.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3U4-X00u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://sandworm.dev/images/audit-terminal-output.gif" alt="Sandworm output" width="880" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zMghZyCO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.sandworm.dev/showcase/treemap-and-tree.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zMghZyCO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.sandworm.dev/showcase/treemap-and-tree.png" alt="Sandworm treemap and tree charts" width="880" height="607"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qPH-Y62c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.sandworm.dev/showcase/csv-snip.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qPH-Y62c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.sandworm.dev/showcase/csv-snip.png" alt="Sandworm CSV output" width="880" height="185"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Build security &amp;amp; compliance into your app's core, today
&lt;/h2&gt;

&lt;p&gt;With Sandworm, you're now able to take ownership of your security workflow, just like you always wanted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You add Sandworm to your CI to enforce security rules. You make a configuration file that you commit to your repo, and define a custom fail policy within it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You also add a permitted license policy to the configuration file, specifying which licenses are ok and which should trigger issues that need to be investigated. Everyone in the team sees what the rules are - you're all on the same page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You create a &lt;code&gt;resolved-issues.json&lt;/code&gt; file in your repo, and tell your team to use &lt;code&gt;sandworm resolve&lt;/code&gt; to dismiss issues that are not addressable by writing code. All resolutions are recorded in the JSON file and your git history, allowing anyone to historically track who resolved what, when, and why.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Updates to the resolution file fit right into your existing code PR approval workflow.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You share your resolution file with your users, to inform them of the issues you've investigated, and what they need to look out for.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try out Sandworm
&lt;/h2&gt;

&lt;p&gt;We're a small team of long-time techies. The story in this article is our story, over the past five years, handling engineering for several JavaScript-powered startups, where we just wanted to get security right. We made Sandworm with ❤️ and ☕, in the hope that it will change all dev's minds about the importance of security practices, by providing them with the tools they always needed.&lt;/p&gt;

&lt;p&gt;You can try out Sandworm in your app by running &lt;code&gt;npx @sandworm/audit@latest&lt;/code&gt;, or by installing it globally with &lt;code&gt;npm i -g @sandworm/audit&lt;/code&gt; and then running &lt;code&gt;sandworm audit&lt;/code&gt; in your app's directory.&lt;/p&gt;

&lt;p&gt;More about how Sandworm works in the &lt;a href="https://docs.sandworm.dev/"&gt;documentation&lt;/a&gt;. Questions, ideas, or feedback? Let me know in the comments! 👇 And if you like Sandworm, please star it on GitHub below to let us know you care about security:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/sandworm-hq"&gt;
        sandworm-hq
      &lt;/a&gt; / &lt;a href="https://github.com/sandworm-hq/sandworm-audit"&gt;
        sandworm-audit
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Security &amp;amp; License Compliance For Your App's Dependencies 🪱
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;

  
  
  &lt;img alt="Sandworm Audit" src="https://res.cloudinary.com/practicaldev/image/fetch/s--BJZi_YcP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/sandworm-hq/sandworm-auditlogo-dark.png" width="478"&gt;

&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Beautiful Security &amp;amp; License Compliance Reports For Your App's Dependencies 🪱&lt;/p&gt;
&lt;h2&gt;
Summary&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Free &amp;amp; open source command-line tool&lt;/li&gt;
&lt;li&gt;Works with any modern JavaScript package manager&lt;/li&gt;
&lt;li&gt;Scans your project &amp;amp; dependencies for vulnerabilities, license, and misc issues&lt;/li&gt;
&lt;li&gt;Supports &lt;a href="https://docs.sandworm.dev/audit/resolving-issues" rel="nofollow"&gt;marking issues as resolved&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Supports &lt;a href="https://docs.sandworm.dev/audit/license-policies" rel="nofollow"&gt;custom license policies&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.sandworm.dev/audit/fail-policies" rel="nofollow"&gt;Configurable fail conditions&lt;/a&gt; for CI / GIT hook workflows&lt;/li&gt;
&lt;li&gt;Outputs
&lt;ul&gt;
&lt;li&gt;JSON issue &amp;amp; license usage reports&lt;/li&gt;
&lt;li&gt;Easy to grok SVG dependency tree &amp;amp; treemap visualizations
&lt;ul&gt;
&lt;li&gt;Powered by D3&lt;/li&gt;
&lt;li&gt;Overlays security vulnerabilities&lt;/li&gt;
&lt;li&gt;Overlays package license info&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CSV of all dependencies &amp;amp; license info&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
Generate a report&lt;/h3&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/3314b677529b1332702a9fd237c25f1caeebf26455bf27daa59d753ddb68407a/68747470733a2f2f6173736574732e73616e64776f726d2e6465762f73686f77636173652f61756469742d7465726d696e616c2d6f75747075742e676966"&gt;&lt;img src="https://camo.githubusercontent.com/3314b677529b1332702a9fd237c25f1caeebf26455bf27daa59d753ddb68407a/68747470733a2f2f6173736574732e73616e64776f726d2e6465762f73686f77636173652f61756469742d7465726d696e616c2d6f75747075742e676966" alt="Running Sandworm Audit"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
Navigate charts&lt;/h3&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/50f8594d5b8e88fa09908cb9b85a9e4c4657f6a87175b3e9f28c57418aad3570/68747470733a2f2f6173736574732e73616e64776f726d2e6465762f73686f77636173652f747265656d61702d616e642d747265652e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/50f8594d5b8e88fa09908cb9b85a9e4c4657f6a87175b3e9f28c57418aad3570/68747470733a2f2f6173736574732e73616e64776f726d2e6465762f73686f77636173652f747265656d61702d616e642d747265652e706e67" alt="Sandworm treemap and tree dependency charts"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
CSV output&lt;/h3&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/51d66d4f0e37b4f00950dced01e527ec3065a351fe2620201a10abd9f9469e2f/68747470733a2f2f6173736574732e73616e64776f726d2e6465762f73686f77636173652f6373762d736e69702e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/51d66d4f0e37b4f00950dced01e527ec3065a351fe2620201a10abd9f9469e2f/68747470733a2f2f6173736574732e73616e64776f726d2e6465762f73686f77636173652f6373762d736e69702e706e67" alt="Sandworm dependency CSV"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
JSON output&lt;/h3&gt;
&lt;div class="highlight highlight-source-json notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;{
  &lt;span class="pl-ent"&gt;"createdAt"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;...&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
  &lt;span class="pl-ent"&gt;"packageManager"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;...&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
  &lt;span class="pl-ent"&gt;"name"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;...&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
  &lt;span class="pl-ent"&gt;"version"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;...&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
  &lt;span class="pl-ent"&gt;"rootVulnerabilities"&lt;/span&gt;: [&lt;span class="pl-ii"&gt;...&lt;/span&gt;],
  &lt;span class="pl-ent"&gt;"dependencyVulnerabilities"&lt;/span&gt;: [&lt;span class="pl-ii"&gt;...&lt;/span&gt;],
  &lt;span class="pl-ent"&gt;"licenseUsage"&lt;/span&gt;: {&lt;span class="pl-ii"&gt;...&lt;/span&gt;},
  &lt;span class="pl-ent"&gt;"licenseIssues"&lt;/span&gt;: [&lt;span class="pl-ii"&gt;...&lt;/span&gt;],
  &lt;span class="pl-ent"&gt;"metaIssues"&lt;/span&gt;: [&lt;span class="pl-ii"&gt;...&lt;/span&gt;],
  &lt;span class="pl-ent"&gt;"errors"&lt;/span&gt;: [&lt;span class="pl-ii"&gt;...&lt;/span&gt;],&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/sandworm-hq/sandworm-audit"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>audit</category>
      <category>security</category>
      <category>licenses</category>
      <category>dependencies</category>
    </item>
    <item>
      <title>Running Eleventy Serverless On AWS Lambda@Edge</title>
      <dc:creator>Gabi Dobocan</dc:creator>
      <pubDate>Fri, 03 Mar 2023 16:04:26 +0000</pubDate>
      <link>https://dev.to/sandworm/running-eleventy-serverless-on-aws-lambdaedge-1aon</link>
      <guid>https://dev.to/sandworm/running-eleventy-serverless-on-aws-lambdaedge-1aon</guid>
      <description>&lt;p&gt;&lt;a href="https://11ty.dev" rel="noopener noreferrer"&gt;Eleventy&lt;/a&gt; is great. It’s a static site generator written in JavaScript, for “&lt;em&gt;Fast Builds and even Faster Web Sites.&lt;/em&gt;” It’s 10 to 20 times faster than the alternatives, like &lt;a href="https://www.gatsbyjs.com/" rel="noopener noreferrer"&gt;Gatsby&lt;/a&gt; or &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;. You get all of your content statically rendered and ready to be CDN-delivered. You needn’t worry about server-side rendering to get those pretty &lt;a href="https://socialsharepreview.com/" rel="noopener noreferrer"&gt;social share unfurls&lt;/a&gt;. And, if you have a large data set, that’s great — Eleventy can &lt;a href="https://github.com/11ty/eleventy/issues/2226" rel="noopener noreferrer"&gt;generate tens of thousands of pages&lt;/a&gt; with no issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  But what if you have a HUGE data set?
&lt;/h3&gt;

&lt;p&gt;When building Sandworm’s &lt;a href="https://sandworm.dev/" rel="noopener noreferrer"&gt;open-source security &amp;amp; license compliance audits for JavaScript packages&lt;/a&gt;, we wanted to generate a catalog of beautiful report visualizations for every library in the npm registry. That is, for every version of every library in the registry. We soon found out — that’s more than &lt;strong&gt;30 million package versions&lt;/strong&gt;. Good luck generating, uploading, and keeping that amount of HTML pages up to date in a decent amount of time, right?&lt;/p&gt;

&lt;p&gt;We looked at reducing our data set to just the most popular packages. We looked at implementing partial builds, where stale report pages would get continuously generated and uploaded.&lt;/p&gt;

&lt;p&gt;But the solution we ended up implementing was &lt;strong&gt;&lt;a href="https://www.11ty.dev/docs/plugins/serverless/" rel="noopener noreferrer"&gt;Eleventy Serverless&lt;/a&gt;&lt;/strong&gt;, a plugin that runs one or more template files &lt;em&gt;at request time&lt;/em&gt; to generate dynamic pages. So instead of going through the entire set of pages at build time, this plugin allows us to separate “regular” content pages rendered at build from “dynamic” pages rendered on demand. We can then simply generate and upload static content (like the homepage, about page, etc.) in the CI, and then deploy some code to a compute provider that will generate an npm package page when a user navigates to a specific URL. Great!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Except: Eleventy Serverless is built to work out-of-the-box with &lt;a href="https://www.netlify.com/products/functions/" rel="noopener noreferrer"&gt;Netlify Functions&lt;/a&gt;, and we’re running on AWS.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The good news is that you &lt;strong&gt;can&lt;/strong&gt; get Eleventy Serverless to run in AWS Lambdas. Even better, you can get it to run in &lt;strong&gt;&lt;a href="https://aws.amazon.com/lambda/edge/" rel="noopener noreferrer"&gt;Lambda@Edge&lt;/a&gt;&lt;/strong&gt;, which runs your code globally at AWS locations close to your users so that you can deliver full-featured, customized content with high performance and low latency.&lt;/p&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%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2A77jAb1V4Z5aDnUPdGau55A.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%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2A77jAb1V4Z5aDnUPdGau55A.png" alt="Eleventy mascot from the acclaimed designer, illustrator, and author Geri Coady"&gt;&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/gabidobo" rel="noopener noreferrer"&gt;
        gabidobo
      &lt;/a&gt; / &lt;a href="https://github.com/gabidobo/11ty-lambda-edge-demo" rel="noopener noreferrer"&gt;
        11ty-lambda-edge-demo
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A simple tutorial for running Eleventy Serverless on AWS Lambda@Edge
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Running Eleventy Serverless On AWS Lambda@Edge&lt;/h1&gt;

&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The good news is that you can get Eleventy Serverless to run in AWS Lambdas. Even better, you can get it to run in Lambda@Edge, which runs your code globally at AWS locations close to your users so that you can deliver full-featured, customized content with high performance and low latency.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Read the full tutorial here: &lt;a href="https://medium.com/sandworm/running-eleventy-serverless-on-aws-lambda-edge-5010b9571c5f" rel="nofollow noopener noreferrer"&gt;https://medium.com/sandworm/running-eleventy-serverless-on-aws-lambda-edge-5010b9571c5f&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/gabidobo/11ty-lambda-edge-demo" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h3&gt;
  
  
  Setting up Eleventy
&lt;/h3&gt;

&lt;p&gt;First things first: let’s get Eleventy running the local build. We start by installing it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i @11ty/eleventy --dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Then, let’s create the simplest template for our static Eleventy page. We’ll write it using &lt;a href="https://shopify.github.io/liquid/" rel="noopener noreferrer"&gt;Liquid&lt;/a&gt;, but since it’s so simple, it won’t take advantage of any useful templating tags for now. Let’s call it &lt;code&gt;index.liquid&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;h1&amp;gt;Hello&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;That’s it, we’re ready to build and serve! Run:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx @11ty/eleventy --serve

[11ty] Writing _site/index.html from ./src/index.liquid
[11ty] Serverless: 3 files bundled to ./serverless/edge.
[11ty] Wrote 1 file in 0.12 seconds (v2.0.0)
[11ty] Watching…
[11ty] Server at http://localhost:8080/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Visit &lt;a href="http://localhost:8080/" rel="noopener noreferrer"&gt;http://localhost:8080/&lt;/a&gt; in your browser at this point, and you should see the “Hello” heading we’ve created above. Neat!&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting up the Eleventy Serverless plugin
&lt;/h3&gt;

&lt;p&gt;The Serverless plugin is bundled with Eleventy and doesn’t require you to npm install anything. We do need to configure it, though. To do that, we need to create an Eleventy config file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// .eleventy.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EleventyServerlessBundlerPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@11ty/eleventy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;EleventyServerlessBundlerPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;edge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;functionsDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./serverless/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;redirects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&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;p&gt;Let’s break the plugin configuration down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Each plugin’s unique &lt;code&gt;name&lt;/code&gt; will determine the build output directory name and will be required when assigning permalinks. You can also instantiate multiple plugins to have different functions handle different pages.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;functionsDir&lt;/code&gt; allows you to specify the path to the build output dir; in our case, the plugin will generate files in the &lt;code&gt;./serverless/edge&lt;/code&gt; directory relative to the app root.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;redirects&lt;/code&gt; configures how Netlify redirects should be handled — since we’re not running on Netlify, we set this to false to skip generating a &lt;code&gt;netlify.toml&lt;/code&gt; file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lastly, in the configuration object we return to Eleventy, we specify an input dir for our content to keep things tidy. We’ll also go ahead and move the &lt;code&gt;index.liquid&lt;/code&gt; file we created earlier in the src directory.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, let's build again by running &lt;code&gt;npx @11ty/eleventy&lt;/code&gt;, and investigate what gets output under &lt;code&gt;./serverless/edge&lt;/code&gt;. You should see the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A number of js and json files starting with &lt;code&gt;eleventy-&lt;/code&gt;. Some are configuration files, and some are built to inform the Netlify bundler of function dependencies — we won’t need those for Lambda.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;eleventy.config.js&lt;/code&gt;, a copy of the main configuration file in the app root.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;src&lt;/code&gt; directory with the &lt;code&gt;index.liquid&lt;/code&gt; template.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An &lt;code&gt;index.js&lt;/code&gt; file with the actual serverless handler code that we’ll update and deploy to Lambda.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s gitignore the build artifacts that we don’t want in our repo and only keep the &lt;code&gt;index.js&lt;/code&gt; file for now. Add this to your &lt;code&gt;.gitignore&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serverless/edge/**
!serverless/edge/index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Good, we’re now ready to create our first dynamically generated page. Let’s make another simple Liquid file for it under &lt;code&gt;src/edge.liquid&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;---
permalink:
  edge: /hello/
---
&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello@Edge&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You’ll notice that for this file, we’ve added some &lt;a href="https://www.11ty.dev/docs/data-frontmatter/" rel="noopener noreferrer"&gt;front matter data&lt;/a&gt; to the liquid template. Specifically, we’ve defined a permalink for our page to respond to when running under the edge plugin. Eleventy won’t generate an &lt;code&gt;edge.html&lt;/code&gt; page when building — this page will only be generated by invoking the serverless handler code.&lt;/p&gt;
&lt;h3&gt;
  
  
  Making things Lambda-compatible
&lt;/h3&gt;

&lt;p&gt;Let’s now look at what’s going on with &lt;code&gt;serverless/edge/index.js&lt;/code&gt;. This is only generated with the initial build, so we’re free to modify it — and we’ll definitely need to in order to support Lambda@Edge.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;First, we can remove the &lt;code&gt;require("./eleventy-bundler-modules.js")&lt;/code&gt;, as that’s only needed for the Netlify bundle process;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next, we’ll need to get a reference to the current request path and query, as Eleventy needs that info to know what content to generate. With Netlify, you get these via &lt;code&gt;event.rawUrl&lt;/code&gt;, &lt;code&gt;event.multiValueQueryString&lt;/code&gt;, and &lt;code&gt;event.queryStringParameters&lt;/code&gt;. With Lambda@Edge, we’ll be getting events generated by CloudFront on origin requests — see &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html#example-origin-request" rel="noopener noreferrer"&gt;an example in the AWS docs&lt;/a&gt;. We’ll also use querystring to handle parsing the query string. Let’s update the code to this:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&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;Records&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&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="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;querystring&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querystring&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;elev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EleventyServerless&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;edge&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="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;functionsDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Finally, we need to update the handler’s returned objects to match the format expected by &lt;a href="mailto:Lambda@Edge"&gt;Lambda@Edge&lt;/a&gt;. Update the success &amp;amp; error responses status and headers to:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;status:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"200"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;headers:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cache-control"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;key:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cache-Control"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;value:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"max-age=0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"content-type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;key:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;value:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text/html; charset=UTF-8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;body:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;We’ve also added a &lt;code&gt;Cache-Control&lt;/code&gt; header to configure how CloudFront caches the returned results. We can get more thoughtful about this when moving to production, but for now, we’ll go with no caching.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One last thing: we’ll want to separate build dependencies from edge handling dependencies, so let’s create a separate &lt;code&gt;package.json&lt;/code&gt; file in &lt;code&gt;serverless/edge&lt;/code&gt;, and install &lt;code&gt;@11ty/edge&lt;/code&gt; as a prod dependency. As our edge function grows, we’ll add more things here, like database clients.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;And as you add more dependencies, it’s time to also build security and compliance into your app early. &lt;a href="https://github.com/sandworm-hq/sandworm-audit" rel="noopener noreferrer"&gt;Sandworm Audit&lt;/a&gt; is the open-source &lt;code&gt;npm audit&lt;/code&gt; that doesn’t suck: it checks for multiple types of issues, like vulnerabilities or license compliance, it outputs SVG charts and CSVs, and you can also run it in your CI to enforce security rules. &lt;a href="https://docs.sandworm.dev/" rel="noopener noreferrer"&gt;Check the docs&lt;/a&gt; and &lt;code&gt;npx @sandworm/audit&lt;/code&gt; in your JavaScript app’s root to try it out 🪱.&lt;/p&gt;
&lt;/blockquote&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%2Fcdn-images-1.medium.com%2Fmax%2F2120%2F1%2AJvTQJzapGNSSCKWQi89hCA.gif" 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%2Fcdn-images-1.medium.com%2Fmax%2F2120%2F1%2AJvTQJzapGNSSCKWQi89hCA.gif" alt="Sandworm Audit"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s our full handler code, for reference:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing it out locally
&lt;/h3&gt;

&lt;p&gt;Good, let’s test this out locally before we deploy! It should be pretty easy to simulate sending an event to our handler function. Let’s create a simple &lt;code&gt;test.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&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="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;Records&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/hello/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;querystring&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}}}]});&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&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;Running &lt;code&gt;node test.js&lt;/code&gt; in the console, you should see:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;status:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;headers:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'cache-control':&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'content-type':&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;body:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&amp;lt;h&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;Hello&amp;lt;/h&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Take a moment to celebrate! You’ve just triggered your first Eleventy build in a serverless function. 🎊&lt;/p&gt;
&lt;h3&gt;
  
  
  Deploying to AWS
&lt;/h3&gt;

&lt;p&gt;Things look good — it’s now time to deploy this to AWS. To handle the deployment, we’ll be using Serverless. No, not the Eleventy Serverless plugin, but &lt;a href="https://www.serverless.com/" rel="noopener noreferrer"&gt;Serverless&lt;/a&gt;, the “&lt;em&gt;zero-friction development tooling for auto-scaling apps on AWS Lambda&lt;/em&gt;” command-line tool. If you don’t have it installed, &lt;code&gt;run npm install -g serverless&lt;/code&gt;. Then create a &lt;code&gt;serverless/edge/serverless.yml&lt;/code&gt; file to configure the deploy:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;This will instantiate a CloudFront distribution connected to the bucket you specified under &lt;code&gt;events&amp;gt;cloudfront&amp;gt;origin&lt;/code&gt;. Any calls to URLs matching the pathPattern will be forwarded to the serverless handler instead of being routed to the bucket.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fun fact: Lambda@Edge functions log console output to their regional CloudWatch. That is, if a user in Germany accesses your pages via the edge at &lt;code&gt;eu-frankfurt-1&lt;/code&gt;, you’ll see logs for that specific run under the &lt;code&gt;eu-frankfurt-1&lt;/code&gt; region and nowhere else. In the yml config, we make sure to give our function proper permissions to write log groups anywhere.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We should also add an exception for the config file to &lt;code&gt;.gitignore&lt;/code&gt; — we want this in the repo.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you already have a CloudFront distribution that you want to connect to your new serverless function, check out the &lt;a href="https://www.serverless.com/plugins/serverless-lambda-edge-pre-existing-cloudfront" rel="noopener noreferrer"&gt;serverless-lambda-edge-pre-existing-cloudfront&lt;/a&gt; plugin.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’re ready to deploy! Make sure you export AWS credentials for an IAM user with proper permissions for deploying the entire stack. When moving to production, for security purposes, you should create a dedicated user with the minimal set of permissions required — however, I haven’t been able to find a comprehensive list of such permissions, so this will likely be a tedious trial-and-error process of figuring them out by trying deploys and seeing what fails. While still in development, an admin user might be easier to use.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;sls deploy --stage prod&lt;/code&gt; to deploy. If all goes well, in a couple of minutes, you should see the URL to your new CloudFront distribution! Your settings will need to propagate globally though, so it might take a few more minutes for everything to be ready. You can check the current status of your distribution under the AWS console dashboard. Once it’s done deploying, navigating to &lt;code&gt;CF_URL/hello&lt;/code&gt; in a browser should display our “&lt;em&gt;Hello@Edge&lt;/em&gt;” html header from the edge.liquid template. We did it! 🙌&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus: making it dynamic
&lt;/h3&gt;

&lt;p&gt;Now let’s quickly make our serverless function actually do something async. Let’s have it accept a URL parameter that’s the name of a Pokémon, and respond with an image of said cute beast. We’ll use &lt;a href="https://pokeapi.co/" rel="noopener noreferrer"&gt;https://pokeapi.co/&lt;/a&gt; to get the image.&lt;/p&gt;

&lt;p&gt;We could do the async work outside of eleventy, and then inject some global data like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eleventy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EleventyServerless&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;serverless&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="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;functionsDir&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="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addGlobalData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;yourData&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;Or, better yet, starting with Eleventy 2.0.0, we can use &lt;a href="https://www.11ty.dev/docs/filters/#asynchronous-filters" rel="noopener noreferrer"&gt;async filters&lt;/a&gt;. Let’s first update our edge.liquid template to include the new HTML we want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight liquid"&gt;&lt;code&gt;---
permalink:
  edge:
    - /hello/
    - /hello/:name/
---
&amp;lt;h1&amp;gt;Hello@Edge \{\{ eleventy.serverless.path.name }}&amp;lt;/h1&amp;gt;
&lt;span class="cp"&gt;{%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;eleventy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;serverless&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;%}&lt;/span&gt;
  &amp;lt;img src="\{\{ eleventy.serverless.path.name | escape | pokeimage }}" /&amp;gt;
&lt;span class="cp"&gt;{%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;endif&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We’ve added a new &lt;code&gt;permalink&lt;/code&gt; that includes a name path parameter. That will become available in the data cascade as &lt;code&gt;eleventy.serverless.path.name&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We’re transforming this &lt;code&gt;name&lt;/code&gt; param via two filters: &lt;code&gt;escape&lt;/code&gt; and &lt;code&gt;pokeimage&lt;/code&gt;. Remember, user input should be treated as potentially malicious 😉.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We need to define our &lt;code&gt;pokeimage&lt;/code&gt; filter. This is where the async magic happens. Add this to your &lt;code&gt;.eleventy.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addAsyncFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pokeimage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://pokeapi.co/api/v2/pokemon/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&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;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sprites&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;front_default&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;We’re relying on node’s built-in &lt;code&gt;fetch&lt;/code&gt; API here — it’s a good thing we’ve set &lt;code&gt;runtime: nodejs18.x&lt;/code&gt; in our &lt;code&gt;serverless.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Let’s update our &lt;code&gt;test.js&lt;/code&gt; file to query the &lt;code&gt;/hello/ditto/&lt;/code&gt; URL, and run &lt;code&gt;node test.js&lt;/code&gt; again. In the console output, you should now see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;status:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;headers:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'cache-control':&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'content-type':&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;body:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&amp;lt;h&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;Hello@Edge&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ditto&amp;lt;/h&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;\n'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;'\n'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;img&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;src=&lt;/span&gt;&lt;span class="s2"&gt;"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/132.png"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/&amp;gt;\n'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One last &lt;code&gt;sls deploy --stage prod&lt;/code&gt; to get this deployed, and done! You’ve mastered setting up Eleventy Serverless on Lambda@Edge.&lt;/p&gt;

&lt;p&gt;All of &lt;a href="https://sandworm.dev/npm/package/npm/4.0.2/" rel="noopener noreferrer"&gt;Sandworm’s npm package report pages&lt;/a&gt; are generated using Eleventy Serverless and &lt;a href="mailto:Lambda@Edge"&gt;Lambda@Edge&lt;/a&gt;. Sandworm is about JavaScript security and compliance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Audit your dependencies and licenses using &lt;a href="https://docs.sandworm.dev/audit" rel="noopener noreferrer"&gt;Sandworm Audit&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sandbox dependencies and monitor your running app using &lt;a href="https://docs.sandworm.dev/guard" rel="noopener noreferrer"&gt;Sandworm Guard&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2At3IsqAZ5SLjQ_qYVc6zEEw.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%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2At3IsqAZ5SLjQ_qYVc6zEEw.png" alt="[https://sandworm.dev](https://sandworm.dev)"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
