<?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: Kacper Zawojski</title>
    <description>The latest articles on DEV Community by Kacper Zawojski (@zawoj).</description>
    <link>https://dev.to/zawoj</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F671386%2F752f9683-f4f7-4754-b5d1-5cfd13d99f65.jpg</url>
      <title>DEV Community: Kacper Zawojski</title>
      <link>https://dev.to/zawoj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zawoj"/>
    <language>en</language>
    <item>
      <title>Payload v4: the MCP plugin exposes your collections to LLMs — and it's opt-out</title>
      <dc:creator>Kacper Zawojski</dc:creator>
      <pubDate>Fri, 26 Jun 2026 09:00:10 +0000</pubDate>
      <link>https://dev.to/zawoj/payload-v4-the-mcp-plugin-exposes-your-collections-to-llms-and-its-opt-out-2nlg</link>
      <guid>https://dev.to/zawoj/payload-v4-the-mcp-plugin-exposes-your-collections-to-llms-and-its-opt-out-2nlg</guid>
      <description>&lt;p&gt;&lt;code&gt;@payloadcms/plugin-mcp&lt;/code&gt; turns your Payload CMS into an MCP server, exposing your collections as tools for LLMs.&lt;/p&gt;

&lt;p&gt;Heads up for v4: after a refactor, every collection is now exposed with full CRUD &lt;strong&gt;by default&lt;/strong&gt;. It's opt-out — you disable individual tools rather than enabling them:&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="nf"&gt;mcpPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// posts is exposed automatically — no entry needed&lt;/span&gt;
    &lt;span class="na"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// find only&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;Custom tools are defined with the &lt;code&gt;defineTool&lt;/code&gt; builder, taking input via &lt;code&gt;zod&lt;/code&gt; v4:&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="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;getPostScores&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;defineTool&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;Score recent posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="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;since&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="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;handler&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;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&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="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="cm"&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 thing to actually do after upgrading: collections you never listed are suddenly reachable through MCP. Review them and disable anything sensitive — an exposed &lt;code&gt;users&lt;/code&gt; collection with &lt;code&gt;delete&lt;/code&gt; is not a great default to inherit by accident.&lt;/p&gt;

</description>
      <category>payload</category>
      <category>zavcode</category>
      <category>tellmeicant</category>
      <category>cms</category>
    </item>
    <item>
      <title>Payload v4: stricter cron — sloppyRanges is gone</title>
      <dc:creator>Kacper Zawojski</dc:creator>
      <pubDate>Wed, 24 Jun 2026 09:00:11 +0000</pubDate>
      <link>https://dev.to/zawoj/payload-v4-stricter-cron-sloppyranges-is-gone-2cj2</link>
      <guid>https://dev.to/zawoj/payload-v4-stricter-cron-sloppyranges-is-gone-2cj2</guid>
      <description>&lt;p&gt;Watch out for this one when upgrading to Payload v4: cron parsing got stricter.&lt;/p&gt;

&lt;p&gt;After the Croner bump to v10, the &lt;code&gt;sloppyRanges&lt;/code&gt; escape hatch is gone. Stepping in a cron expression now has to be syntactically correct, or you get a &lt;code&gt;TypeError&lt;/code&gt; at registration time — instead of it being silently ignored:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- cron: '/10 * * * *'      // missing prefix — throws
&lt;/span&gt;&lt;span class="gi"&gt;+ cron: '*/10 * * * *'
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- cron: '5/15 * * * *'     // numeric prefix — throws
&lt;/span&gt;&lt;span class="gi"&gt;+ cron: '5-59/15 * * * *'  // explicit range
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This applies everywhere you write a schedule: &lt;code&gt;jobs.autoRun[].cron&lt;/code&gt;, &lt;code&gt;schedule.cron&lt;/code&gt;, and the &lt;code&gt;--cron&lt;/code&gt; flag on the binary. The good news is the failure is loud — a malformed expression that used to quietly never fire now throws on startup, so you find out immediately.&lt;/p&gt;

