Skip to Content

Mockup Simulator

Flip simulate: true in nestia.config.ts and the generated SDK gains a second mode of operation: instead of making HTTP calls, each function answers in-process with a typed random payload.

The point: a frontend can ship UI work before the backend exists. The simulator runs the same input validation the real server would, so bad payloads still fail with the same 400, and good payloads come back with realistic-looking data.

New to this feature? Read Tutorial → Mockup Simulator for the friendly walkthrough. This page is the reference.


Enable

nestia.config.ts
import { INestiaConfig } from "@nestia/sdk"; import { NestFactory } from "@nestjs/core"; import { AppModule } from "./src/AppModule"; const config: INestiaConfig = { input: () => NestFactory.create(AppModule), output: "src/api", simulate: true, }; export default config;

Regenerate:

Terminal
npx nestia sdk

Each generated function now has both a real branch (HTTP) and a simulate branch (in-process). Which one runs depends on connection.simulate.


Flip the flag

const connection: api.IConnection = { host: "http://localhost:37001", simulate: true, }; const article = await api.functional.articles.create(connection, { title: "Hello", body: "World", }); // `article` is a fully populated IArticle — generated, not fetched.

Behind the scenes the SDK function does roughly:

return connection.simulate ? create.simulate(connection, input) // typia.random<Output> + input validation : PlainFetcher.fetch(connection, METADATA, input); // real HTTP call

Both branches are typed, so swapping simulate: truesimulate: false is a one-character change.


What the simulator actually does

For each generated function:

  1. Validate input against the parameter type (same validator as the real server).
    • On failure, throw HttpError with status 400 — exactly what the real server returns.
  2. Generate a Primitive<Output> value using typia.random<Output>().
  3. Return it.

That means:

  • Field-level constraints (tags.MinLength<3>, tags.Format<"email">) propagate to the random data. Random strings respect minimum lengths; random Format<"email"> strings look like emails.
  • Unions and discriminated unions are randomized over their cases.
  • Recursive types are bounded automatically.

What it does not do:

  • Persist state. Two calls return two independent random values. There is no in-memory database.
  • Apply business rules. Authorization, idempotency, rate limits, “you can’t delete what doesn’t exist” — none of that is in the simulator.

Customize per-endpoint

Random data is fine for layout work; for demos and tests you usually want a specific shape. Override the simulate function on any endpoint:

import api from "@my-app/api"; import type { IArticle } from "@my-app/api/lib/structures/IArticle"; api.functional.articles.at.simulate = (connection, id) => ({ id, title: "Curated demo title", body: "This article looks real because we wrote it.", created_at: new Date().toISOString(), } satisfies IArticle);

The simulate function on each SDK namespace has the same signature as the real call. Only override the endpoints that matter for your scenario; the rest stay random.


Production builds

The simulator branch is in the same generated file as the real client. If you set connection.simulate to a statically-known false (or omit it), most bundlers will tree-shake the simulator branch out of the production bundle.

For belt-and-braces — if you want the simulator code physically absent from production — keep two IConnection factories and pick at module load:

export const connection: api.IConnection = process.env.NODE_ENV === "production" ? { host: "https://api.example.com" } : { host: "http://localhost:37001", simulate: true };

See also

Last updated on