npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

better-mock-server

v1.4.1

Published

A lightweight and fast mock server built on unjs/h3 for modern web development.

Downloads

26

Readme

better-mock-server

A TypeScript-first mock server library built on top of unjs/h3, providing an elegant and type-safe way to create HTTP mock servers for development and testing.

npm version npm downloads bundle size License: MIT

English | 中文

✨ Features

  • 🎯 Type-Safe: Full TypeScript support with comprehensive type definitions
  • 🚀 Built on H3: Leverages the powerful and minimal H3 framework
  • 🎨 Elegant API: Clean and intuitive configuration syntax
  • 🔧 Flexible Routing: Support for nested routes and multiple HTTP methods
  • 🔌 Middleware Support: Easy middleware registration with route-specific options
  • 🧩 Plugin System: Extensible through H3's plugin architecture
  • 📦 Zero Config: Works out of the box with sensible defaults

📦 Installation

npm install better-mock-server h3

🚀 Quick Start

Basic Usage

import { createAppServer } from 'better-mock-server'

const server = createAppServer({
  port: 3000,
  routes: {
    '/api/hello': (event) => {
      return { message: 'Hello World!' }
    }
  }
})

await server.listen()
console.log(`Server running at ${server.url}`)

// Later: close the server
await server.close()

Random Port

// Use port 0 for automatic port assignment
const server = createAppServer({
  port: 0,
  routes: {
    '/api/ping': () => 'pong'
  }
})

await server.listen()
console.log(`Server running at ${server.url}`) // e.g., http://localhost:54321/
console.log(`Port: ${server.port}`) // e.g., 54321

🎯 Core Concepts

Routes

Routes define the HTTP endpoints and their handlers. You can use simple handlers or detailed route configurations.

Simple Handler (All Methods)

const routes = {
  '/api/ping': (event) => 'pong'
}

Method-Specific Handlers

const routes = {
  '/api/users': {
    GET: (event) => [
      { id: 1, name: 'John' },
      { id: 2, name: 'Jane' }
    ],
    POST: async (event) => {
      const body = await readBody(event)
      return { id: 3, ...body }
    },
    DELETE: (event) => {
      return { success: true }
    }
  }
}

Nested Routes

const routes = {
  '/api': {
    GET: (event) => 'API Root',
    children: {
      '/users': {
        GET: (event) => 'List users',
        children: {
          '/:id': {
            GET: (event) => `Get user ${event.context.params.id}`,
            DELETE: (event) => `Delete user ${event.context.params.id}`
          }
        }
      }
    }
  }
}

Route Options

const routes = {
  '/api/meta': {
    GET: {
      handler: (event) => 'meta options',
      options: {
        meta: { name: 'king3' }
      }
    }
  }
}

Middlewares

Middlewares are functions that run before route handlers, useful for logging, authentication, CORS, etc.

Global Middleware

const middlewares = [
  (event, next) => {
    console.log(`${event.method} ${event.path}`)
    return next()
  }
]

Route-Specific Middleware

const middlewares = [
  {
    route: '/api',
    handler: (event, next) => {
      console.log('API route accessed')
      return next()
    }
  }
]

Middleware with Options

const middlewares = [
  {
    handler: (event, next) => next(),
    options: {
      method: 'POST'
    }
  }
]

Plugins

Plugins extend the functionality of your server using H3's plugin system.

import { definePlugin } from 'better-mock-server'

const loggerPlugin = definePlugin((h3, _options) => {
  if (h3.config.debug) {
    h3.use((req) => {
      console.log(`[${req.method}] ${req.url}`)
    })
  }
})

const server = createAppServer({
  routes: {
    /* ... */
  },
  plugins: [loggerPlugin]
})

await server.listen()

📚 API Reference

Server Functions

createAppServer(options)

Creates an HTTP server with the configured application.

Parameters:

  • options.routes (required): Routes configuration
  • options.middlewares (optional): Middlewares array
  • options.plugins (optional): Plugins array
  • options.port (optional): Port number (default: 0 for random port)
  • options.hostname (optional): Hostname (default: 'localhost')
  • options.protocol (optional): Protocol (default: 'http')

Returns: AppServer object

AppServer Properties:

  • raw: Raw H3 server instance
  • app: H3 application instance
  • port: Server port number (available after listen())
  • url: Server URL (available after listen())
  • listen(port?): Async function to start the server. Auto-closes the previous server if called again
  • close(): Async function to close the server
  • restart(port?): Async function to restart the server. Uses the last listen port if no port is provided

Examples:

const server = createAppServer({
  port: 3000,
  routes: {
    '/api/hello': () => 'Hello'
  }
})

await server.listen()
console.log(`Running at ${server.url}`)

// Or override port when listening
await server.listen(4000)

// Clean up
await server.close()