</description>
      <category>payload</category>
      <category>zavcode</category>
      <category>tellmeicant</category>
      <category>cms</category>
    </item>
    <item>
      <title>Just another app builder for no-code</title>
      <dc:creator>Kacper Zawojski</dc:creator>
      <pubDate>Mon, 22 Jun 2026 14:59:59 +0000</pubDate>
      <link>https://dev.to/zawoj/just-another-app-builder-for-no-code-29i7</link>
      <guid>https://dev.to/zawoj/just-another-app-builder-for-no-code-29i7</guid>
      <description>&lt;p&gt;We're launching an app I'm building with my cousin @Jędrzej Zawojski &lt;/p&gt;

&lt;p&gt;The app is built primarily for no-code, low-code developers and designers. If you keep hearing that your app will eventually fall apart, or that there will come a moment when you won't be able to keep developing it - our app is the answer to that problem.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A set of skills that helps maintain code to the right standard&lt;/li&gt;
&lt;li&gt;Maintaining skills and memory&lt;/li&gt;
&lt;li&gt;Built-in git - version history and the ability to create new branches, meaning different variations of your app, all stored locally on your machine&lt;/li&gt;
&lt;li&gt;A GUI for managing your app - no more burning tokens on restarting, starting, stopping, or committing. Burn tokens on actual development, not on the repetitive stuff developers do from the terminal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also have a set of starters ready for you. Right now mainly built in Payload, because of my specialization - but many more are coming soon. A starter is a developer-prepared package with a base app, a set of skills, memory, and environment setup, done the way I would set it up myself.&lt;/p&gt;

&lt;p&gt;Additional features help you maintain and manage your repository properly - the way a developer would.&lt;/p&gt;

&lt;p&gt;Zero vendor lock-in. Everything works with Claude Code, Codex, and even local models. I'm testing the app on Ollama myself and it works.&lt;/p&gt;

&lt;p&gt;The official release is coming soon, but if you're interested, message me on LinkedIn to get early access.&lt;/p&gt;

&lt;h1&gt;
  
  
  TELLMEICANT #JAAB #PAYLOAD
&lt;/h1&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Payload v4: every block gets its own TypeScript interface (hashed on collision)</title>
      <dc:creator>Kacper Zawojski</dc:creator>
      <pubDate>Mon, 22 Jun 2026 09:00:07 +0000</pubDate>
      <link>https://dev.to/zawoj/payload-v4-every-block-gets-its-own-typescript-interface-hashed-on-collision-1dld</link>
      <guid>https://dev.to/zawoj/payload-v4-every-block-gets-its-own-typescript-interface-hashed-on-collision-1dld</guid>
      <description>&lt;p&gt;A nice quality-of-life change in Payload v4's type generation.&lt;/p&gt;

&lt;p&gt;Previously, a block only got its own TypeScript interface if you set &lt;code&gt;interfaceName&lt;/code&gt;. In v4 the name is derived automatically from the slug (PascalCased), and when two different blocks would collide on a name, the second one gets a stable suffix from a content hash — so types never silently overwrite each other:&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;// slug 'content-block'  -&amp;gt; interface ContentBlock&lt;/span&gt;
&lt;span class="c1"&gt;// a colliding block of a different shape -&amp;gt; Hero_3F2A1B0C  (deterministic hash)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key word is &lt;em&gt;deterministic&lt;/em&gt;: the hash is derived from the block's shape, so regenerating &lt;code&gt;payload-types.ts&lt;/code&gt; doesn't churn your diffs. No more "why did half my types get renamed" after a codegen run.&lt;/p&gt;

</description>
      <category>payload</category>
      <category>zavcode</category>
      <category>tellmeicant</category>
      <category>cms</category>
    </item>
    <item>
      <title>Payload depth: how many relationship levels expand — at runtime, not in schema</title>
      <dc:creator>Kacper Zawojski</dc:creator>
      <pubDate>Sat, 20 Jun 2026 09:00:09 +0000</pubDate>
      <link>https://dev.to/zawoj/payload-depth-how-many-relationship-levels-expand-at-runtime-not-in-schema-3a78</link>
      <guid>https://dev.to/zawoj/payload-depth-how-many-relationship-levels-expand-at-runtime-not-in-schema-3a78</guid>
      <description>&lt;p&gt;&lt;code&gt;depth&lt;/code&gt; controls how many levels of relationships Payload expands — and it's a runtime decision, not something baked into your schema.&lt;/p&gt;

