<?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: Ken Fukuyama</title>
    <description>The latest articles on DEV Community by Ken Fukuyama (@kenfdev).</description>
    <link>https://dev.to/kenfdev</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%2F175198%2F9c0d38c3-8cf3-4997-999e-b8b3184879c8.jpg</url>
      <title>DEV Community: Ken Fukuyama</title>
      <link>https://dev.to/kenfdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kenfdev"/>
    <language>en</language>
    <item>
      <title>Demystifying Playwright Test Agents' seed.spec.ts: What I Learned from Reading the MCP Code</title>
      <dc:creator>Ken Fukuyama</dc:creator>
      <pubDate>Fri, 05 Dec 2025 05:14:20 +0000</pubDate>
      <link>https://dev.to/kenfdev/demystifying-playwright-test-agents-seedspects-what-i-learned-from-reading-the-mcp-code-2482</link>
      <guid>https://dev.to/kenfdev/demystifying-playwright-test-agents-seedspects-what-i-learned-from-reading-the-mcp-code-2482</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I recently had a chance to try out Playwright Test Agents, and I'm impressed—this feels like a powerful new tool that could genuinely change how we approach E2E test creation and maintenance.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/Utbz2fI0vhM"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;In this article, I'll dig into something that tripped me up when using Playwright Test Agents: the role of &lt;strong&gt;&lt;code&gt;seed.spec.ts&lt;/code&gt;&lt;/strong&gt;. The official documentation didn't make it immediately clear to me, so I ended up reading the MCP source code to understand how it actually works. I'll walk you through what I learned.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This article assumes you have some familiarity with Playwright Test Agents. If you haven't tried it yet, you can still follow along and understand what seed.spec.ts does, but I'd recommend watching the video above first for deeper context. It's mainly in Japanese, but you can get the idea from the English auto dubs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where the Official Docs Left Me Confused
&lt;/h2&gt;

&lt;p&gt;The first thing that confused me when starting with Playwright Test Agents was &lt;code&gt;seed.spec.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Looking at the official documentation (as of December 4, 2025), there's just this one line:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ffnndrn18gbx3b85jcaph.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ffnndrn18gbx3b85jcaph.png" alt="Official documentation screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Seed tests provide a ready-to-use page context to bootstrap execution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that I understand how it works, this makes perfect sense. But &lt;strong&gt;when I first read it, I honestly had no idea what it meant&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Specifically, what wasn't clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Whose&lt;/strong&gt; ready-to-use page context?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who&lt;/strong&gt; is doing the bootstrap execution?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The missing subjects make this sentence vague. Is this about Playwright's test execution, or something specific to the Agents feature?&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the MCP Code
&lt;/h2&gt;

&lt;p&gt;Since staring at the documentation wasn't getting me anywhere, I decided to look at the actual MCP (Model Context Protocol—the standard protocol for AI agents to call tools) code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Checking the Agent Prompts
&lt;/h3&gt;

&lt;p&gt;The key is in the two agent configuration files generated by the initialization command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx playwright init-agents &lt;span class="nt"&gt;--loop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vscode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this command generates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;playwright-test-planner.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;playwright-test-generator.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looking at the Planner's prompt, I found this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;1.&lt;/span&gt; &lt;span class="gs"&gt;**Navigate and Explore**&lt;/span&gt;
&lt;span class="p"&gt;   -&lt;/span&gt; Invoke the &lt;span class="sb"&gt;`planner_setup_page`&lt;/span&gt; tool once to set up page before using any other tools
&lt;span class="p"&gt;   -&lt;/span&gt; Explore the browser snapshot
&lt;span class="p"&gt;   -&lt;/span&gt; Do not take screenshots unless absolutely necessary
&lt;span class="p"&gt;   -&lt;/span&gt; Use &lt;span class="sb"&gt;`browser_*`&lt;/span&gt; tools to navigate and discover interface
&lt;span class="p"&gt;   -&lt;/span&gt; Thoroughly explore the interface, identifying all interactive elements, forms, navigation paths, and functionality
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part: it instructs the agent to use the &lt;strong&gt;&lt;code&gt;planner_setup_page&lt;/code&gt; tool&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Checking the Generator similarly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# For each test you generate&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Obtain the test plan with all the steps and verification specification
&lt;span class="p"&gt;-&lt;/span&gt; Run the &lt;span class="sb"&gt;`generator_setup_page`&lt;/span&gt; tool to set up page for the scenario
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one uses &lt;strong&gt;&lt;code&gt;generator_setup_page&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Looking at the MCP Tool Implementation
&lt;/h3&gt;

&lt;p&gt;So what do these tools actually do? Let's look at the implementation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/microsoft/playwright/blob/f9e1797a0d6025a0cc499692d79bb10a806eadc1/packages/playwright/src/mcp/test/plannerTools.ts#L23C14-L40" rel="noopener noreferrer"&gt;https://github.com/microsoft/playwright/blob/f9e1797a0d6025a0cc499692d79bb10a806eadc1/packages/playwright/src/mcp/test/plannerTools.ts#L23C14-L40&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setupPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineTestTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Tool name from the prompt&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="s1"&gt;planner_setup_page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Setup planner page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Setup the page for test planning&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Project to use for setup. For example: "chromium", if no project is provided uses the first project in the config.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="c1"&gt;// Uses seed.spec.ts as seedFile by default&lt;/span&gt;
      &lt;span class="na"&gt;seedFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A seed file contains a single test that is used to setup the page for testing, for example: "tests/seed.spec.ts". If no seed file is provided, a default seed file is created.&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;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;readOnly&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;handle&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="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&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;seed&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOrCreateSeedFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;seedFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// It runs seed.spec.ts first&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;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;}&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runSeedTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;projectName&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;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="na"&gt;isError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;paused&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;The crucial part is &lt;code&gt;context.runSeedTest()&lt;/code&gt;. &lt;strong&gt;Before the Planner or Generator does its job, it first executes seed.spec.ts&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mystery Solved: What seed.spec.ts Really Is
&lt;/h2&gt;

&lt;p&gt;With this understanding, the documentation quote becomes crystal clear:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Seed tests provide a ready-to-use page context to bootstrap execution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Whose ready-to-use page context?&lt;/strong&gt; → &lt;strong&gt;For the Planner and Generator (AI agents)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who does the bootstrap execution?&lt;/strong&gt; → &lt;strong&gt;The Planner and Generator run it&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So &lt;code&gt;seed.spec.ts&lt;/code&gt; &lt;strong&gt;isn't a seed file for Playwright tests—it's a setup file that runs before the Planner or Generator starts navigating and exploring the browser&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Should Go in seed.spec.ts
&lt;/h2&gt;

&lt;p&gt;Given this mechanism, in most cases your &lt;code&gt;seed.spec.ts&lt;/code&gt; should include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Login procedures for the user you want the Planner/Generator to explore as&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data seeding or environment initialization needed for planning/generation&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, if you want to generate tests for an admin panel, you can log in as an admin in seed.spec.ts, and the Planner will start exploring from that logged-in state.&lt;/p&gt;

&lt;p&gt;Here's a concrete example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&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;authFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;playwright/.auth/user.json&lt;/span&gt;&lt;span class="dl"&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;testUsername&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;testuser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test group&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// request is Playwright's APIRequestContext&lt;/span&gt;
  &lt;span class="c1"&gt;// https://playwright.dev/docs/api/class-apirequestcontext&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;seed&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="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&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;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Navigate to login page&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;goto&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="c1"&gt;// Fill in login form&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;getByLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testUsername&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;getByLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password123&lt;/span&gt;&lt;span class="dl"&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;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Verify successful login&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&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;toHaveURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Reset user data for testing&lt;/span&gt;
    &lt;span class="c1"&gt;// Note: This assumes your app has a test data reset API&lt;/span&gt;
    &lt;span class="c1"&gt;// In real projects, implement your own initialization logic as needed&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resetResponse&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;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/test/reset&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;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;testUsername&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resetResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Save login state&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;context&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;storageState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authFile&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;This way, you put any necessary preparation—login procedures, test data resets, etc.—in seed.spec.ts before the agent starts exploring.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Is This Different from Existing Setup/Fixtures?
&lt;/h2&gt;

&lt;p&gt;At this point, I had a question:&lt;/p&gt;

&lt;p&gt;"Wait, doesn't Playwright already have similar mechanisms?"&lt;/p&gt;

&lt;h3&gt;
  
  
  Global Setup / Teardown
&lt;/h3&gt;

&lt;p&gt;Playwright has documented best practices for &lt;code&gt;setup&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://playwright.dev/docs/test-global-setup-teardown" rel="noopener noreferrer"&gt;https://playwright.dev/docs/test-global-setup-teardown&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Fixtures
&lt;/h3&gt;

&lt;p&gt;There's also the Fixtures concept for writing robust tests:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://playwright.dev/docs/test-fixtures" rel="noopener noreferrer"&gt;https://playwright.dev/docs/test-fixtures&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Critical Difference
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;These mechanisms serve fundamentally different purposes from &lt;code&gt;seed.spec.ts&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;For Whom&lt;/th&gt;
&lt;th&gt;When It Runs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Global Setup&lt;/td&gt;
&lt;td&gt;Test execution&lt;/td&gt;
&lt;td&gt;When &lt;code&gt;npx playwright test&lt;/code&gt; runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fixtures&lt;/td&gt;
&lt;td&gt;Test execution&lt;/td&gt;
&lt;td&gt;When each test runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;seed.spec.ts&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AI Agents&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;When Planner/Generator launches&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;seed.spec.ts &lt;strong&gt;isn't executed by Playwright's test runner—it's executed by AI agents via MCP tools&lt;/strong&gt;. It runs on a completely separate lifecycle from test execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  About Comments in Generated Code
&lt;/h2&gt;

&lt;p&gt;Another source of confusion was the comments in test code generated by the Planner/Generator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// spec: specs/basic-operations.md&lt;/span&gt;
&lt;span class="c1"&gt;// seed: tests/seed.spec.ts  // &amp;lt;-- This comment was misleading&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../fixtures&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Adding New Todos&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Add Valid Todo&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="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&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="c1"&gt;// 1. Click in the "What needs to be done?" input field&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todoInput&lt;/span&gt; &lt;span class="o"&gt;=&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;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;textbox&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;What needs to be done?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="c1"&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;Seeing &lt;code&gt;// seed: tests/seed.spec.ts&lt;/code&gt;, I wondered: "Does this comment somehow trigger &lt;code&gt;seed.spec.ts&lt;/code&gt; to run during Playwright test execution?"&lt;/p&gt;

&lt;p&gt;Turns out, &lt;strong&gt;this comment is just metadata&lt;/strong&gt;. It records which seed file was used to generate the test, but doesn't cause any special behavior during test execution.&lt;/p&gt;

&lt;p&gt;No magical new features here—it's simply recording "this test was generated using this seed environment."&lt;/p&gt;

&lt;h2&gt;
  
  
  How This Might Work in Mid-to-Large Scale Projects (Hypothesis)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; This section is speculative. I haven't actually verified this in practice, so take it as food for thought.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With this understanding, I can imagine how this could work in medium to large projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Multiple Seed Files
&lt;/h3&gt;

&lt;p&gt;Looking at the MCP tool implementation, we saw that a &lt;code&gt;seedFile&lt;/code&gt; parameter can be passed. Taking advantage of this, you could create multiple seed files for different actors or use cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tests/
├── seeds/
│   ├── admin-seed.spec.ts      # Logged in as admin
│   ├── member-seed.spec.ts     # Logged in as regular member
│   ├── guest-seed.spec.ts      # Not logged in
│   └── checkout-seed.spec.ts   # Cart has items
└── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When launching the Planner/Generator, you could specify the appropriate seed file for your purpose, enabling more efficient test planning and generation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros and Cons
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Skip repetitive operations like login procedures in each exploration session&lt;/li&gt;
&lt;li&gt;Start test generation from specific states (e.g., items in cart)&lt;/li&gt;
&lt;li&gt;Separate test generation starting points by actor (admin/regular user/guest)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Need to manage seed files (must keep them in sync with app changes)&lt;/li&gt;
&lt;li&gt;Maintenance cost for seed files themselves (this is unavoidable)&lt;/li&gt;
&lt;li&gt;With multiple seed files, you need to decide which one to use (also unavoidable for complex apps)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's not a silver bullet, but used appropriately, it could significantly improve test generation efficiency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, I've explored the role of &lt;code&gt;seed.spec.ts&lt;/code&gt; in Playwright Test Agents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Points&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;seed.spec.ts&lt;/code&gt; is &lt;strong&gt;for the AI agents (Planner/Generator)&lt;/strong&gt;, not for Playwright tests&lt;/li&gt;
&lt;li&gt;It runs &lt;strong&gt;when agents launch via MCP tools&lt;/strong&gt;, not during Playwright test execution&lt;/li&gt;
&lt;li&gt;It serves a &lt;strong&gt;different purpose and lifecycle&lt;/strong&gt; from existing Global Setup and Fixtures&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;// seed: ...&lt;/code&gt; comment in generated tests is &lt;strong&gt;just metadata&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The feature is still new, so documentation hasn't fully caught up yet. But by reading the MCP code, I was able to understand how it works, and now I can use it confidently.&lt;/p&gt;

