# APImposter 🎭 **A small, flexible API mock service designed with microservice environments in mind.** --- ## What is APImposter? APImposter is an easy-to-use service built with Spring Boot, enabling quick mocking of API responses from various systems. It allows you to organize your mock data in YAML files. --- ## Features - **Structured Mock Data:** Organize your mock data in YAML files. - **Automatic URL Mapping:** Automatically generates URL structure based on your folder hierarchy and YAML file names. - **Hot Reloading:** Automatically reloads mock data files when they are changed. - **Dynamic Responses and Global Placeholders:** Supports dynamic values in responses based on URL parameters and you can define global placeholders to reuse values across multiple responses and systems. - **Conditional Responses:** Return different mock responses based on request data (headers, query, body, or path). - **Swagger UI:** Automatically generated Swagger UI from YAML files. --- ## Mocks Structure Mocks are defined in YAML files and stored in a directory specified via `application.properties`. You can organize them in nested folders: ``` /mocks/ ├── globals.yaml ├── system1/ │ ├── users.yaml │ └── index.yaml └── another-system/ ├── endpoint1.yaml └── subdir/ ├── endpoint2.yaml └── index.yaml ``` ### URL Mapping - `/system1/users.yaml` defines mocks under `/system1/users/...` - `/system1/index.yaml` defines mocks under`/system1/...`) - Multiple endpoints can be grouped in one file - Folders in the YAML file structure become part of the URL path automatically. ### Grouped endpoints in `users.yaml` ```yaml # mocks/system1/users.yaml - method: GET path: "/" response: status: 200 body: users: - id: 1 name: "Alice" - id: 2 name: "Bob" - method: GET path: "/{id}" response: status: 200 body: id: "{id:int}" name: "User {id}" - method: POST path: "/" response: status: 200 body: message: "User created" ``` ### Base-level grouping via `index.yaml` ```yaml # mocks/system1/index.yaml - method: GET path: "/" response: status: 200 body: message: "Welcome to system1" - method: GET path: "/info" response: status: 200 body: name: "System One" version: "1.0" ``` ### Placeholder Type Hints You can include optional type hints in placeholders to ensure JSON values are rendered with correct types. #### Supported types | Type | Placeholder | Output | |---------|------------------|-----------------| | string | `{name}` | `"Alice"` | | int | `{id:int}` | `42` *(number)* | | float | `{price:float}` | `99.95` | | boolean | `{enabled:bool}` | `true` | If parsing fails (e.g. `abc` for an `int`), APImposter **falls back to the original string**. ### Relative Date/Time Placeholders APImposter supports dynamic date and time placeholders that are calculated relative to the current time. This is perfect for testing calendar applications, scheduling systems, or any time-sensitive mock data. #### Supported formats | Format | Example | Description | |--------|---------|-------------| | `{datetime+/-offset}` | `{datetime+2d4h30m}` | DateTime with flexible time offset | | `{datetime+/-Nd@HH:MM}` | `{datetime+1d@13:30}` | DateTime N days from now at specific time | | `{date+/-Nd}` | `{date-7d}` | Date N days from now | #### Format specifiers (optional) Add a format specifier after a colon to control the output format: | Specifier | Description | Example Output | |-----------|-------------|----------------| | `iso` (default) | ISO standard format | `2025-09-03T15:30:00` | | `rfc3339` | RFC 3339 with UTC timezone | `2025-09-03T15:30:00Z` | | Custom pattern | Any valid DateTimeFormatter pattern | `Sep 03, 2025` | #### Examples ```yaml # Basic relative dates (ISO format) - method: GET path: "/events" response: body: todayMeeting: "{datetime+0d@08:00}" # Today at 8:00 AM tomorrowLunch: "{datetime+1d@13:00}" # Tomorrow at 1:00 PM nextWeek: "{date+7d}" # Date 7 days from now pastEvent: "{datetime-2d4h30m}" # 2 days, 4h, 30m ago yesterday: "{date-1d}" # Yesterday's date # Different output formats - method: GET path: "/calendar" response: body: # RFC3339 format eventTimeRfc: "{datetime+1d@15:30:rfc3339}" # "2025-09-03T15:30:00Z" # Custom formats eventDate: "{date+1d:MMM dd, yyyy}" # "Sep 03, 2025" timeOnly: "{datetime+0d@14:30:HH:mm}" # "14:30" shortDate: "{date+7d:MM/dd/yy}" # "09/09/25" ``` #### Time components for offset format - `d` = days - `h` = hours - `m` = minutes All components are optional and can be combined: `{datetime+1d2h}`, `{datetime+30m}`, `{datetime-4h15m}` **Note:** If a custom format is invalid, the system automatically falls back to ISO format to prevent errors. --- ## Example Requests and Responses | YAML file | Request | Response Example | |------------------------------------|----------------------------------|----------------------------------------------| | `system1/users.yaml` | `GET /system1/users` | `{ "users": [{ "id": 1 }, { "id": 2 }] }` | | `system1/users.yaml` | `GET /system1/users/42` | `{ "id": 42, "name": "User 42" }` | | `system1/users.yaml` | `POST /system1/users` | `{ "message": "User created" }` | | `system1/index.yaml` | `GET /system1` | `{ "message": "Welcome to system1" }` | | `system1/index.yaml` | `GET /system1/info` | `{ "name": "System One", "version": "1.0" }` | | `another-system/subdir/index.yaml` | `GET /another-system/subdir/...` | *(Defined response in file)* | --- ## Globals Global values can be defined in a dedicated file `globals.yaml`: ```yaml # mocks/globals.yaml globals: currentUserId: "user-123" ``` You can reference global values using `{globals.key}` or `{globals.key:type}` in responses. --- ## Conditional Responses APImposter supports **conditional responses**, allowing different mock responses to be returned **based on request values** - query parameters, headers, path variables, or body fields. This is useful for simulating realistic API behavior, such as authentication, filtering, or alternate success/error outcomes. ### Supported condition types: - `path`: matches against path variables - `query`: matches query parameters (`?filter=active`) - `headers`: matches HTTP headers (case-insensitive) - `body`: matches top-level JSON body fields (for POST/PUT) ### Example: conditionals by path ```yaml - method: "GET" path: "/users/{id}" conditionalResponses: - conditions: path: id: "1" response: status: 200 headers: Content-Type: application/json body: id: 1 name: "Lennart" - conditions: path: id: "2" response: status: 200 headers: Content-Type: application/json body: id: 2 name: "Kurre" response: status: 404 headers: Content-Type: application/json body: error: "User not found" ``` ### Example: conditionals by header ```yaml - method: "GET" path: "/secure" conditionalResponses: - conditions: headers: authorization: "Bearer abc123" response: status: 200 headers: Content-Type: application/json body: message: "Access granted" response: status: 401 headers: Content-Type: application/json body: error: "Unauthorized" ``` ### Example: conditionals by query parameter ```yaml - method: "GET" path: "/products" conditionalResponses: - conditions: query: category: "books" response: status: 200 headers: { Content-Type: "application/json" } body: products: - id: "b1" name: "Data Structures and Algorithms in Java" - conditions: query: category: "tech" response: status: 200 headers: { Content-Type: "application/json" } body: products: - id: "t1" name: "Keyboard" response: status: 200 headers: { Content-Type: "application/json" } body: products: [] ``` ### Example: conditionals by request body ```yaml - method: "POST" path: "/login" conditionalResponses: - conditions: body: username: "admin" password: "secret" response: status: 200 headers: Content-Type: application/json body: token: "abc123" response: status: 403 headers: Content-Type: application/json body: error: "Invalid credentials" ``` ### Testing with `curl` ```bash curl http://localhost:8080/mocks/users/1 curl -H "Authorization: Bearer abc123" http://localhost:8080/mocks/secure curl "http://localhost:8080/mocks/products?category=books" curl -X POST http://localhost:8080/mocks/login \ -H "Content-Type: application/json" \ -d '{"username": "admin", "password": "secret"}' ``` Conditions are evaluated **in order**, and the **first matching block wins**. If no condition matches, the fallback `response:` is used. --- ## Configuration In `application.properties` you can configure the following properties: - `mock.file-path`: The path to the directory containing your mock data YAML files. - `mock.reload-interval-ms`: The interval in milliseconds at which the service checks for changes in the mock data files. - `mock.base-path`: The base path for the mock service. This is prepended to all generated URLs. Default is `/mocks`. - `mock.default-delay-ms`: The default delay in milliseconds for responses. This can be overridden in the YAML files. --- ## Swagger UI OpenAPI Specification is generated from the mock data YAML files and can be accessed via Swagger UI. You will find it at: ``` http://localhost:8080/swagger ``` --- ## Quick Start Clone the project and run it simply with: ```shell ./mvnw spring-boot:run ``` or with Docker: ```shell docker-compose up ``` --- 🎭 **Good luck and happy mocking!** 🎭