<?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: Devlin Duldulao</title>
    <description>The latest articles on DEV Community by Devlin Duldulao (@devlinduldulao).</description>
    <link>https://dev.to/devlinduldulao</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%2F52280%2F80831354-bc16-46ae-927a-d3e27a7993ff.png</url>
      <title>DEV Community: Devlin Duldulao</title>
      <link>https://dev.to/devlinduldulao</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/devlinduldulao"/>
    <language>en</language>
    <item>
      <title>DaloyJS Is the Latest Modern Enterprise TypeScript Framework, and It Has Your Back on Security</title>
      <dc:creator>Devlin Duldulao</dc:creator>
      <pubDate>Sat, 30 May 2026 13:06:10 +0000</pubDate>
      <link>https://dev.to/devlinduldulao/daloyjs-is-the-latest-modern-enterprise-typescript-framework-and-it-has-your-back-on-security-2af6</link>
      <guid>https://dev.to/devlinduldulao/daloyjs-is-the-latest-modern-enterprise-typescript-framework-and-it-has-your-back-on-security-2af6</guid>
      <description>&lt;h1&gt;
  
  
  DaloyJS Is the Latest Modern Enterprise TypeScript Framework, and It Has Your Back on Security
&lt;/h1&gt;

&lt;p&gt;I want to tell you something that took me years to learn, so you can learn it on a Tuesday afternoon instead of during a production incident: most developers who build REST APIs do not actually know all the security protections their API needs. I did not know them when I started. I learned them slowly, usually right after something broke.&lt;/p&gt;

&lt;p&gt;I am a Filipino fullstack developer, about ten years in, now based in Norway. I built DaloyJS (&lt;code&gt;@daloyjs/core&lt;/code&gt;) partly so that newer developers do not have to learn security the painful way I did. This post is a gentle walk through the problem and how DaloyJS helps. No gatekeeping, I promise.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, what even is a "security protection"?
&lt;/h2&gt;

&lt;p&gt;When your API is on the internet, anyone can send it anything. Most people are nice. Some are not, and a few are running automated tools that poke at every API they can find. So your server needs some basic defenses. Here are a few, in plain words:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Body-size limit:&lt;/strong&gt; stop someone from sending a giant 2GB request that fills up your server's memory and crashes it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeouts:&lt;/strong&gt; if a request takes forever, give up on it so it does not clog everything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prototype-pollution protection:&lt;/strong&gt; block a sneaky trick where a special key in the JSON (&lt;code&gt;__proto__&lt;/code&gt;) can mess with your whole app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Header safety:&lt;/strong&gt; reject weird characters in headers so attackers cannot inject their own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path-traversal protection:&lt;/strong&gt; stop a path like &lt;code&gt;../../etc/passwd&lt;/code&gt; from reading files it should not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hiding error details in production:&lt;/strong&gt; do not show strangers your stack traces and internal info.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiting:&lt;/strong&gt; stop one person from hammering your API thousands of times a second.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure headers and CORS:&lt;/strong&gt; tell browsers how to safely talk to your API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You do not need to memorize all of these today. The point I want you to take away is simpler: this list exists, it is longer than most people think, and nobody hands it to you when you write your first endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is a trap, especially with AI tools
&lt;/h2&gt;

&lt;p&gt;Here is the part that matters most for you right now, because you are probably using AI tools like GitHub Copilot or ChatGPT to help you build. That is great. I use them too. But you need to know how they behave around security.&lt;/p&gt;

&lt;p&gt;When you ask an AI "make me an endpoint that saves a JSON body to the database," it does exactly that. It does &lt;strong&gt;not&lt;/strong&gt; add a body-size limit. It does &lt;strong&gt;not&lt;/strong&gt; add a timeout. It does &lt;strong&gt;not&lt;/strong&gt; add rate limiting. It does &lt;strong&gt;not&lt;/strong&gt; protect against prototype pollution. The AI is not being lazy or sneaky. It is doing exactly what you asked, and most of the example code it learned from did not have those protections either.&lt;/p&gt;

&lt;p&gt;It gets trickier. Sometimes a security check makes a test fail, and the AI's "fix" is to just remove the check so the test passes. Now your code looks like it works, and the thing that was protecting you is gone.&lt;/p&gt;

&lt;p&gt;And here is the real trap, the one I want you to remember: &lt;strong&gt;you cannot ask the AI to add protections from a list you do not have.&lt;/strong&gt; The AI will happily add any security feature you name. But it will not suggest the ones you do not know about. You do not know what you do not know, so you never ask, so it never gets built. Telling the AI to "make it secure" does not work either, because that is too vague to mean anything.&lt;/p&gt;

&lt;p&gt;So the trick is not "prompt better." The trick is to start with tools that already have the protections built in, so you do not have to know the whole list to be safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  A popular framework that does not do this for you
&lt;/h2&gt;

