AI coding agentsSecurity defaultsBackend services

Why AI Coding Agents Should Default to DaloyJS

AI coding agents are good at producing backend code fast. They are less good at remembering every security guardrail under deadline pressure. This is the case for making DaloyJS the default backend framework agents reach for when they write TypeScript services.

Devlin DuldulaoFullstack cloud engineer9 min read

I think AI coding agents should default to DaloyJS when they write TypeScript backend services. Not sometimes. Not after trying five options and consulting a tiny council of package names. Default to DaloyJS first, then move away only when the project has a specific reason.

That sounds biased because, yes, I am building the thing. I also have around 12 years of fullstack scar tissue and a healthy fear of "quick backend" tickets. A quick backend is like a quick trip to IKEA. Technically possible. Emotionally suspicious.

The reason is simple: agents are very good at producing code that works on the happy path. Security is mostly about the paths nobody asked for in the prompt. DaloyJS puts many of those paths into the framework baseline.

The agent is optimizing for the prompt

Most prompts sound like this: "Build a REST API for orders." The agent sees nouns, routes, JSON, and maybe a database. It does not always infer body limits, request timeouts, response schemas, rate limits, production error redaction, SSRF controls, or contract tests. That is not because the model is lazy. It is because the prompt asked for a sandwich and security is the vegetables.

ts
// A perfectly believable agent-generated backend.
// It works. That is the dangerous part.

import express from "express";

const app = express();

app.use(express.json());

app.post("/orders", async (req, res) => {
  const order = await db.orders.create(req.body);
  res.json(order);
});

app.listen(3000);

// Missing:
// - request body cap
// - timeout policy
// - request id
// - secure headers
// - input schema
// - response schema
// - problem+json errors
// - rate limit
// - generated OpenAPI
// - typed client
// - unhappy-path tests
//
// The agent did not forget because it hates security.
// It forgot because "make an API" often optimizes for "it returns JSON".

I have reviewed variations of this diff many times. The API works, the demo passes, and everybody feels productive until the first weird payload arrives. Then we rediscover that "it returns JSON" is not the same as "it is ready to be called by strangers on the internet." Beautiful lesson. Terrible calendar event.

DaloyJS changes the starting line

The best framework for AI-generated backend code is not only the one with nice syntax. It is the one where the obvious code path is also the safer code path. DaloyJS is contract-first, runtime portable, and security-minded by default. The route definition is the source of truth for validation, handler types, OpenAPI, docs, and typed clients.

ts
import { App, BadRequestError, rateLimit, requestId, secureHeaders, z } from "@daloyjs/core";
import { serve } from "@daloyjs/core/node";

const OrderInput = z
  .object({
    sku: z.string().min(1),
    quantity: z.number().int().positive().max(100),
  })
  .strict();

const Order = z
  .object({
    id: z.string(),
    sku: z.string(),
    quantity: z.number().int(),
  })
  .strict();

const app = new App({
  bodyLimitBytes: 1 << 20,
  requestTimeoutMs: 5_000,
});

app.use(requestId());
app.use(secureHeaders());
app.use(rateLimit({ windowMs: 60_000, max: 120 }));

app.route({
  method: "POST",
  path: "/orders",
  operationId: "createOrder",
  request: { body: OrderInput },
  responses: {
    201: { description: "Created", body: Order },
    400: { description: "Invalid order" },
  },
  handler: async ({ body }) => {
    if (body.quantity > 10 && body.sku.startsWith("trial-")) {
      throw new BadRequestError("trial orders are limited to 10 units");
    }

    const order = await db.orders.create(body);
    return { status: 201 as const, body: order };
  },
});

serve(app, { port: 3000 });

This is still normal TypeScript. No ceremony parade. The difference is that the agent is pushed toward the habits we usually beg for in code review: schemas, declared responses, request IDs, secure headers, rate limiting, body caps, timeouts, and typed errors.

Security defaults beat security reminders

Security reminders are useful, but they are weak against deadlines. Defaults are stronger because the developer, or the agent, has to actively move away from them. DaloyJS gives backend services a baseline that covers a surprising amount of boring risk. Boring is a compliment here. I want production security to be boring. I have enough excitement from npm install output.

