=============== LIBRARY RULES =============== From library maintainers: - apitwin config files use TOML format by default, but JSON and YAML are also supported - Install via npm: npm install -D @ridakaddir/apitwin - Use 'apitwin serve' to start the mock server, not 'apitwin start' - Directory-based stubs enable automatic CRUD operations on JSON files - Response transitions allow stateful behavior across requests ### Configure Transitions Example Source: https://github.com/ridakaddir/apitwin/blob/main/examples/README.md Start the ApiTwin server with the transitions configuration. ```bash apitwin --config examples/transitions ``` -------------------------------- ### Start Server Command Source: https://github.com/ridakaddir/apitwin/blob/main/examples/cross-refs/README.md Command to start the server with a specific configuration file. Ensure you have Go installed and are in the project directory. ```bash go run . serve -c examples/cross-refs/apitwin.toml ``` -------------------------------- ### Configure Cross-Reference Example Source: https://github.com/ridakaddir/apitwin/blob/main/examples/README.md Start the ApiTwin server with the cross-reference configuration. ```bash apitwin --config examples/cross-refs ``` -------------------------------- ### Configure Dynamic Files Example Source: https://github.com/ridakaddir/apitwin/blob/main/examples/README.md Start the ApiTwin server with the dynamic-files configuration. ```bash apitwin --config examples/dynamic-files ``` -------------------------------- ### Scaffold and Start Apitwin Source: https://github.com/ridakaddir/apitwin/blob/main/docs/cli-reference.md Initializes a configuration file and starts the apitwin server. ```sh apitwin --init apitwin --target https://restcountries.com/v3.1 ``` -------------------------------- ### Interact with the mock API Source: https://github.com/ridakaddir/apitwin/blob/main/examples/openapi-generate/README.md Examples of how to interact with the served mock API using the `http` client. These demonstrate common operations like listing pets, getting a specific pet, creating a pet, and retrieving store inventory or user data. ```sh # List pets by status http ':4000/pet/findByStatus?status=available' ``` ```sh # Get a pet by id http :4000/pet/1 ``` ```sh # Create a pet http POST :4000/pet name=Rex photoUrls:='["https://example.com/rex.jpg"]' status=available ``` ```sh # Get store inventory http :4000/store/inventory ``` ```sh # Get an order http :4000/store/order/1 ``` ```sh # Get a user http :4000/user/johndoe ``` -------------------------------- ### Dynamic File Path Examples Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/dynamic-files.md Illustrates various ways to use placeholders in file paths, including examples for query parameters, nested body fields, request headers, and named path parameters. Each example shows the placeholder syntax and the resulting file path based on sample request data. ```toml # From query parameter file = "stubs/cities-{query.country}.json" # ?country=morocco → stubs/cities-morocco.json ``` ```toml # From body field (dot-notation for nested) file = "stubs/region-{body.geography.region}.json" # {"geography": {"region": "north-africa"}} → stubs/region-north-africa.json ``` ```toml # From header file = "stubs/continent-{header.X-Continent}.json" # X-Continent: africa → stubs/continent-africa.json ``` ```toml # From named path parameter file = "stubs/countries/{path.countryId}.json" # /api/countries/morocco → stubs/countries/morocco.json ``` -------------------------------- ### Start HTTP and gRPC Servers Source: https://github.com/ridakaddir/apitwin/blob/main/docs/cli-reference.md Starts both HTTP and gRPC servers using a specified configuration and proto file. ```sh apitwin --config ./mocks --grpc-proto geo.proto ``` -------------------------------- ### Start apitwin with Default Runtime Mirror Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/runtime-state.md This command starts the apitwin server using the default runtime mirror mode. The mirror is created in `.apitwin/state/` next to the config directory, and all mutations are written there. No flags are needed as this is the default behavior. ```sh apitwin --config ./my-project ``` -------------------------------- ### Development Environment Setup Source: https://github.com/ridakaddir/apitwin/blob/main/README.md Commands to initialize the development environment and list available tasks. ```sh devbox shell # Go 1.25.1, golangci-lint, goreleaser task --list # See available tasks ``` -------------------------------- ### Generate gRPC Config and Start Servers Source: https://github.com/ridakaddir/apitwin/blob/main/docs/quick-start.md Commands to generate configuration and stubs from a proto file, and then start both HTTP and gRPC servers using Apitwin. ```sh # Generate config + stubs apitwin generate --proto service.proto --out ./mocks # Start servers (HTTP on :4000, gRPC on :50051) apitwin --config ./mocks --grpc-proto service.proto ``` -------------------------------- ### Install apitwin via Go Source: https://github.com/ridakaddir/apitwin/blob/main/docs/installation.md Installs the tool using the Go toolchain, requiring Go 1.25 or higher. ```sh go install github.com/ridakaddir/apitwin@latest ``` -------------------------------- ### Minimal TOML Configuration Example Source: https://github.com/ridakaddir/apitwin/blob/main/docs/configuration/README.md Defines a single HTTP GET route with success, empty, and error cases, including a delay for the error case. ```toml [[routes]] method = "GET" match = "/api/countries" enabled = true fallback = "success" [routes.cases.success] status = 200 file = "stubs/countries.json" [routes.cases.empty] status = 200 json = '{"countries": []}' [routes.cases.error] status = 500 json = '{"message": "Internal Server Error"}' delay = 2 ``` -------------------------------- ### Verify apitwin installation Source: https://github.com/ridakaddir/apitwin/blob/main/docs/installation.md Checks the installed version and displays help information. ```sh apitwin --version apitwin --help ``` -------------------------------- ### Start the mock gRPC server Source: https://github.com/ridakaddir/apitwin/blob/main/examples/grpc-directory-persist/README.md Use the apitwin command to start the mock gRPC server with the specified configuration and proto file. The server defaults to listening on port 50051. ```bash apitwin --config examples/grpc-directory-persist --grpc-proto examples/grpc-directory-persist/items.proto ``` -------------------------------- ### Install apitwin via npm Source: https://github.com/ridakaddir/apitwin/blob/main/docs/installation.md Installs the platform-specific binary as a development dependency. ```sh npm install -D @ridakaddir/apitwin ``` -------------------------------- ### Start UI Development Server Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/devtool-ui.md Run this command to start the Vite dev server for UI development. It proxies API requests to localhost:4000 and enables hot module replacement. ```sh task ui:dev ``` -------------------------------- ### Live Directory Reference Example Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/cross-endpoint-references.md Demonstrates how directory references remain as live tokens to resolve dynamically on every read. ```json { "continentId": "{{uuid}}", "countries": "{{ref:stubs/countries/{.continentId}/?template=stubs/templates/country-summary.json}}" } ``` -------------------------------- ### Run HTTP and gRPC examples Source: https://github.com/ridakaddir/apitwin/blob/main/docs/examples.md Commands to execute Apitwin with specific configuration directories for HTTP or gRPC services. ```sh apitwin --config examples/ ``` ```sh apitwin --config examples/ --grpc-proto examples//.proto ``` -------------------------------- ### Generated TOML configuration example Source: https://github.com/ridakaddir/apitwin/blob/main/docs/openapi/generate.md Example of a generated TOML file containing route definitions and response cases. ```toml # Generated from openapi.yaml # Tag: pet # Returns pets based on status [[routes]] method = "GET" match = "/pet/findByStatus" enabled = true fallback = "success" [routes.cases.success] status = 200 file = "stubs/get_pet_findByStatus_200.json" [routes.cases.bad_request] status = 400 file = "stubs/get_pet_findByStatus_400.json" ``` -------------------------------- ### Traffic Split Synchronization Example Configuration Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/cascade-mutations.md Example configuration for synchronizing ML model traffic split values across endpoint and deployment files using cascade mutations. ```toml [[routes]] method = "PATCH" match = "/api/v1/*/environments/*/endpoint/{endpointId}/traffic-split" enabled = true fallback = "updated" [routes.cases.updated] status = 200 persist = true merge = "cascade" # Update endpoint's traffic split configuration [routes.cases.updated.primary] file = "stubs/endpoints/{path.endpointId}.json" merge = "update" path = "trafficSplit" # Update all deployment files for this endpoint [[routes.cases.updated.cascade]] pattern = "stubs/deployments/{path.endpointId}/*.json" merge = "update" path = "deploymentSpec.trafficSplit" transform = "$.trafficSplit" ``` -------------------------------- ### Initialize Apitwin Project Source: https://github.com/ridakaddir/apitwin/blob/main/README.md Initialize a new Apitwin project with default configuration files. This is a good starting point for manual stub creation. ```sh apitwin --init ``` -------------------------------- ### Accessing the Devtool UI Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/devtool-ui.md Instructions on how to start apitwin and access the embedded browser dashboard. ```APIDOC ## Accessing the Devtool UI Start apitwin with your configuration: ```sh apitwin --config ./mocks ``` Then, open your browser to the following URL, replacing `PORT` with the port apitwin is running on: ``` http://localhost:PORT/__ui/ ``` For example, if apitwin is running on port 4000, the UI will be at `http://localhost:4000/__ui/`. ``` -------------------------------- ### Reference Syntax Examples Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/cross-endpoint-references.md Common patterns for referencing files, applying filters, using templates, and spreading objects. ```text {{ref:path}} # Reference all items from a directory or single file {{ref:path?filter=field:value}} # Filter by field equality {{ref:path?template=template.json}} # Transform data shape using Go templates {{ref:path?filter=field:value&template=template.json}} # Both filter and transform "$spread": "{{ref:path}}" # Spread object properties into containing object ``` -------------------------------- ### Example: Create Resource with Key from Request Body Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/directory-stubs.md Demonstrates creating a resource where the filename is derived from the `code` field in the POST request body. ```shell curl -X POST localhost:4000/api/countries -d '{"code": "morocco", "name": "Morocco"}' # Creates file: stubs/countries/morocco.json ``` -------------------------------- ### Download and install binary for Linux x86-64 Source: https://github.com/ridakaddir/apitwin/blob/main/docs/installation.md Downloads and moves the binary to /usr/local/bin for Linux x86-64 systems. ```sh curl -L https://github.com/ridakaddir/apitwin/releases/latest/download/apitwin_linux_amd64.tar.gz | tar xz sudo mv apitwin /usr/local/bin/ ``` -------------------------------- ### Start Record Mode Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/record-mode.md Configures the proxy to record responses from a target API to local stub files. ```sh apitwin --config ./mocks \ --target https://restcountries.com/v3.1 \ --api-prefix /api \ --record ``` -------------------------------- ### Serve generated gRPC mocks Source: https://github.com/ridakaddir/apitwin/blob/main/docs/grpc/generation.md Start the server using the generated configuration and proto files. Ensure import paths match those used during generation. ```sh apitwin --config ./mocks --grpc-proto geo.proto ``` ```sh apitwin generate --proto proto/service/v1/service.proto --import-path proto/ --out ./mocks apitwin --config ./mocks --grpc-proto proto/service/v1/service.proto --import-path proto/ ``` -------------------------------- ### Download and install binary for macOS Intel Source: https://github.com/ridakaddir/apitwin/blob/main/docs/installation.md Downloads and moves the binary to /usr/local/bin for macOS Intel systems. ```sh curl -L https://github.com/ridakaddir/apitwin/releases/latest/download/apitwin_darwin_amd64.tar.gz | tar xz sudo mv apitwin /usr/local/bin/ ``` -------------------------------- ### Basic gRPC Mocking with Apitwin Source: https://github.com/ridakaddir/apitwin/blob/main/examples/README.md Sets up a gRPC mock server using Apitwin, requiring a proto file for service definition. Examples show sending requests with grpcurl and switching mock cases. ```sh apitwin --config examples/grpc-mock \ --grpc-proto examples/grpc-mock/users.proto ``` ```sh # Success (stub file) grpcurl -plaintext -d '{"user_id":"1"}' \ localhost:50051 users.UserService/GetUser # Switch to not_found: edit apitwin.toml and change fallback = "not_found" grpcurl -plaintext -d '{"user_id":"999"}' \ localhost:50051 users.UserService/GetUser # Switch to error with delay: change fallback = "error" grpcurl -plaintext -d '{"user_id":"1"}' \ localhost:50051 users.UserService/GetUser ``` ```sh grpcurl -plaintext -d '{"page":1,"page_size":10}' \ localhost:50051 users.UserService/ListUsers # Empty list: change fallback = "empty" in apitwin.toml ``` ```sh grpcurl -plaintext \ -d '{"name":"Diana","email":"diana@example.com","role":"member"}' \ localhost:50051 users.UserService/CreateUser # Conflict: change fallback = "already_exists" ``` ```sh # Inspect registered services (gRPC reflection is always on): grpcurl -plaintext localhost:50051 list grpcurl -plaintext localhost:50051 describe users.UserService ``` -------------------------------- ### gRPC Command Line Example Source: https://github.com/ridakaddir/apitwin/blob/main/docs/grpc/stubs.md Shows how to use grpcurl to send a request to a gRPC service, demonstrating how the server reads a specific stub file based on the request body. ```sh grpcurl -plaintext -d '{"country_code":"morocco"}' localhost:50051 geo.CountryService/GetCountry # → reads stubs/countries/morocco.json ``` -------------------------------- ### Configuration for Listing Resources Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/directory-stubs.md Configure a GET route to list all resources in a directory. The `file` path points to the directory containing the JSON files. ```toml [[routes]] method = "GET" match = "/api/countries" enabled = true fallback = "list" [routes.cases.list] file = "stubs/countries/" # Returns array of all .json files ``` -------------------------------- ### Run Apitwin with gRPC Proto Source: https://github.com/ridakaddir/apitwin/blob/main/README.md Start Apitwin with gRPC support, specifying the proto file to use for service definitions. This command loads mocks and enables gRPC proxying. ```sh apitwin --config ./mocks --grpc-proto service.proto ``` -------------------------------- ### Basic Array Processing Example Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/array-processing.md Lists countries in a continent with their capital cities. This example uses $each to iterate over countries filtered by continent and $template to select specific fields. ```json { "name": "Africa", "countries": { "$each": "{{ref:countries/?filter=continent:africa}}", "$template": { "name": "{{.name}}", "capital": "{{.capital}}", "population": "{{.population}}" } } } ``` -------------------------------- ### Input Sanitization Examples Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/cascade-mutations.md Shows how dangerous characters are stripped from path parameters. ```text Input: "../../../etc/passwd" Output: "etcpasswd" Input: "test\x00file" Output: "testfile" ``` -------------------------------- ### Example Morocco Country Stub Source: https://github.com/ridakaddir/apitwin/blob/main/README.md A JSON stub file defining a 'Morocco' country resource. This example demonstrates basic key-value pairs for resource properties. ```json { "name": "Morocco", "continent": "Africa", "capital": "Rabat", "population": 37000000, "languages": ["Arabic", "Berber", "French"] } ``` -------------------------------- ### Example: Create Resource with Auto-Generated UUID Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/directory-stubs.md Demonstrates creating a resource when neither the request body nor path parameters provide a key. An auto-generated UUID is used as the filename. ```shell # POST without code and no matching path parameter curl -X POST localhost:4000/api/countries -d '{"name": "New Country"}' # Creates file: stubs/countries/123e4567-e89b-12d3-a456-426614174000.json # Response: {"code": "123e4567-...", "name": "New Country"} ``` -------------------------------- ### Basic Reference Usage Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/cross-endpoint-references.md Examples of referencing entire directories or specific JSON files within a response. ```json { "name": "Africa", "area_km2": 30300000, "countries": "{{ref:stubs/countries/}}" } ``` ```json { "name": "Casablanca", "country": "Morocco", "countryDetails": "{{ref:stubs/countries/morocco.json}}" } ``` -------------------------------- ### View generated file structure Source: https://github.com/ridakaddir/apitwin/blob/main/docs/openapi/generate.md Example directory layout showing generated TOML config files and JSON stubs. ```text mocks/ ├── pet.toml # 13 routes ├── store.toml # 3 routes ├── user.toml # 3 routes └── stubs/ ├── get_pet_petId_200.json ├── get_pet_findByStatus_200.json ├── post_pet_200.json └── ... (42 more) ``` -------------------------------- ### Example: Create Resource with Key from Path Parameter Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/directory-stubs.md Demonstrates creating a resource where the filename is derived from the `continentId` path parameter in the URL. The value is also injected into the saved record. ```shell curl -X POST localhost:4000/api/continents/africa/countries -d '{"name": "Tunisia"}' # Creates file: stubs/countries/africa.json # The continentId is extracted from the URL and injected into the saved record ``` -------------------------------- ### Inspect gRPC Services with grpcurl Source: https://github.com/ridakaddir/apitwin/blob/main/docs/quick-start.md Examples of using grpcurl to interact with a running gRPC server, including listing services, describing a service, and calling a method. ```sh # List all services (via server reflection) grpcurl -plaintext localhost:50051 list # Describe a service grpcurl -plaintext localhost:50051 describe geo.CountryService # Call a method grpcurl -plaintext -d '{"country_code":"morocco"}' localhost:50051 geo.CountryService/GetCountry ``` -------------------------------- ### Selective Mock with Proxy Fallthrough Source: https://github.com/ridakaddir/apitwin/blob/main/docs/cli-reference.md Starts apitwin with a specific configuration directory and forwards unmatched requests to a target URL. ```sh apitwin --config ./mocks --target https://restcountries.com/v3.1 ``` -------------------------------- ### gRPC Background Transitions on Persist Source: https://github.com/ridakaddir/apitwin/blob/main/docs/grpc/config.md Configure background transitions for gRPC routes when persistence is enabled. This example demonstrates setting up transitions for database instance creation, including default files for provisioning and readiness states. ```toml [[grpc_routes]] match = "/database.v1.DatabaseService/CreateDatabaseInstance" fallback = "created" [[grpc_routes.transitions]] case = "provisioning" duration = 30 [[grpc_routes.transitions]] case = "ready" [grpc_routes.cases.created] status = 0 file = "stubs/instances/" persist = true merge = "append" key = "id" defaults = "stubs/defaults/instance-provisioning.json" [grpc_routes.cases.ready] persist = true merge = "update" defaults = "stubs/defaults/instance-ready.json" # 30s after Create, the ready defaults are merged into the file on disk. ``` -------------------------------- ### Download and install binary for macOS Apple Silicon Source: https://github.com/ridakaddir/apitwin/blob/main/docs/installation.md Downloads and moves the binary to /usr/local/bin for macOS Apple Silicon systems. ```sh curl -L https://github.com/ridakaddir/apitwin/releases/latest/download/apitwin_darwin_arm64.tar.gz | tar xz sudo mv apitwin /usr/local/bin/ ``` -------------------------------- ### Using Task Runner for Generation Source: https://github.com/ridakaddir/apitwin/blob/main/docs/cli-reference.md Examples of using a 'task' runner to generate apitwin configurations with different OpenAPI specifications and output directories. ```sh task generate SPEC=openapi.yaml task generate SPEC=https://petstore3.swagger.io/api/v3/openapi.json task generate SPEC=openapi.yaml OUT=./geo-mocks ``` -------------------------------- ### Execute API Operations via CLI Source: https://github.com/ridakaddir/apitwin/blob/main/examples/directory-stubs/README.md Commands to start the mock server and perform CRUD operations on resources stored in the directory structure. ```bash # Start the mock server apitwin --config examples/directory-stubs # List users (directory aggregation) curl http://localhost:4000/users # Get specific user curl http://localhost:4000/users/1 # Create new user (defaults fill in userId, role, active, createdAt) curl -X POST http://localhost:4000/users \ -H "Content-Type: application/json" \ -d '{"name": "Diana Wilson", "email": "diana@example.com"}' # Create user with explicit values (overrides defaults) curl -X POST http://localhost:4000/users \ -H "Content-Type: application/json" \ -d '{"userId": "custom-id", "name": "Eve Brown", "email": "eve@example.com", "role": "admin"}' # Update user (shallow merge) curl -X PATCH http://localhost:4000/users/1 \ -H "Content-Type: application/json" \ -d '{"email": "alice.johnson@newdomain.com", "active": false}' # Delete user curl -X DELETE http://localhost:4000/users/3 ``` -------------------------------- ### Directory structure with mixed formats Source: https://github.com/ridakaddir/apitwin/blob/main/docs/configuration/formats.md Example of a directory configuration containing multiple file formats that are merged alphabetically. ```text mocks/ ├── continents.toml ├── countries.yaml └── cities.json ``` -------------------------------- ### Dynamic Reference Error Handling Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/cross-endpoint-references.md Examples of reference configurations that trigger errors versus those that return empty arrays. ```json // These cause errors: {"data": "{{ref:stubs/{.missingField}/}}"} // Field not in request {"data": "{{ref:stubs/{.emptyField}/}}"} // Field exists but empty {"data": "{{ref:stubs/{.field}/missing.json}}"} // File doesn't exist // This succeeds (returns empty array): {"data": "{{ref:stubs/{.field}/missing-dir/}}"} // Directory doesn't exist ``` -------------------------------- ### Define Protobuf service for auto-extraction Source: https://github.com/ridakaddir/apitwin/blob/main/docs/grpc/persistence.md Example of a service definition that allows apitwin to automatically infer the source field. ```proto service DatabaseService { rpc UpdateDatabaseInstance (UpdateDatabaseInstanceRequest) returns (DatabaseInstance); } message UpdateDatabaseInstanceRequest { string name = 1; DatabaseInstance database_instance = 2; // ← matches response type → auto-extracted } ``` -------------------------------- ### Cancel Order Examples Source: https://github.com/ridakaddir/apitwin/blob/main/examples/README.md Demonstrates different scenarios for cancelling orders using grpcurl. Includes successful cancellation, cancellation due to missing reason (invalid argument), and cancellation simulating an already-shipped scenario. ```bash grpcurl -plaintext \ -d '{"order_id":"o999","reason":"duplicate order placed"}' \ localhost:50051 orders.OrderService/CancelOrder ``` ```bash grpcurl -plaintext \ -d '{"order_id":"o999"}' \ localhost:50051 orders.OrderService/CancelOrder ``` ```bash grpcurl -plaintext \ -d '{"order_id":"o999","reason":"changed mind"}' \ localhost:50051 orders.OrderService/CancelOrder ``` -------------------------------- ### Manual Build and Initialization Source: https://github.com/ridakaddir/apitwin/blob/main/README.md Steps to build the project from source without using devbox. ```sh git clone https://github.com/ridakaddir/apitwin.git cd apitwin go build -o apitwin . ./apitwin --init ``` -------------------------------- ### Request-time Transitions Configuration (TOML) Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/response-transitions.md Configure request-time transitions for a GET route. The response changes based on elapsed time. No file mutation occurs. ```toml [[routes]] method = "GET" match = "/countries/{countryId}/visa-status" enabled = true fallback = "submitted" [[routes.transitions]] case = "submitted" duration = 30 # serve for 30 seconds [[routes.transitions]] case = "under_review" duration = 60 # serve for 60 seconds [[routes.transitions]] case = "approved" # no duration — terminal state [routes.cases.submitted] status = 200 json = '{"country": "morocco", "status": "submitted"}' [routes.cases.under_review] status = 200 json = '{"country": "morocco", "status": "under_review"}' [routes.cases.approved] status = 200 json = '{"country": "morocco", "status": "approved"}' ``` -------------------------------- ### Initialize and Proxy HTTP Server Source: https://github.com/ridakaddir/apitwin/blob/main/docs/quick-start.md Create a starter configuration and set up a proxy to an external API. Apitwin will serve mock responses for matched routes and proxy others to the target URL. ```sh apitwin --init ``` ```sh apitwin --target https://api.example.com ``` -------------------------------- ### Region-Based API Configuration Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/cross-endpoint-references.md Demonstrates using headers and request data to resolve dynamic references in a region-based API setup. ```json { "region": "{header.X-Region}", "continent": "{.continent}", "countries": "{{ref:regions/{header.X-Region}/{.continent}/countries/}}", "metadata": { "createdAt": "{{now}}", "countryId": "{{uuid}}" } } ``` ```bash POST /api/countries X-Region: north-africa { "name": "Tunisia", "continent": "africa" } ``` ```json { "region": "north-africa", "continent": "africa", "countries": [{"code": "morocco", "name": "Morocco", "capital": "Rabat"}], "metadata": { "createdAt": "2026-03-26T10:30:00Z", "countryId": "550e8400-e29b-41d4-a716-446655440000" } } ``` -------------------------------- ### gRPC Proxy Fallthrough Example Source: https://github.com/ridakaddir/apitwin/blob/main/docs/grpc/stubs.md Demonstrates a gRPC route that mocks specific methods (e.g., ListCities for Morocco) while allowing others to fall through to a configured gRPC target. Unmatched methods without a fallback return gRPC UNIMPLEMENTED. ```toml # ListCities is mocked for Morocco only; all other countries are proxied [[grpc_routes]] match = "/geo.CityService/ListCities" enabled = true # No fallback — unmatched conditions go to --grpc-target [[grpc_routes.conditions]] source = "body" field = "country_code" op = "eq" value = "morocco" case = "moroccan_cities" [grpc_routes.cases.moroccan_cities] status = 0 file = "stubs/cities_morocco.json" # GetPopulation is not defined at all — always proxied to --grpc-target ``` -------------------------------- ### Start apitwin in Ephemeral Mode Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/runtime-state.md Use the `--ephemeral` flag to run apitwin with its state mirror in a system temporary directory. This mode ensures no files are written to your project directory, making it suitable for CI, demos, and one-shot tests. The temporary directory is removed upon shutdown. ```sh apitwin --config ./my-project --ephemeral ``` -------------------------------- ### gRPC Directory Persistence Setup Source: https://github.com/ridakaddir/apitwin/blob/main/examples/README.md Sets up API Twin for gRPC directory-based CRUD operations, where each item is stored as a separate JSON file. This demonstrates protobuf-JSON conversion, directory aggregation, and auto-ID generation. ```bash apitwin --config examples/grpc-directory-persist \ --grpc-proto examples/grpc-directory-persist/items.proto ``` -------------------------------- ### Define a route with a single named parameter Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/named-parameters.md Use curly braces to define named parameters that match exactly one path segment. This example shows a GET route for fetching country data. ```toml [[routes]] method = "GET" match = "/api/countries/{countryId}" enabled = true fallback = "success" ``` -------------------------------- ### gRPC Request-time Transitions Configuration Source: https://github.com/ridakaddir/apitwin/blob/main/docs/grpc/config.md Configure request-time transitions for gRPC routes. This example shows how to match a gRPC method and define transition cases with durations and associated JSON payloads. ```toml [[grpc_routes]] match = "/geo.CountryService/GetVisaStatus" enabled = true fallback = "submitted" [[grpc_routes.transitions]] case = "submitted" duration = 10 [[grpc_routes.transitions]] case = "under_review" duration = 50 [[grpc_routes.transitions]] case = "approved" [grpc_routes.cases.submitted] status = 0 json = '{"status": "submitted"}' [grpc_routes.cases.under_review] status = 0 json = '{"status": "under_review"}' [grpc_routes.cases.approved] status = 0 json = '{"status": "approved"}' ``` -------------------------------- ### Template File for Data Transformation Source: https://github.com/ridakaddir/apitwin/blob/main/examples/cross-refs/README.md An example of a template file using Go's text/template syntax to transform JSON data. It selects and renames fields from the source data. ```json { "modelId": "{{.id}}", "name": "{{.modelName}}", "modelVersion": "{{.version}}" } ``` -------------------------------- ### Dynamic file resolution using named parameters Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/named-parameters.md Use `{path.paramName}` syntax in file paths to serve resource-specific stub files. This example maps a country profile request to a specific JSON file. ```toml [[routes]] method = "GET" match = "/api/countries/{countryId}/profile" enabled = true fallback = "country_profile" [routes.cases.country_profile] status = 200 file = "stubs/countries/{path.countryId}.json" ``` -------------------------------- ### Background Transitions (Deployments) Source: https://github.com/ridakaddir/apitwin/blob/main/examples/README.md Demonstrates creating a deployment, checking its status, and observing updates after a delay. Reads reflect the mutated file state. ```sh # 1. Create a deployment http POST :4000/deployments/ep-demo \ deploymentId=dep-001 name="my-app" # 2. GET immediately → "Deploying" http :4000/deployments/ep-demo/dep-001 # 3. Wait 15+ seconds, GET again → "Ready" sleep 16 && http :4000/deployments/ep-demo/dep-001 # 4. List all — also shows "Ready" http :4000/deployments/ep-demo ``` -------------------------------- ### Define a route with mixed named parameters and wildcards Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/named-parameters.md Named parameters can coexist with wildcard `*` patterns to create flexible routing. This example matches a pattern with a wildcard and two named parameters. ```toml [[routes]] method = "GET" match = "/api/v1/*/regions/{regionId}/countries/{countryId}" enabled = true fallback = "success" ``` -------------------------------- ### Merge Traffic Splits from Multiple Deployments Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/type-conversion.md This example shows how to use the $as directive to merge traffic split data from multiple deployment files into a single object. It utilizes a template to extract the relevant field from each deployment. ```json { "$as": "object", "from": "{{ref:stubs/deployments/endpoint-1/?template=stubs/templates/traffic-split.json}}" } ``` -------------------------------- ### gRPC Source Persistence Setup Source: https://github.com/ridakaddir/apitwin/blob/main/examples/README.md Configures API Twin for gRPC source persistence operations. This setup ensures that only the relevant nested object is extracted before merging, preventing metadata fields from leaking into stub files. ```bash apitwin --config examples/grpc-source-persist \ --grpc-proto examples/grpc-source-persist/geography.proto ``` -------------------------------- ### Cascade Operation Responses Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/cascade-mutations.md Example JSON responses for successful and failed cascade operations. ```json { "message": "Cascade operation completed successfully", "primaryFile": "stubs/endpoints/my-endpoint.json", "cascadeTargets": 3, "operationId": "cascade-op-a1b2c3d4" } ``` ```json { "error": "cascade operation failed: invalid JSON in deployment file", "details": { "failedFile": "stubs/deployments/endpoint-1/corrupted.json", "error": "invalid character 'i' looking for beginning of value", "rolledBackFiles": [ "stubs/endpoints/endpoint-1.json" ] }, "operationId": "cascade-op-a1b2c3d4" } ``` -------------------------------- ### Define Geographic Stub Data Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/cross-endpoint-references.md Example structure for continent, country, and city JSON stubs. ```json { "name": "Africa", "area_km2": 30300000, "countries": "{{ref:stubs/countries/?filter=continent:africa}}", "moroccanCities": "{{ref:stubs/cities/?filter=country:morocco&template=stubs/templates/city-summary.json}}" } ``` ```json { "cityName": "{{.name}}", "pop": "{{.population}}" } ``` ```json { "name": "Africa", "area_km2": 30300000, "countries": [ {"code": "morocco", "name": "Morocco", "continent": "africa", "capital": "Rabat"} ], "moroccanCities": [ {"cityName": "Casablanca", "pop": 3360000} ] } ``` -------------------------------- ### Get apitwin Version Source: https://github.com/ridakaddir/apitwin/blob/main/README.md Retrieve the current version of the apitwin tool. This is useful for bug reporting. ```sh apitwin --version ``` -------------------------------- ### Build apitwin from source Source: https://github.com/ridakaddir/apitwin/blob/main/docs/installation.md Clones the repository and builds the binary using Task or the Go compiler. ```sh git clone https://github.com/ridakaddir/apitwin.git cd apitwin task build # requires Task (https://taskfile.dev) # or go build -o apitwin . ``` -------------------------------- ### Configuration for Creating Resources Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/directory-stubs.md Configure a POST route to create new resources. Uses `persist: true` and `merge: "append"` to save to a directory. The `key` field determines the filename. ```toml [[routes]] method = "POST" match = "/api/countries" enabled = true fallback = "created" [routes.cases.created] status = 201 file = "stubs/countries/" # Directory path (trailing /) persist = true merge = "append" key = "code" # Field used as filename ``` -------------------------------- ### Basic Usage Source: https://github.com/ridakaddir/apitwin/blob/main/docs/cli-reference.md Shows the fundamental commands for apitwin. ```sh apitwin [flags] apitwin generate [flags] apitwin reset [flags] ``` -------------------------------- ### HTTP GET Single Country Source: https://github.com/ridakaddir/apitwin/blob/main/examples/README.md Retrieves a single country using HTTP, demonstrating response wrapping. ```sh # GET single — returns {"country": {"code":"morocco","name":"Morocco","continent":"africa"}} http :4000/countries/morocco ``` -------------------------------- ### Security Restrictions Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/cross-endpoint-references.md Examples of blocked reference patterns due to security constraints like path traversal prevention. ```json {"data": "{{ref:../secret.json}}"} // Directory traversal {"data": "{{ref:/etc/passwd}}"} // Absolute path {"data": "{{ref:data/?template=../tpl.json}}"} // Template traversal ``` -------------------------------- ### Test Get All Endpoints Endpoint Source: https://github.com/ridakaddir/apitwin/blob/main/examples/cross-refs/README.md Use curl to test the endpoint that retrieves all endpoints. This demonstrates the cross-references in action. ```bash curl http://localhost:8080/endpoints ``` -------------------------------- ### Directory Mode: Mix Formats Source: https://github.com/ridakaddir/apitwin/blob/main/examples/README.md Demonstrates that TOML, YAML, and JSON files can coexist within the same mock directory. ```tree mocks/ ├── auth.toml ├── users.yaml └── legacy.json ``` -------------------------------- ### Multi-Environment Configuration Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/cascade-mutations.md Syncing configuration across multiple environment files using glob patterns. ```toml [[routes.cases.updated.cascade]] pattern = "stubs/environments/*/configs/{path.serviceId}.json" merge = "update" path = "scaling.config" transform = "$.scaling" ``` -------------------------------- ### Test Get All Models Endpoint Source: https://github.com/ridakaddir/apitwin/blob/main/examples/cross-refs/README.md Use curl to test the endpoint that retrieves all models. This endpoint utilizes directory references. ```bash curl http://localhost:8080/models ``` -------------------------------- ### Route Definition with Fallback Case Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/response-transitions.md Defines a GET route with a fallback case for success, specifying a file path for the response. ```toml [[routes]] method = "GET" match = "/continents/{continentId}/cities" fallback = "success" [routes.cases.success] status = 200 file = "cities/{path.continentId}/" ``` -------------------------------- ### Generate gRPC mocks from proto files Source: https://github.com/ridakaddir/apitwin/blob/main/docs/grpc/generation.md Use the generate command to create configuration and stub files. Supports multiple proto files and custom import paths. ```sh apitwin generate --proto geo.proto --out ./mocks # Multiple proto files apitwin generate --proto countries.proto --proto cities.proto --out ./mocks # With extra import paths for proto imports apitwin generate --proto geo.proto --import-path ./vendor/protos --format yaml ``` -------------------------------- ### Invalid $spread Syntax Error Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/cross-endpoint-references.md Demonstrates an invalid $spread value. The value must be a string token starting with {{ref:...}}. ```json { "$spread": "invalid-value" // ERROR: Must be {{ref:...}} token } ``` -------------------------------- ### Timeline of Resource Creation and Mutation Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/response-transitions.md Illustrates the timeline of a resource's lifecycle, from POST creation with 'pending' status to background mutation resulting in 'verified' status. ```text t = 0s POST creates file → {"status": "pending"} t = 15s background mutation → merges {"status": "verified"} into the file ``` -------------------------------- ### Stub JSON for Countries Data Source: https://github.com/ridakaddir/apitwin/blob/main/docs/quick-start.md Example of a JSON file used as a stub response for the `/api/countries` route, providing a list of country objects. ```json [ {"code": "morocco", "name": "Morocco", "continent": "africa", "capital": "Rabat"}, {"code": "germany", "name": "Germany", "continent": "europe", "capital": "Berlin"}, {"code": "japan", "name": "Japan", "continent": "asia", "capital": "Tokyo"}, {"code": "canada", "name": "Canada", "continent": "north-america", "capital": "Ottawa"} ] ``` -------------------------------- ### Resource Creation with Fallback Detection Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/response-transitions.md Demonstrates creating multiple city resources, showing how API Twin handles new resources even when the transition clock has advanced past the 'pending' state. ```text t = 0s POST /cities (body: {name: "Casablanca"}) → 201 (created) t = 15s background mutation → file updated to "verified" t = 20s POST /cities (body: {name: "Berlin"}) → 201 (created, not 404) ↑ transition clock is past "pending", but apitwin detects the file doesn't exist and uses the fallback "created" case ``` -------------------------------- ### Serve generated apitwin mocks Source: https://github.com/ridakaddir/apitwin/blob/main/examples/openapi-generate/README.md Serve the generated apitwin mock API using the specified configuration directory. All routes defined in the config will be available. ```sh apitwin --config examples/openapi-generate/mocks ``` -------------------------------- ### gRPC Proxy with Mocking Source: https://github.com/ridakaddir/apitwin/blob/main/examples/README.md Demonstrates selective mocking with transparent proxy fallthrough using API Twin. Shows how to configure mocks for specific services and methods, with fallthrough to a gRPC target for unmocked requests. ```bash apitwin --config examples/grpc-proxy \ --grpc-proto examples/grpc-proxy/products.proto ``` ```bash grpcurl -plaintext -d '{"product_id":"prod_001"}' \ localhost:50051 products.ProductService/GetProduct ``` ```bash grpcurl -plaintext -d '{"category":"electronics","limit":5}' \ localhost:50051 products.ProductService/ListProducts ``` ```bash grpcurl -plaintext -d '{"category":"clothing","limit":5}' \ localhost:50051 products.ProductService/ListProducts ``` ```bash apitwin --config examples/grpc-proxy \ --grpc-proto examples/grpc-proxy/products.proto \ --grpc-target localhost:9090 ``` ```bash grpcurl -plaintext -d '{"category":"clothing","limit":5}' \ localhost:50051 products.ProductService/ListProducts ``` ```bash grpcurl -plaintext \ -d '{"product_id":"prod_001","price":29.99,"stock":100}' \ localhost:50051 products.ProductService/UpdateProduct ``` ```bash grpcurl -plaintext localhost:50051 list ``` ```bash grpcurl -plaintext localhost:50051 describe products.ProductService ``` -------------------------------- ### Pattern Matching Configurations Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/cascade-mutations.md Define file targets using wildcards and dynamic placeholders. ```toml pattern = "stubs/deployments/*.json" # All JSON files in directory pattern = "stubs/deployments/{path.id}/*.json" # Dynamic directory with wildcards pattern = "stubs/configs/deployment-*.json" # Prefix matching ``` ```toml pattern = "stubs/environments/{path.env}/configs/*.json" pattern = "stubs/tenants/{query.tenant}/settings/*.json" ``` -------------------------------- ### Directory Mode: Override Order with Prefixes Source: https://github.com/ridakaddir/apitwin/blob/main/examples/README.md Shows how to control the loading order of mock files by using numerical prefixes, allowing for explicit overrides. ```tree mocks/ ├── 01-base.toml └── 02-overrides.toml ``` -------------------------------- ### Using Templates for Nested Data Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/array-processing.md Reshapes nested references using the '?template=' query parameter. This example applies a city-summary template to the nested city data. ```json { "name": "Africa", "countries": { "$each": "{{ref:countries/?filter=continent:africa}}", "$template": { "name": "{{.name}}", "capital": "{{.capital}}", "cities": "{{ref:cities/?filter=country:{{.code}}&template=templates/city-summary.json}}" } } } ``` ```json { "cityName": "{{.name}}", "pop": "{{.population}}" } ``` -------------------------------- ### Test Get Specific Endpoint (Filtered + Transformed) Source: https://github.com/ridakaddir/apitwin/blob/main/examples/cross-refs/README.md Use curl to test fetching a specific endpoint (e.g., 'prod') which shows filtered and transformed models. ```bash curl http://localhost:8080/endpoints/prod ``` -------------------------------- ### JSON Defaults File Example Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/directory-stubs.md Defines default values for a resource, including server-generated fields like UUID and timestamp. Used with POST requests to enrich responses. ```json { "code": "{{uuid}}", "status": "active", "verified": false, "createdAt": "{{now}}" } ``` -------------------------------- ### Basic API Stubbing with Hot-Reload Source: https://github.com/ridakaddir/apitwin/blob/main/examples/README.md Serves product data from stub files. Demonstrates hot-reloading where changes in configuration files are reflected immediately without restarting the server. ```sh apitwin --config examples/basic ``` ```sh http :4000/api/products # list — via stubs/products.json ``` ```sh http :4000/api/products/1 # detail — via stubs/product-detail.json ``` -------------------------------- ### Test Get Specific Endpoint (Directory + Filter) Source: https://github.com/ridakaddir/apitwin/blob/main/examples/cross-refs/README.md Use curl to test fetching a specific endpoint (e.g., 'dev') which shows all models versus active-only models. ```bash curl http://localhost:8080/endpoints/dev ``` -------------------------------- ### Configuration for Reading a Single Resource Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/directory-stubs.md Configure a GET route to read a single resource by ID. The `file` path dynamically constructs the filename using a path parameter. ```toml [[routes]] method = "GET" match = "/api/countries/{countryId}" enabled = true fallback = "country" [routes.cases.country] file = "stubs/countries/{path.countryId}.json" # Dynamic filename from path ``` -------------------------------- ### Background Transitions Configuration (TOML) Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/response-transitions.md Configure background transitions for a POST route. This initiates a sequence of file mutations after resource creation, ideal for lifecycle states. ```toml # POST — creates city, starts background transition [[routes]] method = "POST" match = "/continents/{continentId}/cities" fallback = "created" [[routes.transitions]] case = "pending" duration = 15 [[routes.transitions]] case = "verified" [routes.cases.created] status = 201 file = "cities/{path.continentId}/" persist = true merge = "append" key = "cityId" defaults = "defaults/city.json" [routes.cases.verified] persist = true merge = "update" defaults = "defaults/city-verified.json" # GET by ID — pure read, no transitions needed [[routes]] method = "GET" match = "/continents/{continentId}/cities/{cityId}" fallback = "success" [routes.cases.success] status = 200 file = "cities/{path.continentId}/{path.cityId}.json" ``` -------------------------------- ### Reference All Models from Directory Source: https://github.com/ridakaddir/apitwin/blob/main/examples/cross-refs/README.md Use this to include all stub files from a specified directory. Ensure the directory path is correct. ```json "allModels": "{{ref:stubs/models/}}" ``` -------------------------------- ### Request-time Transitions Configuration (YAML) Source: https://github.com/ridakaddir/apitwin/blob/main/docs/features/response-transitions.md YAML equivalent for configuring request-time transitions. This defines how responses change over time for a GET route without modifying the underlying file. ```yaml routes: - method: GET match: /countries/{countryId}/visa-status enabled: true fallback: submitted transitions: - case: submitted duration: 30 - case: under_review duration: 60 - case: approved cases: submitted: status: 200 json: '{"country": "morocco", "status": "submitted"}' under_review: status: 200 json: '{"country": "morocco", "status": "under_review"}' approved: status: 200 json: '{"country": "morocco", "status": "approved"}' ``` -------------------------------- ### View generated mock structure Source: https://github.com/ridakaddir/apitwin/blob/main/docs/grpc/generation.md The output directory contains a configuration file and individual JSON stubs for each service method. ```text mocks/ ├── apitwin.toml # [[grpc_routes]] for all methods └── stubs/ ├── CountryService_GetCountry.json ├── CountryService_ListCountries.json └── CountryService_CreateCountry.json ``` -------------------------------- ### Generate gRPC Config and Stubs Source: https://github.com/ridakaddir/apitwin/blob/main/docs/grpc/quick-start.md Use this command to generate configuration files and service stubs from a .proto file. Specify the input proto file and the output directory. ```sh apitwin generate --proto geo.proto --out ./mocks ```