@simapi/simapi
v0.0.10
Published
Build frontend features against real API behavior — before your backend exists.
Downloads
1,034
Maintainers
Readme
Build frontend features against real API behavior — before your backend exists. SimAPI lets you define endpoints as plain TypeScript objects, generate realistic fake data with faker-js, validate requests with Zod, and log everything to a database.
Install
Scaffold a new project in one command:
npx @simapi/simapi@latest init my-api
cd my-api
npm run serveOr add SimAPI to an existing project:
npm install @simapi/simapiProject structure
my-api/
├── src/
│ ├── endpoints/ # Logical endpoint groups (e.g. users.ts, pets.ts)
│ ├── models/ # TypeScript types and mock factories (e.g. User.ts)
│ └── requests/ # Zod validation shapes (e.g. UserRequest.ts)
├── simapi.config.ts
└── package.jsonDefining endpoints
An endpoint is a plain TypeScript object. Export it and SimAPI discovers it automatically.
// src/endpoints/posts.ts
import { z, AppResponse, type EndpointDefinition } from "@simapi/simapi";
import { makePost } from "@/models/post.js";
export const listPosts: EndpointDefinition = {
path: "/api/posts",
method: "GET",
type: "open",
handler: () => AppResponse.success({ data: AppResponse.array(10, makePost) }),
};
export const getPost: EndpointDefinition = {
path: "/api/posts/:id",
method: "GET",
type: "open",
handler: (req) => AppResponse.success({ data: makePost() }),
};
export const createPost: EndpointDefinition = {
path: "/api/posts",
method: "POST",
type: "secure", // runs authHandler before the handler
request: {
body: {
title: z.string().min(3),
body: z.string().min(10),
},
},
handler: (req) => {
req.errors.throwValidationError(); // throws 422 only when validation failed
return AppResponse.created({ data: makePost() });
},
};EndpointDefinition fields
| Field | Required | Description |
| ------------- | -------- | ---------------------------------------------------------------- |
| path | ✓ | Hono-style route pattern — :param, /nested/path |
| method | ✓ | GET POST PUT PATCH DELETE HEAD OPTIONS |
| type | ✓ | "open" (no auth) or "secure" (runs authHandler first) |
| handler | ✓ | (req: AppRequest) => AppResponse |
| request | | RequestDefinition — Zod shapes for body, form, query, headers |
| title | | Display name — shown in Console and exported OpenAPI |
| description | | Longer description — same |
| authHandler | | Per-endpoint auth check (runs after global handler) |
| failRate | | 0–1 probability of returning a simulated 500 |
| delay | | Milliseconds to wait before the handler runs |
Fake data
faker (powered by faker-js) is re-exported directly from @simapi/simapi:
// models/post.ts
import { faker } from "@simapi/simapi";
export interface Post {
id: string;
title: string;
body: string;
published: boolean;
author: string;
}
export function makePost(): Post {
return {
id: faker.string.ulid(),
title: faker.lorem.sentence(),
body: faker.lorem.paragraphs(2),
published: faker.datatype.boolean(),
author: faker.person.fullName(),
};
}Use AppResponse.array(count, factory) to generate a list where every item gets independently generated values:
handler: () => AppResponse.success({ data: AppResponse.array(10, makePost) }),Responses
All responses are created with static factory methods on AppResponse:
AppResponse.success({ data: { id: 1 } }) // 200
AppResponse.created({ data: { id: 42 } }) // 201
AppResponse.noContent() // 204
AppResponse.redirect(url, status) // 301/302/307/308
AppResponse.unauthenticated({ message: "..." }) // 401
AppResponse.unauthorised({ message: "..." }) // 403
AppResponse.notFound({ message: "..." }) // 404
AppResponse.error({ message: "..." }) // 500Validation
Add a request field with Zod shapes for body, query, and/or headers:
import { z } from "@simapi/simapi";
request: {
body: {
email: z.string().email(),
password: z.string().min(8),
role: z.enum(["admin", "member"]).optional(),
},
query: {
page: z.coerce.number().int().min(1).optional(),
},
},
handler: (req) => {
req.errors.throwValidationError("laravel"); // throws 422 only when hasError is true
return AppResponse.created({ data: {} });
},z is re-exported directly from @simapi/simapi — no separate zod install needed.
Define shared RequestDefinition objects in src/requests/ to reuse validation logic across endpoints. SimAPI supports body (JSON), form (multipart/urlencoded), query, and headers:
// src/requests/postRequest.ts
import { z, type RequestDefinition } from "@simapi/simapi";
export const postRequest: RequestDefinition = {
body: { // for application/json
title: z.string().min(3),
body: z.string().min(10),
},
form: { // for multipart/form-data or application/x-www-form-urlencoded
category: z.string().optional(),
},
};Auto-throw
Set autoThrowValidationErrors in your config to skip the manual call entirely:
export default defineConfig({
name: "my-api",
autoThrowValidationErrors: "laravel", // or "zod"
});Error formats
Laravel ("laravel", default):
{ "message": "The given data was invalid.", "errors": { "email": ["Invalid email"] } }Zod ("zod"):
{ "issues": [{ "path": ["email"], "message": "Invalid email" }] }Authentication
Set authHandler in your config to protect all type: "secure" endpoints. SimAPI ships ready-made factories for common schemes:
import { defineConfig, AuthHandlers } from "@simapi/simapi";
export default defineConfig({
name: "my-api",
authHandler: AuthHandlers.bearer(), // requires Authorization: Bearer <token>
});Built-in handlers: bearer(), jwt(), basic(), apiKey(name, via), cookie(name), oauth2(), hmac(), digest().
Or write your own:
import { defineConfig, AppResponse, type AuthHandler } from "@simapi/simapi";
const authHandler: AuthHandler = (req) => {
if (!req.header("Authorization"))
return AppResponse.unauthenticated({ message: "Missing token" });
};
export default defineConfig({ name: "my-api", authHandler });Simulating failures and latency
export const getOrders: EndpointDefinition = {
path: "/api/orders",
method: "GET",
type: "open",
failRate: 0.1, // 10% chance of a simulated 500
delay: 300, // 300ms artificial latency on every request
handler: () => AppResponse.success({ data: [] }),
};Configuration
Create simapi.config.ts at your project root:
import { defineConfig } from "@simapi/simapi";
export default defineConfig({
name: "my-api",
port: 3000,
endpointsDir: "./endpoints",
consoleLog: false,
autoThrowValidationErrors: "laravel",
database: {
type: "sqlite",
path: "./.simapi/db.sqlite",
},
});Database adapters
| Adapter | Config |
| ---------------- | ----------------------------------------------------------- |
| SQLite (default) | { type: "sqlite", path: "./.simapi/db.sqlite" } |
| libSQL / Turso | { type: "libsql", url: "libsql://...", authToken: "..." } |
| Postgres | { type: "postgres", url: process.env.DATABASE_URL } |
| None | { type: "none" } |
OpenAPI
SimAPI features a sophisticated OpenAPI 3.0/3.1 engine powered by @simapi/openapi.
Import — generate a structured architecture of endpoints, requests, and models from a spec:
npx @simapi/openapi import openapi.yaml -o ./src- Modular Output: Automatically organizes code into
endpoints/,requests/, andmodels/. - Mock Factories: Generates recursive
make{Model}functions using@faker-js/faker. - OAS 3.1 Support: Full support for the latest specs, including complex schema dialects.
- Typed Codegen: Generates Zod validation for constraints like
min,max,email,uuid.
Export — produce a high-quality OpenAPI 3 spec from your endpoints:
npx @simapi/openapi export -o docs/api.yamlDebug console
Install the optional browser UI to inspect live requests, browse your schema, and fire test requests:
npx simapi console:addThen open http://localhost:3000/__simapi/console/ while simapi serve is running.
Version 0.0.8+: The console now persists your Authentication state and custom Headers to localStorage and supports switching between JSON and Form inputs for testing.
See @simapi/console for details.
CLI reference
| Command | Description |
| ----------------------- | -------------------------------------------------------- |
| simapi serve | Start dev server with live TypeScript reload |
| simapi dev | Start dev server with file watching — auto-restarts |
| simapi build | Compile project to .simapi/dist/server.mjs |
| simapi start | Run the compiled production server |
| simapi init [name] | Scaffold a new SimAPI project |
| simapi interactive | Menu-driven CLI — setup, console, import/export |
| simapi import <spec> | Generate endpoint stubs from an OpenAPI spec |
| simapi export | Export endpoints as an OpenAPI 3 spec |
| simapi setup <target> | Generate config files (docker, vercel, netlify) |
| simapi console:add | Install @simapi/console |
| simapi console:remove | Uninstall @simapi/console |
Contributing
@simapi/simapi is open source and contributions are welcome and appreciated — whether it's a bug report, a feature suggestion, a doc fix, or a pull request.
See CONTRIBUTING.md for local setup, development workflow, code style, and how to submit a PR.