&lt;p&gt;The same document can come back "flat" or with its relationships populated, driven by a single query parameter:&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;// depth: 0 -&amp;gt; author is just an ID&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&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;Hi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;64a...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// depth: 1 -&amp;gt; author is a full object&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&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;Hi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;author&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;64a...&lt;/span&gt;&lt;span class="dl"&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;Ada&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByID&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;posts&lt;/span&gt;&lt;span class="dl"&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="na"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when you want to limit &lt;em&gt;which&lt;/em&gt; fields get populated as a document is expanded from another document, reach for &lt;code&gt;defaultPopulate&lt;/code&gt; on the collection — so you're not dragging entire heavy documents along for the ride.&lt;/p&gt;

&lt;p&gt;Tune &lt;code&gt;depth&lt;/code&gt; per query and you control your payload size without touching the data model.&lt;/p&gt;

</description>
      <category>payload</category>
      <category>zavcode</category>
      <category>tellmeicant</category>
      <category>cms</category>
    </item>
    <item>
      <title>Payload v4: jobs always run at depth 0, with no collection hooks</title>
      <dc:creator>Kacper Zawojski</dc:creator>
      <pubDate>Thu, 18 Jun 2026 09:00:25 +0000</pubDate>
      <link>https://dev.to/zawoj/payload-v4-jobs-always-run-at-depth-0-with-no-collection-hooks-113f</link>
      <guid>https://dev.to/zawoj/payload-v4-jobs-always-run-at-depth-0-with-no-collection-hooks-113f</guid>
      <description>&lt;p&gt;A v4 migration gotcha for anyone using Payload's job queue.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;jobs.depth&lt;/code&gt; and &lt;code&gt;jobs.runHooks&lt;/code&gt; were removed. Tasks now signal their result by throwing — not via a &lt;code&gt;state&lt;/code&gt; field, which is gone:&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;// v3 (no longer works)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myTask&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TaskHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyTask&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&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;input&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;input&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Missing id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;succeeded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// v4&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myTask&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TaskHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyTask&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&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;input&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;input&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="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Missing id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// throw = fail + retry&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;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Jobs always run at &lt;code&gt;depth: 0&lt;/code&gt; now, which means they operate on raw relationship IDs and skip collection hooks. It's faster and far more predictable — no surprise side effects from a hook firing deep inside a background task.&lt;/p&gt;

</description>
      <category>payload</category>
      <category>zavcode</category>
      <category>tellmeicant</category>
      <category>cms</category>
    </item>
    <item>
      <title>Payload Local API: zero HTTP, but full access control when you want it</title>
      <dc:creator>Kacper Zawojski</dc:creator>
      <pubDate>Tue, 16 Jun 2026 09:00:26 +0000</pubDate>
      <link>https://dev.to/zawoj/payload-local-api-zero-http-but-full-access-control-when-you-want-it-3bdo</link>
      <guid>https://dev.to/zawoj/payload-local-api-zero-http-but-full-access-control-when-you-want-it-3bdo</guid>
      <description>&lt;p&gt;&lt;code&gt;payload.find()&lt;/code&gt; hits the database directly, in the same process — no HTTP, no fetch. By default it also &lt;em&gt;bypasses&lt;/em&gt; access control, on the assumption that server-side code trusts itself.&lt;/p&gt;

