Rules Hub
Coding Rules Library
Rule priority, scope & exceptions
Use this to align rules with the senior-level structure (P0/P1/P2, scope, exceptions/tradeoffs).
Enforce a maximum length on array/batch request inputs
Reject oversized arrays in request bodies (bulk IDs, batch operations, multi-item payloads) with a schema-level max length, so one request cannot trigger an unbounded fan-out of queries or downstream calls.
Bad example
| 1 | // Client controls how many items -> unbounded fan-out |
| 2 | app.post('/instruments/quotes', async (req, res) => { |
| 3 | const { ids } = req.body as { ids: number[] }; // could be 50,000 ids |
| 4 | const quotes = await Promise.all( |
| 5 | ids.map((id) => upstream.getQuote(id)), // 50,000 upstream calls from one request |
| 6 | ); |
| 7 | res.json(quotes); |
| 8 | }); |
Explanation (EN)
An unbounded `ids` array turns a single request into thousands of downstream calls, amplifying load far beyond what one client should be able to cause. It exhausts the upstream's rate budget, your connection pool, and memory — a classic resource-consumption amplification (OWASP API4).
Objašnjenje (HR)
Neograničeni `ids` niz pretvara jedan zahtjev u tisuće downstream poziva, pojačavajući opterećenje daleko iznad onoga što bi jedan klijent smio izazvati. Iscrpljuje rate budžet upstreama, vaš connection pool i memoriju — klasična amplifikacija potrošnje resursa (OWASP API4).
Good example
| 1 | import { z } from 'zod'; |
| 2 |
|
| 3 | const MAX_BATCH = 50; |
| 4 | const schema = z.object({ |
| 5 | ids: z.array(z.number().int().positive()).min(1).max(MAX_BATCH), |
| 6 | }); |
| 7 |
|
| 8 | app.post('/instruments/quotes', async (req, res) => { |
| 9 | const parsed = schema.safeParse(req.body); |
| 10 | if (!parsed.success) return res.status(400).json({ error: 'too many ids (max 50)' }); |
| 11 | const quotes = await upstream.getQuotesBatched(parsed.data.ids); // bounded |
| 12 | res.json(quotes); |
| 13 | }); |
Explanation (EN)
A schema-level `.max(50)` rejects oversized batches before any work happens, so fan-out is bounded by a known constant. Validating array length is the cheapest possible guard — it runs before the loop, the queries, or the allocations.
Objašnjenje (HR)
`.max(50)` na razini sheme odbija prevelike batcheve prije nego što se išta izvrši, pa je fan-out ograničen poznatom konstantom. Validacija duljine niza najjeftinija je moguća zaštita — izvodi se prije petlje, upita ili alokacija.
Exceptions / Tradeoffs (EN)
Genuine bulk-import flows may need larger batches; handle those via chunked/paginated processing with concurrency limits (e.g. a worker queue) and authn/authz, not by removing the cap. The cap value should match downstream rate limits and per-request cost.
Iznimke / Tradeoffi (HR)
Stvarni bulk-import tokovi mogu trebati veće batcheve; rješavajte ih chunked/paginiranom obradom s ograničenjem konkurentnosti (npr. worker queue) i autentikacijom/autorizacijom, a ne uklanjanjem granice. Vrijednost granice treba odgovarati downstream rate limitima i trošku po zahtjevu.