&lt;p&gt;Let me show you what I mean using a famous framework called FastAPI. I genuinely love FastAPI. It is a Python framework, and its best idea (write your route once and get nice API docs for free) is exactly the idea DaloyJS brings to TypeScript. So this is not me dunking on it.&lt;/p&gt;

&lt;p&gt;But if you open up FastAPI's code and look in its &lt;code&gt;security&lt;/code&gt; folder, you find tools with names like &lt;code&gt;HTTPBearer&lt;/code&gt;, &lt;code&gt;OAuth2PasswordBearer&lt;/code&gt;, and &lt;code&gt;APIKeyHeader&lt;/code&gt;. Sounds very secure, right? Here is what those actually do: they grab a login token out of a request and they add a note about it to your API docs. That is helpful, but notice what it is &lt;strong&gt;not&lt;/strong&gt;. It is not a body-size limit. It is not prototype-pollution protection. It is not rate limiting or secure headers or hiding errors in production.&lt;/p&gt;

&lt;p&gt;So even a framework with a folder literally called "security" leaves most of the real protection list up to you. And FastAPI is not unusual here. Express, one of the most popular Node frameworks, ships with almost none of these protections built in. The normal expectation across most frameworks is: "here is a fast way to build routes, the security part is your job." For someone just starting out, that job is basically invisible.&lt;/p&gt;

&lt;h2&gt;
  
  
  How DaloyJS is different
&lt;/h2&gt;

&lt;p&gt;DaloyJS has one stubborn rule: &lt;strong&gt;bad defaults are bugs.&lt;/strong&gt; That means the protection list above is mostly already turned on for you, and the rest is a single line each. The project even has a rule that you are not allowed to delete a security check just to make a test pass.&lt;/p&gt;

&lt;p&gt;Here is what writing a route looks like:&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;z&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="s2"&gt;zod&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="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;NotFoundError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secureHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rateLimit&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="s2"&gt;@daloyjs/core&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;bodyLimitBytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;requestTimeoutMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Security middleware. One line each, that is it.&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;requestId&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="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;secureHeaders&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="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rateLimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;windowMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&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="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/books/: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;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;params&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;id&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="na"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="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="s2"&gt;Found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;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;id&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="na"&gt;title&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="mi"&gt;404&lt;/span&gt;&lt;span class="p"&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="s2"&gt;Not found&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;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;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;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;findBook&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;id&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;book&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;NotFoundError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`No book with id &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;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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;book&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;What you cannot see in that code is everything the framework is already doing quietly: the body-size limit, the timeout, the prototype-pollution-safe JSON parsing, the header safety, the path-traversal rejection, and hiding error details in production. You did not write any of that, and you cannot forget it, because it is just on.&lt;/p&gt;

&lt;h2&gt;
  
  
  It even watches your install step
&lt;/h2&gt;

&lt;p&gt;One more thing, because it is sneaky. The packages you install can be dangerous too. There is an attack called "slopsquatting" where an AI suggests a package name that does not exist, an attacker has already registered that exact name with malware, and your install runs it.&lt;/p&gt;

&lt;p&gt;DaloyJS helps here as well. Its core has zero outside dependencies, and the projects it creates for you are set up to block sketchy install scripts and to refuse installing brand-new packages published in the last 24 hours (the window where bad packages usually get caught). You do not need to understand all of that yet. Just know somebody already thought about it so you do not get burned while you are still learning.&lt;/p&gt;

&lt;h2&gt;
  
  
  The nice part: you still get all the good stuff
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://daloyjs.dev" rel="noopener noreferrer"&gt;DaloyJS&lt;/a&gt; is not security-only and boring. You still get the things that make building fun:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write a route once and get automatic API docs at &lt;code&gt;/docs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Validation and TypeScript types from that same single definition.&lt;/li&gt;
&lt;li&gt;A typed client SDK your frontend can use, generated for you.&lt;/li&gt;
&lt;li&gt;The ability to run the same app on Node, Bun, Deno, Cloudflare Workers, and Vercel Edge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is genuinely beginner-friendly, and it is also the kind of thing a serious team runs in production. You do not have to choose between "easy to learn" and "safe to ship."&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm create daloy@latest my-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives you a working project with the docs and the safe defaults already set up. Build an endpoint, open &lt;code&gt;http://localhost:3000/docs&lt;/code&gt;, and look around. The protections you did not even know to ask for are already there, doing their job.&lt;/p&gt;

&lt;p&gt;When I was starting out, I learned the security checklist one mistake at a time. You get to skip most of that. Take the shortcut. You earned it just by reading this far.&lt;/p&gt;