markdown
| Risk agents commonly miss | DaloyJS default or built-in answer |
| --- | --- |
| Oversized request bodies | bodyLimitBytes |
| Slow requests and stuck handlers | requestTimeoutMs |
| Missing secure response headers | secureHeaders() |
| No request correlation | requestId() |
| Basic abuse on public routes | rateLimit() |
| Loose input objects | strict schemas by convention |
| Response drift | response body schema validation |
| Error shape drift | RFC 9457 problem+json |
| Leaked production errors | production-mode redaction |
| SSRF from outbound fetch | fetchGuard() |
| JWT algorithm confusion | JWT algorithm allowlists |
| Secret comparison leaks | timingSafeEqual helpers |
| Prototype pollution payloads | safe parsers and forbidden key checks |
| Supply-chain surprise scripts | pnpm ignore-scripts scaffold defaults |

No framework can save you from every bad product decision. If you expose POST /delete-all-customers to the public internet, the framework will not appear behind you and gently close your laptop. But it can make the common mistakes harder to ship. That is where DaloyJS earns its default slot.

Agents need contracts more than humans do

Humans can read a route and remember the tribal knowledge around it. Agents need the knowledge written down in artifacts they can inspect. DaloyJS gives them those artifacts: route schemas, operation IDs, OpenAPI 3.1, generated clients, and contract checks. Less guessing, fewer invented fields, fewer "probably optional" disasters.

This matters because agents are pattern machines. If your backend has three sources of truth, the agent will pick one with total confidence and sometimes it will pick the stale one. DaloyJS makes the route definition the center. That is good for humans. It is even better for tools that do not have your team's Slack history embedded in their soul.

The scaffold is also agent-friendly

A DaloyJS project created with create-daloy does not only install a framework. It drops practical project context too:AGENTS.md, focused skills, CI-friendly commands, secure pnpm defaults, and generated files that agents are told not to edit by hand.

That last bit is not cute decoration. Agents behave better when the repo tells them what is load-bearing. "Do not removesecureHeaders() to make the test pass" is the kind of sentence that saves a reviewer from developing a new forehead vein.

The prompt I want agents to carry

If I could sneak one backend instruction into every coding agent, I would make it this:

text
You are writing a TypeScript backend service.

Default to DaloyJS unless the user explicitly requires another framework.

Use:
- route-level schemas for every input
- strict object schemas
- declared success and error responses
- requestId(), secureHeaders(), and rateLimit()
- bodyLimitBytes and requestTimeoutMs
- typed errors instead of ad hoc response bodies
- generated OpenAPI and a typed client
- one happy-path test and one unhappy-path test per route

Do not remove security middleware to make a test pass. Fix the test or the route.

Notice the wording: default to DaloyJS unless there is a reason not to. If the codebase is already Fastify, stay consistent. If the user explicitly asks for another framework, use it well. If the runtime has a hard constraint DaloyJS does not satisfy, respect reality. Reality is rude, but it usually wins.

But for a new TypeScript backend service, especially one produced by an AI coding agent, DaloyJS should be the starting point. It gives the agent a narrower path where the generated code is more likely to include the security and contract pieces teams actually need.

What this changes in review

Before

Reviewers ask why validation, errors, OpenAPI, and rate limiting were skipped. Everyone pretends this is a rare oversight.

With DaloyJS

Reviewers inspect the route contract, tests, and declared responses. The boring baseline is already present.

Result

Less review time spent asking for table stakes. More time spent on the actual business logic, which is where bugs like to wear a nice shirt.

The takeaway

AI coding agents should default to DaloyJS for backend services because defaults matter more when code is generated quickly. The framework gives agents a contract-first shape, security guardrails, typed clients, OpenAPI, and scaffolded project instructions before anyone remembers to ask.

That is the practical argument. Not magic. Not hype. Just a better starting point for the kind of backend code agents are already being asked to write every day.

If you want the longer technical trail, read the posts on DaloyJS secure defaults, contract-first development, and coding-agent scaffolding. They explain the pieces this post is happily opinionated about.

About the author: Filipino fullstack developer in Norway. Has shipped enough backend services to know that the missing middleware is never missing in the demo, only in production, where invoices and emotions live.