DEV Community

Cover image for Stop Waiting for Backend APIs: Introducing Fakelab, a TypeScript-First Mock Server
alireza
alireza

Posted on

Stop Waiting for Backend APIs: Introducing Fakelab, a TypeScript-First Mock Server

If you're a frontend developer, you've been here: you're ready to build a feature, but the backend API isn't ready yet. Or it's ready, but it's slow, unreliable, or requires authentication that's a pain to set up locally. You need realistic data to work with, but you don't want to spend hours writing mock handlers or maintaining JSON fixtures that drift out of sync with your types.

I've been there too. That's why I built Fakelab—a mock API server that generates realistic data directly from your TypeScript interfaces. After months of development and real-world testing, I'm releasing v1.0.

The Real Problem with Mocking APIs

Most frontend developers I know use one of these approaches:

Option 1: Hardcoded JSON files

  • Quick to start, but becomes a maintenance nightmare
  • No type safety
  • Data gets stale
  • Hard to generate variations

Option 2: Tools like json-server or MSW

  • json-server is great, but you still write JSON manually
  • MSW is powerful, but requires writing handlers for every endpoint
  • Both require you to maintain mock data separately from your types

Option 3: Wait for the backend

  • Not really an option if you want to ship features

The core issue is duplication: you define types in TypeScript, then define mock data somewhere else. When types change, mocks break. When mocks change, types might be wrong. It's a constant source of friction.

Why Existing Tools Weren't Enough

I tried everything. json-server is solid, but I got tired of writing JSON that didn't match my types. MSW is excellent for testing, but for development, I wanted something simpler—just point it at my TypeScript files and get an API.

MirageJS was close, but it felt heavy for what I needed, and the API felt dated. I wanted something that felt native to modern TypeScript workflows.

What I really wanted was:

  • Define types once, get mocks automatically
  • Type-safe mock data generation
  • Realistic data without manual work
  • A persistent database for testing stateful operations
  • Network simulation for testing edge cases
  • Zero boilerplate

So I built Fakelab.

What is Fakelab?

Fakelab is a Node.js mock API server that reads your TypeScript interfaces and generates REST endpoints with realistic fake data. You annotate your types with Faker.js directives using JSDoc comments, and Fakelab handles the rest.

Here's what makes it different:

TypeScript-first: Your types are the source of truth. No separate mock definitions.

Zero boilerplate: Annotate your interfaces, run the server, get endpoints.

Persistent database: Built-in database mode lets you test create, update, and delete operations.

Snapshot real APIs: Capture responses from real APIs and turn them into reusable mocks.

Network simulation: Test loading states, retries, and error handling without changing your code.

Runtime API: Access mocks from your frontend code with a simple API.

Key Features Explained

TypeScript-First with Faker Annotations

Instead of writing mock data manually, you annotate your TypeScript interfaces:

// fixtures/user.ts
export interface User {
  /** @faker string.ulid */
  id: string;
  /** @faker person.fullName */
  name: string;
  /** @faker location.streetAddress */
  address: string;
  /** @faker phone.number */
  phone: string;
  /** @faker number.int({min:10,max:80}) */
  age: number;
}
Enter fullscreen mode Exit fullscreen mode

Fakelab parses these annotations and uses Faker.js to generate realistic data. When you request /api/User, you get an array of users with proper names, addresses, and phone numbers—not "test" or "asdf".

Snapshot: Capture Real APIs

One of my favorite features is the snapshot command. Sometimes you want to bootstrap mocks from an existing API:

npx fakelab snapshot https://jsonplaceholder.typicode.com/todos --name Todo
Enter fullscreen mode Exit fullscreen mode

Fakelab fetches the response, infers the TypeScript type, and saves it as a mock source. You can refresh it later, or define multiple sources in your config. This is perfect when you're migrating from a real API or need to freeze API responses for offline development.

Database Mode

By default, Fakelab generates fresh data on each request. But for testing stateful operations, you can enable database mode:

export default defineConfig({
  database: { enabled: true },
});
Enter fullscreen mode Exit fullscreen mode

Now you can seed data, query it, update records, and test your app's data flow. The database persists between server restarts (it's just JSON under the hood, using lowdb).

Network Simulation

Testing error states and loading spinners is annoying when your mocks always succeed instantly. Fakelab lets you simulate real network conditions:

network: {
  delay: [300, 1200],      // Random delay between 300-1200ms
  errorRate: 0.1,          // 10% of requests fail
  timeoutRate: 0.05,       // 5% of requests timeout
  errors: {
    statusCodes: [400, 404, 500],
  },
}
Enter fullscreen mode Exit fullscreen mode

You can test your retry logic, error boundaries, and loading states without mocking fetch or axios.

Runtime API

Fakelab exposes a runtime API that your frontend can use:

import { fakelab } from "fakelab/browser";

// Fetch 10 users
const users = await fakelab.fetch("User", 10);

// Get the base URL
const apiUrl = fakelab.url(); // "http://localhost:50000/api"
Enter fullscreen mode Exit fullscreen mode

This is useful when you want to generate data programmatically or integrate Fakelab into your development workflow.

A Short Code Example

Here's a complete example. First, define your types:

// fakelab.config.ts
import { defineConfig } from "fakelab";

export default defineConfig({
  sourcePath: ["./fixtures"],
  server: { port: 50001 },
  database: { enabled: true },
});
Enter fullscreen mode Exit fullscreen mode
// fixtures/user.ts
export interface User {
  /** @faker string.ulid */
  id: string;
  /** @faker person.fullName */
  name: string;
  /** @faker internet.email */
  email: string;
}
Enter fullscreen mode Exit fullscreen mode

Start the server:

npx fakelab serve
Enter fullscreen mode Exit fullscreen mode

Now you have:

  • GET /api/User - Returns an array of users
  • GET /api/User?count=5 - Returns 5 users
  • POST /api/User - Creates a new user (if database is enabled)

In your React app:

import { fakelab } from "fakelab/browser";

const users = await fakelab.fetch("User", 10);
// Or use regular fetch:
const response = await fetch("http://localhost:50001/api/User?count=10");
const users = await response.json();
Enter fullscreen mode Exit fullscreen mode

That's it. No handlers, no JSON files, no boilerplate.

When You Should Use Fakelab (And When You Shouldn't)

Use Fakelab when:

  • You're building a frontend and the backend isn't ready
  • You want to develop offline or without backend dependencies
  • You need to test with realistic data variations
  • You want type-safe mocks that stay in sync with your types
  • You're prototyping and need an API quickly

Don't use Fakelab when:

  • You need complex business logic in your mocks (use MSW for that)
  • You're doing integration testing with a real backend (use the real API)
  • You need GraphQL (Fakelab is REST-only for now)
  • You need authentication/authorization logic (Fakelab is intentionally simple)

Fakelab is a development tool, not a testing framework. It's designed to make frontend development faster and more independent, not to replace integration tests.

Who It's Built For

Fakelab is built for:

  • Frontend developers who want to work independently
  • React/Next.js developers who need realistic data quickly
  • Teams where frontend and backend work in parallel
  • Developers who value type safety and want mocks that reflect their types

If you're familiar with tools like MSW, json-server, or MirageJS, Fakelab should feel familiar but simpler. If you've never used a mock server, Fakelab is probably the easiest one to get started with.

Future Roadmap

Fakelab v1 is stable and feature-complete for most use cases, but there's more coming:

  • GraphQL support: Generate a GraphQL schema from your types
  • Custom generators: Write your own data generators beyond Faker
  • Relationships: Define relationships between types (e.g., User has many Posts)
  • Webhook improvements: Retry logic and better error handling
  • Performance: Optimize for larger datasets and faster startup

I'm also open to feature requests. If Fakelab doesn't do something you need, open an issue and let's discuss it.

Try It Out

Fakelab is open source and MIT licensed. You can install it with:

npm install fakelab --save-dev
Enter fullscreen mode Exit fullscreen mode

Check out the documentation or browse the examples to see it in action.

If you try it and it helps you ship faster, let me know. If you hit issues or have ideas, open an issue or PR. This is a tool I built for myself, but I'm sharing it because I think other developers will find it useful too.

Closing Thoughts

Building Fakelab taught me a lot about what developers actually need from tooling. We don't need more features—we need tools that get out of our way and let us focus on building. Fakelab isn't trying to be everything to everyone. It's trying to do one thing well: give you a mock API server that works with your TypeScript types, not against them.

If that sounds useful to you, give it a try. If not, that's fine too. But if you're tired of waiting for backend APIs or maintaining mock data that's always out of sync, Fakelab might be what you're looking for.

Top comments (0)