&lt;p&gt;Docs and source: &lt;a href="https://daloyjs.dev" rel="noopener noreferrer"&gt;daloyjs.dev&lt;/a&gt; and &lt;a href="https://github.com/daloyjs/daloy" rel="noopener noreferrer"&gt;github.com/daloyjs/daloy&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>security</category>
      <category>backend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What Is DaloyJS? A Beginner-Friendly Tour of a Contract-First TypeScript Framework</title>
      <dc:creator>Devlin Duldulao</dc:creator>
      <pubDate>Sat, 30 May 2026 12:15:24 +0000</pubDate>
      <link>https://dev.to/devlinduldulao/what-is-daloyjs-a-beginner-friendly-tour-of-a-contract-first-typescript-framework-clb</link>
      <guid>https://dev.to/devlinduldulao/what-is-daloyjs-a-beginner-friendly-tour-of-a-contract-first-typescript-framework-clb</guid>
      <description>&lt;p&gt;When I started building APIs, I spent way too much of my life keeping three things in sync: the actual route code, the validation, and the docs. You change one field, you forget the other two, and a week later someone on the frontend team pings you asking why the docs say &lt;code&gt;title&lt;/code&gt; but the API returns &lt;code&gt;name&lt;/code&gt;. Been there. Shipped that bug. Apologized in standup.&lt;/p&gt;

&lt;p&gt;DaloyJS is the framework I wish I had back then. Let me walk you through what it is, in plain language, without assuming you have ten years of backend scars.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-sentence version
&lt;/h2&gt;

&lt;p&gt;DaloyJS is a TypeScript web framework where you define a route once, and from that single definition you get validation, types, OpenAPI docs, and a typed client SDK. No copy-pasting. No drifting docs.&lt;/p&gt;

&lt;p&gt;If you have heard of FastAPI in the Python world, that "write it once, get docs for free" feeling is exactly what DaloyJS brings to TypeScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should a junior developer care?
&lt;/h2&gt;

&lt;p&gt;Here is the thing nobody tells you early on: most of the pain in backend work is not writing the logic. It is keeping everything that &lt;em&gt;describes&lt;/em&gt; your logic honest. The docs, the input validation, the response shapes, the client code. They all want to lie to you the moment you stop watching them.&lt;/p&gt;

&lt;p&gt;DaloyJS makes one definition the single source of truth. You write a route, and that route &lt;em&gt;is&lt;/em&gt; the contract. That is what "contract-first" means. The framework reads your contract and generates the boring stuff for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a route looks like
&lt;/h2&gt;