&lt;p&gt;That assumption is exactly where holes appear. In server functions and endpoints that run on behalf of a user, you have to consciously turn enforcement on:&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;// DANGEROUS — full access, ignores access control&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;docs&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;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// SAFE — respects access control for the given user&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;docs&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;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;overrideAccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;user&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 trap is the default: &lt;code&gt;overrideAccess&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; unless you say otherwise. Any Local API call that returns data to an end user should pass &lt;code&gt;overrideAccess: false&lt;/code&gt; and the &lt;code&gt;user&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>payload</category>
      <category>zavcode</category>
      <category>tellmeicant</category>
      <category>cms</category>
    </item>
    <item>
      <title>Payload field hooks run per-field, not per-document</title>
      <dc:creator>Kacper Zawojski</dc:creator>
      <pubDate>Sun, 14 Jun 2026 09:00:22 +0000</pubDate>
      <link>https://dev.to/zawoj/payload-field-hooks-run-per-field-not-per-document-2paf</link>
      <guid>https://dev.to/zawoj/payload-field-hooks-run-per-field-not-per-document-2paf</guid>
      <description>&lt;p&gt;In most CMSs, hooks live on the whole document. In Payload, a hook lives on a specific field and receives that field's context — &lt;code&gt;siblingData&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt;, &lt;code&gt;operation&lt;/code&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&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;slug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kd"&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="nx"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;beforeChange&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="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;siblingData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;operation&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;operation&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;create&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;value&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="nf"&gt;slugify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;siblingData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// compute the slug from a sibling field&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;value&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;Combine that with &lt;code&gt;virtual: true&lt;/code&gt; and you get purely computed fields: the hook produces the value on &lt;code&gt;afterRead&lt;/code&gt;, and nothing ever lands in the database.&lt;/p&gt;

&lt;p&gt;It's a small shift in mental model, but it makes logic local to the data it touches instead of scattered across one giant document-level hook.&lt;/p&gt;

</description>
      <category>payload</category>
      <category>zavcode</category>
      <category>tellmeicant</category>
      <category>cms</category>
    </item>
    <item>
      <title>Payload virtual path fields: relationship data with zero extra columns</title>
      <dc:creator>Kacper Zawojski</dc:creator>
      <pubDate>Fri, 12 Jun 2026 09:00:33 +0000</pubDate>
      <link>https://dev.to/zawoj/payload-virtual-path-fields-relationship-data-with-zero-extra-columns-3on</link>
      <guid>https://dev.to/zawoj/payload-virtual-path-fields-relationship-data-with-zero-extra-columns-3on</guid>
      <description>&lt;p&gt;Any field in Payload can be marked &lt;code&gt;virtual&lt;/code&gt;. The best version is &lt;code&gt;virtual&lt;/code&gt; as a &lt;em&gt;path&lt;/em&gt; — it automatically resolves the value from a relationship, with nothing written to the database:&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&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;authorName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kd"&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="nx"&gt;virtual&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;author.name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// walks through the `author` relationship down to `name`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when the path crosses a &lt;code&gt;hasMany&lt;/code&gt; relationship, you get an array back:&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&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;categoryTitles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kd"&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="nx"&gt;hasMany&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;virtual&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;categories.title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; ['Tech', 'News', 'Updates']&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The field shows up in your API responses and in your generated types — but it never takes up a column. Perfect for flattening relationship data without denormalizing your schema.&lt;/p&gt;

</description>
      <category>payload</category>
      <category>zavcode</category>
      <category>tellmeicant</category>
      <category>cms</category>
    </item>
    <item>
      <title>What would you ask the Framer team if you had the chance?</title>
      <dc:creator>Kacper Zawojski</dc:creator>
      <pubDate>Wed, 10 Jun 2026 12:59:59 +0000</pubDate>
      <link>https://dev.to/zawoj/what-would-you-ask-the-framer-team-if-you-had-the-chance-33nk</link>
      <guid>https://dev.to/zawoj/what-would-you-ask-the-framer-team-if-you-had-the-chance-33nk</guid>
      <description>&lt;p&gt;What would you ask the Framer team if you had the chance? 👇&lt;br&gt;
Drop your questions in the comments - I'll take them with me and ask on your behalf.&lt;/p&gt;

&lt;p&gt;Here's why: on June 16, Framer is dropping its biggest release in years - a fundamentally new way to work in the tool. I'll be watching the launch live at a small, invite-only Watch Party here in Warsaw, then jumping into a hackathon to build with it that same night.&lt;/p&gt;