createApp(options)

Creates an H3 application instance without starting a server. Useful when you want to integrate with existing server setup.

Parameters:

  • options.routes (optional): Routes configuration
  • options.middlewares (optional): Middlewares array
  • options.plugins (optional): Plugins array

Returns: H3 application instance

Example:

import { createApp } from 'better-mock-server'
import { serve } from 'h3'

const app = createApp({
  routes: {
    '/api/hello': () => 'Hello'
  }
})

// Use with your own server configuration
const server = serve(app, { port: 4000 })
await server.ready()
console.log(`Server running at ${server.url}`)

Route Functions

defineRoutes(routes)

Provides type-safe route definitions with IDE auto-completion.

Example:

import { defineRoutes } from 'better-mock-server'

const routes = defineRoutes({
  '/api/users': {
    GET: () => [],
    POST: async (event) => {
      const body = await readBody(event)
      return body
    }
  }
})

parseRoutes(routes, basePath?)

Parses nested route structures into a flat array of route definitions. Mainly for internal use.

Parameters:

  • routes: Routes configuration object
  • basePath (optional): Base path for nested routes

Returns: Array of parsed route objects

registerRoutes(app, routes?)

Registers routes to an H3 application instance.

Parameters:

  • app: H3 application instance
  • routes (optional): Routes configuration

Middleware Functions

defineMiddleware(input)

Defines middleware with type safety. Accepts either a function or configuration object.

Example:

import { defineMiddleware } from 'better-mock-server'

// With function
const mw1 = defineMiddleware((event, next) => {
  console.log('Middleware')
  return next()
})

// With config
const mw2 = defineMiddleware({
  route: '/api',
  handler: (event, next) => next(),
  options: { method: 'POST' }
})

parseMiddlewares(middlewares)

Parses middleware configurations into standardized tuple format. Mainly for internal use.

Parameters:

  • middlewares: Array of middleware functions or configurations

Returns: Array of parsed middleware tuples

registerMiddlewares(app, middlewares?)

Registers middlewares to an H3 application instance.

Parameters:

  • app: H3 application instance
  • middlewares (optional): Middlewares array

Plugin Functions

definePlugin

Re-export of H3's definePlugin for convenience.

Example:

import { definePlugin } from 'better-mock-server'

const myPlugin = definePlugin((h3, _options) => {
  // Plugin setup
})

registerPlugins(app, plugins?)

Registers plugins to an H3 application instance.

Parameters:

  • app: H3 application instance
  • plugins (optional): Plugins array

Utility Functions

buildServerUrl(protocol, hostname, port?)

Builds a server URL string from protocol, hostname, and optional port. Automatically normalizes the protocol (adds ':' if not present).

Parameters:

  • protocol: Protocol string (e.g., 'http', 'https', 'http:', 'https:')
  • hostname: Hostname or IP address
  • port (optional): Port number or string

Returns: Full URL string

Examples:

import { buildServerUrl } from 'better-mock-server'

buildServerUrl('http', 'localhost', 3000)
// 'http://localhost:3000/'

buildServerUrl('https:', 'example.com', 443)
// 'https://example.com:443/'

buildServerUrl('http', '127.0.0.1')
// 'http://127.0.0.1/'

buildServerUrl('http', '::1', 8080)
// 'http://[::1]:8080/'

joinPaths(...paths)

Joins multiple path segments into a normalized path.

Example:

import { joinPaths } from 'better-mock-server'

joinPaths('/api', 'users') // '/api/users'
joinPaths('/api/', '/users/') // '/api/users'
joinPaths('api', '', 'users') // 'api/users'

📝 Type Definitions

Routes Types

import type { EventHandler, RouteOptions } from 'h3'

type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
type AllHTTPMethod = 'ALL'

interface RouteHandlerConfig {
  handler: EventHandler
  options?: RouteOptions
}

type RouteHandler = EventHandler | RouteHandlerConfig

interface RouteConfig {
  GET?: RouteHandler
  POST?: RouteHandler
  PUT?: RouteHandler
  PATCH?: RouteHandler
  DELETE?: RouteHandler
  children?: Routes
}

interface Routes {
  [route: string]: RouteHandler | RouteConfig
}

interface ParsedRoute {
  route: string
  method: HTTPMethod | AllHTTPMethod
  handler: EventHandler
  options?: RouteOptions
}

Middleware Types

import type { Middleware, MiddlewareOptions } from 'h3'

interface MiddlewareConfig {
  route?: string
  handler: Middleware
  options?: MiddlewareOptions
}

type Middlewares = Array<Middleware | MiddlewareConfig>

type ParsedMiddleware =
  | [Middleware]
  | [string, Middleware]
  | [Middleware, MiddlewareOptions]
  | [string, Middleware, MiddlewareOptions]

Plugin Types

