Repository navigation
The mock server is now apigenie-server, with a document store for CRUD resources and several services in one
process, and a fourth binary, apigenie-catalog, keeps a repository's API catalog. apigenie-generator applies
OpenAPI overlays before generating, and the three binaries share one table of exit statuses.
Highlights
apigenie-server, the mock server and validating proxy under its new name, with a dashboard at
/__apigenie/ui(Traffic, Operations, Scenarios, Contract, Data and, for AsyncAPI, Channels), several services on
one port with a prefix each, a document store (ctx.db,db.crud: autoresources, seeding, snapshots), session
recording and replay (--record DIR,--replay DIR), client certificates, MQTT 5 properties, and validating
proxies for Kafka (--proxy-to kafka://) and AMQP (--proxy-to amqp://) brokers.apigenie-catalog(experimental), a fourth binary:catalog listfinds the API specifications of a
repository andcatalog initrecords them inapigenie.yaml.- Code generation:
--overlay FILEon every OpenAPI and AsyncAPI target; the Elixir family (client elixirand
server phoenixfor OpenAPI and AsyncAPI,server absinthefor GraphQL,server elixir-grpcand the Elixir gRPC
client for Protobuf);server mcp(experimental), a Model Context Protocol server from an OpenAPI document;
server orpcat the conformance bar. apigenie testspeaks Kafka and AMQP (KAFKAandAMQPregions), GraphQL subscriptions over graphql-ws,
gRPC client and bidirectional streams, MQTT 5 and mutual TLS.- Towards 1.0: one table of exit statuses for the binaries,
formatVersionin every JSON report with published
JSON Schemas, a JSON Schema forapigenie.yamlbehindapigenie config,config checkandconfig schema,
configuration discovery without--config, the Compatibility page, and experimental labels on what is outside
the coming promise. - Linting and diffing: the OpenAPI format registry as the list of formats, with
schema-format-deprecated,
schema-format-unknownand ahintseverity;diffknows sunset dates and no longer rates deprecating as
breaking;apigenie evolve plan OLD NEW(experimental) plans an expand/contract migration. - Also:
convert --from wsdl --to openapi31,i18n checkandi18n sync, OpenAPI formatting at every depth with
layout options, Windows console colors, and a docs package published as@mykulyak/apigenie-docs.
Upgrading
-
Upgrading from 0.9:
apigenie-mockis nowapigenie-server. The mock server and validating proxy is
installed asapigenie-serverby every archive and package, the npm package and Homebrew, and its Docker image is
mykulyak/apigenie-server. The old names keep working until 1.1, each with a one-line deprecation notice on
standard error:apigenie-mockis a symlink toapigenie-server(a copy in the Windows archive, a command of the
npm package), and theAPIGENIE_MOCK_*variables are read when theAPIGENIE_SERVER_*variable of the same name
is unset. The lastmykulyak/apigenie-mockimage (latestand this version) only prints the new name and exits
with 2; older tags are unchanged.apigenie-server.yamlis read beforeapigenie-mock.yaml, which still works;
commands.mock, thex-apigenie-mockextensions, theX-Apigenie-Mock-*headers and the/__apigenieadmin API
are unchanged. What the server sends saysapigenie-serverinstead: theServerandViaheaders, the HAR
creator, the admin statusname, theBasicrealm, the MQTT client ids, and the contract report'sreport
member (apigenie-server-contract) and JUnit suite name (apigenie-server contract). The reference page moved to
server; the old address redirects. The design is ADR-147. -
One table of exit statuses for
apigenie,apigenie-generatorandapigenie-server. 0 success, 1 findings
(lint errors, breaking changes,format --checkdifferences,i18n apply --strictunknown keys,overlay --validateproblems), 2 a usage, input or unexpected error, 3 and 4apigenie-server's document and address
errors, 5 a failed contract report (apigenie-server,apigenie test). The 1.0 compatibility promise freezes it.
Scripts that check for the old numbers need these changes:- errors that exited with 255 (
-1) now exit with 2: unreadable or invalid input files inbundle,convert,
changelog,diff,i18n,lintandoverlay, bad configurations, and every error ofapigenie-generator; - errors that exited with 1 now exit with 2: unknown options and commands, missing files, configuration files that
can't be parsed,formaton a file with syntax errors or an unsupported document,bundleof a.protofile
with errors,license --librarywith an unknown name, and unexpected errors inapigenie-serverandapigenie test; apigenie testexits with 2 instead of 3 when a request file, an environment file or the specification can't be
read or parsed;apigenieandapigenie-generatorwithout a command, or with an unknown one, exit with 2 (255 before) and
print the help of the command above it on the standard error.
apigenie-serverkeeps 3, 4, 5 and 130. The design is ADR-141
(exit status). - errors that exited with 255 (
-
--versionlists the binaries installed next to it.apigenie --versionprints the version on its first line,
thenapigenie-generator VERSIONandapigenie-server VERSIONfor those found in the same directory (the same for
the other two binaries). Scripts comparing the version should read the first line:apigenie --version | head -n 1. The GitHub Action, the Homebrew formula and the release workflows do. Subcommands no longer have a--version
of their own (apigenie lint --versionprinted 1.0). -
lint --formatter jsonanddiff --report detailed --formatter jsonwrite an object. Lint JSON is
{ "formatVersion": 1, "results": [...] }and the detailed diff report{ "formatVersion": 1, "changes": [...] };
both were bare arrays. Read.resultsand.changesinstead
(compatibility). -
diffno longer rates deprecating as breaking. In therecommendedrules, setting or clearingdeprecatedon an
operation, a parameter, a header or a schema isnon_breaking: it announces a removal, and the removal is the
breaking change (rated bysunset_passed, see Added). Gates that relied on it should setoperation.deprecated: breaking(andparameter,header,schema) incommands.diff.rules. The design is ADR-172. -
Seeded faker values change. Faker now draws its values with algorithms of its own, the same on every platform, and
name,emailand other values of a--seeddiffer from 0.9.0's. Usernames and emails are ASCII (Cieślarbecomes
Cieslar), anddate.betweenis a range ({date.between: {from: 2024-01-01, to: 2024-12-31}}) rather than an alias of
date.recent. -
Faker arguments are checked.
x-faker,@mock(faker:, args:),graphql.fieldsandfake()reject arguments a
provider doesn't take, of the wrong type or out of bounds (a negative length, a probability above 1) with faker's
message, where they were clamped or ignored. -
apigenie formatkeeps the format of OpenAPI, Swagger and AsyncAPI files without a configuration. Formatting
api.jsonwith no--configused to writeapi.yamlnext to it; JSON files now stay JSON. Setformat: yamlto
convert them. -
The OpenAPI format configuration is checked. Unknown keys (
indnet), unknown objects inproperty_order,
indents outside 2 to 9 and unknown output formats are reported with their line and column, and nothing is
formatted; they used to be ignored or to abort the command. -
The
recommendedOpenAPI preset changes more of a document. It now reaches nested schemas, inline callbacks
and the objects of OpenAPI 3.2, sorts the path templates ofpaths(itspaths: sortedhad no effect), orders
more schema keywords ($ref,$id,$defs,default,examples,readOnly,writeOnly,deprecated...) and
the fields of 3.2 objects, and orders Reference Objects byreference. Expect a one-time diff when reformatting
withextends: [recommended].property_order.responsesapplies to the status codes of operations; the names of
components.responsesfollow the newcomponentNameskey. -
The configuration file is validated against a JSON Schema when it is loaded, by
apigenie,
apigenie-generatorandapigenie-server. Settings that were silently ignored are now errors (exit status 2) with
the line, the column and the JSON pointer of the value: unknown keys undercommands.*(the OpenAPI, AsyncAPI and
Swagger linters, and the WebSocket generators, used to skip them), sections of commands that have no settings
(commands.bundle,commands.changelog:changelogusescommands.diff), and values of the wrong type.
apigenie config checklists the errors of a file; fix them, or remove the settings that did nothing. The design is
ADR-145. -
apigenieandapigenie-generatorfind the configuration file without--config:apigenie.yaml(.yml,
.json, or.apigenie.*) in the current directory or a parent up to the root of the git repository. Pass
--no-configto run with the defaults, as before, in a repository that has such a file.apigenie-serverfalls
back to the same file after its ownapigenie-server.yamlandapigenie-mock.yaml. -
The docs package is
@mykulyak/apigenie-docs. The npm project thatapigenie-generator docswrites now
depends on@mykulyak/apigenie-docs(^0.1.0), the new name of the web components package. 0.9.0 wrote
@mykulyak/apigenie-web-componentsintopackage.json, and that package was never published under that name, so
npm installfailed. In a project generated by 0.9.0, replace that dependency with
"@mykulyak/apigenie-docs": "^0.1.0", or runapigenie-generator docsinto it again. -
apigenie testspeaks MQTT 5. MQTT regions connect with MQTT 5 (falling back to 3.1.1 when the broker refuses
it), and the messages they publish are checked with their MQTT 5 properties against the AsyncAPI MQTT bindings. A
region publishing to a channel whose binding states properties (apayloadFormatIndicator,contentType,
correlationData,responseTopic, or an operation'smessageExpiryInterval) fails withmessage.properties.invalid
until it gives them with# @propertylines, or keeps MQTT 3.1.1 with theversion: 3.1.1setting. The design is
ADR-110 (amended).
Added
-
The OpenAPI format registry (https://spec.openapis.org/registry/format/) is API Genie's list of formats.
apigenie-server(mock and--proxy-to, OpenAPI and AsyncAPI),apigenie test's contract checks and every other
JSON Schema validation check the registry's formats beyond JSON Schema's:- int8, int16, uint8, uint16, uint32, uint64 and double-int ranges, with int64 and uint64 also as integer text;
- decimal and decimal128, as numbers or text (decimal128: 34 significant digits, NaN, INF);
- date-time-local and time-local (RFC 3339 without an offset);
- http-date (IMF-fixdate and RFC 9110's obsolete forms) and unixtime;
- ipv4-cidr and ipv6-cidr;
- language (RFC 5646 tags);
- media-range and char;
- base64url and the RFC 8941 structured field items (sf-binary, sf-boolean, sf-decimal, sf-integer, sf-string,
sf-token).
commonmark, html, password and binary stay unchecked, as the registry allows.
openapi.custom_formatsin
apigenie.yaml(or the server's configuration file) now reachesapigenie-server. Its formats are accepted as
declared, and an entry naming a registry format replaces that format's type and turns its check off. The registry
is vendored byscripts/update-format-registry(--checkverifies the copy is current). The design is ADR-166. -
Lint rules for formats:
schema-format-deprecatedandschema-format-unknown, in the OpenAPI, AsyncAPI and
Swagger linters.schema-format-deprecated(a warning in every ruleset) reportsbyte,binaryandbase64url, which the format
registry deprecates from OpenAPI 3.1, with what replaces them (contentEncoding: base64,contentMediaType,
contentEncoding: base64url). In OpenAPI 3.0 and Swagger 2.0 documents, which have no replacement, the issues
are hints.schema-format-unknown(off inrecommended, a warning instrictandall) reports a format that is neither in
the registry nor inopenapi.custom_formats, such as a misspelleddate_time.apigenie lintpassesopenapi.custom_formatsto the format rules.
-
A
hintseverity for lint issues. Hints are shown by every formatter (SARIFnote, checkstyle and Visual Studio
info,hintelsewhere, andN hintsin the stylish summary), but never count as errors or warnings and never
change the exit status. Rules can be configured withhint(orinfo) inapigenie.yaml. -
The generators map the format registry's formats.
- Java, Spring and the AsyncAPI Java targets:
uint32,double-int,unixtimeandsf-integerbecomeLong, and
uint64becomesBigIntegerin the OpenAPI targets.date-time-localandtime-localbecomeLocalDateTime
andLocalTimein the AsyncAPI targets too. - C# and ASP.NET:
int8touint64becomesbyte,short,byte,ushort,uintandulong;double-int
andunixtimebecomelong,decimal128becomesdecimal, anddate-time-localbecomesDateTime. - Go:
double-int,unixtimeandsf-integerbecomeint64. - Python and FastAPI: numbers of format
decimal128becomeDecimallikedecimalones,date-time-local
becomes pydantic'sNaiveDatetime, andtime-localbecomesdatetime.time. Decimals oftype: stringstay
strings in every generator but C#, keeping theirpattern. - Elixir:
date-time-localbecomesNaiveDateTime.t(), andtime-localbecomesTime.t(). - TypeScript validators and Zod schemas check the ranges of
int8touint64anddouble-int. Zod validates
date-time-localwithz.iso.datetime({ local: true })andtime-localwithz.iso.time(). apigenie-generator dataand the server's generated values pass the checks of every registry format: integer
widths keep to their range,unixtimeis a time in 2020..2029, andhttp-date, the CIDRs, language tags, media
ranges, thesf-*items, JSON pointers and regular expressions get values of their syntax.
- Java, Spring and the AsyncAPI Java targets:
-
The documentation components link formats to the format registry. A schema's
format, inapg-schema's type
label or its facts, links to the format's page of the registry (partformat-link), and the generated examples
have values for the registry's formats. The website's new Formats page lists every registry format with its JSON
types and API Genie's support, and the lint rules, the configuration page and the server page link to it. -
apigenie-server --proxy-to kafka://host:portcontract-tests an AsyncAPI document's Kafka channels against a
real cluster. There is no broker in the server: it is a validating client of the cluster (ADR-170).- Reading. It reads the channels' topics (the Kafka binding's
topic, else the address; parameters match the
topic's segments) from the offsets they had at start, outside any consumer group, so the application's consumers
and offsets are left alone. - Checking. Every record is checked as the application's (a channel with only
sendoperations), a client's
(onlyreceiveoperations) or whichever matches. The value is checked against the messages, the headers against
the message'sheaders, the key against the Kafka binding'skey(new checkmessage.key.invalid) and the
partition against the channel binding'spartitions(new checkmessage.partition.invalid). - Results. Records are logged as
kafka-inandkafka-outevents and count in the coverage and the contract
report like any other messages. Enforce mode reports, as records can't be stopped. - Admin publishes.
POST /__apigenie/asyncapi/publishproduces a Kafka channel's record to the cluster after
the same checks, with the newkey,headersandpartitionfields, and answers where it went. - Connection.
kafkas://connects over TLS. SASL PLAIN credentials come fromAPIGENIE_KAFKA_USERNAMEand
APIGENIE_KAFKA_PASSWORD. The cluster needs Kafka 2.3 or later; compressed record batches are reported and
skipped.
- Reading. It reads the channels' topics (the Kafka binding's
-
apigenie-server --proxy-to amqp://host:port/vhostcontract-tests an AsyncAPI document's AMQP channels against a
real AMQP 0-9-1 broker (ADR-171). It is a client of the broker, not a broker.- Reading. It binds an exclusive queue of its own to the exchange of every channel whose address is a routing
key (the address's parameters as*words of a topic exchange, any key of a fanout or headers exchange) and
checks the messages it gets like the Kafka tap's records, plus their properties against the AMQP bindings
(deliveryMode,priority,expiration,userId,replyTo,timestamp,contentEncoding,messageType;
message.properties.invalid). Queue channels are left alone, as reading them would take the application's
messages, and the tap says so. - Results. Messages are logged as
amqp-inandamqp-outevents and count in the coverage and the contract
report. - Admin publishes.
POST /__apigenie/asyncapi/publishpublishes an AMQP channel's message to the broker, with
the newroutingKey,propertiesandheaders, mandatory and confirmed (409 when no queue takes it). - Connection.
amqps://connects over TLS. Credentials come fromAPIGENIE_AMQP_USERNAMEand
APIGENIE_AMQP_PASSWORD.
- Reading. It binds an exclusive queue of its own to the exchange of every channel whose address is a routing
-
AMQPrequests inapigenie test. AnAMQP amqp://host:port/vhostregion publishes its body's messages to
exchangeandrouting-keywith# @propertybasic properties and headers, and readssubscribequeues and
private queues bound withbind: EXCHANGE KEY; a message no queue takes is an error. The messages are checked like
the tap's, and replies are paired by a correlation id in a header or a property (RabbitMQ's direct reply-to
works).--generate --amqp-urlmakes one region per operation of an AMQP channel. -
KAFKArequests inapigenie test. AKAFKA kafka://host:portregion produces its body's records totopic
with akey, apartitionand# @property NAME VALUEheaders, and readssubscribe(ortopic) until
# @expectrecords came; the region's own records are left out.- A
topicthat a cluster creating topics on first use (auto.create.topics.enable) doesn't have yet is
created by the region's first record and read from its start, also on Kafka 4, which reports such a topic
unknown while it creates it. - With an AsyncAPI
--spec, the records are checked like MQTT messages, plus their key and partition, and replies
are paired with requests by a correlation id in a header or the payload. - Credentials come from
usernameandpassword, elseAPIGENIE_KAFKA_USERNAMEandAPIGENIE_KAFKA_PASSWORD,
and are redacted. kafkas://uses--ca-cert,--insecureand--client-cert.--generate --kafka-url kafka://host:portmakes one region per operation of a Kafka channel.
- A
-
apigenie convert --from wsdl --to openapi31imports SOAP services. A WSDL 1.1 document with SOAP 1.1 or 1.2
document/literal bindings over HTTP becomes an OpenAPI 3.1 document. The command also reads the WSDL documents
and XML Schemas it imports and includes, as local files; remote ones aren't fetched. In the result:- Each SOAP operation is
POST {address path}/{operation}, withx-soap-addressandx-soap-action. The
operations of a SOAP 1.1 and a SOAP 1.2 binding at one address merge, with atext/xmland an
application/soap+xmlbody. - Requests and responses are SOAP envelopes whose schemas carry
xmlobjects, soapigenie-servervalidates
SOAP requests and answers SOAP envelopes. Faults are 500 responses, and one-way operations answer 202. - The XML Schema types (sequences, choices, attributes, simple content, restrictions and enumerations,
extensions, occurrence bounds, nillable elements) are components, and the WSDL documentation is in the
descriptions. - What has no counterpart is told as warnings with file, line and column:
xs:any, substitution groups, lists,
and decimals. WSDL 2.0, rpc/encoded and rpc/literal services are refused with exit status 2.
The result lints without errors with
recommended, anddiffand the generators read it.--from swagger2|wsdl
is optional: an XML input is WSDL. The library issrc/wsdl_convert, tested on Apache CXF, NWS, ONVIF and WSDL
1.1 Note documents vendored indata/tests/wsdlwith their licences. The design is ADR-164. - Each SOAP operation is
-
apigenie evolve plan OLD NEW(experimental) plans safe API changes. It splits the breaking changes between two
OpenAPI 3.x documents into an expand wave (ship now: the old elements stay next to the new ones,deprecated: true
with anx-sunsetdate) and a contract wave (ship after the window: the target document), writes1-expand.yaml,
2-contract.yamlandplan.yamlto--out(defaultevolve-out/), and proves the plan withdiffbefore writing
anything: the expand wave is non-breaking against the old document, the contract wave has only scheduled removals
(and what its units and the residue own) against the expand wave, and it equals the target. Ten recipes: removed
operations and paths, moved paths (--hints FILEwithrenamesasMETHOD /pathor diff's location syntax, a
windowand areason), removed and renamed response properties, renamed request properties and parameters,
removed request parameters, newly required request properties and parameters, removed request enum values
(x-deprecated-enum); renames are detected when one member goes and one with the same schema comes. What no recipe
makes safe is residue, listed with a reason (exit status 1). Each unit carries a server note (emit both, accept both,
apply a default) and the report lists them as a checklist.--window 90d|12w,--today DATE,--config(the
commands.diffblock),--format text|json(formatVersion: 1,
apigenie-evolve-report.json). The reference page is
evolve; the design is ADR-172. -
diffknows sunset dates. The rule conditionsunset_passedholds when the removed element is deprecated and
itsx-sunset(an RFC 3339 date or date-time) is on or before the date of the comparison,--today(default: the
current date; OpenAPI documents only). Therecommendedrules use it forremove_itemof operations, path items
(every operation sunset), parameters, component schemas and parameters, response properties and theirrequired
items, and request enum values listed in the schema'sx-deprecated-enum: a removal after the sunset is
non-breaking, before it breaking, and documents withoutx-sunsetare rated as before. A property deprecated next
to its$refcounts as deprecated. -
apigenie-generator <target> --overlay FILEapplies OpenAPI Overlays (1.0 to 1.2) to the input before generating,
in order, so the vendor extensions of a target can live in an overlay of their own instead of in the API
description. Every target of OpenAPI and AsyncAPI documents takes it (AsyncAPI ones bundled first), and
commands.generate.<target>.overlaylists overlays in the configuration file, relative to it, applied before the
--overlayones. An action whose target selects nothing is a warning with its position in the overlay, and an error
with--strict(nothing is generated, exit status 2). Warnings and read errors about a value an overlay set point
at the overlay's line (spring.overlay.yaml:9:9 Warning ...), and the document's own lines are kept, since
apigenie overlayand every document built withjv::JVBuildernow keep the positions of what they copy. Each
generator's reference page lists thex-extensions it reads. The codegen conformance suite generates its Spring
target with--overlay --strictinstead of a separateapigenie overlaystep. The design is ADR-156. -
Client certificates (mutual TLS) for
apigenie-serverandapigenie test.--client-cert FILE --client-key FILEpresents a certificate to the servers that ask for one: thehttps://,wss://,mqtts://and gRPC TLS
upstreams ofapigenie-server --proxy-to(alsotls.client_certandtls.client_keyin the configuration,
APIGENIE_SERVER_CLIENT_CERTandAPIGENIE_SERVER_CLIENT_KEY), and everyhttps://,grpcs://,wss://and
mqtts://request ofapigenie test, with--insecuretoo.apigenie-server --tls-client-ca FILE
(tls.client_ca,APIGENIE_SERVER_TLS_CLIENT_CA) requires a client certificate issued by those authorities on
every HTTPS, WSS, gRPC and MQTTS connection, the admin API included. A refused client is a warning in the log that
says why. The subject of an accepted certificate isclientSubjectin the JSON log and the admin API's log, and
the contract report lists the clients (clientsin JSON, aclientproperty in JUnit, aClients:summary
line). A client certificate or key that can't be loaded, a lone--client-certor--client-key, and
--tls-client-cawithout--tls-certexit with status 2. A server that refuses the client's certificate now
says so in the error (the server requires a client certificate) instead of "the connection closed before a
response". Session resumption and revocation checks (CRL, OCSP) are not supported. The design is the amendment of
ADR-104 (server,
test). -
apigenie-server --tls-certserves MQTT over TLS. The MQTT broker of an AsyncAPI document uses the server's
certificate (and--tls-client-ca), on port 8883 unless--mqtt-portorasyncapi.mqtt_portsays otherwise;
the startup line names itmqtts://. Before, it stayed cleartext on 1883. -
apigenie-serverhas a dashboard athttp://127.0.0.1:4010/__apigenie/ui(the startup banner names it): Traffic as
it happens, with an exchange's request, answer and validation problems, Copy as curl, Replay, Save as stub and Add to
.http; Operations by tag with their hits and what answers them, and a fixed answer per operation (a status, an
example, a generated body, a latency) saved as an override, with thePreferheader that does the same for one
request; Scenarios with their rules and hits, a rule builder and the network simulation with the last response
times; Contract with the gate against--fail-onand--min-coverage, the coverage, the violations by check, the
operations never called and the report files; Data with the document store's collections, seeds and snapshots and
ctx.state. Every panel copies its settings as configuration YAML orx-apigenie-mock. The page is built from
the web components (src/app_web, theapg-server-*elements,@mykulyak/apigenie-docs/server) and embedded
in the binary; it follows the admin API's rules (--admin off,admin.allow_remote,admin.port). The admin
API says more for it:/routesitems carrytags,deprecated, the declaredresponseswith their
examples andlastSource,GET /overridesanswers whole rules and the scenarios list theirs asruleList,
the status gainsurl,startedAtandlogSize. The design is ADR-167
(Dashboard). -
The dashboard has a Channels screen for AsyncAPI documents, listed when a served document has channels: the
channels by WebSocket, MQTT and HTTP with their operations, the clients connected and the messages counted; for a
channel, its messages in and out as they happen, its clients, and Publish, a message sent as the application
would (POST /__apigenie/asyncapi/publish) to every client of the channel or to one, with a payload started from
the document's examples or left to the server; a payload the message's schema refuses shows the violations and
goes nowhere, until Send anyway; Copy as curl. The admin API says more for it:GET /__apigenie/asyncapi/channels
items carrymessageList, each message with itscontentType,summaryandexamples(payload and headers),
andPOST /__apigenie/asyncapi/publishtakesclient, an id of/asyncapi/clients, to send to that one client
(404asyncapi-unknown-clientwhen it isn't connected). The design is the amendment of ADR-167
(Dashboard); gRPC and GraphQL variants of the
dashboard come after 1.0. -
apigenie-server's admin API serves what a dashboard needs.GET /__apigenie/eventsstreams the log as it happens,
as Server-Sent Events: the entry's id asid:, its kind asevent:(request,ws-out,reload, and the new
scenarioandcontract-updated), aviolationevent after an entry with violations,?type=,?level=,
?service=, and?after=orLast-Event-IDto replay what the log still holds. While a stream is open every
exchange's request and response headers and bodies are captured; entries gainresponseHeaders, Base64 bodies for
bytes that aren't UTF-8, and*BodyTruncated, andlog.body_limitnow bounds what the log keeps and what/log
returns./routesand/scenarioscounthitswithlastStatusandlastAt(DELETE /__apigenie/hitsstarts
over).POST /__apigenie/stubs {"id": 42}makes a stub file of a logged exchange, written todiror
proxy.record_dir, or answered.GET /__apigenie/contract.jsonand/contract.xmldownload the report as
--reportand--report-junitwould write it. For AsyncAPI documents,GET /__apigenie/asyncapi/channelsand
/asyncapi/clientslist the channels and the WebSocket and MQTT clients, andPOST /__apigenie/asyncapi/publish
sends a message (given, or made as the schedule would) to a channel's clients, answering 422 when it doesn't match.
A server of several services names the service of every item and event. The design is ADR-161
(Admin API). -
apigenie-serverserves several services at once, on one port with a prefix per service: severalSPECs
(apigenie-server pets.yaml orders.yaml events.yamlmounts/pets,/ordersand/events) or the configuration's
serviceslist, each withname,spec,mount(a path prefix, default/<name>;{host: ...};{port: ...},
which a.protofile's service needs) and its ownproxy,stubs,scenarios,validation,security,
operationsand other document settings over the server's. AsyncAPI WebSocket channels share the port; the MQTT
broker is one per server, and services whose MQTT channels collide have their topics served under<name>/. Every
event, admin API item (/routes,/scenarios,/overrides,/log, the status's documents), HAR entry and report
section names its service;GET /__apigenie/serviceslists them and?service=NAMEnarrows any admin endpoint. The
contract report has a section per service and a combined summary, to which--fail-onand--min-coverageapply.
The startup line lists the services and their URLs. OneSPECis served as before. The design is ADR-159. -
apigenie-generator server mcp(experimental) generates a Model Context Protocol server in TypeScript from an
OpenAPI 3.x document, on the official MCP SDK (@modelcontextprotocol/server2), so that AI agents call the API
through typed tools: one tool per operation, named after itsoperationId, described by its summary and
description, with a Zod input schema of its parameters and body. The tools call the API through a generated fetch
client (src/client) and return the status and body of the response as structured content, with an output schema;
failed requests and responses the document doesn't describe are error results that keep the body. Binary bodies and
multipart files are base64 strings.include_tags,exclude_tagsandx-mcp: falseselect the operations; the
base URL and the credentials of the security schemes come from environment variables (<PREFIX>_BASE_URL,
<PREFIX>_API_KEY...).--mockaddsnpm run mock, serving the document withapigenie-server, and makes it the
default base URL for demos. Configured undercommands.generate.server_mcp; the design is ADR-158. The codegen
conformance suite checks it at Levels 1 to 3, Level 3 driving the tools with an MCP client against
apigenie-server --proxy-to. The fetch client generator exposes how its functions call the operations
(client_fetch::Plugin::bindings()), and the operation selection of the Elixir and Go generators moved to
openapi_generator. -
apigenie-generator client elixiralso takes AsyncAPI 3.x documents and generates an Elixir library for the
counterpart of the described application (or, withperspective: application, the application's Kafka library).
Its WebSocket channels connect on Mint.WebSocket, with the Phoenix Channels protocol ofserver phoenix(topics
joined on/socket/websocket, replies to refs, heartbeats) or with the plain frames of the document at the
channels' addresses:connecttakes the address parameters and the ws binding's query,send_*sends, a request
function waits for its reply (matched by correlation id on plain connections), and what the server sends is
decoded and given to the callbacks of a behaviour. Its Kafka channels havesend_*andrequest_*functions on
brod and a consumer group (:brod_group_subscriber_v2) per received operation, sharing the dispatch, decoding and
replies ofserver phoenix's pipelines; the library depends onsnappyer, so that brod reads Snappy-compressed
records. Configured undercommands.generate.client_elixir.asyncapi; the design is
ADR-154. The codegen conformance suite checks both perspectives at Levels 1 and 2 (withmix dialyzer) and, at
Level 3, runs it againstserver phoenix: the WebSocket channels in both framings, the plain one through
apigenie-server --proxy-to ws://…, and the Kafka channels both ways on the broker of the workflow's service
container. -
apigenie-generator server phoenixalso takes AsyncAPI 3.x documents and generates a Phoenix application of their
WebSocket and Kafka channels. Each WebSocket channel is a Phoenix channel (its address as the topic,
/tracking/{shipmentId}astracking:<shipmentId>, joined on/socket/websocket) and also a plain WebSocket at
its address, whose frames are the messages of the document; both decode the address parameters, the ws binding's
query and the received messages into model structs before a behaviour's callbacks get them, and the channel
module'spush_*andbroadcast_*functions send. A request's callback returns its reply, which gets the request's
correlation id. Each received Kafka operation is a Broadway pipeline (broadway_kafka) dispatching records by
ce_type, payload property or decoding to the callbacks of its behaviour, withon_unknownandon_invalid
hooks (a replier's:oksends no reply) and batching fromx-apigenie-broadwayof the Kafka binding or the configuration; each sent operation has
send_*functions on brod, and requests wait for their replies by correlation id on the reply topic; the project
depends onsnappyer, so that brod reads Snappy-compressed records. MQTT, AMQP
and HTTP channels are skipped with an info line, Avro payloads with a warning. Configured under
commands.generate.server_phoenix.asyncapi(perspective: counterpartgenerates the Kafka counterpart); the
design is ADR-154. The codegen conformance suite checks both perspectives at Levels 1 and 2 (mix compile --warnings-as-errors,mix format --check-formatted,mix credo --strict) and, at Level 3, the plain
WebSockets throughapigenie-server --proxy-to ws://…and the application against its counterpart on the Kafka
broker of the workflow's service container. -
apigenie-generator server absinthegenerates an Absinthe server of a GraphQL schema: a Mix project on Absinthe
1.12, Absinthe.Plug, Plug and Bandit. Its schema module imports the SDL shipped with the project (import_sdl,
descriptions, deprecations, defaults and@oneOfincluded), andhydrate/2attaches:- resolvers: a behaviour per object type with a callback per root field and per field with arguments, calling
the implementation configured for it (config :app, App.Resolvers.Query, impl: MyApp.Query), an error
without one; - the resolution of interfaces and unions (
__typenameor an implementation); - parse and serialize stubs of the custom scalars.
Custom directives are declared in a prototype schema. An optional Dataloader source is added with
dataloader,
and the router serves the endpoint and, in dev, GraphiQL. Subscriptions get resolvers but no transport.
Configured undercommands.generate.server_absinthe; the design is ADR-152. The codegen conformance suite checks
it at Levels 1 to 3 (mix compile --warnings-as-errors,mix format --check-formatted,mix credo --strict,
and the application answeringapigenie testand the fixture's dataset with the data ofapigenie-server). - resolvers: a behaviour per object type with a callback per root field and per field with arguments, calling
-
The foundation of GraphQL code generation (ADR-151):
graphql_generatorreads a GraphQL schema into a
target-neutral model (types with their extensions merged, fields, arguments, enum values, input types, scalars,
directives, descriptions, deprecations and default values) for the GraphQL targets ofapigenie-generator, and
the codegen conformance suite gains a GraphQL fixture: the Carpet Express GraphQL API
(data/fixtures/graphql/carpet-express.graphql), the GraphQL face of the OpenAPI and AsyncAPI fixtures, with its
feature catalog, field coordinates (Query.shipment) as probe identifiers, and a dataset of operations whose
answers fromapigenie-serverback the Level 3 of GraphQL servers. -
apigenie-generator server elixir-grpcgenerates an Elixir gRPC server of a.protofile set on elixir-grpc
(grpc_server1.0) and protobuf-elixir 0.17: the modules of the messages, enums and services as
protoc-gen-elixir writes them (no protoc needed), a behaviour per service with a callback per method (client and
server streams as Elixir enumerables), aGRPC.Servercalling the implementation configured withconfig :app, <Service>, impl: ...(UNIMPLEMENTED without one), the endpoint and the application (mix run --no-halt, port
50051 orPORT).apigenie-generator client elixirgiven a.protofile generates a gRPC client ongrpc1.0
and Mint: the same modules with the stubs, and a module per service with a typed function per method returning
{:ok, response}or{:error, %GRPC.RPCError{}}, deadlines and metadata as options. Configured under
commands.generate.server_elixir_grpcandcommands.generate.client_elixir.protobuf; the design is ADR-153. The
codegen conformance suite checks both at Levels 1 to 3 (mix compile --warnings-as-errors,mix format --check-formatted,mix credo --strict,mix dialyzer; the server underapigenie test, the client calling
all 24 methods throughapigenie-server). -
Protobuf code generation starts: the
protobuf_generatorlibrary gives the Protobuf targets of
apigenie-generatora model of a.protofile set (the file and its imports, found with-I/--proto-pathand
among the embeddedgoogle/protobuf,google/apiandgoogle/rpcfiles) on the resolved descriptors of the
Protobuf runtime: messages with their nesting, fields with their cardinality, oneofs, maps, enums, services and
the streaming kind of their methods, comments, deprecations and(google.api.field_behavior). Proto3 only. The
codegen conformance suite has a Protobuf fixture, the gRPC services of Carpet Express
(data/fixtures/protobuf: 7 services, 24 methods of all four streaming kinds, a catalog of 42 features), and
runs Protobuf servers under the gRPC contract tester and clients throughapigenie-serveras a validating gRPC
proxy. The design is ADR-150; Connect-ES and connect-go are the next targets. -
The Compatibility page states the 1.0 promise: what semantic
versioning covers from 1.0.0 (commands, options, configuration keys, environment variables, exit statuses, JSON
report versions, the docs bundle, theapigenie-serveradmin API, thex-apigenie-*extensions), what it doesn't,
and how a removal is deprecated first. The--helpofapigenie,apigenie-generatorandapigenie-serverlinks
to it. The design is ADR-155. -
apigenie-catalog, a fourth binary (experimental), keeps the API catalog of a repository. Every archive, the
.deband.rpm, the npm package and Homebrew install it next toapigenie,apigenie-generatorand
apigenie-server;--versionand--helpof each binary name it, andapigenie catalog ...prints the
apigenie-catalogcommand to run instead (catalog). The
design is ADR-160. -
apigenie-catalog list [DIR]lists the API specifications of a repository with no configuration: every OpenAPI,
AsyncAPI and Swagger document, Protobuf file and GraphQL schema that git tracks or doesn't ignore (every file
outside git), lesscatalog.exclude, with its name (from the catalog section,info.title, the Protobuf package
or the file name), format and version, owner, path and the commit that last changed it.--jsonwrites a report
withformatVersionand a published schema,
apigenie-catalog-report.json. -
apigenie-catalog init [DIR]writes acatalogsection toapigenie.yaml(created at the root of the repository
when there is none) with one entry per specification: name, path and anowner: nullplaceholder. Running it
again adds new specifications, keeps edited entries and their comments, and reports the entries whose files are
gone (--pruneremoves them);--dry-runprints the file. Only the section's lines change in an existing YAML
file. -
The
catalogsection of the configuration file (specswithpath,nameandowner;exclude) is part of
the configuration schema, soconfig check,config --for catalogand editors know it
(configuration). -
apigenie-serverkeeps documents in a store that scripts share (see
Document store). Scripts getctx.db.<collection>
withinsert,get,find(where, {limit, offset, sort}),count,all,update(a JSON merge patch),replace,
deleteandclear;wheretakes equality and$eq,$ne,$in,$gt,$gte,$lt,$lteand$containson
fields and paths (address.city), and documents get the next id unless they bring one.ctx.stateis unchanged.
Withdb.crud: auto(or--crud), an OpenAPI document's/shipmentsand/shipments/{id}are served from the
store without a script:POSTstores the body (itsreadOnlyproperties generated, the document checked against
its schema),GETlists with the operation's own paging, filter andsortparameters (envelopes gettotaland
friends),PUT,PATCH(a merge patch) andDELETEchange it, and an unknown id is 404;x-apigenie-mock: {resource: ...}names a resource (always served), binds it to a schema or switches it off. Writes that don't match
the schema are reported, or refused with--strict. Collections are seeded from the files ofdb.seed, else the
list operation's example, elsedb.generatedocuments generated with the run's seed, andctx.db.reset()orPOST /__apigenie/db/resetputs them back. A request'sX-Apigenie-Testheader gives each test run a namespace of its own that starts from the seeds.
The admin API shows the store (GET /__apigenie/db,/db/{name}), replaces or clears a collection, and takes and
restores named snapshots (PUT /__apigenie/db/snapshots/{name},POST .../restore).--db-file(db.file, env
APIGENIE_SERVER_DB_FILE) keeps the store in a JSON file across runs, written when the server stops or onPOST /__apigenie/db/save; writes aredbevents in the log. With several services, each has a store of its own (and
--db-fileis refused). Configured undercommands.mock.db; the design is ADR-157. -
apigenie-generator server phoenixgenerates a Phoenix 1.7 application of an OpenAPI 3.x document, on Bandit and
Jason, without Ecto, HTML or OpenApiSpex: a router with a route per operation (static segments first,QUERYand
COPYthroughmatch, segments with several parameters split by the runtime), a behaviour per tag whose callbacks
receive the decoded parameters and body, and a controller per tag calling the configured implementation (501
without one). Requests are checked before they reach it: missing credentials give 401, parameters and bodies that
don't match their schemas 400, other media types 415, all as problem documents; credentials go to an optional
authenticator.mix phx.serverserves it. Configured undercommands.generate.server_phoenix; the design is
ADR-131. The codegen conformance suite checks it at Levels 1 to 3 (mix compile --warnings-as-errors,mix format --check-formatted,mix credo --strict, andapigenie testagainstmix phx.server). -
apigenie-generator client elixirgenerates an Elixir client of an OpenAPI 3.x document on Req and Jason: a Mix
project with a module per tag and a function per operation,op(client, [body,] params \\ [], opts \\ []),
returning{:ok, body}(the response decoded into its model, a list of them, text, bytes ornil) or
{:error, %ApiError{}}, and a!variant raising the error. Parameters are a keyword list encoded in their
styles (deepObject, delimited arrays,contentparameters as JSON), bodies are model structs or maps sent as
JSON, forms or multipart parts (files, lists of files), andnew/1takes the base URL, Req options for retries
and timeouts, and credentials for every security scheme: API keys in headers, the query or cookies, HTTP bearer
and basic, OAuth2 and OpenID tokens, or a supplier called before each request. Configured under
commands.generate.client_elixir; the design is ADR-131. The codegen conformance suite checks it at Levels 1 to 3
(mix compile --warnings-as-errors,mix format --check-formatted,mix credo --strict,mix dialyzer, and
every operation called throughapigenie-server --proxy-to). -
Elixir models for the coming Elixir targets (
client elixir,server phoenix): one module per schema
with a struct,@type tandnew/1,new!/1andto_map/1decoding and encoding JSON maps; string enums
as atoms,oneOf/anyOfwith a discriminator dispatching on the tag,allOfflattened, dates, times and
base64 decoded,additionalPropertiesas a map field. The code passesmix format --check-formattedand
mix compile --warnings-as-errors, whichWITH_ELIXIR_TESTSchecks (ctest -L elixir); the design is
ADR-131. -
apigenie i18n checkcompares a translation file with the texts of a spec, Protobuf or GraphQL file (or an
i18n extractoutput) and fails unless every text is translated and every key is a text; it reports missing, empty,
unknown and invalid keys and the coverage, as text or JSON (--format json,--allow-empty)
(i18n). -
apigenie i18n syncupdates a translation file after the document changed: it keeps the translations of unchanged
texts and reports the new texts, the changed ones whose translations may be stale (--drop-changeddrops them) and
the removed ones (--prunedrops them). -
i18n extractextracts more OpenAPI texts: thesummaryanddescriptionof Reference Objects (3.1+), keyed at
the$ref, thesummaryof tags andnameof servers (3.2), the descriptions of OAuth scopes, and the titles and
descriptions of schemas under$defs,dependentSchemasandadditionalItems. -
The
openapi_i18nWebAssembly module applies translations (apply), compares and updates translation catalogs
(compare,sync), and itsTranslatorlists, replaces and removes locales and falls back on other locales
(translateWithFallback). -
apigenie generate ...exits with 2 and prints the command to run instead:code generation moved to apigenie-generator; run: apigenie-generator <the same arguments>.apigenie mock ...points toapigenie-server
the same way. -
The
--helpof each binary ends with the exit statuses and a line naming the other two binaries and what they do. -
Experimental commands, outside the 1.0 compatibility promise, say so in their
--helpand on the first line they
print (on the standard error, so that their output stays usable):apigenie convert(one target) and
apigenie-generator schema zod(no conformance Level 3 yet). Their reference pages and the CLI overview carry the
same label;docs/experimental.mdlists what is experimental and why. -
Every JSON report has a top-level
formatVersion(1): lint, diff (status,changelog,detailed), changelog,
theapigenie testreport and the summary line ofapigenie test --formatter json, the contract report of
apigenie-server, and the docs bundle manifest (next toapigenieDocs). Adding fields keeps the version; removing
or changing one bumps it (ADR-140). -
JSON Schemas of these formats, published at
https://apigenie.pl/schemas/(apigenie-lint-report.json,
apigenie-diff-report.json,apigenie-changelog-report.json,apigenie-test-report.json,
apigenie-contract-report.json,apigenie-docs-manifest.json) and linked from each command's reference page, and
a Compatibility page listing the versioned outputs. -
XML request and response bodies (
application/xml,text/xml,+xml) are checked byapigenie-server, its validating
proxy andapigenie test: a body is parsed, mapped to the JSON value its schema'sxmlobjects describe (name,
namespace,prefix,attribute,wrapped, OpenAPI 3.2nodeType) and validated like a JSON body. A body that
isn't well-formed isrequest.body.malformed(400) orresponse.body.malformedwith its line and column; a root
element other than the schema's is reported too. The mock answers XML media types with XML, andapigenie test --generatesends XML bodies, written the same way
(XML bodies). The design is ADR-130. -
apigenie teststreams. GraphQL subscriptions go over WebSocket with the graphql-ws protocol
(graphql-transport-ws) instead of a singlePOST. The request's headers go on the handshake and in
connection_init. The events are the response (?? body $[0].data...,response.messages), and each is checked
against the operation's selection set atnext[i].# @expectand# @timeoutend the subscription, and
--generateadds a request perSubscriptionfield.gRPC client- and bidi-streaming methods are no longer skipped. Their messages are written as for WS regions, with
===between them and=== wait-for-serverto wait for an answer. Each message is checked against the.proto
file atmessage[i], and a bidi call's body is the array of its answers.--generatesends two messages to each
stream.Both count in the coverage. The design is ADR-109 and ADR-111 (amended 2026-10-12)
(GraphQL subscriptions,
gRPC requests). -
The OpenAPI model reads 3.2's
xml.nodeType(x-nodeTypein 3.0 and 3.1 documents) and writes it back. -
Internal: the
xmllibrary, a strict XML 1.0 + Namespaces 1.0 parser (a pull reader and an immutable tape DOM like
jv's, positions on every node) and a streaming writer withescapeText/escapeAttribute. DTDs and entities other
than the five predefined ones are refused, so external entities and entity expansion ("billion laughs") are errors;
depth, attribute count and input size are limited. The design is ADR-129. -
Colored output in Windows consoles: the CLI,
apigenie-generatorandapigenie-serverstyle their output when it
goes to a console, as they do in terminals elsewhere, and still not when it is redirected orNO_COLORis set. -
lint --watchprints the files whose change triggered the run (on standard error, so that the report stays
parseable), and no longer misses edits made while a run is in progress. -
Faker providers take typed and named arguments and describe themselves.
x-fakeraccepts{"string.alpha": {length: 6, casing: upper}}and{provider, args, options}, the GraphQL mock'sargsandgraphql.fieldstake a list
or an object, and mock scripts callfake('internet.email', {provider: 'example.org'}). 295 providers (from 215)
cover a person's sex and country, an email's names and host, phone formats, string casing, address countries, date
formats and more (faker reference). -
Faker records:
person.profile,location.address,finance.card,finance.currencyandcommerce.product
make related values together (one sex for a person's names, a username and email of those names, one country for an
address with its time zone, one issuer for a card), written as objects in mocked responses and generated data. -
helpers.fromPattern(aliashelpers.fromRegExp) andhelpers.replaceSymbols, anddate.between,
date.birthdateByYearanddate/timeformats for dates. -
OpenAPI formatting at every depth. The formatter recognizes the objects of OpenAPI 3.0, 3.1 and 3.2 by walking
the document from its root: schemas underproperties,items,allOf,$defs,if/then/elseand every
other applicator, path items and operations of callbacks at any depth, 3.2queryoperations,
additionalOperations,itemSchema,prefixEncodinganditemEncoding, the content and examples of headers,
and individual OAuth flows (oauthFlow). Example payloads and extension values are never taken for OpenAPI
objects. The design is ADR-149. -
Independent orders for named maps and raw values:
componentNames,webhooks,callbacks,callback,
content,headers,properties,scopes,extensionandexampleValue, so that, for example, component
schemas can be sorted by name while the properties of each schema keep their order. -
Layout options for OpenAPI:
line_width,newline(lf,crlf),final_newlineanddocument_marker,
backed by new jv write options. -
style_overridesgive chosen fields their own YAML styles, by field name and object kind, such as literal
descriptions or flowrequiredlists. -
apigenie format --openapi-fragment KINDformats files holding a single OpenAPI object (a schema, a path
item, a response...) with the rules of that object. -
A typed options API (
openapi_format::FormatOptions,Formatter::setOptions) for C++ consumers of the
formatter library, validated like the configuration file. -
apigenie configprints the effective configuration (YAML, or JSON with--json): the values of the file, those
set byAPIGENIE_SERVER_*environment variables (or theirAPIGENIE_MOCK_*fallbacks), and the defaults of the
settings the file leaves out.--originannotates each value with the file, the variable ordefaultthat set it,
and--for <section>limits it to one section (lint,format,diff,generate,generate.<target>,mock)
(config). -
apigenie config check [FILE]validates a configuration file and exits with 2 on errors, reporting every unknown
setting at once with a suggestion for a misspelled name, and the allowed values of a setting with a fixed set. -
apigenie config schema [--for <section>] [--standalone-server] [--out FILE]prints the JSON Schema (draft-7) of
the configuration file, of one section, or of a standaloneapigenie-server.yaml. Each release publishes them at
https://apigenie.pl/schemas/apigenie-config/<version>.jsonand.../apigenie-server-config/<version>.json, with
latest.json, for# yaml-language-server: $schema=...comments in editors. -
--no-configforlint,format,diff,changelogand everyapigenie-generatortarget. -
MQTT 5 message properties end to end.
apigenie-server --proxy-to mqtt://relays user properties, payload format
indicator, content type, response topic, correlation data and message expiry both ways and checks them against the
AsyncAPI MQTT bindings (0.2.0): the new contract checkmessage.properties.invalid(error) for a payload format,
content type, correlation data or response topic other than the message binding says, and a missing or longer
message expiry than the operation binding's; the user properties are checked as the message'sheaders
(message.headers.invalid); a reply's header correlation id and Correlation Data are compared with the request's.
The mock's own broker sends the properties its bindings and headers state, checks its MQTT 5 clients' and answers
requests on their reply address or Response Topic.apigenie testconnects with MQTT 5 (version: 5,3.1.1or
auto), sets properties with# @property content-type ...,response-topic,correlation-data,
payload-format-indicator,message-expiry-intervalanduser NAME VALUE, subscribes to its response topic, pairs
replies by reply-address user property or Response Topic, and--generatewrites the properties of the bindings.
Reports count the messages whose properties were checked (summary.properties). The design is ADR-112 and ADR-110
(amended); see test and
server. -
apigenie-server --record DIRrecords the session: every exchange the server answered, of every service and every
protocol (HTTP, GraphQL operations, gRPC calls with their streams, WebSocket and MQTT messages of both sides), in
the order it happened, each as a file underDIR/<service>/numbered across the session, withsession.yamlnaming
the services (with the SHA-256 of their documents) and listing the exchanges. Each exchange carriessequence,
serviceandtest, the test being the request'sX-Apigenie-Testheader, a gRPC call'sx-apigenie-test
metadata, or the mark set withPOST /__apigenie/record/mark {"test": ...}. HTTP and gRPC exchanges keep the stub
forms of--record-dir, so--stubs DIR/<service>still loads them; GraphQL operations the example form; messages a
form of their own.POST /__apigenie/record/startand/stoprecord a part of a run,GET /__apigenie/recordsays
whether one is on, and the dashboard's Traffic screen has Record session and Save session.--record-redact(and
record.redact) hides more headers and, by JSON pointer, values of bodies and messages.apigenie-server session export DIR --har FILEwrites a session's HTTP exchanges, GraphQL operations and gRPC calls as HAR 1.2, in order,
with the test of each entry.--record-dirand--record-harare unchanged. The design is ADR-168
(Session recording). -
apigenie-server --replay DIRreplays a recorded session for a test suite, without the real servers: each request
gets the next unused recorded exchange of its test that matches it, so the same request twice in a test gets the
first recorded answer, then the second. The test is theX-Apigenie-Testheader (x-apigenie-testmetadata for
gRPC); untagged requests share one test drawing from the whole session;POST /__apigenie/replay/reset {"test": ...}
restarts a test's counters. Matching leaves out dates, thereadOnlyfields of OpenAPI request bodies, volatile
headers and what--replay-ignore HEADER|/POINTERnames.--replay-missingsays what a request none matches gets:
fail(a 502 problem naming the closest recording and the differences, the default),generate(the mock's answer)
orpassthrough(--proxy-to's, and with--replay-updatethe run replaces the session). Every protocol replays:
HTTP, GraphQL operations and subscriptions, AsyncAPI WebSocket, MQTT and HTTP channels (the recorded messages as the
server's side, in order) and gRPC calls, bidirectional streams in their recorded interleaving. When the server stops
it reports the exchanges never replayed and the requests that matched none, and exits with status 5 if there are
any;GET /__apigenie/replaygives the report at any time, andreplay:configures it all. The reference page has a
Playwright example. The design is ADR-169
(Session replay).
Changed
apigenieandapigenie-serverare about 360 KB smaller: Mbed TLS is built with only what their TLS uses (TLS 1.2
and 1.3 with certificates, AES-GCM and ChaCha20-Poly1305, x25519 and the NIST P-256, P-384 and P-521 curves). HTTPS,
WSS, gRPC and MQTTS servers that offer only static RSA or DHE key exchange, or only other ciphers, can no longer be
reached by--proxy-toorapigenie test; the handshake fails with a fatal alert. ADR-104.schema-format-type-mismatchandschema-format-definedfollow the OpenAPI format registry, in the OpenAPI,
AsyncAPI and Swagger linters, and accept the formats ofopenapi.custom_formats.schema-format-type-mismatchused
to check a fixed list of formats; it now checks every registry format (int8on a string,date-time-localon an
integer, ...), so existing specifications can get new errors. Its messages say which values the format is for
(Schema type and format do not match: "int8" is a format of number values). Some pairs it used to report are
accepted now, as the registry allows them:int64anduint64on strings,decimalon strings and numbers, an
integer format ontype: number.schema-format-definedaccepts the registry's integer formats (andunixtime) for
integers and its number formats (decimal, ...) for numbers. The Swagger linter keeps to the formats Swagger 2.0
defines.- Declarative lint rules with
severity: info,hint,2or3report hints instead of warnings. apigenie-generator server orpcmeets the codegen conformance bar of the other servers: Level 1 runs on OpenAPI 3.0,
3.1 and 3.2, and Levels 2 (tsc, ESLint) and 3 (apigenie testagainst the generated server) are wired, with only
QUERY and COPY, which oRPC doesn't route, expected to fail; it is no longer listed as not settled in
docs/experimental.md. The generated server now reads what oRPC doesn't decode by itself: cookie parameters,
content: application/jsonparameters, a path segment of several parameters (/labels/{labelId}.{extension}),
JSON media types other thanapplication/jsonand JSON Lines bodies (parsed by the server), form fields converted
to their types, and file uploads; bodies and responses take every media type of the operation, with binary responses
asBlobs andtext/event-streamas aneventIterator()of theitemSchema. A discriminator'sdefaultMapping
selects the default option, operations with servers of their own are also served at their paths,4XX/5XXranges
anddefaultresponses become errors,writeOnlyproperties are left out of responses, and the handler stubs and
server.tspass typescript-eslint's recommended rules (regenerateserver.tsand the handlers, which are written
once, to get them).- The Zod schemas of
generate schema zod,server mcpandserver orpccheckunevaluatedProperties: falseagainst
the properties the schema and its$ref,allOfand conditional schemas declare, instead of ignoring it with a
warning: closed allOf subtypes such as Person or ParcelShipment reject unknown properties. apigenie-server's status (GET /__apigenie) counts the clients' requests only: the admin API's and the
dashboard's own exchanges and CORS preflights are no longer inrequests(ADR-167).- XML bodies of
apigenie-server, its validating proxy andapigenie test(ADR-130) changed in three ways, found
serving SOAP envelopes (ADR-164):- Child elements are written in the order the schema declares its properties, as XML Schema sequences require.
Before, generated values put required properties first. - An element with the local name of a property but in another namespace than the property's
xml.namespaceis
no longer taken for it. It is kept as{namespace}name, soadditionalProperties: falsereports it and the
property counts as missing. - A request without
Accept(or with*/*) gets its response in the media type of its body when the response
has it: a SOAP 1.2 request (application/soap+xml) gets a SOAP 1.2 envelope rather than the first media type
listed.
- Child elements are written in the order the schema declares its properties, as XML Schema sequences require.
diffrates a component schema by where it is used. An OpenAPI component schema referenced only from
parameters and request bodies (following the references between schemas, in either document) gets therequest
rules, one referenced only from responses theresponserules; before, every component schema got the stricter of
the two, so removing a property from a request-only schema, or adding a required one to a response-only schema, was
breaking. Schemas used both ways, or not at all, are rated as before. Callbacks and webhooks swap the directions, as
they always did.- The GraphQL contract checks of
apigenie testand ofapigenie-server's GraphQL proxy warn about field errors
(response.field-error): every error of an operation the server ran, next todatathat isnullor partial.
Before, such answers passed silently. A server answering every field with an error passedapigenie test --generatewith no violations.--fail-on warningsfails on them;--severityorcontract.severitychanges the
level. Errors withoutdatastayresponse.request-error. apigenie-mockis renamedapigenie-server, withapigenie-mockand theAPIGENIE_MOCK_*variables kept as
deprecated aliases until 1.1, and themykulyak/apigenie-serverDocker image (see Upgrading).i18n applytranslates only the textsi18n extractextracts from OpenAPI, Swagger and AsyncAPI specs: a key
pointing to another string, such as anoperationIdor a URL, is now reported as addressing nothing (a warning, an
error with--strict) instead of being applied, and a key pointing to an object is no longer an error. The spec must
be valid.- The OpenAPI reader reads the schemas under
$defs,dependentSchemasandadditionalItemsas schemas, solint
rules,generateand the other commands see them;$defsanddependentSchemasare written after
patternProperties. - Loading translations into the
Translator(apigenie-generator docs --i18n) is atomic: an invalid file changes
nothing, and the error names the locale and the key. Empty translations are skipped, so the source text is shown. - JUnit reports (
--report-junitofapigenie-serverandapigenie test) escape element content and attribute values
separately: quotes in<failure>and<system-out>text are written as they are, line breaks in attribute values
as
, and characters XML can't carry as U+FFFD instead of?. response.body.unvalidatableno longer covers XML bodies, which are now checked.- File errors name their cause: "Failed to open file: openapi.yaml (not found)." instead of "Failed to open file:
openapi.yaml.". - The notes "not checkable over MQTT 3.1.1" of
apigenie testappear only for regions that speak MQTT 3.1.1; over
MQTT 5 header correlation ids and reply addresses are checked. A broker's MQTT 5 refusal names its reason code
(reason code 0x86 (bad user name or password)).
Deprecated
versionin the contract report ofapigenie-serverand in theapigenie testreport: it duplicates
formatVersionand will be removed in the next release.
Removed
apigenie creditsno longer lists TinyXML2, which API Genie stopped using long ago.
Fixed
-
Bidirectional gRPC session replay preserves recorded response order when several requests arrive
before the initial replies are sent, including when mock latency delays those replies. -
Format checks of numbers:
format: int32,int64,floatanddoublewere only applied to strings, so they
rejected every string and never checked a number. A format now applies to its JSON types: int32 checks that a
number is an integer in range (1.0 counts), and a string under it is left totype.format: passwordno longer
rejects an empty string. -
The gRPC answers
apigenie-servergenerates set the fields marked(google.api.field_behavior) = REQUIRED,
messages, lists and maps included, and not to their default values, so a validating proxy in front of the mock
no longer reportsresponse.message.incompatiblefor its own answers; fields markedINPUT_ONLYare left out.
The requests ofapigenie test --generatefor.protofiles set REQUIRED fields too and leave outOUTPUT_ONLY
ones. A REQUIRED field whose message would contain itself stays optional. -
The gRPC checks of
apigenie-server(mock and--proxy-to) andapigenie testreport a field whose wire type isn't
its declared type's, such as a string where the.protofile declares anint64. Decoding drops such a field, as
protobuf parsers do, so the message used to pass as if the field were absent. It is now
request.message.incompatibleorresponse.message.incompatible(codewire-type), at the field's location, and
the mock in enforce mode refuses such a request with INVALID_ARGUMENT. -
@mykulyak/apigenie-docsis published on npm (2026-10-11,docs-0.1.0). The 0.9.0 release notes said that the
web components were "on npm" before they had been published, and under their old name. The release gate now
installs and builds a generated docs project against the npm registry, so a CLI can't be released while the
package it depends on is missing or out of range. -
format --outputwith more than one input, or with an output file of an unknown extension, failed with an empty
error message; it now says what's wrong. -
license --librarywith an unknown name printed its error on the standard output. -
apigenie-serveransweredAccept: application/xmlwith a JSON body under an XMLContent-Type. -
Paths longer than 260 characters work on Windows, without the system-wide long-path setting.
-
test import --outputno longer fails when the output directory's name contains a dot (--output api.v2): the
directory used to be mistaken for a file and left uncreated. -
lint --fixand the other writers that replace a file in place create their temporary file exclusively, next to
the target, and never leave it behind. -
Faker's IBANs have valid check digits, routing numbers a valid ABA checksum,
book.isbna valid ISBN-13 check digit,
and Polish, US, Spanish and British national ids follow their rules. -
Faker's full names use one sex for first and last names, female middle names are generated, git commit dates name
their own weekday, and usernames get their.,_and-separators back. -
JSON output writes empty objects and arrays as
{}and[]instead of spreading them over three lines with a blank
one in between (format,diffandchangelogJSON reports,lint --formatter jsonandsarif, docs bundles). -
property_order.pathswas ignored:/pathswas ordered by the default order. -
property_order.referencewas never applied: Reference Objects are recognized by their$ref, except in schemas,
where$refis a keyword, and in path items that have their own operations. -
The error for an
openapi.custom_formatsthat is not an array said an object was expected; errors in the
configuration file now name the file and the position. -
apigenie-servernamed no file in its error for an invalid configuration file that it found itself.