Build complete frontend features, even if your API doesn't exist.

Mirage JS is an API mocking library that lets you build, test and share a complete working JavaScript application without having to rely on any backend services.

Create a Server

Mirage runs alongside the rest of your frontend JavaScript code — no new server processes or terminal windows needed. Use the devtools you know and love to write UI code that's ready for the network.

The best DX for frontend development.

Say goodbye to configuring painful backend environments just to hack on your UI. Mirage runs alongside the rest of your frontend code, so there’s no new infrastruture for you to learn.

New collaborators can clone your frontend codebase, run npm install, and have a complete local offline dev environment up and running in seconds – no environment variables or auth tokens needed.

import React from "react"
import ReactDOM from "react-dom"
import App from "./App"
import { Server } from "miragejs"

new Server({
  routes() {
    this.namespace = 'api'

    this.get('/todos', ({ db }) => {
      return db.todos;
    }))
  }
})

ReactDOM.render(
  <App />,
  document.getElementById("root")
)

Honestly, I can't recommend this tool enough. Finally, an idiomatic way for frontend developers to prototype and test an entire feature without touching a real API! Productivity just goes through the roof.

Roman Sandler

Developer at 500tech

Write high-level UI tests that stress your networking code.

With Mirage, you can write automated tests against your API no matter what state it's in. Test how your app handles 0 blog posts, 10, or 1000 – or even how it behaves when your server is slow or responds with an error.

No messy mocking code or handcrafted API responses in your tests. Just real-world scenarios validating the entire functionality of your full application.

Browsers
it("shows a message if there are no todos", async () => {
  const { getByTestId } = render(<App />)
  await waitForElementToBeRemoved(() => getByTestId("loading"))

  expect(getByTestId("no-todos")).toBeInTheDocument()
})

it("shows existing todos", async () => {
  server.createList("todo", 3)

  const { getByTestId, getAllByTestId } = render(<App />)
  await waitForElementToBeRemoved(() => getByTestId("loading"))

  expect(getAllByTestId("todo")).toHaveLength(3)
})

it("can complete a todo", async () => {
  server.create("todo", { text: "Todo 1", isDone: false })
  server.create("todo", { text: "Todo 2", isDone: false })

  const { getByTestId, getAllByTestId } = render(<App />)
  await waitForElementToBeRemoved(() => getByTestId("loading"))
  const todos = getAllByTestId("todo")
  userEvent.click(todos[1].querySelector("input[type='checkbox']"))
  await waitForElementToBeRemoved(() => getByTestId("saving"))

  expect(todos[0].querySelector('input[type="checkbox"]').checked).toBe(false)
  expect(todos[1].querySelector('input[type="checkbox"]').checked).toBe(true)
  expect(server.db.todos[1].isDone).toBe(true)
})

it("can create a todo", async () => {
  const { getByTestId } = render(<App />)
  await waitForElementToBeRemoved(() => getByTestId("loading"))

  const newTodoForm = await waitForElement(() => getByTestId("new-todo-form"))
  userEvent.type(newTodoForm.querySelector("input"), "Walk the dog")
  fireEvent.submit(getByTestId("new-todo-form"))
  await waitForElementToBeRemoved(() => getByTestId("saving"))

  const todo = getByTestId("todo")
  expect(todo.querySelector('input[type="checkbox"]').checked).toBe(false)
  expect(todo.querySelector('input[type="text"]').value).toBe("Walk the dog")
  expect(server.db.todos.length).toBe(1)
  expect(server.db.todos[0].text).toBe("Walk the dog")
})

it("shows a message if there are no todos", async () => {
  const { getByTestId } = render(<App />)
  await waitForElementToBeRemoved(() => getByTestId("loading"))

  expect(getByTestId("no-todos")).toBeInTheDocument()
})

it("shows existing todos", async () => {
  server.createList("todo", 3)

  const { getByTestId, getAllByTestId } = render(<App />)
  await waitForElementToBeRemoved(() => getByTestId("loading"))

  expect(getAllByTestId("todo")).toHaveLength(3)
})

it("can complete a todo", async () => {
  server.create("todo", { text: "Todo 1", isDone: false })
  server.create("todo", { text: "Todo 2", isDone: false })

  const { getByTestId, getAllByTestId } = render(<App />)
  await waitForElementToBeRemoved(() => getByTestId("loading"))
  const todos = getAllByTestId("todo")
  userEvent.click(todos[1].querySelector("input[type='checkbox']"))
  await waitForElementToBeRemoved(() => getByTestId("saving"))

  expect(todos[0].querySelector('input[type="checkbox"]').checked).toBe(false)
  expect(todos[1].querySelector('input[type="checkbox"]').checked).toBe(true)
  expect(server.db.todos[1].isDone).toBe(true)
})

it("can create a todo", async () => {
  const { getByTestId } = render(<App />)
  await waitForElementToBeRemoved(() => getByTestId("loading"))

  const newTodoForm = await waitForElement(() => getByTestId("new-todo-form"))
  userEvent.type(newTodoForm.querySelector("input"), "Walk the dog")
  fireEvent.submit(getByTestId("new-todo-form"))
  await waitForElementToBeRemoved(() => getByTestId("saving"))

  const todo = getByTestId("todo")
  expect(todo.querySelector('input[type="checkbox"]').checked).toBe(false)
  expect(todo.querySelector('input[type="text"]').value).toBe("Walk the dog")
  expect(server.db.todos.length).toBe(1)
  expect(server.db.todos[0].text).toBe("Walk the dog")
})

Share a fully-working UI without running a backend.

Because Mirage realistically mocks out your entire API server, you can share a clickable, working prototype of your JavaScript application without needing to run any backend services.

Get high-quality feedback from your users and iterate faster than ever, before you start investing in expensive server-side infrastructure.

I work on a large team, and the owners of different parts of the codebase change over time. Writing high-level tests against a network layer gives me assurance that my code works even as my organization evolves. I can't imagine writing an app without it.

Mehul Kar

Frontend Engineer at Apple

Get started using Mirage

Mirage works with all major JavaScript frameworks, libraries and test runners.