import type { H3Plugin } from 'h3'

type Plugins = H3Plugin[]

Server Types

import type { H3 as H3Instance, serve } from 'h3'
import type { ServerOptions } from 'srvx'

type Server = ReturnType<typeof serve>

type App = H3Instance

interface AppOptions {
  routes?: Routes
  middlewares?: Middlewares
  plugins?: Plugins
}

type srvxServerOptions = Omit<ServerOptions, 'fetch' | 'middleware' | 'plugins'>

interface AppServerOptions extends AppOptions, srvxServerOptions {
  routes: Routes
}

interface AppServer {
  raw: Server | undefined
  app: App
  port: number | string | undefined
  url: string | undefined
  listen: (listenPort?: number) => Promise<void>
  close: () => Promise<void>
  restart: (listenPort?: number) => Promise<void>
}

💡 Complete Example

import {
  createAppServer,
  defineMiddleware,
  definePlugin
} from 'better-mock-server'
import { readBody } from 'h3'

// Define a logger middleware
const logger = defineMiddleware((event, next) => {
  console.log(`[${new Date().toISOString()}] ${event.method} ${event.path}`)
  return next()
})

// Define a custom plugin
const corsPlugin = definePlugin((h3, _options) => {
  // CORS setup logic
})

// Create server with full configuration
const server = createAppServer({
  port: 3000,

  plugins: [corsPlugin],

  middlewares: [
    logger,
    {
      route: '/api',
      handler: (event, next) => {
        event.context.apiAccess = true
        return next()
      }
    }
  ],

  routes: {
    '/': () => 'Welcome to Better Mock Server!',

    '/api': {
      GET: () => ({ version: '1.0.0' }),

      children: {
        '/users': {
          GET: () => [
            { id: 1, name: 'Alice', email: '[email protected]' },
            { id: 2, name: 'Bob', email: '[email protected]' }
          ],

          POST: async (event) => {
            const body = await readBody(event)
            return {
              id: Date.now(),
              ...body,
              createdAt: new Date().toISOString()
            }
          },

          children: {
            '/:id': {
              GET: (event) => {
                const id = event.context.params.id
                return {
                  id,
                  name: `User ${id}`,
                  email: `user${id}@example.com`
                }
              },

              PUT: async (event) => {
                const id = event.context.params.id
                const body = await readBody(event)
                return {
                  id,
                  ...body,
                  updatedAt: new Date().toISOString()
                }
              },

              DELETE: (event) => {
                const id = event.context.params.id
                return {
                  success: true,
                  deletedId: id
                }
              }
            }
          }
        },

        '/posts': {
          GET: () => [
            { id: 1, title: 'First Post', content: 'Hello World' },
            { id: 2, title: 'Second Post', content: 'TypeScript is awesome' }
          ]
        }
      }
    }
  }
})

await server.listen()
console.log(`🚀 Server running at ${server.url}`)

// Graceful shutdown
process.on('SIGINT', async () => {
  console.log('\n👋 Shutting down...')
  await server.close()
  process.exit(0)
})

✅ Best Practices

1. Use Port 0 for Testing

Let the system assign an available port automatically:

const server = createAppServer({
  port: 0, // Random port
  routes: {
    /* ... */
  }
})
await server.listen()
console.log(`Test server running on port ${server.port}`)

2. Use defineRoutes for Type Safety

Always wrap your routes with defineRoutes() for better IDE support and type checking.

3. Order Matters

Middlewares and routes are registered in the order they appear. Place global middlewares before route-specific ones.

4. Async Handlers

When working with request bodies or async operations, always use async handlers:

;async (event) => {
  const body = await readBody(event)
  return body
}

5. Error Handling

Use H3's error handling utilities:

import { createError } from 'h3'
;(event) => {
  throw createError({
    statusCode: 404,
    message: 'User not found'
  })
}

6. Path Parameters

Access route parameters through event.context.params:

const routes = {
  '/:id': {
    GET: (event) => {
      const id = event.context.params.id
      return { id }
    }
  }
}

7. Nested Routes

Use the children property for better organization:

const routes = {
  '/api': {
    children: {
      '/users': {
        /* ... */
      },
      '/posts': {
        /* ... */
      }
    }
  }
}

⚠️ Constraints & Limitations

  • The library is built on H3, so all H3 limitations apply
  • Route definitions must be known at server startup (no dynamic route registration)
  • Middleware execution order follows the registration order
  • Port 0 will assign a random available port

📄 License

MIT License © 2025-PRESENT king3

🤝 Contributing

Contributions, issues and feature requests are welcome!

Feel free to check the issues page.

🔗 Related Projects

  • unjs/h3 - Minimal H3 HTTP framework
  • unjs/srvx - Universal Server based on web standards
  • unjs - Unified JavaScript Tools