&lt;p&gt;Either way, Playwright Test Agents has the potential to make working with E2E tests more enjoyable if used well. I'm excited to see how it evolves!&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>ai</category>
      <category>typescript</category>
      <category>testing</category>
    </item>
    <item>
      <title>Deciphering GraphQL: A Deep Dive into Type Merging and Schema Stitching</title>
      <dc:creator>Ken Fukuyama</dc:creator>
      <pubDate>Wed, 27 Sep 2023 02:30:07 +0000</pubDate>
      <link>https://dev.to/kenfdev/deciphering-graphql-a-deep-dive-into-type-merging-and-schema-stitching-3b05</link>
      <guid>https://dev.to/kenfdev/deciphering-graphql-a-deep-dive-into-type-merging-and-schema-stitching-3b05</guid>
      <description>&lt;p&gt;Recently, I've been studying GraphQL Stitching, and no matter how many times I read about Type Merging, it didn't quite click. So, I made an effort to fully understand it.&lt;/p&gt;

&lt;p&gt;The official documentation for Type Merging can be found here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://the-guild.dev/graphql/stitching/docs/approaches/type-merging" rel="noopener noreferrer"&gt;https://the-guild.dev/graphql/stitching/docs/approaches/type-merging&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Assumptions for this article
&lt;/h2&gt;

&lt;p&gt;In this article, I won't be explaining what GraphQL Gateway or GraphQL Stitching are. 🙏 There are plenty of good articles explaining those aspects, so I recommend checking them out first.&lt;/p&gt;

&lt;p&gt;For reference, the official documentation can be found here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://the-guild.dev/graphql/stitching" rel="noopener noreferrer"&gt;https://the-guild.dev/graphql/stitching&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Type Merging in GraphQL Stitching
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Type Merging?
&lt;/h3&gt;

&lt;p&gt;To put it succinctly, Type Merging is a mechanism that allows us to treat multiple schemas defining the same type as if they were just one type.&lt;/p&gt;

&lt;p&gt;For example, let's say there's a GraphQL Gateway, and behind it, there are GraphQL servers handling &lt;code&gt;manufacturers&lt;/code&gt;, &lt;code&gt;products&lt;/code&gt;, and &lt;code&gt;storefronts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F5xi9ycgy7xze1noolnyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F5xi9ycgy7xze1noolnyf.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although the servers are separate, you'll notice that types like &lt;code&gt;Product&lt;/code&gt; and &lt;code&gt;Manufacturer&lt;/code&gt; are defined on each. These can be merged, and the Gateway can provide the following schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&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="n"&gt;manufacturer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Manufacturer&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;_manufacturer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Manufacturer&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;storefront&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Storefront&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Manufacturer&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="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c"&gt;# From the manufacturers schema&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;products&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="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;]!&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c"&gt;# From the products schema&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Product&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="c"&gt;# Mainly from the products schema&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;upc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;manufacturer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Manufacturer&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Storefront&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="c"&gt;# From the storefronts schema&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;products&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="n"&gt;Product&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Up to this point, it's relatively intuitive, but the "How do we merge?" part felt somewhat elusive when I read the documentation. I kept reading and forgetting, so I decided to take a closer look at the official sample's inner workings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ardatan/schema-stitching/tree/master/examples/type-merging-single-records" rel="noopener noreferrer"&gt;https://github.com/ardatan/schema-stitching/tree/master/examples/type-merging-single-records&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Verifying the Merging Flow in practice
&lt;/h3&gt;

&lt;p&gt;When you check the official documentation's &lt;a href="https://the-guild.dev/graphql/stitching/docs/approaches/type-merging#merging-flow" rel="noopener noreferrer"&gt;MergingFlow&lt;/a&gt;, there's a diagram and a description of each step, explaining how the merging happens.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fcpd8mlg5furc529035z1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fcpd8mlg5furc529035z1.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The diagram slightly omits certain explanations, making it somewhat challenging to grasp at a glance.&lt;/p&gt;