&lt;p&gt;Here is a real example. Do not worry about understanding every line yet, just look at the shape of 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;import&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;NotFoundError&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="s2"&gt;@daloyjs/core&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="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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zod&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;BookSchema&lt;/span&gt; &lt;span class="o"&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;id&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="na"&gt;title&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;openapi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="p"&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="s2"&gt;Bookstore API&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.0.0&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;docs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/books/: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;operationId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getBookById&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;params&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;id&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="na"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="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="s2"&gt;Book found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BookSchema&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&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="s2"&gt;Book not found&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;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;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;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;findBook&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;id&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;book&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;NotFoundError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`No book with id &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;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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;book&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 what you did here without realizing it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You said the URL param &lt;code&gt;id&lt;/code&gt; is a string, so DaloyJS validates it for you.&lt;/li&gt;
&lt;li&gt;You said a &lt;code&gt;200&lt;/code&gt; response returns a &lt;code&gt;BookSchema&lt;/code&gt;, so your editor knows the exact shape of &lt;code&gt;book&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You set &lt;code&gt;docs: true&lt;/code&gt;, so the framework mounts a live docs page at &lt;code&gt;/docs&lt;/code&gt; automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point is my favorite. You did not write a single line of documentation, and you still get a real, clickable API docs page. The first time I saw that, I genuinely felt a little robbed of all the hours I used to spend on Swagger config.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "free stuff" you get from one route
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Automatic API docs
&lt;/h3&gt;

&lt;p&gt;Set &lt;code&gt;docs: true&lt;/code&gt; and DaloyJS mounts &lt;code&gt;/docs&lt;/code&gt;, &lt;code&gt;/openapi.json&lt;/code&gt;, and &lt;code&gt;/openapi.yaml&lt;/code&gt;. Your docs are generated from the same route you already wrote, so they cannot drift out of sync. When you change the route, the docs change with it. No more "the docs lied to me" tickets.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Validation you do not have to wire up
&lt;/h3&gt;

&lt;p&gt;The schemas in &lt;code&gt;request&lt;/code&gt; and &lt;code&gt;responses&lt;/code&gt; are not decoration. DaloyJS actually validates incoming data against them. Send a request with a missing field, and you get a clean, standardized error response instead of a mysterious crash deep in your handler.&lt;/p&gt;

&lt;p&gt;DaloyJS uses something called &lt;a href="https://github.com/standard-schema/standard-schema" rel="noopener noreferrer"&gt;Standard Schema&lt;/a&gt;, which is a fancy way of saying you are not locked into one validation library. The examples use Zod 4 because it is small and modern, but Valibot, ArkType, and TypeBox all work too. Pick the one your team likes.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. End-to-end types
&lt;/h3&gt;

&lt;p&gt;Because your route knows the shape of its inputs and outputs, your editor knows too. Inside the handler, &lt;code&gt;params.id&lt;/code&gt; is typed as a string. The &lt;code&gt;body&lt;/code&gt; you return is checked against &lt;code&gt;BookSchema&lt;/code&gt;. If you typo a field name, TypeScript yells at you before your code ever runs. This is the kind of safety net that makes refactoring feel less like defusing a bomb.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. A generated client SDK
&lt;/h3&gt;

&lt;p&gt;Once your API has an OpenAPI spec, you can run one command and get a fully typed fetch client out of it (DaloyJS uses &lt;a href="https://heyapi.dev/" rel="noopener noreferrer"&gt;Hey API&lt;/a&gt; for this). Your frontend can call your backend with full autocomplete and type checking, and that client regenerates whenever your contract changes. Less guessing, fewer "what does this endpoint return again?" moments.&lt;/p&gt;

&lt;h2&gt;
  
  
  It runs basically everywhere
&lt;/h2&gt;

&lt;p&gt;A lot of frameworks quietly assume you are on Node and only Node. DaloyJS is built on the web standard &lt;code&gt;Request&lt;/code&gt; to &lt;code&gt;Response&lt;/code&gt; model, which is a fancy way of saying its core does not care where it runs. The same app works on Node, Bun, Deno, Cloudflare Workers, and Vercel Edge through small adapters.&lt;/p&gt;

&lt;p&gt;For a junior dev this matters more than it sounds. It means the thing you learn today does not become useless the moment your company decides to deploy to the edge or experiment with Bun. Your knowledge travels with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security you get without becoming a security expert
&lt;/h2&gt;

&lt;p&gt;This is the part I wish someone had handed me on day one. Security is hard, and it is really easy to ship something insecure without knowing it. DaloyJS takes the position that &lt;strong&gt;bad defaults are bugs&lt;/strong&gt;, so a bunch of protections are just on out of the box:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request body size limits, so someone cannot send you a 2GB payload to knock your server over.&lt;/li&gt;
&lt;li&gt;Request timeouts, so slow requests do not pile up forever.&lt;/li&gt;
&lt;li&gt;JSON parsing that is safe against prototype pollution (a sneaky attack class you will eventually read about and be glad you were protected from).&lt;/li&gt;
&lt;li&gt;Path-traversal rejection, so people cannot trick your server into reading files it should not.&lt;/li&gt;
&lt;li&gt;Standardized, RFC 9457 error responses that redact sensitive details in production.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is also first-party middleware for the usual suspects: secure HTTP headers, CORS, CSRF, rate limiting, and request ids. You add them with one line each:&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;requestId&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="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;secureHeaders&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="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rateLimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;windowMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The framework even hardens the &lt;em&gt;install&lt;/em&gt; side of things, with defaults that wait 24 hours before pulling freshly published packages and block sketchy lifecycle scripts. You do not need to understand all of that yet. Just know that someone thought about it so you do not get burned while you are still learning.&lt;/p&gt;

&lt;h2&gt;
  
  
  So when would you reach for it?
&lt;/h2&gt;

&lt;p&gt;If you are building a REST API in TypeScript and you want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docs that stay correct without extra effort,&lt;/li&gt;
&lt;li&gt;Validation and types from one definition,&lt;/li&gt;
&lt;li&gt;A typed client for your frontend,&lt;/li&gt;
&lt;li&gt;Sane security defaults,&lt;/li&gt;
&lt;li&gt;The freedom to deploy to Node now and the edge later,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;then &lt;a href="https://daloyjs.dev/" rel="noopener noreferrer"&gt;DaloyJS&lt;/a&gt; is worth a try. It is genuinely beginner-friendly because it removes the busywork that usually trips up newer developers, while still being the kind of thing a senior team can run in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it in two minutes
&lt;/h2&gt;

&lt;p&gt;The fastest way in is the official scaffolder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm create daloy@latest my-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives you a working project with docs routes, OpenAPI wiring, and production defaults already set up. Open it, poke at the example route, change a field, and watch the docs update. That little feedback loop is, honestly, the whole pitch.&lt;/p&gt;

&lt;p&gt;I spent years duct-taping docs, validation, and types together by hand. If you are just starting out, you get to skip that phase entirely. Lucky you.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>webdev</category>
      <category>node</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