&lt;p&gt;The catch? It's a tiny room (~20 builders), so most of the community can't be there in person - but your questions can. Whatever you're curious about - the new features, the workflow shift, what it means for how we build - send it my way, and I'll bring the answers back here for everyone.&lt;/p&gt;

&lt;p&gt;Big things ahead for Framer. Let's explore them together. 💛&lt;/p&gt;

&lt;p&gt;PS: And no, I don't only do Payload. Sometimes. 😏&lt;/p&gt;

</description>
      <category>nocode</category>
      <category>ai</category>
    </item>
    <item>
      <title>Payload v4: forceSelect is now a select() function — and it's a subtle trap</title>
      <dc:creator>Kacper Zawojski</dc:creator>
      <pubDate>Wed, 10 Jun 2026 09:00:25 +0000</pubDate>
      <link>https://dev.to/zawoj/payload-v4-forceselect-is-now-a-select-function-and-its-a-subtle-trap-42h8</link>
      <guid>https://dev.to/zawoj/payload-v4-forceselect-is-now-a-select-function-and-its-a-subtle-trap-42h8</guid>
      <description>&lt;p&gt;In Payload v3, &lt;code&gt;forceSelect&lt;/code&gt; was a static object that deep-merged with the caller's &lt;code&gt;select&lt;/code&gt;. In v4 it became a function — and that function &lt;em&gt;replaces&lt;/em&gt; the caller's select instead of adding to it:&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;Posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CollectionConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// Careful: you must spread the caller's select yourself, or you'll overwrite it!&lt;/span&gt;
  &lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;select&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;select&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="nx"&gt;select&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="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="cm"&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;Returning &lt;code&gt;undefined&lt;/code&gt; means "leave the caller's select untouched." You also get &lt;code&gt;operation&lt;/code&gt; and &lt;code&gt;req&lt;/code&gt;, so you can hand back a different select for the REST API than for the admin panel.&lt;/p&gt;

&lt;p&gt;One important detail: there's no document data here — &lt;code&gt;select&lt;/code&gt; runs &lt;em&gt;before&lt;/em&gt; read.&lt;/p&gt;

</description>
      <category>payload</category>
      <category>zavcode</category>
      <category>tellmeicant</category>
      <category>cms</category>
    </item>
    <item>
      <title>Payload access control isn't a boolean — it's a query constraint</title>
      <dc:creator>Kacper Zawojski</dc:creator>
      <pubDate>Mon, 08 Jun 2026 09:00:34 +0000</pubDate>
      <link>https://dev.to/zawoj/payload-access-control-isnt-a-boolean-its-a-query-constraint-2j62</link>
      <guid>https://dev.to/zawoj/payload-access-control-isnt-a-boolean-its-a-query-constraint-2j62</guid>
      <description>&lt;p&gt;This is, in my opinion, Payload's most underrated feature.&lt;/p&gt;

&lt;p&gt;An &lt;code&gt;access&lt;/code&gt; function can return &lt;code&gt;true&lt;/code&gt;/&lt;code&gt;false&lt;/code&gt; — or it can return a &lt;code&gt;Where&lt;/code&gt; object that Payload stitches into every query at the database level. That's row-level security, for free:&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;canReadPage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Access&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;// A guest only sees public documents — the filter goes to the DB, not to JS&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;isPublic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't fetch everything and then filter in memory — this &lt;code&gt;Where&lt;/code&gt; becomes part of the &lt;code&gt;WHERE&lt;/code&gt; clause in SQL (or &lt;code&gt;$match&lt;/code&gt; in Mongo). And it works identically for &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt;, and &lt;code&gt;delete&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once it clicks that access control is a &lt;em&gt;constraint&lt;/em&gt;, not a &lt;em&gt;gate&lt;/em&gt;, a whole class of "filter the user's own records" problems just disappears.&lt;/p&gt;

</description>
      <category>payload</category>
      <category>zavcode</category>
      <category>tellmeicant</category>
      <category>cms</category>
    </item>
  </channel>
</rss>