&lt;p&gt;Thus, I visualized the flow in more detail using the &lt;code&gt;Storefront&lt;/code&gt;, &lt;code&gt;Product&lt;/code&gt;, and &lt;code&gt;Manufacturer&lt;/code&gt; examples. (For a fullscreen view, click &lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rgncjo661hz1nq5mlcmg.png" rel="noopener noreferrer"&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Frgncjo661hz1nq5mlcmg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Frgncjo661hz1nq5mlcmg.png" alt=" " width="800" height="699"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's go through the steps.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Client request
&lt;/h4&gt;

&lt;p&gt;The client sends a request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fs0retula7ofl6l411cxr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fs0retula7ofl6l411cxr.png" alt=" " width="435" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the Gateway schema matches the one mentioned above, this is a regular GraphQL query, simply requesting the desired data.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. The original request goes to the storefront server
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Feprawoq5tmutj85f8y0p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Feprawoq5tmutj85f8y0p.png" alt=" " width="489" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, since the request is intended for the storefront, it's sent there via the gateway. At this point, the gateway filters the query, &lt;strong&gt;sending only the parts relevant to the storefront&lt;/strong&gt;. The storefront only understands the &lt;code&gt;Product&lt;/code&gt; type with just the &lt;code&gt;upc&lt;/code&gt;, so the original query's &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;manufacturer&lt;/code&gt; are omitted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ffwkhfie4oqwoyhitkfml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ffwkhfie4oqwoyhitkfml.png" alt=" " width="269" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, if we only had this, we'd lose the information we want to retrieve from the &lt;code&gt;products&lt;/code&gt;, and we wouldn't be able to fetch data from the other servers. This is where &lt;code&gt;selectionSet&lt;/code&gt; comes in. Citing the official documentation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;selectionSet specifies one or more key fields required from other services to perform this query. Query planning will automatically resolve these fields from other subschemas in dependency order.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In essence, it's declaring, "To get data for type ○○, we at least need △△ information." In this case, the products server has a configuration like:&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="nx"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// This service provides _all_ unique fields for the `Product` type.&lt;/span&gt;
    &lt;span class="c1"&gt;// Again, there's unique data here so the gateway needs a query configured to fetch it.&lt;/span&gt;
    &lt;span class="c1"&gt;// This config delegates to `product(upc: $upc)`.&lt;/span&gt;
    &lt;span class="na"&gt;selectionSet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{ upc }&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;product&lt;/span&gt;&lt;span class="dl"&gt;'&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="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;upc&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;upc&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;You can see a &lt;code&gt;selectionSet&lt;/code&gt; of &lt;code&gt;{ upc }&lt;/code&gt;. It's declaring, "When merging data for the Product type, we need the &lt;code&gt;upc&lt;/code&gt;." Having this information, the gateway implicitly adds &lt;code&gt;upc&lt;/code&gt; when sending a request to the storefront server. Checking the actual query log confirms the presence of &lt;code&gt;upc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fe6kg01mti4b6vyq96s07.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fe6kg01mti4b6vyq96s07.png" alt=" " width="697" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Response from storefront
&lt;/h4&gt;

&lt;p&gt;From the previous request, the response from the storefront is as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fen498ef7zyb75nr0g7m3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fen498ef7zyb75nr0g7m3.png" alt=" " width="303" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Creation of the merger query
&lt;/h4&gt;

&lt;p&gt;Once the storefront responds, the next step is to send a request to the products server. Someone responsible for merging (let's call them the "merger") sends the query, utilizing the &lt;code&gt;merge&lt;/code&gt; setting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fv84vcjvkzcp1cfobd6mr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fv84vcjvkzcp1cfobd6mr.png" alt=" " width="309" height="223"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, the products server's &lt;code&gt;merge&lt;/code&gt; setting specified for the gateway is in focus. Particularly, note the &lt;code&gt;fieldName&lt;/code&gt; and &lt;code&gt;args&lt;/code&gt;. This means that when merging the &lt;code&gt;Product&lt;/code&gt; type, the server uses its &lt;code&gt;product&lt;/code&gt; query, and for the argument, it uses the originating object's &lt;code&gt;upc&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;It's a bit tricky, but the relationship is as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F7fal0a7imutpv4cv5toj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F7fal0a7imutpv4cv5toj.png" alt=" " width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using this information, the query for step 5 is generated.&lt;/p&gt;

&lt;h4&gt;
  
  
  5. Request to the products
&lt;/h4&gt;

&lt;p&gt;server&lt;/p&gt;

&lt;p&gt;Based on the information from step 4, a query is generated for the products server, which looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Flg9bjlbttfxbfxo7uwkc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Flg9bjlbttfxbfxo7uwkc.png" alt=" " width="436" height="678"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks to the &lt;code&gt;args&lt;/code&gt; definition, the &lt;code&gt;upc&lt;/code&gt; is injected with the value &lt;code&gt;6&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;From here on, it becomes repetitive, but only the query aspects that the products server can handle are filtered. The original query requested the &lt;code&gt;manufacturer's&lt;/code&gt; &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;products&lt;/code&gt;, but the products server isn't aware of the &lt;code&gt;name&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fv1m293ovnz9cqc2maucs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fv1m293ovnz9cqc2maucs.png" alt=" " width="182" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thus, &lt;code&gt;name&lt;/code&gt; is omitted.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;Manufacturer type&lt;/code&gt;, there's a &lt;code&gt;selectionSet&lt;/code&gt; of &lt;code&gt;{ id }&lt;/code&gt; set for the manufacturer server, as seen below:&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="nx"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// This schema provides one unique field of data for the `Manufacturer` type (`name`).&lt;/span&gt;
  &lt;span class="c1"&gt;// The gateway needs a query configured so it can fetch this data...&lt;/span&gt;
  &lt;span class="c1"&gt;// this config delegates to `manufacturer(id: $id)`.&lt;/span&gt;
  &lt;span class="nl"&gt;Manufacturer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;selectionSet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{ id }&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;manufacturer&lt;/span&gt;&lt;span class="dl"&gt;'&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="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&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;id&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;This is implicitly injected into the query, as can be seen from the actual request below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fhezz2bcqaq3w4z284v17.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fhezz2bcqaq3w4z284v17.png" alt=" " width="741" height="846"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  6. Response from products
&lt;/h4&gt;

&lt;p&gt;From the previous request, the response from the products is as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F28aidy5lho88cl1qx58f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F28aidy5lho88cl1qx58f.png" alt=" " width="367" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  7,8,9. Similar requests are sent to the manufacturer server
&lt;/h4&gt;

&lt;p&gt;I'll skip the details for the manufacturer server request, as it's repetitive. It looks like this (for a full view, click &lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bcz4aiodb6pj2vx1i7dn.png" rel="noopener noreferrer"&gt;here&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fbcz4aiodb6pj2vx1i7dn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fbcz4aiodb6pj2vx1i7dn.png" alt=" " width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  10,11. All results are merged into the appropriate type and returned
&lt;/h4&gt;

&lt;p&gt;Finally, all the results are merged, and the client receives the response they requested.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fgu0u8z9lhu3iq3l194um.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fgu0u8z9lhu3iq3l194um.png" alt=" " width="727" height="691"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;After delving deeper than the official documentation, I feel like I've come to grasp Type Merging much better. I particularly had a hard time understanding &lt;code&gt;selectionSet&lt;/code&gt;, but seeing the actual requests helped me understand its implicit injection.&lt;/p&gt;

&lt;p&gt;While I only touched on a small part about GraphQL Gateway, I believe others might also struggle to understand Type Merging, so I hope this article helps someone out there.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>typescript</category>
      <category>node</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Upgrading from graphql-yoga v2 to v4</title>
      <dc:creator>Ken Fukuyama</dc:creator>
      <pubDate>Wed, 13 Sep 2023 04:59:25 +0000</pubDate>
      <link>https://dev.to/kenfdev/upgrading-from-graphql-yoga-v2-to-v4-2gpf</link>
      <guid>https://dev.to/kenfdev/upgrading-from-graphql-yoga-v2-to-v4-2gpf</guid>
      <description>&lt;h2&gt;
  
  
  Key Points on Upgrading from graphql-yoga v2 to v4
&lt;/h2&gt;

&lt;p&gt;This article outlines a tricky point when upgrading graphql-yoga v2 to v4. For a comprehensive migration guide, please refer to the official &lt;a href="https://the-guild.dev/graphql/yoga-server/docs/migration/migration-from-yoga-v2"&gt;migration guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Removal of &lt;code&gt;endpoint&lt;/code&gt; and &lt;code&gt;graphiql.endpoint&lt;/code&gt; options
&lt;/h3&gt;

&lt;p&gt;One of the significant changes in v4 is the removal of the &lt;code&gt;endpoint&lt;/code&gt; and &lt;code&gt;graphiql.endpoint&lt;/code&gt; options. This can have implications, especially for those using &lt;code&gt;express&lt;/code&gt; alongside.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Changes in the Configuration Approach
&lt;/h3&gt;

&lt;p&gt;The configuration method in v2 was as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;graphQLServer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;//... previous configuration details&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In v4, the new configuration method is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;graphQLServer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createYoga&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;createSchema&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="c1"&gt;//... new configuration details&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;h3&gt;
  
  
  3. Changes regarding the Endpoint
&lt;/h3&gt;

&lt;p&gt;In v4, the default endpoint is set to &lt;code&gt;/graphql&lt;/code&gt;. If you want to use an alternate endpoint, you'll need to explicitly specify it using the &lt;code&gt;graphqlEndpoint&lt;/code&gt; option.&lt;/p&gt;

&lt;p&gt;For instance, in v2, setting the endpoint to &lt;code&gt;/graphql-other&lt;/code&gt; worked seamlessly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//... &lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/graphql-other&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;graphQLOtherServer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, with the same setup in v4, you would encounter a &lt;code&gt;NOT FOUND&lt;/code&gt; error.&lt;/p&gt;

&lt;p&gt;The solution is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;graphQLOtherServer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createYoga&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;graphqlEndpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/graphql-other&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// explicitly setting the endpoint&lt;/span&gt;
  &lt;span class="c1"&gt;//... other configurations&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/graphql-other&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;graphQLOtherServer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This change can be a bit tricky, so it's crucial to be aware to avoid potential pitfalls during the upgrade.&lt;/p&gt;




&lt;p&gt;I hope this sheds light on some nuances of the upgrade process and helps yo&lt;a href="https://dev.tourl"&gt;&lt;/a&gt;u navigate any challenges smoothly.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>node</category>
    </item>
    <item>
      <title>Cross Module Transaction with Prisma</title>
      <dc:creator>Ken Fukuyama</dc:creator>
      <pubDate>Tue, 14 Jun 2022 13:14:22 +0000</pubDate>
      <link>https://dev.to/kenfdev/cross-module-transaction-with-prisma-5d08</link>
      <guid>https://dev.to/kenfdev/cross-module-transaction-with-prisma-5d08</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It's possible to write transactions in the application layer using Prisma with the help of &lt;code&gt;cls-hooked&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Here's some &lt;a href="https://github.com/kenfdev/prisma-auto-transaction-poc/blob/deaa43679b8e474b11c0a094dc60680e0ab3d876/src/usecases/__tests__/integration/createOrder.test.ts" rel="noopener noreferrer"&gt;sample codes&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The PoC code: &lt;a href="https://github.com/kenfdev/prisma-auto-transaction-poc" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/kenfdev/prisma-auto-transaction-poc" rel="noopener noreferrer"&gt;https://github.com/kenfdev/prisma-auto-transaction-poc&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prisma and Interactive Transaction
&lt;/h2&gt;

&lt;p&gt;There's no doubt that &lt;a href="https://www.prisma.io/" rel="noopener noreferrer"&gt;Prisma&lt;/a&gt; boosts your productivity when dealing with Databases in Node.js + TypeScript. But as you start creating complex software, there are some cases you can't use Prisma the way you'd like to out of the box. One of them is when you want to use the &lt;a href="https://www.prisma.io/docs/concepts/components/prisma-client/transactions#interactive-transactions-in-preview" rel="noopener noreferrer"&gt;interactive transaction&lt;/a&gt; across modules.&lt;/p&gt;

&lt;p&gt;What I mean by &lt;strong&gt;cross module&lt;/strong&gt; is a bit obscure. Let's look at how you can write interactive transactions in Prisma. The following code is from the official docs.&lt;/p&gt;

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

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$transaction&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="nx"&gt;prisma&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="c1"&gt;// 1. Decrement amount from the sender.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sender&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;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;decrement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amount&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="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;from&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="c1"&gt;// 2. Verify that the sender's balance didn't go below zero.&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&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="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; doesn't have enough to send &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;amount&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="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// 3. Increment the recipient's balance by amount&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recipient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amount&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="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;to&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;recipient&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;The point is that you call &lt;code&gt;prisma.$transaction&lt;/code&gt; and you pass a callback to it with the parameter &lt;code&gt;prisma&lt;/code&gt;. Inside the transaction, you use the &lt;code&gt;prisma&lt;/code&gt; instance passed as the callback to use it as the &lt;strong&gt;transaction prisma client&lt;/strong&gt;. It's simple and easy to use. But what if you don't want to show the &lt;code&gt;prisma&lt;/code&gt; interface inside the transaction code? Perhaps you're working with a enterprise-ish app and have a layered architecture and you are not allowed to use the &lt;code&gt;prisma&lt;/code&gt; client in say, the application layer.&lt;/p&gt;

&lt;p&gt;It's probably easier to look at it in code. Suppose you would like to write some transaction code like this:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;$transaction&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="c1"&gt;// call multiple repository methods inside the transaction&lt;/span&gt;
  &lt;span class="c1"&gt;// if either fails, the transaction will rollback&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orderRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notificationRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`Successfully created order: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="p"&gt;});&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;There are multiple Repositories that hide the implementation details(e.g. Prisma, SNS, etc.). You would not want to show &lt;code&gt;prisma&lt;/code&gt; inside this code because it is an implementation detail. So how can you deal with this using Prisma? It's actually not that easy because you'll somehow have to pass the Transaction Prisma Client to the Repository across modules without explicitly passing it. &lt;/p&gt;
&lt;h2&gt;
  
  
  Creating a custom TransactionScope
&lt;/h2&gt;

&lt;p&gt;This is when I came across &lt;a href="https://github.com/prisma/prisma/issues/5729#issuecomment-959137819" rel="noopener noreferrer"&gt;this issue comment&lt;/a&gt;. It says you can use &lt;a href="https://www.npmjs.com/package/cls-hooked" rel="noopener noreferrer"&gt;cls-hooked&lt;/a&gt; to create a thread-like local storage to temporarily store the Transaction Prisma Client, and then get the client from somewhere else via CLS (Continuation-Local Storage) afterwards.&lt;/p&gt;

&lt;p&gt;After looking at how I can use &lt;code&gt;cls-hooked&lt;/code&gt;, here is a &lt;code&gt;TransactionScope&lt;/code&gt; class I've created to create a transaction which can be used from any layer:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrismaTransactionScope&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;TransactionScope&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;transactionContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transactionContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// inject the original Prisma Client to use when you actually create a transaction&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// A CLS namespace to temporarily save the Transaction Prisma Client&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transactionContext&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="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// attempt to get the Transaction Client&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;PRISMA_CLIENT_KEY&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TransactionClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// if the Transaction Client&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// exists, there is no need to create a transaction and you just execute the callback&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// does not exist, create a Prisma transaction &lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$transaction&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="nx"&gt;prisma&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="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runPromise&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="c1"&gt;// and save the Transaction Client inside the CLS namespace to be retrieved later on&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PRISMA_CLIENT_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

          &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// execute the transaction callback&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// unset the transaction client when something goes wrong&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PRISMA_CLIENT_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&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;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;You can see that the Transaction Client is created inside this class and is saved inside the CLS namespace. Hence, the repositories who want to use the Prisma Client can retrieve it from the CLS indirectly.&lt;/p&gt;

&lt;p&gt;Is this it? Actually, no. There's one more point you have to be careful when using transactions in Prisma. It's that the &lt;code&gt;prisma&lt;/code&gt; instance inside the transaction callback has different types than the original &lt;code&gt;prisma&lt;/code&gt; instance. You can see this in the type definitions:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TransactionClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$connect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$disconnect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$on&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$transaction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$use&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;Be aware that the &lt;code&gt;$transaction&lt;/code&gt; method is being &lt;code&gt;Omit&lt;/code&gt;ted. So, you can see that at this moment you cannot create nested transactions using Prisma.&lt;/p&gt;

&lt;p&gt;To deal with this, I've created a &lt;code&gt;PrismaClientManager&lt;/code&gt; which returns a Transaction Prisma Client if it exists, and if not, returns the original Prisma Client. Here's the implementation:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrismaClientManager&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;transactionContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transactionContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transactionContext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;getClient&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TransactionClient&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;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;PRISMA_CLIENT_KEY&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TransactionClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prisma&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;prisma&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prisma&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;It's simple, but notice that the return type is &lt;code&gt;Prisma.TransactionClient&lt;/code&gt;. This means that the Prisma Client returned from this &lt;code&gt;PrismaClientManager&lt;/code&gt; always returns the &lt;code&gt;Prisma.TransactionClient&lt;/code&gt; type. Therefore, this client cannot create a transaction.&lt;/p&gt;

&lt;p&gt;This is the constraint I made in order to achieve this cross module transaction using Prisma. In other words, you cannot call &lt;code&gt;prisma.$transaction&lt;/code&gt; from within repositories. Instead, you always use the &lt;code&gt;TransactionScope&lt;/code&gt; class I mentioned above.&lt;/p&gt;

&lt;p&gt;It will create transactions if needed, and won't if it isn't necessary. So, from repositories, you can write code like this:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrismaOrderRepository&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OrderRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;clientManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PrismaClientManager&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;transactionScope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TransactionScope&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;clientManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PrismaClientManager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;transactionScope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TransactionScope&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientManager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;clientManager&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionScope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transactionScope&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="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// you don't need to care if you're inside a transaction or not&lt;/span&gt;
    &lt;span class="c1"&gt;// just use the TransactionScope&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&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;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getClient&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;newOrder&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;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="k"&gt;for &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;productId&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productIds&lt;/span&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;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orderProduct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="na"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newOrder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;productId&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;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;If the repository is used inside a transaction, no transaction will be created again (thanks to the &lt;code&gt;PrismaClientManager&lt;/code&gt;). If the repository is used outside a transaction, a transaction will be created and consistency will be kept between the &lt;code&gt;Order&lt;/code&gt; and &lt;code&gt;OrderProduct&lt;/code&gt; data.&lt;/p&gt;

&lt;p&gt;Finally, with the power of the &lt;code&gt;TransactionScope&lt;/code&gt; class, you can create a transaction from the application layer as follows:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateOrder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;orderRepo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrderRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;notificationRepo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NotificationRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;transactionScope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TransactionScope&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;orderRepo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrderRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;notificationRepo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NotificationRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;transactionScope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TransactionScope&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orderRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;orderRepo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notificationRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notificationRepo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionScope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transactionScope&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="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;productIds&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;CreateOrderInput&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;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productIds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// create a transaction scope inside the Application layer&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&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="c1"&gt;// call multiple repository methods inside the transaction&lt;/span&gt;
      &lt;span class="c1"&gt;// if either fails, the transaction will rollback&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orderRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notificationRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Successfully created order: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="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;Notice that the &lt;code&gt;OrderRepository&lt;/code&gt; and &lt;code&gt;NotificationRepository&lt;/code&gt; are inside the same transaction and therefore, if the Notification fails, you can rollback the data which was saved from the &lt;code&gt;OrderRepository&lt;/code&gt; (leave the architecture decision for now 😂. you get the point.). Therefore, you don't have to mix the database responsibilities with the notification responsibilities.&lt;/p&gt;
&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;I've shown how you can create a TransactionScope using Prisma in Node.js. It's not ideal, but looks like it's working as expected. I've seen people struggling about this architecture and hope this post comes in some kind of help.&lt;/p&gt;

&lt;p&gt;Feedbacks are extremely welcome!&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/kenfdev" rel="noopener noreferrer"&gt;
        kenfdev
      &lt;/a&gt; / &lt;a href="https://github.com/kenfdev/prisma-auto-transaction-poc" rel="noopener noreferrer"&gt;
        prisma-auto-transaction-poc
      &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;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Prisma cross module transaction PoC&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;This is a PoC to see if cross module transaction is possible with Prisma.&lt;/p&gt;

&lt;p&gt;Despite Prisma being able to use interactive transaction, it forces you to use a newly created &lt;code&gt;Prisma.TransactionClient&lt;/code&gt; as follows:&lt;/p&gt;

&lt;div class="highlight highlight-source-ts notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;// copied from official docs https://www.prisma.io/docs/concepts/components/prisma-client/transactions#batchbulk-operations&lt;/span&gt;
&lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;prisma&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;$transaction&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-k"&gt;async&lt;/span&gt; &lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;prisma&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-c"&gt;// 1. Decrement amount from the sender.&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;sender&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;prisma&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;account&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;update&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-c1"&gt;data&lt;/span&gt;: &lt;span class="pl-kos"&gt;{&lt;/span&gt;
      &lt;span class="pl-c1"&gt;balance&lt;/span&gt;: &lt;span class="pl-kos"&gt;{&lt;/span&gt;
        &lt;span class="pl-c1"&gt;decrement&lt;/span&gt;: &lt;span class="pl-s1"&gt;amount&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
      &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
    &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
    &lt;span class="pl-c1"&gt;where&lt;/span&gt;: &lt;span class="pl-kos"&gt;{&lt;/span&gt;
      &lt;span class="pl-c1"&gt;email&lt;/span&gt;: &lt;span class="pl-s1"&gt;from&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
    &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-c"&gt;// 2. Verify that the sender's balance didn't go below zero.&lt;/span&gt;
  &lt;span class="pl-k"&gt;if&lt;/span&gt; &lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;sender&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;balance&lt;/span&gt; &lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt; &lt;span class="pl-c1"&gt;0&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-k"&gt;throw&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-smi"&gt;Error&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;`&lt;span class="pl-s1"&gt;&lt;span class="pl-kos"&gt;${&lt;/span&gt;&lt;span class="pl-s1"&gt;from&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/span&gt; doesn't have enough to send &lt;span class="pl-s1"&gt;&lt;span class="pl-kos"&gt;${&lt;/span&gt;&lt;span class="pl-s1"&gt;amount&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/span&gt;`&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;
  &lt;span class="pl-c"&gt;// 3. Increment the recipient's balance by amount&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;recipient&lt;/span&gt; &lt;span class="pl-c1"&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/kenfdev/prisma-auto-transaction-poc" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
      <category>prisma</category>
      <category>node</category>
      <category>database</category>
      <category>transaction</category>
    </item>
    <item>
      <title>Policy enforcement with Vue.js and OPA WebAssembly</title>
      <dc:creator>Ken Fukuyama</dc:creator>
      <pubDate>Mon, 16 Dec 2019 08:24:05 +0000</pubDate>
      <link>https://dev.to/kenfdev/policy-enforcement-with-vue-js-and-opa-webassembly-3doe</link>
      <guid>https://dev.to/kenfdev/policy-enforcement-with-vue-js-and-opa-webassembly-3doe</guid>
      <description>&lt;p&gt;Recently, there was an exciting announcement from OPA about "Rego on WebAssembly".&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="https://blog.openpolicyagent.org/opa-v0-15-1-rego-on-webassembly-81c226c51be4" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1d1P0gi9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/fit/c/96/96/1%2AgiqthN-QroOUSK1sQex9lA.png" alt="Torin Sandall"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://blog.openpolicyagent.org/opa-v0-15-1-rego-on-webassembly-81c226c51be4" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;OPA v0.15.1: Rego on WebAssembly. We’re excited to announce that with OPA… | by Torin Sandall | Open Policy Agent&lt;/h2&gt;
      &lt;h3&gt;Torin Sandall ・ &lt;time&gt;Nov 19, 2019&lt;/time&gt; ・ 5 min read
      &lt;div class="ltag__link__servicename"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KBvj_QRD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/medium_icon-90d5232a5da2369849f285fa499c8005e750a788fdbf34f5844d5f2201aae736.svg" alt="Medium Logo"&gt;
        blog.openpolicyagent.org
      &lt;/div&gt;
    &lt;/h3&gt;
&lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;As explained in the post above, this will open lots of possibilities to &lt;strong&gt;enforce policies&lt;/strong&gt; to all kinds of places. One out of many I wanted to try was in the Front-end world, especially with a SPA like Vue.js. This post describes how I integrated OPA WebAssembly with Vue.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;If you want to jump right to a working example, here's the repo for the Proof of Concept.&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--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/kenfdev"&gt;
        kenfdev
      &lt;/a&gt; / &lt;a href="https://github.com/kenfdev/vue-opa-wasm"&gt;
        vue-opa-wasm
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      PoC OPA WASM integration with Vue.js
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Basic knowledge about &lt;a href="https://www.openpolicyagent.org/"&gt;Open Policy Agent&lt;/a&gt; and the &lt;a href="https://www.openpolicyagent.org/docs/latest/policy-language/"&gt;Rego Language&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Basic knowledge about &lt;a href="https://vuejs.org/"&gt;Vue.js&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;What I did for this PoC is listed below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Rego file and declare rules&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;wasm&lt;/code&gt; file from Rego files with the OPA CLI&lt;/li&gt;
&lt;li&gt;Prepare Vue.js to use &lt;code&gt;wasm&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;Create and inject a &lt;code&gt;policy&lt;/code&gt; instance inside Vue.js&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;policy&lt;/code&gt; instance inside Vue.js templates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dig into details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Details
&lt;/h2&gt;

&lt;p&gt;For simplicity, I've followed the &lt;a href="https://github.com/open-policy-agent/npm-opa-wasm/tree/master/examples/nodejs-app"&gt;nodejs-app&lt;/a&gt; example to create the wasm but I'm going to briefly explain about it anyway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a Rego file and declare rules
&lt;/h3&gt;

&lt;p&gt;The Rego for this example is extremely simple. The following is the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# example.rego
package example

default hello = false

hello {
    x := input.message
    x == data.world
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It means the &lt;code&gt;hello&lt;/code&gt; rule will be &lt;code&gt;true&lt;/code&gt; if "The &lt;code&gt;message&lt;/code&gt; key's &lt;code&gt;value&lt;/code&gt; of the &lt;code&gt;input&lt;/code&gt; equals the value of &lt;code&gt;data.world&lt;/code&gt;".&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a &lt;code&gt;wasm&lt;/code&gt; file from Rego files with the OPA CLI
&lt;/h3&gt;

&lt;p&gt;After the Rego file has been created, the Wasm needs to be built using the OPA CLI. Compiling Rego files to Wasm is added in v0.15.1 of the OPA CLI so you need to download the version newer than that:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/open-policy-agent/opa/releases"&gt;https://github.com/open-policy-agent/opa/releases&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The actual command to build the Wasm file is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;opa build -d example.rego 'data.example = x'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;One thing to note is that, as &lt;a href="https://www.openpolicyagent.org/docs/latest/wasm/#compiling-policies"&gt;explained in the docs&lt;/a&gt;, you need to specify a &lt;code&gt;query&lt;/code&gt; to be used for the Wasm at compile time. In this example, I have queried the entire &lt;code&gt;data.example&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;If the compile succeeds, you will see a &lt;code&gt;policy.wasm&lt;/code&gt; file in the path you executed the command.&lt;/p&gt;

&lt;p&gt;Preparing the Wasm to be used in the front-end completes here. Next, we'll dive into how this can be integrated in Vue.js.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prepare Vue.js to use &lt;code&gt;wasm&lt;/code&gt; files
&lt;/h3&gt;

&lt;p&gt;At the moment, OPA provides a light-weight sdk to use Wasm wih JavaScript called &lt;a href="https://www.npmjs.com/package/@open-policy-agent/opa-wasm"&gt;@open-policy-agent/opa-wasm&lt;/a&gt;. In this library, you can pass a Wasm &lt;code&gt;ArrayBuffer&lt;/code&gt; to load Rego policies to be used by JavaScript.&lt;/p&gt;

&lt;p&gt;For simplicity, I decided to load the Wasm at build time of the Vue.js app, and have chosen to use the &lt;a href="https://www.npmjs.com/package/arraybuffer-loader"&gt;arraybuffer-loader&lt;/a&gt; to load the wasm with the &lt;code&gt;import&lt;/code&gt; syntax via webpack.&lt;/p&gt;

&lt;p&gt;To add a loader for the Vue.js app, you'll need to create and modify a &lt;code&gt;vue.config.js&lt;/code&gt; file as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// vue.config.js&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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;chainWebpack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&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="nx"&gt;module&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;arraybuffer&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;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;javascript/auto&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;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;wasm$/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;arraybuffer-loader&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;loader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;arraybuffer-loader&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;With this configuration, you can load the wasm like this in your JavaScript files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;wasm&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./assets/policy.wasm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: Be sure to add the &lt;code&gt;type('javascript/auto')&lt;/code&gt; line or else loading the wasm file will fail. &lt;a href="https://github.com/pine/arraybuffer-loader/issues/12#issuecomment-390834140"&gt;This issue&lt;/a&gt; helped me solve it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Create and inject a &lt;code&gt;policy&lt;/code&gt; instance inside Vue.js
&lt;/h3&gt;

&lt;p&gt;Now that we have everything prepared, let's load the Wasm before we instantiate the Vue application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Rego&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@open-policy-agent/opa-wasm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;wasm&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./assets/policy.wasm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rego&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Rego&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;rego&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;load_policy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wasm&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;policy&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// add the policy instance to the Vue.prototype&lt;/span&gt;
  &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$policy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;$mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&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;p&gt;Fortunately, the &lt;code&gt;opa-wasm&lt;/code&gt; SDK let's us easily load the Wasm as a policy instance by calling the &lt;code&gt;rego.load_policy&lt;/code&gt; method. The promise returns a &lt;code&gt;policy&lt;/code&gt; instance so I've added this instance to the &lt;code&gt;Vue.prototype&lt;/code&gt; in order for it to be used in the Vue templates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use the &lt;code&gt;policy&lt;/code&gt; instance inside Vue.js templates
&lt;/h3&gt;

&lt;p&gt;Here's an example on how we can use the &lt;code&gt;$policy&lt;/code&gt; instance inside the Vue templates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;{{ msg }}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;pre&amp;gt;&lt;/span&gt;{{ $policy.evaluate({ message: "world" }) }}&lt;span class="nt"&gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&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;HelloWorld&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;created&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// set the data.world to "world"&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;set_data&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;world&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;world&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I've set the &lt;code&gt;data.world&lt;/code&gt; to &lt;code&gt;"world"&lt;/code&gt; at the &lt;code&gt;created&lt;/code&gt; life cycle of the component. And in the template itself, I'm calling &lt;code&gt;$policy.evaluate&lt;/code&gt; with &lt;code&gt;input.message&lt;/code&gt; set to &lt;code&gt;"world"&lt;/code&gt;. Since both &lt;code&gt;data.world&lt;/code&gt; and &lt;code&gt;input.world&lt;/code&gt; have the same value &lt;code&gt;"world"&lt;/code&gt;, the &lt;code&gt;hello&lt;/code&gt; rule evaluates to &lt;code&gt;true&lt;/code&gt;. You can check it out in the following screen capture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LuBgt4Kz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/WnymqcK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LuBgt4Kz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/WnymqcK.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;In this post, I've shown you how to build an OPA Wasm from a Rego file and use it inside a Vue application.  I used Vue.js in this post but anything JavaScript should be able to use the &lt;code&gt;policy&lt;/code&gt; instance in the same way.&lt;/p&gt;

&lt;p&gt;Being able to use the &lt;code&gt;policy&lt;/code&gt; instance in the front-end is going to be extremely powerful. I can enforce policies nearly the same way in the front-end as I would in the back-end using the OPA server.&lt;/p&gt;

&lt;p&gt;The application above is just a PoC to show you that using Rego policies in the front-end is &lt;strong&gt;possible&lt;/strong&gt;. I can think of few things to improve at the moment such as:&lt;/p&gt;

&lt;h4&gt;
  
  
  Dynamically fetch wasm
&lt;/h4&gt;

&lt;p&gt;I've imported the Wasm using the &lt;code&gt;import&lt;/code&gt; statement but this won't scale. Perhaps one would like to prepare a Wasm per logged in user. It would probably be better to fetch the Wasm dynamically on the fly, and then load and convert it to a &lt;code&gt;policy&lt;/code&gt; instance to be used.&lt;/p&gt;

&lt;h4&gt;
  
  
  Built-ins
&lt;/h4&gt;

&lt;p&gt;At the time of writing, built-ins cannot be used in the Rego files unless you implement them by yourself. built-ins are extremely powerful and makes your Rego files more readable and performant. It would be nice to implement these in JavaScript to be used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stay tuned!
&lt;/h2&gt;

&lt;p&gt;The OPA Wasm is still in its early stages but is actively being developed. Let's keep an eye on the &lt;a href="https://www.openpolicyagent.org/docs/latest/wasm/"&gt;official docs&lt;/a&gt; and the &lt;a href="https://slack.openpolicyagent.org/"&gt;Slack community&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>opa</category>
      <category>vue</category>
      <category>webassembly</category>
      <category>rego</category>
    </item>
    <item>
      <title>Continuously enforce policies on your configs with Conftest and CircleCI</title>
      <dc:creator>Ken Fukuyama</dc:creator>
      <pubDate>Mon, 21 Oct 2019 15:12:10 +0000</pubDate>
      <link>https://dev.to/kenfdev/continuously-enforce-policies-on-your-configs-with-conftest-and-circleci-1afd</link>
      <guid>https://dev.to/kenfdev/continuously-enforce-policies-on-your-configs-with-conftest-and-circleci-1afd</guid>
      <description>&lt;p&gt;I'm assuming many engineers have struggled to enforce some kind of policy (e.g. style guides, best practices) on their structured data (especially configuration data). Code linting tools do a really good job in this area (e.g. eslint, golangci-lint, etc.) and I can't imagine working with colleagues without linters any more. What I wanted was to have a similar experience with my configurations such as YAML files and remembered watching a very interesting presentation at KubeCon called "Unit Testing Your Kubernetes Configuration with Open Policy Agent" by &lt;a href="https://twitter.com/garethr"&gt;@garethr&lt;/a&gt;:&lt;/p&gt;


&lt;div class="ltag_speakerdeck"&gt;
  &lt;iframe height="463" id="talk_frame_7b9164fa3042476aaf1073d5109999a4" src="//speakerdeck.com/player/7b9164fa3042476aaf1073d5109999a4" width="710"&gt;&lt;/iframe&gt;
&lt;/div&gt;


&lt;p&gt;Conftest and &lt;a href="https://openpolicyagent.org"&gt;Open Policy Agent&lt;/a&gt; are the key points here.&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--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/open-policy-agent"&gt;
        open-policy-agent
      &lt;/a&gt; / &lt;a href="https://github.com/open-policy-agent/conftest"&gt;
        conftest
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Write tests against structured configuration data using the Open Policy Agent Rego query language
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&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--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/open-policy-agent"&gt;
        open-policy-agent
      &lt;/a&gt; / &lt;a href="https://github.com/open-policy-agent/opa"&gt;
        opa
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An open source, general-purpose policy engine.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;If you are new to &lt;a href="https://github.com/instrumenta/conftest"&gt;Conftest&lt;/a&gt; and &lt;a href="https://github.com/open-policy-agent/opa"&gt;Open Policy Agent&lt;/a&gt;, here is an interesting read written by &lt;a href="https://twitter.com/LennardNL/"&gt;@LennardNL&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.blokje5.dev/posts/validating-terraform-plans/"&gt;Validating Terraform plans with the Open Policy Agent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.blokje5.dev/posts/compliance-in-cicd/"&gt;Building in compliance in your CI/CD pipeline with conftest&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm not digging into details about Conftest and Open Policy Agent in this post, so I definitely recommend reading the posts above (otherwise, this post might not make any sense to you).&lt;/p&gt;

&lt;p&gt;What I wanted to do is continuously enforce my policies in &lt;a href="https://circleci.com"&gt;CircleCI&lt;/a&gt;. Also, since I use CircleCI in vast amounts of projects, I wanted to easily be able to use it inside my CI and without polluting my &lt;code&gt;circleci/config.yml&lt;/code&gt;. As a result, I made a CircleCI Orbs for conftest called, without surprise, &lt;a href="https://circleci.com/orbs/registry/orb/kenfdev/conftest-orb"&gt;conftest-orb&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let me show how you can use this in the further sections of this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;The simplest CircleCI config YAML for conftest-orb would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.1&lt;/span&gt;
&lt;span class="na"&gt;orbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;conftest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kenfdev/conftest-orb@x.y&lt;/span&gt;
&lt;span class="na"&gt;workflows&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;conftest/test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;pre-steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;checkout&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;config_to_test.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Note that there are some prerequisites in order for this pipeline to work such as the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;config_to_test.yaml&lt;/code&gt; is in the root of your repository&lt;/li&gt;
&lt;li&gt;You have the Rego policies in a directory called &lt;code&gt;policy&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the above in mind, this CircleCI workflow will enforce your policy on &lt;code&gt;config_to_test.yaml&lt;/code&gt;. Simple isn't it?&lt;/p&gt;
&lt;h2&gt;
  
  
  Example with serverless.yaml
&lt;/h2&gt;

&lt;p&gt;I've created an example with the &lt;a href="https://serverless.com"&gt;Serverless Framework&lt;/a&gt; YAML which I just copied from the &lt;a href="https://github.com/instrumenta/conftest/tree/master/examples/serverless"&gt;conftest examples&lt;/a&gt; and integrated with CircleCI:&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--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/kenfdev"&gt;
        kenfdev
      &lt;/a&gt; / &lt;a href="https://github.com/kenfdev/conftest-serverless-circleci"&gt;
        conftest-serverless-circleci
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Let's take a look at the &lt;code&gt;.circleci/config.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.1&lt;/span&gt;
&lt;span class="na"&gt;orbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;conftest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kenfdev/conftest-orb@0.0.8&lt;/span&gt;
&lt;span class="na"&gt;workflows&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;conftest/test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;pre-steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;checkout&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;serverless.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;You can see how the prerequisites explained above are satisfied with the following file structure:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kenfdev/conftest-serverless-circleci
├── policy
│   ├── base.rego
│   └── util.rego
└── serverless.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;serverless.yaml&lt;/code&gt; which will be under test looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-python-scheduled-cron&lt;/span&gt;

&lt;span class="na"&gt;frameworkVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;=1.2.0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;2.0.0'&lt;/span&gt;

&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python2.7&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;this&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;field&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;required'&lt;/span&gt;

&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;handler.run&lt;/span&gt;
    &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python2.7&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cron(0/2 * ? * MON-FRI *)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I'm not going into details about the &lt;a href="https://github.com/kenfdev/conftest-serverless-circleci/blob/master/policy/base.rego"&gt;rego files&lt;/a&gt; but the policies which are going to be enforced are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should set &lt;code&gt;provider&lt;/code&gt; &lt;code&gt;tags&lt;/code&gt; for author&lt;/li&gt;
&lt;li&gt;Python 2.7 cannot be the default &lt;code&gt;provider&lt;/code&gt; &lt;code&gt;runtime&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Python 2.7 cannot be used as the &lt;code&gt;runtime&lt;/code&gt; for &lt;code&gt;functions&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see how the first policy is satisfied, but the latter two aren't. Hence, when the CircleCI runs it will fail and you'll see something like the following screen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7Ra1srZZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/eomks05bqoz21td214nr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7Ra1srZZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/eomks05bqoz21td214nr.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Centralizing your Rego policies
&lt;/h2&gt;

&lt;p&gt;Looking good! But wait a minute. Keeping the policies inside every single repository doesn't seem like a good idea (I can smell something DRY...). But fear not, this is also an area where conftest shines. With the power of &lt;a href="https://github.com/instrumenta/conftest#configuration-and-external-policies"&gt;push and pull&lt;/a&gt;, conftest can save and load &lt;strong&gt;external policies&lt;/strong&gt; from OCI registries. I'm no expert in OCI registries, but I know that the &lt;a href="https://hub.docker.com/_/registry"&gt;Docker Registry&lt;/a&gt; is OCI compatible.&lt;/p&gt;

&lt;p&gt;Since I don't want to pay for a self-hosted Docker Registry (at least for now), I've came up with a hack to embed the policies inside the container image via CircleCI. Here's the repository where I save policies for the CircleCI orb YAML in order to enforce &lt;a href="https://circleci.com/docs/2.0/orbs-best-practices/#orb-best-practices-guidelines"&gt;best practices mentioned in the docs&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://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/kenfdev"&gt;
        kenfdev
      &lt;/a&gt; / &lt;a href="https://github.com/kenfdev/conftest-circleci-orb-policies"&gt;
        conftest-circleci-orb-policies
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;I'm not digging into details here either but the following diagram is a rough picture of how the Docker Registry Image gets built in the CI (and here's the &lt;a href="https://github.com/kenfdev/conftest-circleci-orb-policies/blob/master/.circleci/config.yml"&gt;config&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3bKZH78e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/66bhgmwixtjsfwak1zvc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3bKZH78e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/66bhgmwixtjsfwak1zvc.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that I have an OCI registry which includes policies out of the box, I can use them from the CircleCI orbs. The cool thing about &lt;code&gt;conftest-orb&lt;/code&gt; development is that in each CI, I'm running integration tests to test the features of the orb, and &lt;strong&gt;at the same time&lt;/strong&gt; I'm enforcing the CircleCI best practices on the &lt;code&gt;orb.yml&lt;/code&gt;! It's a pretty cool developer experience to be able to dogfood your project inside the CI. &lt;/p&gt;

&lt;p&gt;The following is how the orbs' integration test looks like (full code &lt;a href="https://github.com/kenfdev/conftest-orb/blob/c151f1914d1c3025985b5640716e3e6b5139debc/.circleci/config.yml#L66-L85"&gt;here&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;general_usecase_test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;executor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;machine&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;checkout&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;circleci-cli/install&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pack the orb.yml&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;circleci config pack src &amp;gt; orb.yml&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;conftest/install&lt;/span&gt;
      &lt;span class="c1"&gt;# start the OCI registry(this command is declared in a different place)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;start_oci_registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kenfdev/circleci-orbs-policies&lt;/span&gt;
      &lt;span class="c1"&gt;# pull the policies from the OCI registry&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;conftest/pull&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;policy_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;policy&lt;/span&gt;
          &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;127.0.0.1:5000/policies:latest&lt;/span&gt;
      &lt;span class="c1"&gt;# test with minimum options&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;conftest/test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;policy_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;policy&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orb.yml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It looks a bit verbose but that is because I need to spin up the Docker Registry in the CI. If you already have an OCI registry running outside, all you have to write is something like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.1&lt;/span&gt;
&lt;span class="na"&gt;orbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;conftest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kenfdev/conftest-orb@0.0.8&lt;/span&gt;
&lt;span class="na"&gt;workflows&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;conftest/test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;pre-steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;checkout&lt;/span&gt;
          &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;path-to-your-oci-registry&amp;gt;&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;serverless.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This will pull your policies from &lt;code&gt;&amp;lt;path-to-your-oci-registry&amp;gt;&lt;/code&gt; and run &lt;code&gt;conftest&lt;/code&gt; to on the &lt;code&gt;file&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to the OCI registry feature, I can now create several CircleCI orbs and enforce the same policy to all of them via this &lt;code&gt;conftest-orb&lt;/code&gt;. Isn't this pretty awesome? Let's wrap up! &lt;/p&gt;
&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;In this post I showed how you can enforce policies in your CircleCI pipeline using conftest orbs. By using the orbs you can easily start enforcing policies to your structured data. IMHO, sharing policies is still a bit tricky but there is an interesting PR waiting to be merged here:&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/instrumenta/conftest/pull/107"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg"&gt;
      &lt;span class="issue-title"&gt;
        Supporting one-liner policy pulls + http/https/s3/gcs/git/etc via Go-getter integration into conftest
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#107&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/Blokje5"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--3SVDjM83--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars3.githubusercontent.com/u/26072918%3Fv%3D4" alt="Blokje5 avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/Blokje5"&gt;Blokje5&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/instrumenta/conftest/pull/107"&gt;&lt;time&gt;Oct 11, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;Related issues: #102 #101&lt;/p&gt;
&lt;p&gt;As suggested by #nicolasbernard using go-getter can be a good way to get support for multiple sources of Rego files. I did a small PoC to check if it was possible to integrate OCI registry pulling with go-getter, which worked well. Now the question is: should we use it or not.&lt;/p&gt;
&lt;p&gt;pros: Support for http, https, s3, gcs, git out of the box. Checksumming. Support for decompression.
cons: Assymetrical, so no pull support, which could lead to a bad Ux for conftest push/pull. Might lead to unexpected bugs. Not backwards competable (now you need to specify &lt;code&gt;oci://localhost:5000/policies:tag&lt;/code&gt; when using a non-azure registry).&lt;/p&gt;
&lt;p&gt;Please let me know what you think. Should we move forward with &lt;a href="https://github.com/hashicorp/go-getter"&gt;go-getter&lt;/a&gt;? It does over a lot to the project as we could immediately support pulling policies from blob-storage, git and http.&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/instrumenta/conftest/pull/107"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;If this gets merged, conftest will be able to fetch policies via &lt;code&gt;http/https/s3/gcs/git/etc&lt;/code&gt;, which will open a wide range of possibilities to centralize your Rego policies! This is going to be &lt;strong&gt;REALLY&lt;/strong&gt; exciting!&lt;/p&gt;

&lt;h3&gt;
  
  
  Open Policy Agent
&lt;/h3&gt;

&lt;p&gt;Another important thing I haven't mentioned much in this post is &lt;a href="https://openpolicyagent.org"&gt;Open Policy Agent&lt;/a&gt;, the policy engine which Conftest uses under the hood. I really recommend taking a look at this project and getting your hands dirty with the &lt;a href="https://www.openpolicyagent.org/docs/latest/#rego"&gt;Rego language&lt;/a&gt;. It's a bit tricky at first but after you get used to it, the flexibility is extremely powerful.&lt;/p&gt;

&lt;p&gt;You can join the super supportive community &lt;a href="https://slack.openpolicyagent.org"&gt;here&lt;/a&gt;. Also, there is a #conftest channel specific to Conftest.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try it yourself!
&lt;/h3&gt;

&lt;p&gt;If you find this post interesting, please give &lt;a href="https://circleci.com/orbs/registry/orb/kenfdev/conftest-orb"&gt;conftest-orb&lt;/a&gt; a try! Feedbacks will be greatly appreciated :)&lt;/p&gt;

</description>
      <category>conftest</category>
      <category>openpolicyagent</category>
      <category>circleci</category>
      <category>rego</category>
    </item>
    <item>
      <title>Merge 2 Objects in Rego</title>
      <dc:creator>Ken Fukuyama</dc:creator>
      <pubDate>Tue, 15 Oct 2019 21:25:36 +0000</pubDate>
      <link>https://dev.to/kenfdev/merge-2-objects-in-rego-k83</link>
      <guid>https://dev.to/kenfdev/merge-2-objects-in-rego-k83</guid>
      <description>&lt;p&gt;This is the final post of "Object Merging in Rego" series.&lt;/p&gt;

&lt;p&gt;If you are not comfortable with Rego yet, I'd recommend you read the previous posts before continuing.&lt;/p&gt;

&lt;p&gt;When the keys collide with the merging objects, let's say &lt;strong&gt;the latter object's value wins&lt;/strong&gt;. With that in mind, in order to merge objects, we need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Gather the keys of the 2 objects&lt;/li&gt;
&lt;li&gt;Pick the values of the objects and if the keys collide, pick the latter object's value&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In order to implement this, "&lt;a href="https://www.openpolicyagent.org/docs/latest/policy-language/#set-comprehensions"&gt;Set Comprehensions&lt;/a&gt;" and "&lt;a href="https://www.openpolicyagent.org/docs/latest/policy-language/#object-comprehensions"&gt;Object Comprehensions&lt;/a&gt;" come in handy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gather keys of 2 objects
&lt;/h3&gt;

&lt;p&gt;Suppose we have 2 objects like the following:&lt;/p&gt;

&lt;h5&gt;
  
  
  object1
&lt;/h5&gt;



&lt;div class="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="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value2"&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;h5&gt;
  
  
  object2
&lt;/h5&gt;


&lt;div class="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="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"c"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value4"&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;If we're merging these 2 objects, what we first want is the keys &lt;code&gt;a, b, c&lt;/code&gt;. How can we gather these? With Set comprehensions.&lt;/p&gt;

&lt;p&gt;This is how it will look like in Rego:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;collect_keys = ks {
  ks := {k | some k; _ = input.a[k]} | {k | some k; _ = input.b[k]}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Maybe it looks a bit difficult for beginners but you'll get used to it sooner or later.&lt;/p&gt;

&lt;p&gt;Let's look at one part of it:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{k | some k; _ = input.a[k]}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This is like saying &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Iterate over &lt;code&gt;input.a&lt;/code&gt;'s keys&lt;/li&gt;
&lt;li&gt;Put it inside the local variable &lt;code&gt;k&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Collect it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if &lt;code&gt;input.a&lt;/code&gt; is &lt;code&gt;{ "a": "value1", "b": "value2" }&lt;/code&gt;, the result will be &lt;code&gt;{ "a", "b" }&lt;/code&gt;. In addition, when the &lt;code&gt;input.b&lt;/code&gt; is &lt;code&gt;{ "b": "value3", "c": "value4" }&lt;/code&gt;, the result will be &lt;code&gt;{ "b", "c" }&lt;/code&gt; and the rule above will look like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;collect_keys = ks {
  ks := { "a", "b" } | { "b", "c" }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Which evaluates to &lt;code&gt;ks == { "a", "b", "c" }&lt;/code&gt; because it is evaluating an OR with 2 sets.&lt;/p&gt;

&lt;p&gt;You can check this out in the following playground:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://play.openpolicyagent.org/p/bw0aM0bRHM"&gt;https://play.openpolicyagent.org/p/bw0aM0bRHM&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Pick values from 2 objects
&lt;/h3&gt;

&lt;p&gt;Now that we have the keys of the final merged object, we need to gather the values of the 2 objects. What we'd want to do is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Iterate over the keys we gathered above&lt;/li&gt;
&lt;li&gt;Pick the value corresponding with the key. Be sure to prioritize the latter object's value over the former one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is where the &lt;code&gt;pick_first&lt;/code&gt; function comes in handy from the 2nd post of this series.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/kenfdev" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iKacRd5Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--KVqtvmIk--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/175198/9c0d38c3-8cf3-4997-999e-b8b3184879c8.jpg" alt="kenfdev image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/kenfdev/picking-the-first-value-you-find-from-2-objects-in-rego-o6c" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Picking the first value you find from 2 objects in Rego&lt;/h2&gt;
      &lt;h3&gt;Ken Fukuyama ・ Sep 30 '19 ・ 2 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#rego&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#opa&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#openpolicyagent&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;p&gt;Assuming that &lt;code&gt;pick_first&lt;/code&gt; has already been declared, the following Rego function shows how 2 objects get merged.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;merge_objects(a, b) = c {
    ks := {k | some k; _ = a[k]} | {k | some k; _ = b[k]}
    c := {k: v | some k; ks[k]; v := pick_first(k, b, a)}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note that the object &lt;code&gt;b&lt;/code&gt; comes first and object &lt;code&gt;a&lt;/code&gt; next in the &lt;code&gt;pick_first&lt;/code&gt; function which means that the values of object &lt;code&gt;b&lt;/code&gt; are prioritized.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrap up
&lt;/h3&gt;

&lt;p&gt;The final version of merging 2 objects in Rego looks like this (which you can find &lt;a href="https://www.openpolicyagent.org/docs/latest/policy-cheatsheet/#merge-objects"&gt;here&lt;/a&gt; in the official docs):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;has_key(x, k) { _ = x[k] }

pick_first(k, a, b) = a[k]
pick_first(k, a, b) = b[k] { not has_key(a, k) }

merge_objects(a, b) = c {
    ks := {k | some k; _ = a[k]} | {k | some k; _ = b[k]}
    c := {k: v | some k; ks[k]; v := pick_first(k, b, a)}
}

merged = o {
  o := merge_objects(input.x, input.y)
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If the &lt;code&gt;input&lt;/code&gt; is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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="nl"&gt;"x"&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;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&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;"y"&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;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"c"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;merged&lt;/code&gt; output becomes like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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="nl"&gt;"merged"&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;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"c"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note that object &lt;code&gt;y&lt;/code&gt;'s key/value &lt;code&gt;"b": "foo"&lt;/code&gt; is chosen because it is the latter object when merging.&lt;/p&gt;

&lt;p&gt;For those who want to play around, here is the playground code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://play.openpolicyagent.org/p/LzJDiUQQFY"&gt;https://play.openpolicyagent.org/p/LzJDiUQQFY&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Reference
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.openpolicyagent.org/docs/latest/policy-language/#composite-values"&gt;Composite Values&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.openpolicyagent.org/docs/latest/policy-language/#comprehensions"&gt;Comprehensions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rego</category>
      <category>opa</category>
      <category>openpolicyagent</category>
    </item>
    <item>
      <title>Picking the first value you find from 2 objects in Rego</title>
      <dc:creator>Ken Fukuyama</dc:creator>
      <pubDate>Mon, 30 Sep 2019 13:13:15 +0000</pubDate>
      <link>https://dev.to/kenfdev/picking-the-first-value-you-find-from-2-objects-in-rego-o6c</link>
      <guid>https://dev.to/kenfdev/picking-the-first-value-you-find-from-2-objects-in-rego-o6c</guid>
      <description>&lt;p&gt;This post is about "how you can pick the first value you find from 2 objects" in &lt;a href="https://www.openpolicyagent.org/docs/latest/policy-language/"&gt;Rego&lt;/a&gt;, a query language used in &lt;a href="https://www.openpolicyagent.org/"&gt;OPA (Open Policy Agent)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This use case is a little confusing and you'll probably think "When is this necessary?". One example is shared in &lt;a href="https://www.openpolicyagent.org/docs/latest/policy-cheatsheet/#merge-objects"&gt;"Merge Objects"&lt;/a&gt; in the official docs.&lt;/p&gt;

&lt;p&gt;When merging multiple objects, you'll definitely need to think about &lt;strong&gt;colliding keys&lt;/strong&gt; and how to decide which &lt;code&gt;key&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt; to choose. This is where you'll want to use "Pick the first value you find from 2 objects".&lt;/p&gt;

&lt;p&gt;Let's dive into details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logical OR in Rego
&lt;/h2&gt;

&lt;p&gt;The function which "Picks the first value it finds from 2 objects" will be called &lt;code&gt;pick_first&lt;/code&gt;. Here's how the implementation will look like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;has_key(x, k) { 
    _ := x[k]
}

pick_first(k, a, b) = a[k]
pick_first(k, a, b) = b[k] { not has_key(a, k) }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;See how there are 2 definitions of &lt;code&gt;pick_first(k, a, b)&lt;/code&gt;. This is how you express a &lt;a href="https://www.openpolicyagent.org/docs/latest/#logical-or"&gt;Logical OR&lt;/a&gt; in Rego. So the expression above is like saying:&lt;/p&gt;

&lt;p&gt;"For key &lt;code&gt;k&lt;/code&gt;, choose the value from object &lt;code&gt;a&lt;/code&gt;, &lt;strong&gt;OR&lt;/strong&gt;, choose the value from object &lt;code&gt;b&lt;/code&gt; if object &lt;code&gt;a&lt;/code&gt; &lt;strong&gt;DOES NOT HAVE&lt;/strong&gt; key &lt;code&gt;k&lt;/code&gt;."&lt;/p&gt;

&lt;p&gt;If you feel uncomfortable with the &lt;code&gt;has_key&lt;/code&gt; function, look at my previous post specifically explaining about it here:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/kenfdev" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iKacRd5Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--KVqtvmIk--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/175198/9c0d38c3-8cf3-4997-999e-b8b3184879c8.jpg" alt="kenfdev image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/kenfdev/check-if-key-exists-in-object-in-rego-42pp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Check if key exists in object in Rego&lt;/h2&gt;
      &lt;h3&gt;Ken Fukuyama ・ Sep 28 '19 ・ 2 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#openpolicyagent&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#opa&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#rego&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;Here's a complete example demonstrating the usage of &lt;code&gt;pick_first&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;has_key(x, k) { 
    _ := x[k]
}

pick_first(k, a, b) = a[k]
pick_first(k, a, b) = b[k] { not has_key(a, k) }

x := {"a": true, "b": false}
y := {"b": "foo", "c": 4}

first_values = fv {
    a := pick_first("a", x, y)
    b := pick_first("b", x, y)
    c := pick_first("c", x, y)
    fv := {
        "a": a,
        "b": b,
        "c": c
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you evaluate &lt;code&gt;first_values&lt;/code&gt;, the result would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"c"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&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;Notice how key &lt;code&gt;false&lt;/code&gt; was chosen from object &lt;code&gt;x&lt;/code&gt;'s key &lt;code&gt;b&lt;/code&gt;. The only time the values of object &lt;code&gt;y&lt;/code&gt; are selected are when the key doesn't exist in object &lt;code&gt;x&lt;/code&gt; (e.g. key &lt;code&gt;c&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;You can check this out for yourself in the following playground:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://play.openpolicyagent.org/p/puwMTreKjD"&gt;https://play.openpolicyagent.org/p/puwMTreKjD&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.openpolicyagent.org/docs/latest/#logical-or"&gt;Logical OR in Rego&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.openpolicyagent.org/docs/latest/policy-language/#negation"&gt;Negation in Rego&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rego</category>
      <category>opa</category>
      <category>openpolicyagent</category>
    </item>
    <item>
      <title>Check if key exists in object in Rego</title>
      <dc:creator>Ken Fukuyama</dc:creator>
      <pubDate>Sat, 28 Sep 2019 04:41:41 +0000</pubDate>
      <link>https://dev.to/kenfdev/check-if-key-exists-in-object-in-rego-42pp</link>
      <guid>https://dev.to/kenfdev/check-if-key-exists-in-object-in-rego-42pp</guid>
      <description>&lt;p&gt;Recently, I'm writing &lt;a href="https://www.openpolicyagent.org/docs/latest/policy-language/#what-is-rego"&gt;Rego&lt;/a&gt;(a query language to use in &lt;a href="https://www.openpolicyagent.org/docs/latest/policy-language/#what-is-rego"&gt;Open Policy Agent&lt;/a&gt;) every day and have decided to post some tricky syntaxes that took me a little time to understand.&lt;/p&gt;

&lt;p&gt;In this post, I'm going to look at &lt;strong&gt;how to check if a key exists in an object&lt;/strong&gt; in Rego.&lt;/p&gt;

&lt;p&gt;I've pulled the sample code from the &lt;a href="https://www.openpolicyagent.org/docs/latest/policy-cheatsheet/#merge-objects"&gt;Policy Cheatsheet&lt;/a&gt; in the official docs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;has_key(x, k) { _ = x[k] }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Looks simple, but has a bit of a gotcha (at least for me) despite consuming the function is pretty intuitive (here's the &lt;a href="https://play.openpolicyagent.org/p/Bd9VsdhB1n"&gt;playground&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;has_key({"foo": "bar"}, "foo") # true
has_key({"foo": "bar"}, "baz") # undefined
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It's checking if key &lt;code&gt;k&lt;/code&gt; exists in object &lt;code&gt;x&lt;/code&gt;. What confused me at first is &lt;code&gt;_ = x[k]&lt;/code&gt;. Why do we need the &lt;code&gt;_ =&lt;/code&gt; part?&lt;/p&gt;

&lt;h3&gt;
  
  
  Without the "_ ="
&lt;/h3&gt;

&lt;p&gt;Let's see what happens if the function becomes like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;has_key(x, k) { x[k] }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;At first, this looks like it's working. But you have to think about the case where the value of &lt;code&gt;x[k]&lt;/code&gt; is &lt;strong&gt;actually&lt;/strong&gt; &lt;code&gt;false&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The return value of the function is &lt;code&gt;true&lt;/code&gt; whenever the function body is satisfied. That is the case when x[k] &lt;strong&gt;unifies to anything&lt;/strong&gt; &lt;em&gt;that is not &lt;code&gt;false&lt;/code&gt;&lt;/em&gt; -- if x[k] unifies to &lt;code&gt;false&lt;/code&gt;, the function body is not satisfied... (&lt;a href="https://www.openpolicyagent.org/docs/latest/faq/#functions-versus-rules"&gt;functions are not very different from rules&lt;/a&gt;). Here's an example(and the &lt;a href="https://play.openpolicyagent.org/p/BlEybGlNId"&gt;playground&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;has_key(x, k) { x[k] }

default foo_exists = false
foo_exists = has_key({"foo": false}, "foo") # false!!!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To avoid this unexpected behavior, the assigning(or union) part (&lt;code&gt;_ =&lt;/code&gt;) is mandatory.&lt;/p&gt;

&lt;h3&gt;
  
  
  With the "_ ="
&lt;/h3&gt;

&lt;p&gt;Let's put the &lt;code&gt;_ =&lt;/code&gt; back inside the function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;has_key(x, k) { _ = x[k] }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With this, the function body is satisfied if it unifies -- i.e. there's an x[k], not &lt;code&gt;undefined&lt;/code&gt; -- but it &lt;strong&gt;doesn't matter&lt;/strong&gt; if it's &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;, because the x[k] is put into a context where its value doesn't matter: &lt;code&gt;_ = y&lt;/code&gt; never fails, regardless of the value of y, as long as y is &lt;strong&gt;not&lt;/strong&gt; &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Any other construct that doesn't regard the value would work as well. Here are some alternatives (which are not &lt;em&gt;better&lt;/em&gt;, just &lt;em&gt;different&lt;/em&gt;): &lt;/p&gt;

&lt;p&gt;&lt;a href="https://play.openpolicyagent.org/p/eed4f2wVGS"&gt;https://play.openpolicyagent.org/p/eed4f2wVGS&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://play.openpolicyagent.org/p/LgJo6T0tUz"&gt;https://play.openpolicyagent.org/p/LgJo6T0tUz&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Wrap up
&lt;/h3&gt;

&lt;p&gt;Again, you can check the correct version in &lt;a href="https://play.openpolicyagent.org/p/Bd9VsdhB1n"&gt;this playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're still uncomfortable with the Rego syntax used inside the function, check out the &lt;a href="https://www.openpolicyagent.org/docs/latest/policy-language/#variable-keys"&gt;Policy Language#Variable Keys&lt;/a&gt; section in the official docs. Also, you can check the Policy &lt;a href="https://www.openpolicyagent.org/docs/latest/policy-cheatsheet/#objects-1"&gt;Cheatsheet#Objects&lt;/a&gt; section.&lt;/p&gt;

&lt;p&gt;Happy Rego coding!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;P.S. I'd like to greatly appreciate @srenatus from the OPA community for reviewing my post and giving me accurate advice!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>openpolicyagent</category>
      <category>opa</category>
      <category>rego</category>
    </item>
    <item>
      <title>Digging into API Authorization with OPA</title>
      <dc:creator>Ken Fukuyama</dc:creator>
      <pubDate>Tue, 02 Jul 2019 00:51:49 +0000</pubDate>
      <link>https://dev.to/kenfdev/digging-into-api-authorization-with-opa-3jnf</link>
      <guid>https://dev.to/kenfdev/digging-into-api-authorization-with-opa-3jnf</guid>
      <description>&lt;p&gt;I've recently been getting my hands on with &lt;a href="https://www.openpolicyagent.org/" rel="noopener noreferrer"&gt;Open Policy Agent&lt;/a&gt; to find a way to gain a fine-grained permission control feature for my custom API service.&lt;/p&gt;

&lt;p&gt;In this post I'm assuming you have at least heard what Open Policy Agent is. If you haven't, I definitely recommend watching the following presentations from &lt;a href="https://twitter.com/sometorin" rel="noopener noreferrer"&gt;Torin Sandal&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Intro: Open Policy Agent&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/CDDsjMOtJ-c"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;OPA is extremely powerful and flexible. You should take a look at some of the tutorials in the official docs starting from the, well, "&lt;a href="https://www.openpolicyagent.org/docs/latest/get-started/" rel="noopener noreferrer"&gt;Get Started&lt;/a&gt;".&lt;/p&gt;

&lt;p&gt;One tutorial I found very interesting is the &lt;a href="https://www.openpolicyagent.org/docs/latest/http-api-authorization/" rel="noopener noreferrer"&gt;HTTP API Authorization&lt;/a&gt;. I think many people have experienced API Authorization and have struggled all the &lt;code&gt;if&lt;/code&gt; statements to &lt;strong&gt;allow&lt;/strong&gt; or &lt;strong&gt;deny&lt;/strong&gt; a user doing something.  With OPA, you can &lt;strong&gt;delegate the decision outside of your code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;"Delegate the decision outside?", at first, this was hard for me to imagine so I've decided to migrate an API from a non-OPA version to an OPA version. &lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;You can look at the full code in the &lt;a href="https://github.com/kenfdev/opa-api-auth-go" rel="noopener noreferrer"&gt;repository&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/kenfdev" rel="noopener noreferrer"&gt;
        kenfdev
      &lt;/a&gt; / &lt;a href="https://github.com/kenfdev/opa-api-auth-go" rel="noopener noreferrer"&gt;
        opa-api-auth-go
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A sample API using OPA as the Policy Decision Point.
    &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;OPA API Authorization&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A sample to show the difference between using OPA as the Policy Decision Point.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How to&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;The master branch does not include OPA as the Policy Decision Point. OPA is added in the &lt;a href="https://github.com/kenfdev/opa-api-auth-go/tree/add-opa" rel="noopener noreferrer"&gt;add-opa branch&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can compare the difference in &lt;a href="https://github.com/kenfdev/opa-api-auth-go/pull/1" rel="noopener noreferrer"&gt;this PR&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you want to spin up the OPA version, do the following steps:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;git checkout add-opa

docker-compose up&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;Requesting the API&lt;/h4&gt;

&lt;/div&gt;
&lt;p&gt;The API is available in the &lt;code&gt;GET /finance/salary/:username&lt;/code&gt; endpoint. You can &lt;code&gt;GET&lt;/code&gt; to this endpoint but a JWT is needed to properly make a request.&lt;/p&gt;
&lt;p&gt;You can easily create an JWT at &lt;a href="https://jwt.io/" rel="nofollow noopener noreferrer"&gt;jwt.io&lt;/a&gt;. This application assumes that the algorithm is HS256 and the secret is simply &lt;code&gt;secret&lt;/code&gt;. Also, the payload should look something like this:&lt;/p&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;"sub"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;1234567890&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
  &lt;span class="pl-ent"&gt;"iat"&lt;/span&gt;: &lt;span class="pl-c1"&gt;1516239022&lt;/span&gt;,
  &lt;span class="pl-ent"&gt;"user"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;bob&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
  &lt;span class="pl-ent"&gt;"subordinates"&lt;/span&gt;: [&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;alice&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;],
  &lt;span class="pl-ent"&gt;"hr"&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/kenfdev/opa-api-auth-go" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;You can run the OPA authorized API from &lt;a href="https://github.com/kenfdev/opa-api-auth-go/tree/add-opa" rel="noopener noreferrer"&gt;this branch&lt;/a&gt; of the repository&lt;/li&gt;
&lt;li&gt;The diff of non-OPA version and OPA version can be found &lt;a href="https://github.com/kenfdev/opa-api-auth-go/pull/1/files" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The API
&lt;/h2&gt;

&lt;p&gt;The API we are going to use is based on the tutorial in the OPA official docs. The tutorial uses python but I've decided to build it with Go and with labstack's &lt;a href="https://echo.labstack.com/" rel="noopener noreferrer"&gt;echo&lt;/a&gt; framework.&lt;/p&gt;

&lt;p&gt;Here is the overview of the application (including the policies).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Members are as follows:

&lt;ul&gt;
&lt;li&gt;Alice&lt;/li&gt;
&lt;li&gt;Bob&lt;/li&gt;
&lt;li&gt;Charlie&lt;/li&gt;
&lt;li&gt;Betty&lt;/li&gt;
&lt;li&gt;David&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Bob is Alice's manager and Betty is Charlie's manager. David belongs to the HR department.&lt;/li&gt;

&lt;li&gt;Users can view their own salary&lt;/li&gt;

&lt;li&gt;Managers can view their subordinates' salary&lt;/li&gt;

&lt;li&gt;HRs can view everybody's salary&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Here is an overview diagram.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9z9ulzctqcwwv4hb4006.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9z9ulzctqcwwv4hb4006.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's get started with the non-OPA version.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Policy Decision without OPA
&lt;/h2&gt;

&lt;p&gt;I've created a Policy Enforcement middleware to enforce the policy on every request made to the API endpoint. In the middleware, I'm simply asking a &lt;code&gt;PolicyGateway&lt;/code&gt; to check if the requester is allowed to call the API. The code below shows the overview.(&lt;a href="https://github.com/kenfdev/opa-api-auth-go/blob/40408e7b5b4d99df9e98d12e92de9cd503403919/middleware/policyenforcer.go#L14-L24" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;logrus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Enforcing policy with middleware"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;allow&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gateway&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;allow&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;logrus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Action is allowed, continuing process"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;logrus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Action was not allowed, cancelling process"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusForbidden&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Action not allowed"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interface for &lt;code&gt;PolicyGateway&lt;/code&gt; is just 1 method &lt;code&gt;Ask&lt;/code&gt; which returns a &lt;code&gt;bool&lt;/code&gt; meaning Allow or Deny.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;PolicyGateway&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've called the implementation for the gateway the &lt;code&gt;PolicyLocalGateway&lt;/code&gt; and basically the following code is what it does.(&lt;a href="https://github.com/kenfdev/opa-api-auth-go/blob/master/gateway/policy.go#L25-L82" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gw&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;PolicyLocalGateway&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;checkGETSalary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;claims&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TokenClaims&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;userID&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;yes&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkIfOwner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;yes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;logrus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Allowing because requester is the owner"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;yes&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkIfSubordinate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;yes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;logrus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Allowing because target is a subordinate of requester"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;yes&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkIfHR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;yes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;logrus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Allowing because requester is a member of HR"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;logrus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Denying request"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see lots of &lt;code&gt;if&lt;/code&gt; statements and can assume this will easily increase the amount of code in the long run. Also, it is a little hard to understand what is going on with all these &lt;code&gt;if&lt;/code&gt; statements and indentation (well... that might be my fault, but you get the point).&lt;/p&gt;

&lt;p&gt;Let's check some HTTP requests to see what happens.&lt;/p&gt;

&lt;p&gt;First, let's start with Alice. Since Alice is a normal staff, she can only view her own salary.&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="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJ1c2VyIjoiYWxpY2UiLCJzdWJvcmRpbmF0ZXMiOltdLCJociI6ZmFsc2V9.WTR-Or-vS1yFBFHk7UyqZxsNhtTNSWeazJ57SJ7V4qY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  http://localhost:1323/finance/salary/alice

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

&lt;/div&gt;



&lt;p&gt;You can see that the value &lt;code&gt;100&lt;/code&gt; came back with no errors. Let's see if Alice can view Bob's salary.&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="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;Alice's JWT&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  http://localhost:1323/finance/salary/bob

Action not allowed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is now forbidden because Alice is a normal staff and cannot view other employee's salary. The logs show the request has been denied as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;app_1  | &lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Enforcing policy with middleware"&lt;/span&gt;
app_1  | &lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Checking GET salary policies"&lt;/span&gt; &lt;span class="nv"&gt;claims&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;amp;{alice [] false { 0  1516239022  0 1234567890}}"&lt;/span&gt; &lt;span class="nv"&gt;userID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bob
app_1  | &lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Denying request"&lt;/span&gt;
app_1  | &lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Action was not allowed, cancelling process"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the other hand, let's see if Bob can view Alice's salary.&lt;/p&gt;

&lt;p&gt;Bob's JWT payload looks like the following:&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="nl"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1234567890"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"iat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1516239022&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subordinates"&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="s2"&gt;"alice"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&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;He has &lt;code&gt;alice&lt;/code&gt; as his subordinate which means he is Alice's manager.&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="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;Bob's JWT&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  http://localhost:1323/finance/salary/alice

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

&lt;/div&gt;



&lt;p&gt;You can confirm that Bob is allowed to view Alice's salary. This is checked in the logic &lt;code&gt;gw.checkIfSubordinate&lt;/code&gt; in the code above. Now how about Bob viewing David's salary?&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="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;Bob's JWT&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  http://localhost:1323/finance/salary/david

Action not allowed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, Bob is not allowed to view David's salary because he has no permission to do so.&lt;/p&gt;

&lt;p&gt;Now, let's see what David as a member of HR can do?&lt;/p&gt;

&lt;p&gt;David's JWT payload is as follows.&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="nl"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1234567890"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"iat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1516239022&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"david"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subordinates"&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;"hr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;Since he is a member of HR, he has &lt;code&gt;hr: true&lt;/code&gt; in his JWT payload.&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="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;David's JWT&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  http://localhost:1323/finance/salary/bob

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

&lt;/div&gt;



&lt;p&gt;Success! And he can also see Betty, Alice, Charlie's salary as well. This is because he passes the check &lt;code&gt;gw.checkIfHR&lt;/code&gt; in the code above.&lt;/p&gt;

&lt;p&gt;This should have been pretty straight forward. Now, let's see what happens when OPA comes into the game.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Policy Decision with OPA
&lt;/h2&gt;

&lt;p&gt;Adding OPA is pretty simple. The implementation of the &lt;code&gt;PolicyGateway&lt;/code&gt; will change. I've called it the &lt;code&gt;PolicyOpaGateway&lt;/code&gt; and this time, I didn't implement anything related to policy rules (none of those &lt;code&gt;if&lt;/code&gt; statements anymore) because I wanted to &lt;strong&gt;delegate&lt;/strong&gt; them to OPA. &lt;/p&gt;

&lt;p&gt;The main changes are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I wrote policies in Rego to &lt;code&gt;*.rego&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;I added the OPA server next to the main app&lt;/li&gt;
&lt;li&gt;I have implemented the &lt;code&gt;PolicyGateway&lt;/code&gt; to make an external HTTP request to the OPA server with enough payloads in order for OPA to make decisions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Writing the policy in Rego
&lt;/h3&gt;

&lt;p&gt;The policy written in Rego is as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package httpapi.authz

# io.jwt.decode_verify
# https://www.openpolicyagent.org/docs/latest/language-reference/#tokens
token = t {
  [valid, _, payload] = io.jwt.decode_verify(input.token, { "secret": "secret" })
  t := {
    "valid": valid,
    "payload": payload
  }
}

default allow = false

# Allow users to get their own salaries.
allow {
  token.valid

  some username
  input.method == "GET"
  input.path = ["finance", "salary", username]
  token.payload.user == username
}

# Allow managers to get their subordinate's salaries.
allow {
  token.valid

  some username
  input.method == "GET"
  input.path = ["finance", "salary", username]
  token.payload.subordinates[_] == username
}

# Allow HR members to get anyone's salary.
allow {
  token.valid

  input.method == "GET"
  input.path = ["finance", "salary", _]
  token.payload.hr == true
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is nearly identical with the &lt;a href="https://www.openpolicyagent.org/docs/latest/http-api-authorization/" rel="noopener noreferrer"&gt;tutorial version&lt;/a&gt;. A slight difference is that it is verifying the JWT token with &lt;code&gt;io.jwt.decode_verify&lt;/code&gt; using the key &lt;code&gt;secret&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Side Note: The Rego Playground
&lt;/h4&gt;

&lt;p&gt;One awesome feature I want to point out about OPA is the &lt;a href="https://play.openpolicyagent.org/" rel="noopener noreferrer"&gt;Rego Playground&lt;/a&gt;. &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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmqrb9d6ohgfqwm3x50iy.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmqrb9d6ohgfqwm3x50iy.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can write policies and quickly evaluate them on the browser! In addition, you can share the policy via a link for others to look at (e.g. they can debug your policies). Here's the link for the policy above. The Input is set to Alice's JWT and the path to her own salary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://play.openpolicyagent.org/p/ww8qdEHdn0" rel="noopener noreferrer"&gt;https://play.openpolicyagent.org/p/ww8qdEHdn0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can check the OUTPUT to see that the result is allowed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding the OPA server
&lt;/h3&gt;

&lt;p&gt;After creating a policy with Rego, I've set the OPA service next to the main app by adding the following lines in the &lt;a href="https://github.com/kenfdev/opa-api-auth-go/blob/add-opa/docker-compose.yml#L11-L17" rel="noopener noreferrer"&gt;docker-compose.yml&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;pdp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openpolicyagent/opa:0.12.0&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8181:8181&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./opa:/etc/opt/opa&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;run"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--server"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/etc/opt/opa/authz.rego"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;FYI, PDP stands for Policy Decision Point. I just wanted to give it a generic name.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Requesting OPA for decisions
&lt;/h3&gt;

&lt;p&gt;Now that OPA is ready, I have implemented the &lt;code&gt;PolicyGateway&lt;/code&gt; so it makes an external HTTP request to OPA server for decisions.&lt;/p&gt;

&lt;h4&gt;
  
  
  Preparing the input
&lt;/h4&gt;

&lt;p&gt;OPA needs enough information in order to make decisions. For this simple app, I needed to tell OPA:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who the requester is - including attributes (JWT token)&lt;/li&gt;
&lt;li&gt;What endpoint was requested (request path and HTTP method)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the code looks like this. I have used echo's &lt;a href="https://echo.labstack.com/middleware/jwt" rel="noopener noreferrer"&gt;JWT middleware&lt;/a&gt; to easily extract the JWT data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"token"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;// After splitting, the first element isn't necessary&lt;/span&gt;
&lt;span class="c"&gt;// "/finance/salary/alice" -&amp;gt; ["", "finance", "salary", "alice"]&lt;/span&gt;
&lt;span class="n"&gt;paths&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestURI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;

&lt;span class="c"&gt;// create input to send to OPA&lt;/span&gt;
&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;opaInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;// don't forget to wrap it with `input`&lt;/span&gt;
&lt;span class="n"&gt;opaRequest&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;opaRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A slight gotcha is that the HTTP request payload needs to be wrapped with an &lt;code&gt;input&lt;/code&gt; key as shown in the code above. Of course, this is mentioned in the &lt;a href="https://www.openpolicyagent.org/docs/latest/rest-api/#get-a-document-with-input" rel="noopener noreferrer"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  HTTP Request
&lt;/h4&gt;

&lt;p&gt;I have injected OPA's endpoint to the PolicyGateway through an environment variable (&lt;a href="https://github.com/kenfdev/opa-api-auth-go/blob/add-opa/docker-compose.yml#L10" rel="noopener noreferrer"&gt;here&lt;/a&gt; and &lt;a href="https://github.com/kenfdev/opa-api-auth-go/blob/add-opa/main.go#L17-L18" rel="noopener noreferrer"&gt;here&lt;/a&gt;). Therefore, it is accessible by calling &lt;code&gt;gw.endpoint&lt;/code&gt;. So the request will be like the following code.(&lt;a href="https://github.com/kenfdev/opa-api-auth-go/blob/add-opa/gateway/policy.go" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;opaResponse&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Result&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="s"&gt;`json:"result"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="c"&gt;// request OPA&lt;/span&gt;
&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestBody&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ioutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;opaResponse&lt;/span&gt; &lt;span class="n"&gt;opaResponse&lt;/span&gt;
&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;opaResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's simply asking OPA and returning the result back to the caller. Now that the code is prepared, let's give it a shot again!&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="c"&gt;# David (a member of HR) viewing Betty's salary&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;David's JWT&amp;gt;"&lt;/span&gt; http://localhost:1323/finance/salary/betty

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

&lt;/div&gt;



&lt;p&gt;Looks good. Let's look at the logs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;app_1  | &lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Enforcing policy with middleware"&lt;/span&gt;
app_1  | &lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Requesting PDP for decision"&lt;/span&gt; &lt;span class="nv"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;GET &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"[finance salary betty]"&lt;/span&gt; &lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJ1c2VyIjoiZGF2aWQiLCJzdWJvcmRpbmF0ZXMiOltdLCJociI6dHJ1ZX0.TBXtM_p-VIlgx9NSLn4An6hELr2UB3CrqHUJmofQorM
pdp_1  | l&lt;span class="s2"&gt;":"&lt;/span&gt;info&lt;span class="s2"&gt;","&lt;/span&gt;msg&lt;span class="s2"&gt;":"&lt;/span&gt;Sent response.&lt;span class="s2"&gt;","&lt;/span&gt;req_id&lt;span class="s2"&gt;":2,"&lt;/span&gt;req_method&lt;span class="s2"&gt;":"&lt;/span&gt;POST&lt;span class="s2"&gt;","&lt;/span&gt;req_path&lt;span class="s2"&gt;":"&lt;/span&gt;/v1/data/httpapi/authz/allow&lt;span class="s2"&gt;","&lt;/span&gt;resp_bytes&lt;span class="s2"&gt;":15,"&lt;/span&gt;resp_duration&lt;span class="s2"&gt;":4.1901,"&lt;/span&gt;resp_status&lt;span class="s2"&gt;":200,"&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="s2"&gt;":"&lt;/span&gt;2019-06-30T14:29:52Z&lt;span class="s2"&gt;"}
app_1  | msg=Decision result=true
app_1  | msg="&lt;/span&gt;Action is allowed, continuing process&lt;span class="s2"&gt;"
app_1  | msg="&lt;/span&gt;Processing salary request&lt;span class="s2"&gt;" id=betty
app_1  | msg="&lt;/span&gt;Fetched salary&lt;span class="s2"&gt;" id=betty salary=200
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app is requesting OPA and receiving &lt;code&gt;true&lt;/code&gt; as the result. You can see the same result in the following playground.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://play.openpolicyagent.org/p/w0kohf47we" rel="noopener noreferrer"&gt;https://play.openpolicyagent.org/p/w0kohf47we&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's also see if Alice's request to Bob (to her manager) will fail.&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="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;Alice's JWT&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  http://localhost:1323/finance/salary/bob

Action not allowed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome! And the logs?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;app_1  | &lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Enforcing policy with middleware"&lt;/span&gt;
app_1  | &lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Requesting PDP for decision"&lt;/span&gt; &lt;span class="nv"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;GET &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"[finance salary bob]"&lt;/span&gt; &lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;Alice&lt;span class="s1"&gt;'s JWT&amp;gt;
pdp_1  | l":"info","msg":"Received request.","req_id":1,"req_method":"POST","req_path":"/v1/data/httpapi/authz/allow","time":"2019-06-30T21:29:59Z"}
app_1  | msg=Decision result=false
app_1  | msg="Action was not allowed, cancelling process"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything is working as expected! (Here's the &lt;a href="https://play.openpolicyagent.org/p/j1xmddfd9R" rel="noopener noreferrer"&gt;playground&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;We have successfully replaced the local policy engine to OPA. You can see how the separation of concerns are met by delegating the policy decisions to OPA.&lt;/p&gt;

&lt;p&gt;You can view the full changes to the code in &lt;a href="https://github.com/kenfdev/opa-api-auth-go/pull/1/files" rel="noopener noreferrer"&gt;my PR here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;This was a very simple example to show how you can leverage the power of OPA with an HTTP API server.&lt;/p&gt;

&lt;p&gt;Some Pros and Cons I can think of from this simple example is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;br&gt;
Separation of concerns. In my main app, I can concentrate on the business logic I need to implement. I don't have to write all those &lt;code&gt;if&lt;/code&gt; statements and change my code each time a policy changes. If I change my code, that means I have to deploy it again. As for OPA, you can dynamically change your policies on the run. No redeploying is necessary when you change policies.&lt;/p&gt;

&lt;p&gt;In addition, I was not able to mention it in this post, but Rego can be tested with Rego, too. You should have a look at "&lt;a href="https://www.openpolicyagent.org/docs/latest/how-do-i-test-policies/" rel="noopener noreferrer"&gt;How Do I Test Policies?&lt;/a&gt;".&lt;/p&gt;

&lt;p&gt;Some additional Pros I've heard in the slack channel are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Security/Compliance can audit policy separate from auditing the code.&lt;/li&gt;
&lt;li&gt;Consistent logging of decisions; helpful for SIEM integration (&lt;a href="https://www.openpolicyagent.org/docs/latest/decision-logs/" rel="noopener noreferrer"&gt;Decision Logs&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Consistent policy language (Rego) across different microservices; helps people trying to understand what is actually authorized across a chain of services&lt;/li&gt;
&lt;li&gt;When building a UI, it also needs to implement the same policies; easier if decoupled and consistent across services (e.g. take a look at Chef Automate's &lt;a href="https://github.com/chef/automate/tree/master/components/authz-service#introspection-how-to-query-permissions" rel="noopener noreferrer"&gt;Introspection&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Start with a solution purpose-built where you can start simple but that grows with you as your authz needs evolve.&lt;/li&gt;
&lt;li&gt;Avoid a series of one-off extensions to an authz system that in the end looks like a frankenstein. You can see how the monolith app I built above will become like in the long run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;br&gt;
I don't see a major downside in using OPA, but as this post has shown, a standard monolithic service turned into a microservice approach. Which means it will add complexity than not using OPA. But this is more about microservices than OPA itself. Also, Rego is an additional learning curve for newbies. I'd say it's worth the effort and I'm assuming Rego is going to become more and more popular as OPA grows.&lt;/p&gt;

&lt;p&gt;Finally, some questions that may arise are:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I have to pass all information to OPA for decisions?&lt;/strong&gt;&lt;br&gt;
AFAIK, the simplest approach is to pass everything, but that may not be easy or performant in many situations. One way is to use the &lt;a href="https://www.openpolicyagent.org/docs/latest/language-reference/#http" rel="noopener noreferrer"&gt;http.send built-in function&lt;/a&gt; to make external request from within OPA. You can also use &lt;a href="https://www.openpolicyagent.org/docs/latest/bundles/" rel="noopener noreferrer"&gt;Bundles&lt;/a&gt; for OPA to fetch data periodically from external services. Further information can be found in the official docs "&lt;a href="https://www.openpolicyagent.org/docs/latest/guides-identity/" rel="noopener noreferrer"&gt;Guides: Identity and User Attributes&lt;/a&gt;". It has really good detail in it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I use OPA as a library?&lt;/strong&gt;&lt;br&gt;
Maybe you don't want an extra container for various reasons. Yes, OPA can be used as a Go library and you can look at some awesome OSS projects that use OPA as a library. Some that I know are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://automate.chef.io/" rel="noopener noreferrer"&gt;Chef Automate&lt;/a&gt; (&lt;a href="https://github.com/chef/automate/tree/master/components/authz-service/engine/opa" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.ory.sh/docs/keto/" rel="noopener noreferrer"&gt;ory/keto&lt;/a&gt; (&lt;a href="https://github.com/ory/keto/tree/master/engine/ladon" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/instrumenta/conftest" rel="noopener noreferrer"&gt;conftest&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Can I create something like an AWS IAM with OPA?&lt;/strong&gt;&lt;br&gt;
I have heard this question several times (including myself) in the OPA slack and if you have the same question, you should definitely look at the &lt;a href="https://github.com/chef/automate/tree/master/components/authz-service" rel="noopener noreferrer"&gt;Chef Automate authorization service documentation&lt;/a&gt;. This document is amazing. I really mean it. There are definitely many ways to implement something like an AWS IAM with OPA but this one is surely a great live example.&lt;/p&gt;

&lt;p&gt;I'm assuming there are many more questions, but the best way to get support is to join the &lt;a href="https://slack.openpolicyagent.org/" rel="noopener noreferrer"&gt;OPA slack channel&lt;/a&gt;. The people there are super supportive and I've gained a lot of knowledge there.&lt;/p&gt;

&lt;p&gt;Thank you for reading this long post!!! I'd like to greatly appreciate Ash(&lt;a href="https://twitter.com/ashtalk" rel="noopener noreferrer"&gt;@ashtalk&lt;/a&gt;) and Tim(&lt;a href="https://twitter.com/tlhinrichs" rel="noopener noreferrer"&gt;@tlhinrichs&lt;/a&gt;) for reviewing this post upfront! I'm hoping I can contribute and give back with what I have to the OPA community!&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Deep Dive: Open Policy Agent
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Vdy26oA3py8"&gt;
&lt;/iframe&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opa</category>
      <category>openpolicyagent</category>
      <category>authorization</category>
      <category>abac</category>
    </item>
  </channel>
</rss>
