A simple, file-based API mocking server.
Project description
Simple API Mock Server
A simple, file-based API mocking server built with Flask, designed to help developers quickly simulate API endpoints for testing and development.
🔧 Features
Core Functionality
- Dynamic Routing: Define API endpoints from a
api.jsonorapi.yamlfile. - OpenAPI Specification: Automatically generates a rich OpenAPI v3 specification at
/openapi.json. - Metrics Endpoint: Exposes Prometheus-style metrics at
/metrics. - Graceful 404 Handling: Custom JSON 404 responses for unknown routes.
- CORS Support: Integrated
Flask-Corsto handle Cross-Origin Resource Sharing.
Configuration & Customization
- YAML Configuration: Supports
.ymland.yamlconfiguration files. - Templating for Request Body Parameters: Responses can be dynamically templated using values from the request body.
- Custom Response Headers per Method: Define specific HTTP headers for different methods within the same route.
- Descriptions and Metadata: Add optional
descriptionandtagsfields to your API endpoints for better documentation and categorization. - Example Request Bodies: Include optional
request_bodyfields inapi.jsonfor documenting expected request payloads. - Static File Serving: Serve static files (e.g., UI assets) from a specified folder via a CLI argument.
Authentication & Security
- Improved Authentication Logic: Refactored for clarity, consistency, and maintainability.
- Granular Rate Limiting: Rate limiting can now be applied per client IP or API key for more realistic throttling. Old rate limit entries are pruned to prevent memory growth.
- API Key in Query Params: API keys can be sent via query parameters (
?api_key=...) as well as headers. - Auth Challenge Headers: 401 Unauthorized responses for Basic and Bearer auth now include
WWW-Authenticateheaders.
Development & Operations
- Enhanced Metrics Tracking: Thread-safe and semantically clearer metrics using
collections.Counter. - Robust Error Handling: Improved error logging with
logger.exception()and user-friendly messages for port binding errors and malformed JSON. - Thread-Safe Rate Limiting: Implemented
threading.Lockto prevent race conditions in rate limiting. - Hot Reloading: Automatically restarts the server when
api.jsonchanges (requires--debugflag). - Graceful Shutdown: Handles
SIGINT,SIGTERM, andKeyboardInterruptfor clean server termination. - Comprehensive Logging: Detailed logging for server operations, requests, and errors.
- Modular Configuration: Separated configuration validation and loading logic into a dedicated module.
Installation
From PyPI (Recommended, once published)
pip install simple-api-mock-server
From Source
-
Clone the repository:
git clone https://github.com/sanatladkat/simple-api-mock-server.git cd simple-api-mock-server
-
Create a virtual environment:
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate
-
Install dependencies:
pip install -r requirements.txt pip install -e .
🚀 Quick Start
To get a mock server up and running quickly:
-
Create a file
api.jsonorapi.yamlwith the following content:- path: /hello methods: - GET response: data: message: "Hello, world!" code: 200
-
Run the server from your project root:
simple-mock-server --config api.yaml
-
Visit:
http://localhost:5001/helloin your browser or API client.
📦 PyPI Package
You can install via pip:
pip install simple-api-mock-server
📄 View on PyPI # Placeholder: Update with actual PyPI link after publishing
Usage
-
Create an
api.jsonorapi.yamlfile:This file defines your API endpoints and their responses. See the Configuration section for details and examples.
-
Run the server:
simple-mock-server --config api.json --port 5001 --debug --verbose --static-folder ./static
CLI Options:
--config <path>: Path to the API configuration file (JSON or YAML). (default:api.json).--port <number>: Port to run the server on (default:5001).--host <address>: Host to bind the server to (default:127.0.0.1).--debug: Enable Flask debug mode and hot reloading (auto-reloader for code changes).--verbose: Enable verbose logging.--static-folder <path>: Path to a static folder to serve files from (e.g., for UI assets). Static files will be served at/static/<filename>.
-
Access the mock API:
The server will be running at
http://127.0.0.1:5001(or your specified port).
⚠️ Security Note
This mock server is intended for development and testing purposes only. It should not be exposed to the public internet or used in production environments, as it lacks proper authentication, authorization, and input validation safeguards beyond simulation.
🛠️ Configuration Reference
The configuration file (JSON or YAML) is a list of route objects. Each route object can have the following properties:
path(string, required): The URL path for the endpoint (e.g.,/users,/users/{user_id}). Flask's route variable syntax is supported.methods(array of strings, required): A list of HTTP methods this endpoint responds to (e.g.,["GET", "POST"]).response(object, required): An object defining the response to return.data(object or array, required): The JSON content to return as the response body.- Dynamic Responses:
- Route Variables: Use
{variable_name}in the response JSON to inject values from route variables (e.g.,"id": "{user_id}"). - Query Parameters: Use
{query_param:param_name}to inject values from URL query parameters (e.g.,"message": "Hello, {query_param:name}!"). - Request Body Parameters: Use
{body_param:param_name}to inject values from the JSON request body (e.g.,"received_name": "{body_param:name}").
- Route Variables: Use
- Echo Request Body: Include
"echo": truein thedataobject to have the server return the same JSON request body it received (instead of the static data response). Useful for testing POST/PUT payloads.
- Dynamic Responses:
code(integer, optional): The HTTP status code to return (default:200). For204 No Contentresponses, the body will be empty.delay(number, optional): The delay in seconds before sending the response, simulating network latency (default:0).headers(object, optional): A dictionary of custom HTTP headers to include in the response (e.g.,"X-Custom-Header": "MyValue"). Headers can also be templated.
description(string, optional): A brief description of the endpoint's purpose.tags(array of strings, optional): A list of tags for categorizing the endpoint.auth(object, optional): Configuration for authentication simulation.api_key(string, required ifauthis present): The expected API key. The server will look for this in theX-API-Keyheader orapi_keyquery parameter. If it doesn't match, a401 Unauthorizedresponse is returned.basic_auth(object, optional): Basic authentication credentials (username,password). If authentication fails, a401 Unauthorizedresponse with aWWW-Authenticate: Basic realm="Authentication Required"header is returned.bearer_token(string, optional): Bearer token. If authentication fails, a401 Unauthorizedresponse with aWWW-Authenticate: Bearer realm="Authentication Required"header is returned.skip_auth(boolean, optional): Set totrueto disable authentication for this endpoint.
rate_limit(object, optional): Configuration for rate limiting.requests(integer, required): Maximum number of requests allowed.window(integer, required): Time window in seconds for the rate limit.- Rate limit headers (
X-RateLimit-Limit,X-RateLimit-Remaining,Retry-After) are automatically included in responses. Rate limiting is applied per client IP or API key.
request_body(object, optional): An example or schema for the expected request body (for documentation/validation).query_params(array of objects, optional): A list of query parameters for the endpoint.name(string, required): The name of the query parameter.required(boolean, optional): Whether the query parameter is required (default:false).type(string, optional): The type of the query parameter (default:string).
api.yaml Example
- path: /
methods:
- GET
description: Welcome endpoint
tags:
- General
response:
data:
message: Welcome to the mock server!
code: 200
- path: /api/users
methods:
- GET
description: Get all users
tags:
- Users
response:
data:
- id: 1
name: John Doe
- id: 2
name: Jane Doe
code: 200
- path: /api/users
methods:
- POST
description: Create a new user
tags:
- Users
request_body:
name: string
email: string
response:
data:
message: User {body_param:name} created successfully
code: 201
- path: /api/users/{user_id}
methods:
- GET
description: Get user by ID
tags:
- Users
response:
data:
id: "{user_id}"
name: "User {user_id}"
headers:
X-User-Id: "{user_id}"
code: 200
- path: /api/greet
methods:
- GET
description: Greet a user by name from query param
tags:
- Utilities
query_params:
- name: name
required: true
type: string
response:
data:
message: "Hello, {query_param:name}!"
code: 200
- path: /api/protected
methods:
- GET
description: Protected endpoint with API key authentication
tags:
- Authentication
response:
data:
message: Access granted!
code: 200
auth:
api_key: my-secret-api-key
- path: /api/rate-limited
methods:
- GET
description: Endpoint with rate limiting enabled (2 requests per 60 seconds)
tags:
- Rate Limiting
response:
data:
message: You are not rate limited yet!
code: 200
rate_limit:
requests: 2
window: 60
For a more comprehensive example, refer to the api.json example below.
api.json Example
[
{
"path": "/",
"methods": ["GET"],
"description": "Welcome endpoint",
"tags": ["General"],
"response": {
"data": {"message": "Welcome to the mock server!"},
"code": 200
}
},
{
"path": "/api/users",
"methods": ["GET"],
"description": "Get all users",
"tags": ["Users"],
"response": {
"data": [
{"id": 1, "name": "John Doe"},
{"id": 2, "name": "Jane Doe"}
],
"code": 200
}
},
{
"path": "/api/users",
"methods": ["POST"],
"description": "Create a new user",
"tags": ["Users"],
"request_body": {"name": "string", "email": "string"},
"response": {
"data": {"message": "User {body_param:name} created successfully"},
"code": 201
}
},
{
"path": "/api/users/{user_id}",
"methods": ["GET"],
"description": "Get user by ID",
"tags": ["Users"],
"response": {
"data": {"id": "{user_id}", "name": "User {user_id}"},
"headers": {"X-User-Id": "{user_id}"},
"code": 200
}
},
{
"path": "/api/users/{user_id}",
"methods": ["PUT"],
"description": "Update user by ID",
"tags": ["Users"],
"request_body": {"name": "string"},
"response": {
"data": {"message": "User {user_id} updated successfully"},
"code": 200
}
},
{
"path": "/api/users/{user_id}",
"methods": ["DELETE"],
"description": "Delete user by ID",
"tags": ["Users"],
"response": {
"data": {},
"code": 204
}
},
{
"path": "/api/echo",
"methods": ["POST"],
"description": "Echoes the request body",
"tags": ["Utilities"],
"response": {
"data": {"echo": true},
"code": 200
}
},
{
"path": "/api/greet",
"methods": ["GET"],
"description": "Greet a user by name from query param",
"tags": ["Utilities"],
"response": {
"data": {"message": "Hello, {query_param:name}!"},
"code": 200
}
},
{
"path": "/api/slow-response",
"methods": ["GET"],
"description": "Simulates a slow network response",
"tags": ["Utilities"],
"response": {
"data": {"message": "This was a slow response"},
"delay": 2,
"code": 200
}
},
{
"path": "/api/custom-headers",
"methods": ["GET"],
"description": "Returns a response with custom headers",
"tags": ["Utilities"],
"response": {
"data": {"message": "This response has custom headers"},
"headers": {
"X-Custom-Header": "MyValue",
"Content-Type": "application/json"
},
"code": 200
}
},
{
"path": "/api/protected",
"methods": ["GET"],
"description": "Protected endpoint with API key authentication",
"tags": ["Authentication"],
"response": {
"data": {"message": "Access granted!"},
"code": 200
},
"auth": {"api_key": "my-secret-api-key"}
},
{
"path": "/api/basic-auth",
"methods": ["GET"],
"description": "Protected endpoint with Basic authentication",
"tags": ["Authentication"],
"response": {
"data": {"message": "Basic Auth successful!"},
"code": 200
},
"auth": {"basic_auth": {"username": "user", "password": "pass"}}
},
{
"path": "/api/bearer-token",
"methods": ["GET"],
"description": "Protected endpoint with Bearer token authentication",
"tags": ["Authentication"],
"response": {
"data": {"message": "Bearer Token successful!"},
"code": 200
},
"auth": {"bearer_token": "my-secret-token"}
},
{
"path": "/api/public",
"methods": ["GET"],
"description": "Public endpoint without authentication",
"tags": ["Authentication"],
"response": {
"data": {"message": "This is a public endpoint!"},
"code": 200
},
"auth": {"skip_auth": true}
},
{
"path": "/api/error",
"methods": ["GET"],
"description": "Simulates an internal server error",
"tags": ["Error Simulation"],
"response": {
"data": {"message": "Something went wrong!"},
"code": 500
}
},
{
"path": "/api/not-found",
"methods": ["GET"],
"description": "Simulates a resource not found error",
"tags": ["Error Simulation"],
"response": {
"data": {"message": "Resource not found!"},
"code": 404
}
},
{
"path": "/api/rate-limited",
"methods": ["GET"],
"description": "Endpoint with rate limiting enabled (2 requests per 60 seconds)",
"tags": ["Rate Limiting"],
"response": {
"data": {"message": "You are not rate limited yet!"},
"code": 200
},
"rate_limit": {"requests": 2, "window": 60}
}
]
Testing
To run the unit and integration tests, navigate to the project root and execute:
pip install ".[dev]" # Install development dependencies (requests, pytest) for running tests and contributing to development.
pytest
❗ Troubleshooting
Versioning and Changelog
See the CHANGELOG.md for details on releases and changes.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file simple-api-mock-server-0.3.3.tar.gz.
File metadata
- Download URL: simple-api-mock-server-0.3.3.tar.gz
- Upload date:
- Size: 19.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.7.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a33dcd6e0305f691f1b7ac38f8afcb1571b3b3b8c36500d40443c9e2df19959e
|
|
| MD5 |
bb4178fcd91a1f600fd60978eb9a3c6a
|
|
| BLAKE2b-256 |
f3ca883e0979a58eda1a92ed02ebc00e91558604d71eec0a23abce7d648aacdd
|
File details
Details for the file simple_api_mock_server-0.3.3-py3-none-any.whl.
File metadata
- Download URL: simple_api_mock_server-0.3.3-py3-none-any.whl
- Upload date:
- Size: 18.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.7.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
08bc42bf4e73403879562e2ce845c7f42c48915e3a00e17fea8148e7abef3bd0
|
|
| MD5 |
ec19ecc1e70f6f67e0cbd526543766c1
|
|
| BLAKE2b-256 |
2b333a707a32ac332cf0b498e8de316482d271bd3a574e6902d70158fff01c83
|