Changelog
What changed in each release of the CLI. The newest release is on the downloads page.
0.10.0 (2026-10-25)
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 elixirandserver 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 ismykulyak/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’sreportmember (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--versionof 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ślarbecomesCieslar), anddate.betweenis a range ({date.between: {from: 2024-01-01, to: 2024-12-31}}) rather than an alias ofdate.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. Formattingapi.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 ofcomponents.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, sonpm 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.invaliduntil 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_formatsinapigenie.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 Studioinfo,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, anduint64becomesBigIntegerin the OpenAPI targets.date-time-localandtime-localbecomeLocalDateTimeandLocalTimein the AsyncAPI targets too. - C# and ASP.NET:
int8touint64becomesbyte,short,byte,ushort,uintandulong;double-intandunixtimebecomelong,decimal128becomesdecimal, anddate-time-localbecomesDateTime. - Go:
double-int,unixtimeandsf-integerbecomeint64. - Python and FastAPI: numbers of format
decimal128becomeDecimallikedecimalones,date-time-localbecomes 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 validatesdate-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_USERNAMEandAPIGENIE_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_USERNAMEandAPIGENIE_AMQP_PASSWORD.
- 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
-
AMQPrequests inapigenie test. AnAMQP amqp://host:port/vhostregion publishes its body’s messages toexchangeandrouting-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 totopicwith 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 anapplication/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|wsdlis 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: truewith 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, awindowand 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(thecommands.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 theirrequireditems, 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), andcommands.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, sinceapigenie 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://andmqtts://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 andctx.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/channelsitems 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 newscenarioandcontract-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/logreturns./routesand/scenarioscounthitswithlastStatusandlastAt(DELETE /__apigenie/hitsstarts over).POST /__apigenie/stubs {"id": 42}makes a stub file of a logged exchange, written todirorproxy.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/publishsends 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’sserviceslist, 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 againstapigenie-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 toopenapi_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 throughapigenie-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 byce_type, payload property or decoding to the callbacks of its behaviour, withon_unknownandon_invalidhooks (a replier’s:oksends no reply) and batching fromx-apigenie-broadwayof the Kafka binding or the configuration; each sent operation hassend_*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 undercommands.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 implementation configured for it (
-
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 undercommands.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 ofapigenie-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, the
apigenie-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-generatorandapigenie-server;--versionand--helpof each binary name it, andapigenie catalog ...prints theapigenie-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, envAPIGENIE_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,QUERYandCOPYthroughmatch, 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 undercommands.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-formattedandmix 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 ani18n 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-serverthe 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) andapigenie-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 ofapigenie-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 inconnection_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.protofile 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.productmake 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 under
properties,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 athttps://apigenie.pl/schemas/apigenie-config/<version>.jsonand.../apigenie-server-config/<version>.json, withlatest.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.1orauto), 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-testmetadata, 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(andrecord.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 indocs/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’sdefaultMappingselects 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 andserver.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.
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 therequestrules, 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, solintrules,generateand the other commands see them;$defsanddependentSchemasare written afterpatternProperties. - 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 duplicatesformatVersionand 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_ONLYones. 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 nowrequest.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.
0.9.0 (2026-10-10)
API Genie 0.9.0 translates OpenAPI, Swagger 2.0, AsyncAPI, Protobuf and GraphQL files, and its mock server
(apigenie-mock, now apigenie-server) mocks and contract-checks HTTP, WebSocket, MQTT, gRPC and GraphQL APIs. The
new GitHub Action mykulyak/api-genie-action installs the CLI in workflows on Linux, macOS and Windows.
Highlights
- Translations for all five standards.
apigenie i18n extractandi18n applywork on OpenAPI, Swagger 2.0, AsyncAPI, Protobuf and GraphQL files: the texts are extracted keyed by JSON pointers, declaration names or schema coordinates, and translations are put back without touching the rest of the file (i18n). - Contract testing across OpenAPI, Swagger, AsyncAPI, gRPC and GraphQL, with TLS.
apigenie testruns.httpfiles (HTTP, gRPC, WebSocket, MQTT and GraphQL requests) and generated requests against a live API, andapigenie-mock --proxy-tosits between clients and a real server or broker (HTTP, WebSocket, MQTT, gRPC, GraphQL), checking every request and response against the contract. Both report coverage, JSON and JUnit reports, and fail CI with--fail-onand--min-coverage. TLS is supported throughout: the mock serves HTTPS, WSS and gRPC over TLS (--tls-cert,--tls-key), and requests and upstreams can behttps://,wss://andmqtts://, verified against the system’s CA certificates (--ca-cert,--insecure) (test, mock server). - Code generation for five stacks. Clients and servers for TypeScript (fetch, Express, oRPC, WebSocket, Kafka on
KafkaJS, Zod), Python (httpx, FastAPI, FastStream), Go (
net/http, Kafka), Java (Spring clients and servers, Kafka) and C# (HttpClient, ASP.NET Core, Kafka), from OpenAPI 3.0–3.2 and, for event-driven APIs, AsyncAPI 3.x. Every target is checked by a conformance suite: snapshots, the target’s own compiler and linter, and a run againstapigenie-mock --proxy-to. The generators are a separate executable,apigenie-generator, installed with the CLI (apigenie-generator). - Web components for every spec type, and
generate docs. Theapg-*custom elements (@mykulyak/apigenie-web-componentson npm) document OpenAPI, Swagger 2.0, AsyncAPI, Protobuf/gRPC and GraphQL APIs, with Try it out for HTTP and GraphQL, built on the same engine as the CLI compiled to WebAssembly.apigenie-generator docscompiles any of these files, several at once, into a docs bundle that the components render into a static site (npm install && npm run build), with translations, the git changelog and example files; it replacesgenerate doc html(see Upgrading).
Upgrading
apigenie generateis nowapigenie-generator. The code generators moved out ofapigenieinto a separate executable,apigenie-generator, installed next to it by every archive and package, the npm package and Homebrew, and included in themykulyak/apigenieDocker image (docker run --entrypoint apigenie-generator ...). Dropgeneratefrom the command line:apigenie generate client fetch spec.yaml -o outbecomesapigenie-generator client fetch spec.yaml -o out. The options and thecommands.generatesection of the configuration file are unchanged;apigenie generatefails as an unknown command. The banners of generated files name the new command. The entries below still call the generatorsgenerate <target>. The design is ADR-123.generate doc htmlis replaced bygenerate docs, with no alias.apigenie-generator docs openapi.yaml -o docsreplacesapigenie generate doc html openapi.yaml --output docs; it takes several inputs of mixed families (OpenAPI, Swagger 2.0, AsyncAPI, GraphQL, Protobuf) and makes one site, and-c/--config,--i18n LOCALE=FILE,--package-nameand--package-versionkeep their meaning. The output is no longer an Astro project but a docs bundle (apigenie-docs.jsonanddocuments/<id>/document.<locale>.json, ADR-122) with apackage.jsonthat depends on@mykulyak/apigenie-web-componentsinstead of@mykulyak/api-genie-doc-html-template;npm install && npm run buildstill builds the static site, now intodist/, prerendered from the web components.--bundle-onlywrites the bundle alone, for a site built by other means. The CLI does not run Node. The configuration sectioncommands.generate.doc_htmliscommands.generate.docs:html_title_prefixistitle,i18n.default_localeandi18n.prefix_default_localemove to the top of the section (the locales come from--i18n),changelogkeeps its name (also--changelog), anddocumentslists the inputs (file,id,title,proto_paths,examples).pages,operations,resources,search,sidebar,html_meta,template,openapi_schema,request_code_examples,use_request_body_examples,generate_response_schema,use_response_examplesandfail_on_generator_errordescribed the Astro layout and have no replacement: the site has the layout of<apg-api-reference>(navigation with search, one page per operation and schema), themed with the components’ CSS custom properties and::part(). Generated Astro projects keep building with the template they depend on, but nothing regenerates them: rungenerate docsinto a new directory.- New dependency: Mbed TLS.
apigenieandapigenie-mocklink Mbed TLS 3.6 (Apache-2.0) forhttps://,wss://andmqtts://connections; release binaries grow by about 0.9 MB. Builds from source get it from vcpkg (mbedtls >= 3.6.2). Verifying servers needs the system’s CA certificates: the Docker images now include them; in your own minimal images installca-certificates.apigenie creditsandapigenie license --library mbedtlslist it. - Telemetry is opt-in. The CLI no longer writes
.apigenie.telemetryin the current directory, and sends nothing. SetAPIGENIE_TELEMETRY_FILEto a path to keep a local telemetry log; old.apigenie.telemetryfiles can be deleted. i18nhas subcommands.apigenie i18n FILE --output DESTbecomesapigenie i18n extract FILE --output DEST.lintfails on errors.lintexits with status 1 when issues of severityerrorremain (after--fix), so CI jobs that ran it only for its report fail now; lower the severities in the configuration to keep them passing.apigenie-mock --stubswith a GraphQL schema or an AsyncAPI document exits with status 2 instead of ignoring the option.
Added
-
generate docsdocuments OpenAPI, Swagger 2.0, AsyncAPI, GraphQL and Protobuf files, several at once, as one static site: it writes a docs bundle (apigenie-docs.json; per document and locale the resolved document, or the documentation model of AsyncAPI, GraphQL and Protobuf files; with--changelogthe git changelog, asapigenie changelogcomputes it; the example files next to a GraphQL schema and those listed in the configuration) and the npm project that renders it with@mykulyak/apigenie-web-components(npm install && npm run build): prerendered pages with declarative shadow DOM that hydrate in the browser, Try it out, a sitemap, a locale switcher for the--i18nlocales, a changelog page and the examples.--bundle-onlywrites the bundle alone. Internal: thedocs_bundlelibrary (compileDocsBundle),changelog_helpers(the changelog of any family, shared with thechangelogcommand), andgraphql_describeandprotobuf_describe, which write the documentation models that thegraphqlandprotobufWebAssembly modules used to write themselves, asasyncapi_describedoes for AsyncAPI. The design is ADR-122. -
generate server spring(OpenAPI) routes QUERY operations andadditionalOperationssuch as COPY: their handlers carry@ExtensionMethod("QUERY"), which the generatedExtensionMethodConfigurationmatches through a custom request condition of the request mapping (WebMvcRegistrations, orWebFluxRegistrationswithreactive=true).pipeDelimitedandspaceDelimitedarray query parameters are split on their separator (@DelimitedParameter), and query parameters with JSONcontentare read as JSON into their model (@JsonParameter), which is null and left unvalidated when the parameter is absent; both converters are registered byParameterConversionConfiguration. Request media types whose bodies have different types get a handler each, named as the methods ofgenerate client java(updateShipmentforapplication/merge-patch+json,updateShipmentWithJsonPatchfor theapplication/json-patch+jsonarray). The newApiExceptionHandleranswers invalid requests with a 400 Problem (type,title,status,detailand, for constraint violations of the parameters,errorswith thename,pathandmessageof each), where aConstraintViolationExceptionused to answer 500. Conformance Level 3 of the target only expects the x-internal operation, which servers leave out, to fail. -
generate server aspnet(OpenAPI) falls back to the default mapping of a discriminator (defaultMapping, orx-defaultMappingbefore 3.2): unions are read and written by the converters ofgenerate client csharp, written next to the abstract record and attached with[JsonConverter]instead of[JsonPolymorphic], so a missing or unmapped discriminator reads as the default type, and the discriminator may be anywhere in the object on .NET 8 too. Request media types whose bodies have different types get an endpoint and API method each, named as the methods of the C# client (UpdateShipmentforapplication/merge-patch+json,UpdateShipmentJsonPatchfor theapplication/json-patch+jsonarray) and restricted to their media types withAccepts<T>(), so routing picks the endpoint by Content-Type; the JSON Patch array used to be bound as aShipmentPatchobject (400). Conformance Level 3 of the target passes with only the x-internal operation, which the server leaves out, expected to fail. -
The codegen conformance runner decides that the x-internal operations of the fixture are expected to fail for servers that leave them out:
omitsInternalOperationson a Level 3 entry oftests/conformance/targets.json(set for the ASP.NET and Spring servers) expects every request of those operations, the request set’s and the generated one, to fail, instead of hand-writtenexpectedentries; a server that starts serving one still fails the level. -
generate client kafkajsandgenerate server kafkajsgenerate TypeScript (Node.js) packages for the Kafka channels of AsyncAPI 3.x documents, on KafkaJS 2.2.4: the server for the described application (KafkaApp, taking a handler per received operation, withstart()andstop()), the client for its counterpart (KafkaClient, withconnect(),listen<Operation>(handler)andclose());perspectiveswaps either. Every sent operation gets a sender with asend<Message>(message)method per message, which validates the message, writes the constant headers (e.g.ce_type), the key and the topic, and, for an operation with a reply, sends the request with a new correlation id unless it has one and resolves with the typed reply, matched by correlation id on one reply consumer per operation (ReplyTimeoutErrorwhen none arrives in time). Received operations get handler interfaces withon<Message>(message, record)andonUnknown, dispatched byce_typeheader, payload property or deduction; repliers return the reply, sent with the request’s correlation id. Payloads are validated by dependency-free functions generated from the schemas (MessageValidationError), headers by their constraints. Avro payloads, topics with parameters and replies at runtime-expression addresses are skipped with a warning. Configured undercommands.generate.client_kafkajsandcommands.generate.server_kafkajs(layout,import_extension,perspective,validation); the design is ADR-120. The codegen conformance suite checks both commands in both perspectives at Levels 1 and 2 (tscand ESLint) and, at Level 3, runs the client against the server on the Kafka service container both ways, as for Go and C#. -
generate client wsandgenerate server wsgenerate the replies of operations whosereplyis on the request’s channel: the client gets typedon<Message>handlers for the reply messages and, when the messages have a correlation id in their payloads,request<Operation>(payload, { timeoutMs })resolving with the reply carrying the request’s id; the server session getssend<Message>methods for them andreplyTo<Operation>(request, reply), which copies the request’s correlation id into the reply. On the AsyncAPI conformance fixture,requestPickup()andreplyToRequestPickup(). Replies on another channel or at a runtime-expression address are still reported as unsupported. -
apigenie testsends WS regions towss://and MQTT regions tomqtts://(orssl://) URLs, with ports 443 and 8883 by default. Their certificates are checked as forhttps://, with SNI and the URL’s host name, so--ca-certand--insecureapply. MQTT regions send theirusernameandpasswordsettings in CONNECT (MQTT 3.1.1). A setting the region doesn’t give comes fromAPIGENIE_MQTT_USERNAMEandAPIGENIE_MQTT_PASSWORD. A refusal names the reason (bad user name or password, not authorized). Credentials show as[redacted]in the console and the JSON and JUnit reports, as OAuth 2 secrets do, and a URL’suser:password@is shown as***@. These URLs used to fail with “speaks plain WebSocket/MQTT only”, and the credentials were noted as not sent. MQTT 5 is still not supported. The design is the amendment of ADR-110. -
apigenie testwith an AsyncAPI--specchecks HTTP requests against the document’s HTTP channels, with the code and checks of the validating proxy: the channel and operation by path and method (areceiveoperation’s HTTP binding’s method, GET for asendone) or by# @operation, areceiveoperation’s body, query and header fields against its messages and a 2XX response against its reply, asendoperation’s response against its messages. The requests count for their operations in the coverage, which used to show them as not tested, and--generatesends one request per HTTP channel operation (to--base-url, aws://URL taken ashttp://). A path of no HTTP channel is a note. Bothapigenie testandapigenie-mock --proxy-tonow check thequeryof an operation’s HTTP binding (operation.query.invalid) and the header fields of a message’sheadersand HTTP bindingheaders, its traits’ included (message.headers.invalid); responses other than 2XX are still not checked. The design is ADR-118, which amends ADR-110 and ADR-112. -
apigenie testruns httpYac’s OAuth 2 authorization instead of skipping it:Authorization: oauth2 [GRANT] [PREFIX](andopenid) with theclient_credentials,passwordandrefresh_tokengrants requests a token from<prefix>_tokenEndpoint(or the OpenID Connect discovery document of<prefix>_issuer) with the<prefix>_clientId,_clientSecret,_scope,_audience,_username,_passwordand_useAuthorizationHeadervariables, keeps it for the run, requests it again (with its refresh token) when it expires, and sendsAuthorization: Bearer <token>. Token endpoints are reached like other requests,https://included (--ca-cert,--insecure); a refused token request fails its request, which isn’t sent. Theauthorization_code,implicitanddevice_codegrants are still skipped, with a reason naming the grant. With--spec, requests sent with a token are checked by the newrequest.authorization.oauth2-schemeandrequest.authorization.oauth2-scopewarnings (the operation has no OAuth 2 scheme, the requested scopes aren’t its). Tokens, client secrets, passwords andAuthorizationcredentials show as[redacted]in the output, the reports and warnings.apigenie test importmaps Postman’s client credentials and password grants to these variables instead of disabling the requests. The design is ADR-117. -
The
asyncapiWebAssembly module (asyncapi.mjs, andasyncapi.web.mjsfor browsers) exportsdescribe(text, {fileName, files}). It returns the documentation model of an AsyncAPI 3.0 or 3.1 document as plain JSON ({"apigenieAsyncAPI": 1, …}):- servers, channels, operations and messages with their JSON pointers and source positions;
- references followed and external files given in
filesbundled asapigenie asyncapi bundledoes; - traits applied with JSON Merge Patch, and the defaults of the specification;
- headers and payloads as schemas (other formats, such as Avro, kept with their
schemaFormat), and a position table; - the problems found as
issues.
AsyncAPI 2.x is an error. It is built on the new
asyncapi_describelibrary; the JSON Merge Patch of traits moved into theasyncapilibrary (asyncapi/traits.hpp), where the models generators use it too. ADR-119 has the design. -
apigenie-mock, its validating proxy (--proxy-to) andapigenie test --spectake Swagger 2.0 documents: they convert them to OpenAPI 3.2 in memory, asconvert --to openapi32does, each time the document is read, and write no file. Locations stay those of the Swagger file: the lines of warnings, the lines and JSON pointers ofx-apigenie-mockproblems, and the newpointerof each operation in the contract reports. What the conversion can only approximate is told once at startup asinfo:lines with the Swagger file’s line and JSON pointer ({"event":"info"}withapigenie test --formatter json). Swagger 2.0 documents used to be refused with exit status 3. -
apigenie-mock --proxy-to mqtt://host:port(ormqtts://, verified likehttps://upstreams) with an AsyncAPI document contract-tests a real MQTT broker: the mock’s broker relays, each client getting a connection of its own to the broker with its identifier, version, clean start, keep-alive, will and credentials, and the broker’s CONNACK and SUBACK. Every PUBLISH is checked against the topic’s channel: what clients publish against itsreceiveoperations, with the QoS of their MQTT binding (message.qos.unexpected), what the broker delivers against itssendoperations (a channel without them: itsreceiveoperations and the replies sent there). In enforce mode invalid messages aren’t forwarded (MQTT 5 clients get reason code 0x99). Wills, refusals and an unreachable broker (server unavailable) are passed on, clients stay connected across reloads, the log hasmqtt-connectandmqtt-disconnectevents, and coverage,--report,--report-junit,--fail-onand--min-coveragework as for WebSocket.--proxy-tocan be repeated to proxy a WebSocket server and an MQTT broker at once (proxy.targettakes a list). The design is ADR-112, which supersedes parts of ADR-097. -
An AsyncAPI document’s HTTP channels are proxied to the
http(s)://orws(s)://server of--proxy-tothrough the OpenAPI proxy’s transport and checked against the AsyncAPI document: areceiveoperation’s request body against its messages and a 2XX response’s body against its reply, asendoperation’s response (GET) against its messages. In enforce mode invalid requests get 422 and invalid responses are replaced by 502; paths of no channel get 404 or 405. -
The AsyncAPI checks have
channel.parameter.invalid: a value of an address parameter (a topic level, a path segment) outside the parameter’senum, for WebSocket, MQTT and HTTP channels. -
mqtt::ClientConnection, an MQTT 3.1.1 and 5 client on the event loop (over TLS too), and broker hooks that decide CONNECT and SUBSCRIBE later and let the owner relay instead of delivering. -
apigenie testruns GraphQL requests instead of skipping them:GRAPHQL URLrequests of httpYac and the JetBrains HTTP client (the query, then its variables as JSON after a blank line;# @operationis theoperationName) andPOSTrequests whose body is a query are sent as GraphQL over HTTP, and JSON{"query": ...}bodies as they are. With a GraphQL schema as--spec(.graphql,.graphqls,.gql), each operation is validated against the schema before it is sent (an invalid one fails its request, not the run, and isn’t sent), and each response is checked asapigenie-mock --proxy-tochecks it: the shape ofdataanderrors, value types,nullin non-null fields that no error explains,__typenameof abstract types and schema drift. Coverage is per field and per root field (--min-coverageis the share of fields), and--reportand--report-junitare the GraphQL contract report ofapigenie-mockwith the run’stestsandrequests.--generatesends one request per field ofQueryandMutation, with required arguments made up from their types as the mock makes up values;--base-urlis the endpoint.--severitytakes the GraphQL checks. The design is ADR-111; the GraphQL contract report moved from theapigenie-mockexecutable into thegraphql_mocklibrary, with the same output. -
apigenie testruns httpYac’sWSandMQTTrequests and checks their messages against an AsyncAPI 3.x--spec. A region connects to aws://server or anmqtt://broker (MQTT 3.1.1;topic,publish,subscribe,qos,retainheaders), sends the messages of its body (separated by===,=== wait-for-server) and waits for the server’s until# @expect Ncame or# @timeout(--message-timeout, 5000 ms) passed; the messages received are the response, a JSON array (?? body $[0].type == pickup.confirmed,?? body $.length == 2,response.messagesin scripts). The channel comes from the URL’s path or the topic (# @channelnames it); messages sent are checked against the channel’s receive operations and messages received against its send operations, with the AsyncAPI proxy’s checks. A message of a request/reply operation is paired with its reply, checked by the new contract checksmessage.reply.missing(warn) andmessage.reply.uncorrelated(error, a payloadcorrelationIdthat differs); correlation ids and reply addresses in message headers are noted as not checkable over MQTT 3.1.1. Coverage per operation and message and the reports (--report,--report-junit,--formatter json,--min-coverage) are the AsyncAPI proxy’s.--generatesends a region per WebSocket and MQTT channel with the receive operations’ example messages, at--base-url ws://...and the new--mqtt-url. The design is ADR-110, extending ADR-136 and ADR-097. -
apigenie-mock --proxy-tocontract-tests a real gRPC server against its.protofile: the proxy checks the upstream’s messages against their output types (also fields marked[(google.api.field_behavior) = REQUIRED], under proto3’s presence rules, and deprecated fields and enum values), its status (HTTP status, content type,grpc-status, the percent-encoding ofgrpc-message,grpc-status-details-binas agoogle.rpc.Statuswhose code isgrpc-status), the number of messages of unary calls and the-binmetadata, with check ids such asresponse.message.incompatibleandresponse.status.details-incompatiblethatcontract.severitychanges. Locations name the field (message[0]/items/2/sku). It counts the calls of each service and method, and writes the contract reports the OpenAPI, GraphQL and AsyncAPI proxies write (--reportwith"specification": "grpc",--report-junitwith a test case per method,--fail-on,--min-coverageas the share of methods, exit status 5,GET /__apigenie/contract).--strict(orgrpc.validation.response: enforce) checks each upstream message before forwarding it and fails the call with INTERNAL. The upstream isHOST:PORT(cleartext HTTP/2) orhttps://host[:port](HTTP/2 over TLS). The design is ADR-109. -
apigenie testsendsGRPCrequests of.httpfiles (httpYac’sGRPC host:port/package.Service/Methodwith the request message as a proto3 JSON body, metadata as headers) with a.protofile as--spec, its imports looked up in the new--proto-pathdirectories. Unary and server-streaming calls are made over HTTP/2, cleartext or over TLS forgrpcs://URLs (with--ca-certand--insecure), and checked like the proxy’s; a body that isn’t a valid message fails the request with the field’s path before it is sent. Assertions and scripts see the status code asstatus(?? status == 0), metadata and trailers as headers and the message as a JSON body (an array for server streaming). Client- and bidi-streaming requests are reported as skipped with a warning.--generate --base-url HOST:PORT(orgrpcs://HOST:PORT) sends a request per method with a generated message. The reports are the gRPC contract report plus the requests (grpc,grpcStatus). -
apigenie-mock --tls-cert FILE --tls-key FILEserves HTTPS, WSS and gRPC over TLS. Clients that ask forh2by ALPN, such as browsers and gRPC clients, get HTTP/2. The settings are alsotls.certandtls.keyin the configuration, andAPIGENIE_MOCK_TLS_CERTandAPIGENIE_MOCK_TLS_KEY. A separate admin port uses the same certificate, and the MQTT broker stays cleartext. The startup line showshttps://andwss://URLs and"tls": true. TLS 1.2 and 1.3 come from Mbed TLS; the design is ADR-104. -
apigenie-mock --proxy-toreaches TLS upstreams:https://for the OpenAPI and GraphQL validating proxies,wss://for the AsyncAPI WebSocket proxy, andhttps://host[:port]for gRPC (HTTP/2 over TLS with ALPNh2, shown asgrpcs://). Certificates are verified against the system’s roots: the macOS keychains, the Windows ROOT store, the Linux CA bundle, orSSL_CERT_FILEandSSL_CERT_DIR.--ca-cert FILE(repeatable;tls.ca_certs,APIGENIE_MOCK_CA_CERT) adds authorities, and--insecure(tls.insecure,APIGENIE_MOCK_INSECURE=1) skips verification. A rejected certificate gets the usual 502 with the reason. -
apigenie testsendshttps://requests, with the same--ca-cert FILEand--insecureoptions. Imported collections no longer need their HTTPS URLs pointed at a cleartext server. -
generate server goalso takes AsyncAPI 3.x documents and generates a Go module (Go 1.22) for their Kafka channels on franz-go (github.com/twmb/franz-go/pkg/kgo, pinned at v1.18.1, with a completego.sum): structs, enums and sealed-interface unions withjsontags andValidatemethods for the payloads, a struct per message (key, typed headers, payload) validated before it is sent and after it is received, and anAppwith a consumer group and a handler interface per receive operation (several messages told apart by a constant payload property or header, e.g. thece_typeof binary-mode CloudEvents; others go toOnUnknown) and a typed sender per send operation. Request/reply operations with a reply channel return the reply from the sender, matched by its correlation id, and repliers send the handler’s reply with the correlation id copied. Consumer groups and client ids come from the Kafka operation bindings.cmd/<package>/main.gotakes the brokers from-brokersorKAFKA_BROKERS, wires stub handlers and stops cleanly on SIGINT and SIGTERM. Channels of other protocols are skipped with an info line, Avro payloads with a warning. Configured undercommands.generate.server_go.asyncapi; the design is ADR-105. The codegen conformance suite checks both perspectives at Levels 1 and 2 and, at Level 3, has the application and the counterpart exchange the fixture’s Kafka messages on the conformance workflow’s new Kafka service container, including therequestLabelRenderreply, an unknownce_typeand an invalid payload. -
generate client goalso takes AsyncAPI 3.x documents and generates a Go Kafka client package (a Go 1.22 module on franz-go v1.18.1, withoutmain.go), by default for the counterpart of the described application and withperspective: applicationfor the application itself.NewClient(options)creates the producer,Close()closes every client, andOptionstakes the brokers, a client id and franz-go options passed to every client. A send operation getsSend<Operation>(ctx, message), which writes the constant headers (e.g.ce_type), the key and the topic from the bindings; with several messages it takes an interface they implement. A request/reply operation gets a blockingRequest<Operation>(ctx, message)that sets a fresh correlation id, waits for the reply until the context’s deadline and returns it typed (an interface of the replies when there are several), with one reply consumer per operation shared by all requests. A receive operation getsListen<Operation>(handler), consuming in the background with the server’s dispatch, validation and repliers. Configured undercommands.generate.client_go.asyncapi; the design is ADR-106. The codegen conformance suite checks both perspectives at Levels 1 and 2 and, at Level 3, runs the client againstgenerate server goon the Kafka service container both ways: the server’s events and label requests reach the client’s listeners and replier, and the client’s sends and requests (concurrent ones and one ending at its context’s deadline included) reach the server’s handlers. -
Options.ClientIDof the Go Kafka code ofgenerate server goandgenerate client goreplaces the producer’s client id. -
generate server aspnetalso takes AsyncAPI 3.x documents and generates an ASP.NET Core project (.NET 8) for their Kafka channels on Confluent.Kafka 2.15.1, so a REST API and its events can live in one application: the payload models ofgenerate client csharp(records, enums, unions read by converters of the System.Text.Json source-generated context), a record per message (key, typed headers with DataAnnotations, payload) validated before it is sent and after it is received, and per receive operation a handler interface, registered with dependency injection and resolved per record, and a consumer hosted as aBackgroundServicein the operation’s consumer group (several messages told apart by a constant payload property or header, e.g. thece_typeof binary-mode CloudEvents; others go toOnUnknownAsync). Records are committed once their handler returns and handled again when it throws; invalid records are logged and committed. Each send operation gets a typed sender; request/reply operations with a reply channel return the reply fromRequest<Message>Async, matched by correlation id, and repliers send the handler’s reply with the correlation id copied. TheAsyncApi:Kafkasection ofappsettings.jsonholds the brokers, topics, group and client ids (from the Kafka operation bindings) and further librdkafka properties, overridable by environment variables; stopping the host closes the consumers. Configured undercommands.generate.server_aspnet.asyncapi; the design is ADR-107. The codegen conformance suite checks both perspectives at Levels 1 and 2 and, at Level 3, runs the application and the counterpart on the conformance workflow’s Kafka service container, as for Go. -
generate client csharpalso takes AsyncAPI 3.x documents and generates a C# Kafka client library (.NET 8 class library on Confluent.Kafka 2.15.1), by default for the counterpart of the described application and withperspective: applicationfor the application itself.<Name>KafkaClienthas a producer per channel it sends to, whoseSend<Message>Asyncwrites the constant headers (e.g.ce_type), the key and the topic from the bindings; a request/reply operation getsRequest<Message>Async, which sets a fresh correlation id, waits for the reply with a timeout (ReplyTimeoutor an argument) and aCancellationToken, and returns it typed (an interface of the replies when there are several), with one reply consumer per client and a pending request per correlation id. Receive operations get the handler interfaces and hosted consumers ofgenerate server aspnet, with its dispatch, validation and repliers.Add<Name>KafkaClient(configuration, configure)binds the options to theAsyncApi:Kafkasection and registers the client and its producers, and its builder’sAdd<Operation>Handler<THandler>()registers a handler with its consumer.--package-nameis the root namespace (default: the title followed by.Client), as for OpenAPI. Configured undercommands.generate.client_csharp.asyncapi; the design is ADR-108. The codegen conformance suite checks both perspectives at Levels 1 and 2 and, at Level 3, runs the client againstgenerate server aspneton the Kafka service container both ways, including concurrent requests, a request timing out and a cancelled one. -
generate client javaalso takes AsyncAPI 3.x documents and generates a Java Kafka client library (a Maven project for Java 21 on the Apache Kafka client 4.1.0, Jackson and Hibernate Validator, without Spring), by default for the counterpart of the described application and withperspective: applicationfor the application itself. It shares the message records, models, dispatch, validation and repliers ofgenerate server spring.<Name>KafkaClient(with<Name>KafkaClientOptions: servers, topics, client and group ids, reply timeout, producer and consumer properties) getssend<Message>(message)per send operation, writing the constant headers (e.g.ce_type), the key and the topic from the bindings;<operation>(message[, timeout])per request/reply operation, which sets a fresh correlation id (header, payload pointer orkafka_correlationId) and completes with the typed reply, with one group-less reply consumer per client; andlisten<Operation>(handler)per receive operation, consuming on a virtual thread withonUnknownandonErrordefaults.--libraryand-pare errors with AsyncAPI input. Configured undercommands.generate.client_java.asyncapi; the design is ADR-121. The codegen conformance suite checks both perspectives at Levels 1 and 2 (mvn package,javac -Xlint:all -Werror) and, at Level 3, runs the client againstgenerate server springon the Kafka service container both ways, including concurrent requests and a timeout. -
generate client pythonalso takes AsyncAPI 3.x documents and generates an asyncio Kafka client package on aiokafka with the Pydantic models ofgenerate server faststream, by default for the counterpart of the described application and withperspective: applicationfor the application itself.messages.pyhas a dataclass per message (payload, headers, key) with headers models and their constant headers;<Name>KafkaClient, an async context manager, getssend_<message>(),request_<operation>(message, *, timeout=None)(fresh correlation id, typed reply, one group-less reply consumer per client,TimeoutError) andlisten_<operation>(handler)with abstract handler classes (on_unknownandon_errordefaults), by the Kafka rules of ADR-102. The package passesmypy --strictand ruff, and depends onaiokafka[lz4,snappy,zstd], so it reads topics other producers compress. Configured undercommands.generate.client_python.asyncapi; the design is ADR-121. The codegen conformance suite checks both perspectives at Levels 1 and 2 and, at Level 3, runs the client’s counterpart against the Kafka broker ofgenerate server faststream’s application and the client’s application against its counterpart on the Kafka service container, including concurrent requests and a timeout. -
generate server springalso takes AsyncAPI 3.x documents and generates a Spring Boot application (Maven project) for their Kafka channels: a record per payload and per message (key, payload, headers) with Jackson and Jakarta Bean Validation annotations, a@KafkaListenerand a handler interface per receive operation (several messages told apart by a constant payload property or header, e.g. thece_typeof binary-mode CloudEvents), and a sender overKafkaTemplateper send operation. Request/reply operations with a reply channel useReplyingKafkaTemplate, and replies carry the correlation id from thecorrelationIdlocation. Topics, group and client ids (from the Kafka bindings) and servers go toapplication.yaml; message headers are merged with those of their traits as AsyncAPI does. Constraints Bean Validation can’t express (draft-07dependencies,if/then/else, tuples) become comments. Channels of other protocols are skipped with an info line, Avro payloads with a warning. Configured undercommands.generate.server_spring.asyncapi; the design is ADR-102. The codegen conformance suite builds both perspectives at Level 2 and, at Level 3, has the application and the counterpart exchange the fixture’s Kafka messages on an embedded broker (@EmbeddedKafka), including therequestLabelRenderreply. -
generate client gogenerates a Go client for thenet/httppackage of Go 1.22 from an OpenAPI 3.x document, using the standard library only. OneClientover a configurable*http.Clienttakes functional options (base URL, request editors, user agent, an option per security scheme such asWithAPIKey), and has a method per operation and request media type taking acontext.Contextfirst. Each method returns a response struct with one typed field per documented status and media type (JSON200,ApplicationProblemJSON404 *ProblemDetails) and the raw*http.Response; statuses of 400 and above also return an*APIErrorwith the RFC 9457 problem details.text/event-streamandapplication/jsonlresponses are iterators typed fromitemSchema(x-itemSchemain 3.0 and 3.1), binary responses stay open asio.ReadCloser, multipart bodies are structs whose binary parts (fromcontentMediaTypeorformat: binary) areFilevalues honouringencoding,contentparameters are sent as JSON, operation and path servers are honoured (WithOperationServers(false)ignores them), credentials go to the operations that accept them, andx-internaloperations are left out. Types and validation are shared withgenerate server go. Configured undercommands.generate.client_go. -
generate client csharpgenerates a C# client of an OpenAPI 3.0, 3.1 or 3.2 document for .NET 8 (or 9 and 10), built onHttpClientandSystem.Text.Json: a class library with one client per tag taking theHttpClientin its constructor (client.Shipments.ListShipmentsAsync()), a client of the whole API, andAdd{Api}Client()registering them withIHttpClientFactory. Methods areasyncwith aCancellationToken; they return the body of the success response, or a*Resultby status when statuses have different bodies, and throwApiExceptionwith the RFC 9457 problem details of error responses. Every parameter style is supported, objects andcontentparameters (as JSON) included; bodies are JSON, text, binary, forms and multipart withFileParts forformat: binaryandcontentMediaTypefields; other media types of bodies and responses are methods of their own (UpdateShipmentJsonPatchAsync); server-sent events and JSON Lines areIAsyncEnumerable<T>typed byitemSchema(orx-itemSchema). Operation and path servers are honoured,x-internaloperations left out, and bearer, basic, OAuth 2.0, OpenID Connect and API key credentials sent byDelegatingHandlers. The models are those ofgenerate server aspnet, with a source-generatedJsonSerializerContextand converters that read everyoneOf/anyOfwith a discriminator wherever the discriminator is, falling back todefaultMapping(orx-defaultMapping). The code builds with-warnaserrorand the .NET analyzers. Configured undercommands.generate.client_csharp. -
The OpenAPI reader reads
itemSchemaof media types anddefaultMappingof discriminators (OpenAPI 3.2), and the writer keeps them. -
apigenie test import COLLECTION -o DIRimports Collection Format v2.0/v2.1 JSON exports into folder-based.httptests with environments, bearer/basic/API-key references, upload assets, inherited scripts and a marker-gated QuickJSpmcompatibility subset. Status-only tests become??assertions; unsupported script blocks are preserved as comments with source-line warnings, and OAuth 2 requests are disabled.--specadds operation bindings and saved-response example overlays without editing the spec.--environmentcan be repeated;--forceopts into replacing generated files. Authentication secrets are stored only in the generated environment file. -
apigenie testreadsQUERYrequests (the safe method with a body of OpenAPI 3.2) in.httpfiles, and--spectakes OpenAPI 3.2 documents. -
apigenie testruns the requests of.httpfiles (the httpYac flavour, which the JetBrains HTTP client and VS Code’s REST Client read too) against a live server and validates every request and response against an OpenAPI document (--spec) with the checks ofapigenie-mock’s validating proxy. Files have###regions,{{var}}variables,@var = valuedefinitions,# @name/# @ref/# @forceRefchaining with{{name.response.body.$.id}}references,{{ }}scripts and JetBrains> {% %}handlers (QuickJS:request,response,client,exports,test,assert,console),??assertions, and.envandhttp-client.env.jsonenvironments (--env,--env-file,--var). Requests are bound to operations by method and path;# @operation <operationId>picks one,# @operation offskips the checks.--generatesends one request per operation, from the document’s examples or generated values. The run fails with exit status 5 when a request fails or when the contract gate (--fail-on,errorsby default, and--min-coverage) does;--reportand--report-junitwriteapigenie-mock’s contract report with the requests added. Unknown directives are warnings. GraphQL, gRPC, HTTPS and OAuth 2 are not supported yet. -
--strip-commentsforformat,overlayandlint --fixdrops the comments of the files they write, which they keep otherwise (ADR-093): YAML comments of OpenAPI, Swagger and AsyncAPI documents, comments of Protobuf files and#comments of GraphQL documents, whose descriptions are kept. -
i18n applyapplies translations to Protobuf (.proto) and GraphQL (.graphql,.graphqls,.gql) files, keyed asi18n extractwrites them: the leading comments of Protobuf declarations and the descriptions of GraphQL definitions are rewritten in place, in their style (//lines,/* */blocks, block or quoted strings), and every other byte of the file is kept, comments included, so applying untranslated texts returns the file unchanged. Keys addressing no comment or description are warnings, or errors with--strict(ADR-103). -
i18n extractextracts the texts of Swagger 2.0 documents, recognized by theirswaggerproperty, as it does for OpenAPI: titles, summaries and descriptions keyed by JSON pointers, whichi18n applyputs back (ADR-103). -
i18n apply --strip-commentsdrops the YAML comments of a spec or the#comments of a GraphQL file (descriptions are kept), asformat,overlayandlint --fixdo (ADR-093); it is refused for Protobuf files, whose translated texts are comments. -
i18n applyapplies translations to OpenAPI and AsyncAPI documents (JSON or YAML): each key of a translation file, a JSON pointer asi18n extractwrites it, has the string it points to replaced by its value, and everything else is kept, YAML comments included.--translationscan be repeated (later files override earlier ones), the result goes to--outputor the standard output in the input format or that of--format, and keys pointing to nothing are warnings, or errors with--strict. -
apigenie-mock --proxy-to ws://host:portwith an AsyncAPI document is a validating proxy for contract testing of a WebSocket API: clients of a WebSocket channel are relayed to the real server (handshake headers and subprotocol passed on, 502 or 504 when it can’t be reached), and every message of both sides is checked against the channel’sreceive(client messages) andsend(server messages) operations. Violations carry checks such asmessage.incompatibleandchannel.send.undeclared, go to the log, and--strictkeeps invalid messages from going on (the client gets aninvalid-messageorinvalid-upstream-messageproblem instead). Coverage counts each message of each operation;--report,--report-junit,--fail-on,--min-coverageandGET /__apigenie/contractwork as for OpenAPI. HTTP channels and the MQTT broker keep being mocked.contract.severityaccepts the new checks. -
apigenie-mock --proxy-to ... --record-dir DIRwith an OpenAPI document writes each exchange the server answered as a stub file (getPet-1.yaml, orGET_health-1.yamlfor a route of no operation: the request’s method, path, query, headers and body, and the response’s status, headers and body), which--stubs DIR(orstubs:in the configuration) replays without the server: before the document’s examples, afterPreferand scenario rules, withX-Apigenie-Mock-Source: stub:getPet-1.--record-har FILEwrites the whole session through the proxy, the mock’s own answers included, as a HAR 1.2 file when the server stops, withcomment(proxyormock) and_apigenie(source, operation, upstream status, violations) on each entry. Both hide the values ofAuthorization,Cookie,Set-Cookie,Proxy-AuthorizationandX-Api-Key;proxy.redactsets the list. Alsoproxy.record_harandAPIGENIE_MOCK_RECORD_HAR. -
apigenie-mock --proxy-mode fallbackwith an OpenAPI document and--proxy-to: every request still goes to the real server, but when the server can’t be reached, doesn’t answer in time, or answers 404 or 501 for an operation of the document, the mock answers as it would without--proxy-to(examples, generated data, scenarios,Prefer). Such answers carryX-Apigenie-Mock-Upstream-Status, are logged withfallback: trueand aren’tresponse.status.unknownviolations. The contract report tells the server’s answers from the mock’s:summary.sources, each operation’ssourcesandmockedResponses, two JUnit properties and aSources:line in the summary. Configured underproxy.mode. -
apigenie-mockserves OpenAPI 3.2 documents: thequerymethod and the methods ofadditionalOperations(such asCOPY) are routed, validated and recorded in contract reports like the others. Streams described byitemSchemaare answered with an empty body. -
A codegen conformance suite runs every OpenAPI generator on one shared, realistic OpenAPI 3.2 fixture, the Carpet Express Shipping API (
data/fixtures/openapi, with its 3.1 and 3.0 variants, Spring and mock overlays and a feature catalog). Level 1, part of every ctest run, compares each generator’s output byte for byte with a checked-in snapshot and checks a per-target manifest of the features it doesn’t support yet. Levels 2 and 3 type-check and lint the output with the target’s own toolchain and run the generated code against the fixture’s contract (clients behindapigenie-mock --proxy-to, servers withapigenie test --generateand a hand-written request set), for the fetch, C#, Go, Java and Python clients and the FastAPI, Spring, Go, ASP.NET and Express servers, and the zod schemas at Level 2, in a Linux workflow that runs on generator changes and on demand. The results are indocs/codegen_conformance.md; the design is ADR-098. -
The codegen conformance suite covers the AsyncAPI generators:
generate client wsandgenerate server wsrun on the Carpet Express Event Platform (data/fixtures/asyncapi), the AsyncAPI 3.0 event side of the same service (Kafka, WebSocket, MQTT 5, HTTP and AMQP channels, three request/reply styles, traits with a merge trap, bindings for all five protocols, draft-07 schemas), with a mock overlay and a feature catalog. Level 1 compares their output with snapshots and checks their manifests (the twelve non-WebSocket channels don’t concern them; the reply of a request/reply operation is not generated yet). Level 2 type-checks and lints the generated packages; Level 3 drives the generated client and server throughapigenie-mockas a validating WebSocket proxy. The matrix indocs/codegen_conformance.mdhas an AsyncAPI section; the design is ADR-100. -
apigenie generate server faststreamgenerates a Python FastStream application from the Kafka, AMQP (RabbitMQ) and MQTT channels of an AsyncAPI 3.x document: a broker per server (URL from an environment variable), a subscriber per receive operation (topics and patterns, consumer groups, queues, exchanges and routing keys, QoS from the bindings; several messages dispatched by a constant property) calling a handler you write inhandlers.py, which regenerating keeps, and a publisher with a typed publish function per send operation. Payloads and headers become Pydantic v2 models, the same asgenerate client python’s, with trait headers merged; headers are typedHeader()parameters. Requests wait for their reply on the document’s reply channel by correlation id; reply addresses in headers becomereply_toarguments. WebSocket and HTTP channels and Avro payloads are skipped and reported. Configuration iscommands.generate.server_faststream; it is in the codegen conformance suite at Levels 1 to 3. The design is ADR-101. -
The codegen conformance suite runs
generate server faststreamat Level 3: the conformance workflow has RabbitMQ (with the fixture’s virtual host) and Mosquitto service containers, pinned by digest, next to its Kafka one, and the generated application runs on the three brokers next to a counterpart. The counterpart publishes the fixture’s examples (MQTT courier positions and sorter scans, AMQP customs decisions) to the application’s handlers, receives what the application publishes (shipment events and label requests on Kafka, job offers on MQTT, customs filings on thecustomsexchange) and answers the label requests on their reply topic. The MQTT traffic goes through apigenie-mock’s MQTT bridge, whose contract report checks it; the Kafka and AMQP messages and the MQTT 5 user properties are checked against the fixture in the harness (checkMessages(), ajv). Every item passes. -
apigenie-mock --proxy-to http://host:port/graphqlwith a GraphQL schema is a validating proxy for contract testing: GraphQL requests are checked against the schema (graphql-js’s rules, variables, deprecated usage) and forwarded to the real server, whose answers are checked field by field against the schema and the request’s selection set (missing and unexpected fields, nulls no error explains, scalar, enum and__typenamevalues, lists, the shape oferrors, statuses and media types). The proxy also introspects the server and reports where its schema differs (schema.drift,schema.addition). Violations carry checks such asresponse.data.incompatible, go to the log andX-Apigenie-Mock-Violations-Detail, and--strictanswers bad requests itself and replaces bad answers (502). Field and root field coverage,--report,--report-junit,--fail-onand--min-coveragework as for OpenAPI.--proxy-mode fallbacklets examples, scenarios and scripts answer first, and--record-dirwrites the server’s answers as examples the mock replays. -
generate server fastapigenerates a Python FastAPI server from an OpenAPI 3.x document, with the project layout and options of openapi-generator’spython-fastapigenerator: pydantic 2 models (enums, unions with discriminators, recursive models, aliases and constraints), a router and a base class to implement per tag, security dependencies for API keys, HTTP, OAuth2 and OpenID Connect schemes with alternatives and scopes,pyproject.toml, a Dockerfile and pytest tests. Routes declare their status codes and response classes, parameters and bodies of every kind (forms, uploads, text, binary) are typed, and the implementation package, the tests and.openapi-generator-ignoreare never overwritten. Configured undercommands.generate.server_fastapi. -
generate client pythongenerates a Python client of an OpenAPI document, built on httpx (or httpx2) and Pydantic 2:ClientandAsyncClientclasses with one resource per tag (client.pets.list_pets()), Pydantic models with Python field names, tagged unions for discriminators andInputvariants forreadOnly/writeOnlyproperties, all parameter styles, JSON, form, multipart, text and binary bodies, typed errors parsed with the documented schemas, retries with backoff honoringRetry-After, per-request options, raw and streaming responses, server-sent events, server variables, and authentication by security scheme, including the OAuth 2.0 client credentials flow. The code passesmypy --strict. Configured undercommands.generate.client_python. -
generate client javagenerates a Java client of an OpenAPI document as a Maven project (Java 17, Spring Framework and Jackson versions from the Spring Boot BOM, without Spring Boot): anApiClienton Spring’sRestClient(--library restclient, the default) orRestTemplate(resttemplate) with one class per tag, whose operations haveop(...),opWithHttpInfo(...),opStream(...)(a lazyStreamoftext/event-streamand JSON Lines items) andopWith<MediaType>(...)methods for further request media types (updateShipmentWithJsonPatch); every parameter style, JSON-encodedcontentparameters, JSON, JSON Lines, form, multipart (Resourceparts) and binary bodies, QUERY and other methods, operation and path servers, credentials per security scheme (bearer, basic, API keys in headers, queries and cookies) applied to the operations requiring them, andApiException/ProblemDetailsException(RFC 9457) for errors;x-internaloperations are left out.--library feignand--library http-interfacegenerate Spring Cloud OpenFeign and Spring HTTP interface clients. Configured undercommands.generate.client_java. -
generate server expressgenerates an Express 5 server stub in TypeScript from OpenAPI 3.x documents: typed handler interfaces per tag, implemented in files of your own, andcreateRouter(handlers, options), a router which checks security requirements with security handlers per scheme, decodes parameters in all OpenAPI styles and converts them to the types of their schemas, parses bodies, validates requests (and optionally responses) with Ajv or with generated validators without dependencies (validation), answers errors with RFC 9457 problem details and operations without handlers with 501. It can include the document and an example server. Configured undercommands.generate.server_express. -
generate server orpcgenerates a TypeScript server based on oRPC (https://orpc.dev) from an OpenAPI document: Zod schemas, a procedure contract per operation with its route, input, output and type-safe errors (one per 4xx/5xx response, sent with the response body of the document), handler stubs, and a Node.js server serving the procedures with oRPC’sOpenAPIHandlerand their OpenAPI reference. Inputs use oRPC’s compact structure unless the request needs the detailed one, parameters are converted from strings, and operations with several success responses or response headers return status, headers and body. Handlers and the server files are written once, so regenerating keeps them. Targets oRPC 1, or oRPC 2 (beta) withorpc_version: 2. Configured undercommands.generate.server_orpc. -
generate server aspnetgenerates an ASP.NET Core server in C# built on Minimal APIs, for .NET 8 to 10 and without NuGet packages: records and enums of the schemas, one interface per group of operations whose methods returnResults<...>unions of typed results (generated result types cover statusesTypedResultslacks), endpoints binding path, query, header and cookie parameters (array styles included) and JSON, text, binary, form and multipart bodies, and a project withProgram.csand stub implementations written only when missing.oneOfwith a discriminator becomes[JsonPolymorphic]records, enums keep the values of the document, an endpoint filter validates requests against the constraints of their schemas (400 validation problems), and operations with security requirements callRequireAuthorization().layout: sourceswrites only the sources, for existing projects. Configured undercommands.generate.server_aspnet. -
apigenie-mock --proxy-to http://host:portwith an OpenAPI document is a validating proxy for contract testing: it forwards every request to the real server and checks the request and the server’s response against the document (parameters, bodies, security, statuses, media types, headers). Violations carry stable checks such asresponse.body.incompatibleand show up in the log, inX-Apigenie-Mock-Violations-Detailand atGET /__apigenie/contract;--strictrejects bad requests (422) and replaces bad responses (502). The mock counts the operations, request bodies and responses the traffic covered, and writes--report(JSON) and--report-junitwhen it stops;--fail-onand--min-coveragemake it exit with 5. Configured underproxyandcontract. -
overlaycommand applies OpenAPI Overlays (versions 1.0, 1.1 and 1.2 of the specification) to an OpenAPI document, or to any JSON or YAML document:apigenie overlay INPUT --overlay OVERLAY, with--overlayrepeated to apply several overlays in order. Updates, copies and removals follow the merge rules of the specification, reusable actions are supported, YAML comments are kept, and actions that select nothing are reported as warnings.--validateonly checks the overlays, with the line and column of each issue. Targets are RFC 9535 JSONPath queries, functions (length(),count(),match(),search(),value()) included. Theopenapi_overlayWebAssembly module exports the sameapplyandvalidate. -
generate server gogenerates a Go server for thenet/http.ServeMuxof Go 1.22 from an OpenAPI 3.x document, using the standard library only. Names and layering follow oapi-codegen:ServerInterfacereceives bound parameters,StrictServerInterfacerequest and response objects (typed headers, statuses, JSON, form, multipart, text and binary bodies). Types carryValidate()methods, and parameters and bodies are validated before the handlers run, with RFC 9457 problem details for failures;oneOf/anyOfbecome structs with one field per variant, optional nullable propertiesNullable[T]. Patterns end with{$}where needed, and the patternshttp.ServeMuxcan’t register together share one that dispatches to the most specific operation. ASecurityHandlerand middlewares run before parameters are bound. Configured undercommands.generate.server_go. -
generate schema zodgenerates Zod (v4) schemas, with their inferred TypeScript types, from the root schema and the$defsof a JSON Schema document (drafts 4 to 2020-12), or from the component schemas of an OpenAPI 3.x, Swagger 2.0 or AsyncAPI document. Objects follow JSON Schema and keep unknown properties unlessadditionalPropertiessays otherwise; formats, numeric and string constraints, tuples,patternProperties,not,if/then/elseand dependencies are validated, discriminated unions are detected, and recursive schemas become getters. Configured undercommands.generate.schema_zod. -
generate client wsandgenerate server wsgenerate TypeScript code from the WebSocket channels of AsyncAPI 3.x documents: a client with one class per channel (browsers and Node.js, no dependencies) and a Node.js server built on thewspackage, with session classes and handler interfaces. Payloads, address parameters and the query of the WebSocket binding are typed; received messages get handlers of their own when a property with a constant value tells them apart. The server implements the described application and the client its counterpart, unless theperspectiveoption says otherwise. Configured undercommands.generate.client_wsandcommands.generate.server_ws. -
format --checkreports the files that formatting would change, one path per line, and exits with 1 when there are any, without writing anything. -
GitHub Action (
src/github_action, not published yet) that installs the CLI on Linux, macOS and Windows runners and runslint,format --checkordiff(against the pull request’s base), with annotations on the files and a job summary. Withsarif: true, lint issues are also uploaded to GitHub code scanning; on pull requests, thediffreport is posted as a comment that later runs update in place. -
sarifformatter oflint: SARIF 2.1.0 for GitHub code scanning and other dashboards, with paths relative to the current directory and fingerprints based on JSON pointers, so that issues are tracked across runs. -
apigenie-mock, a mock server for OpenAPI 3.0 and 3.1 documents, installed withapigenieby every package and published as themykulyak/apigenie-mockDocker image. It answers with the document’s examples or with data generated from its schemas, validates requests (422 with problem details) and its own responses, checks security requirements, followsPreferheaders (code,example,dynamic,delay,drop,scenario), simulates latency, jitter, dropped connections and bandwidth limits, and logs every exchange as text or JSON lines. Scenarios force error states from a configuration file (apigenie-mock.yamlorcommands.mock),x-apigenie-mockdocument extensions or an admin API under/__apigenie, which also reloads, reseeds and stops the server.--watchreloads the document and the configuration when they change. Schemas can pick faker functions withx-faker. -
apigenie-mockserves AsyncAPI 3.x documents: WebSocket clients of a channel get its send operations’ messages on a schedule and their messages are checked against its receive operations and answered with their replies; HTTP channels take and answer messages; an in-house MQTT 3.1.1 and 5 broker serves the channels of mqtt servers. -
apigenie-mockserves the gRPC services of.protofiles over cleartext HTTP/2, on the same port as HTTP: unary and streaming methods answered from YAML or JSON stub files (--stubs,grpc.stubs) with request and metadata matchers, templates, delays and error statuses withgoogle.rpcdetails, or with messages generated from the response types. It honours deadlines and message limits, followsx-apigenie-mock-*metadata, applies scenarios (grpc_status,grpc_message) and scripts, serves server reflection (v1 and v1alpha) and health checks, proxies unanswered calls to an upstream server (--proxy-to) and records them as stub files (--record-dir). The admin API manages stubs and has the call history and averifyendpoint under/__apigenie/grpc. Imports are looked up in--proto-pathdirectories and among the embedded well-known,google/apiandgoogle/rpcfiles. -
apigenie-mockalso answers OpenAPI documents over cleartext HTTP/2 (prior knowledge). -
apigenie-mockserves GraphQL schemas (.graphql,.graphqls,.gql) at/graphql: GET, POST and batched requests per GraphQL over HTTP, validated against the schema with the rules and messages of graphql-js, executed with generated data (by type, field name and custom scalar name, or as graphql-faker’s@fake,@examplesand@listLengthand the mock’s@mockdirectives say), full introspection, GraphiQL and the SDL. Entities (types with anid) keep their values across requests, anduser(id:),users,usersConnection(Relay) andcreateUser,updateUser,deleteUser-like fields read and change them. Examples (graphql.examples, example files,<schema>_examples/) answer operations or fields, cut to the selection set and completed with generated values; scenario rules match operations and root fields and inject errors at field paths. Subscriptions are served over WebSocket (graphql-transport-wsand the legacygraphql-ws) and server-sent events. Configured undergraphql. -
apigenie-mockruns JavaScript hooks (--script,scripts):onRequestanswers requests,onResponsechanges responses and their simulated delays or drops,onMessagechanges or drops AsyncAPI messages,onGraphQLanswers GraphQL operations, with state kept between requests, logging, seeded random numbers, faker functions and scenarios. -
Linter validates documents against the OpenAPI schema, and more rules support autofixing, including the new
schema-no-optional-nullable-propertiesrule. -
diffcommand (openapi-diff) and the HTML documentation generator are available again. -
diffseverities are configurable per context:requestandresponserule overlays (callbacks and webhooks swap them),overridesfor single locations, conditions such asif: required, andadd_first_item/remove_last_itemrules. Schema properties are compared, respectingreadOnlyandwriteOnly. -
diffdetailed reports describe each change in all formats. -
diffcommand compares AsyncAPI 3.x documents: servers, channels, operations, messages and their payloads and headers, security schemes, bindings (property by property for HTTP, WebSockets, Kafka, AMQP and MQTT) and components, with the same reports, formatters, exit status and--update-versionas OpenAPI documents. The severity of a message change depends on whether the application sends or receives the message (sendandreceiverule overlays). Configured undercommands.diff.asyncapi. -
diffcommand compares Protobuf (proto3.proto) files: package, imports, options, messages, fields (matched by their numbers), oneofs, enums, services, methods, extend blocks, reserved numbers and names, and comments, with the same reports, formatters and exit status as OpenAPI documents. Type names are resolved before they are compared, and removing a field whose number the new file reserves is not breaking. Severities can depend onreserved,deprecatedandwire_compatibleconditions, and options are configured by their names. Configured undercommands.diff.protobuf. -
diffcommand compares Swagger 2.0 documents: host, base path, schemes, media types, paths, operations, parameters (matched by location and name, the body parameter by location), responses, headers, schemas (following references to definitions), security definitions and requirements, and tags, with the same reports, formatters, exit status and--update-versionas OpenAPI documents. Body parameters and consumed media types get therequestrules, responses and produced media types theresponseones, and definitions the stricter of the two. Configured undercommands.diff.swagger. -
diffcommand compares GraphQL schemas (.graphql,.graphqlsand.gqlfiles): root operation types, types of every kind, fields, arguments, input fields, default values, interfaces, union members, enum values, directive definitions, applied directives and descriptions, with the same reports, formatters and exit status as OpenAPI documents. Type extensions are merged into their types, and operations are ignored. Severities follow graphql-js’s breaking changes and can depend onrequired,compatible,deprecatedand boolean conditions; directives are configured by their names. Configured undercommands.diff.graphql. -
changelogcommand builds the changelog of an OpenAPI file from its git history: one entry per commit or per release tag, or the history of each operation (--by operation, filtered with--operation), as stylish text, Markdown, HTML or JSON. Files the specification references are followed in each version. -
changelogcommand builds the changelog of AsyncAPI 3.x files too: added, removed and changed operations, channels and servers, with the changes of a channel’s messages shown under the operations using it, rated with thecommands.diff.asyncapirules.--operationselects AsyncAPI operations by id, bysend/receiveand channel address, or by channel address. -
changelogcommand builds the changelog of Protobuf (.proto) files too: added, removed and changed methods (namedService.Method), messages, enums, services and extensions, package changes shown like version changes, rated with thecommands.diff.protobufrules. The file is compared on its own, without the files it imports.--by operationgroups the changes by method, and--operationselects methods by full name, method name or service name. -
Swagger 2.0 input support.
-
changelogcommand builds the changelog of Swagger 2.0 documents too: added, removed, deprecated and changed operations, with the changes of a path item shown under its operations, and the host, base path, schemes, media types and security of the API as metadata, rated with thecommands.diff.swaggerrules. Files the specification references are followed in each version. -
changelogcommand builds the changelog of GraphQL (.graphql,.graphqls,.gql) schema files too: added, removed and changed operations (the fields of the root operation types, named likeQuery.books), scalars, object types, interfaces, unions, enums, input objects and directive definitions, with changes of the root operation types, the schema description and the schema directives as metadata, rated with thecommands.diff.graphqlrules. The file is compared on its own.--by operationgroups the changes by operation, and--operationselects operations by full name, field name, root type name or operation type (query,mutation,subscription). -
bundlecommand bundles Swagger 2.0 documents and the local files they reference into a single file: referenced schemas, parameters and responses become entries ofdefinitions,parametersandresponses, and referenced path items take the content of their files. -
bundlecommand bundles AsyncAPI 3.x documents and the local files they reference into a single file. Root servers, channels and operations kept in other files take the content of those files, so that operations still refer to their channels and channel messages; other referenced objects become components. Schemas in other formats, such as Avro, are copied as they are, and references by URL are kept. -
bundlecommand bundles Protobuf (.proto) files: the input file and the messages, enums and extensions it uses from the files it imports (searched in the-I/--proto-pathdirectories) are combined into one proto3 file. Declarations from other packages are renamed with a prefix made from their package, and well-known type imports are kept. -
formatcommand formats AsyncAPI 3.x documents, configured undercommands.format.asyncapi. -
i18ncommand extracts the texts of AsyncAPI 3.x documents for translation: the titles, summaries and descriptions of servers, channels, operations, messages, schemas and the other objects, keyed by their JSON pointers. -
i18ncommand extracts the leading comments of Protobuf (.proto) files for translation, keyed by the fully qualified name of the message, field, oneof, enum, enum value, service or method they document; enum values are keyed under their enum, and the comment of the file (that of itssyntaxstatement) under the package name. -
i18ncommand extracts the descriptions of GraphQL (.graphql,.graphqls,.gql) files for translation, keyed by full name: the schema (schema), types, fields, arguments, input fields, enum values and directives by their schema coordinates (e.g.Query.search(text:),Genre.POETRY,@key), operations and their variables (e.g.query FindBooks,query FindBooks($text:)) and fragments (e.g.fragment BookSummary). -
formatcommand formats Swagger 2.0 documents, configured undercommands.format.swagger. -
formatcommand formats GraphQL schemas and operations (.graphql,.graphqls,.gql) in the layout of Prettier, keeping all comments, and optionally sorts definitions and the members of types. Configured undercommands.format.graphql. -
lintcommand lints AsyncAPI 3.x documents with 31 rules (ported from the OpenAPI ones where they apply, pluschannel-parameters-defined,channel-addresses-uniqueandserver-hosts), rulesets, custom rules and autofixes, configured undercommands.lint.asyncapi. -
lintcommand lints Swagger 2.0 documents with 46 rules (ported from the OpenAPI ones where they apply, plushost-defined,schemes-secure,media-types-definedandswagger-validation, which reports the specification violations found by the Swagger validator), rulesets, custom rules and autofixes, configured undercommands.lint.swagger. -
lintcommand lints Protobuf (proto3.proto) files with 39 rules modelled on buf’s: naming conventions, packages (versions, directories, consistent language options, import cycles between packages), imports (unresolved, unused, public, weak, cycles, order), enums, services and RPCs, comments and spell checking. Imports are looked up in--proto-pathdirectories, theimport_pathsof the configuration and the directories above the file, with the well-knowngoogle/protobuffiles built in.--fixremoves unused imports and sorts imports; an// apigenie-lint-disable <rules>comment disables rules for a declaration. Configured undercommands.lint.protobufwith therecommended(default),strictandallrulesets. -
lintcommand lints GraphQL schemas and operations (.graphql,.graphqls,.gql) with 87 rules modelled on graphql-eslint, graphql-schema-linter and Apollo’s schema linter: the type system and operation rules of the specification, naming conventions, descriptions, deprecations (reasons, deletion dates), schema design (root types, mutations and payloads, unreachable types, identifiers,@oneOf, circular input types), the Relay connection conventions, ordering, spelling, and operations checked against the schema (unknown fields, types and arguments, required arguments, deprecated fields, selection depth, identifier selections). Operations are checked against the files named by--graphql-schemaor by theschemaoption, or against the type system of their own document. A# apigenie-lint-disable <rules>comment disables rules for a definition. Configured undercommands.lint.graphqlwith therecommended(default),strict,allandrelayrulesets;--fixdoesn’t apply to GraphQL files. -
Minimal telemetry.
-
Linter rules reasoning about schemas:
schema-no-contradictions(constraints rejecting every value, and properties forbidden by anallOfmember withadditionalProperties: false),schema-oneof-no-overlap(oneOfbranches a single value matches, shown in the report),schema-required-properties-definedandschema-conditional-effective(if/then/elsewithout effect), all in therecommendedruleset, andpatch-request-body-partial(required properties inPATCHbodies) in thestrictruleset. -
Official release binaries for macOS (arm64), Linux (x64, static, runs on any distribution) and Windows (x64), plus
.deband.rpmpackages, a Homebrew formula, per-platform npm packages and a versioned Docker image. -
generate server expressandgenerate client fetchfollowdiscriminator.defaultMapping(OpenAPI 3.2;x-defaultMappingin 3.0 and 3.1). Express validates such aoneOfby the discriminating property: with Ajv as anif/then/elsechain that ends with the default schema, withvalidation: generatedby a dispatch reporting the same issues. A value whose property matches no mapping, or lacks it, is validated against the default schema instead of failingoneOf. The types of both targets get a type guard per member (isParcelShipment(value: Shipment)), the default member’s taking the values no mapping matches. The fetch client’s conformance snapshots cover OpenAPI 3.1 and 3.0 too.
Changed
-
The documentation smoke run of the codegen conformance suite is
generate docs(test_docs_bundle) instead ofgenerate doc html, and thee2e/generate_doc_htmlbenchmark cases aree2e/generate_docs. -
apigenie test importwrites the credentials it finds (bearer tokens, basic credentials, API keys, OAuth 2 client secrets and passwords) to a generatedhttp-client.private.env.jsoninstead ofhttp-client.env.json, which keeps the other variables and no secret;apigenie testreads both. Thesensitive-environmentwarning names the private file. -
The web components read AsyncAPI documents with the
asyncapiWebAssembly module, as they read GraphQL and Protobuf: setasyncapi-wasm-urlon<apg-spec>or<apg-api-reference>, or callconfigureAsyncApiEngine(url).npm run prerendertakes--asyncapi-wasm. There is no JavaScript reader for AsyncAPI any more; without the module, AsyncAPI documents fail to load with a message saying what to set. The views are unchanged.@mykulyak/apigenie-asyncapi-viewreads the module’s documentation model; itsparseandtraitsmodules are gone. Server-rendered pages embed the model, so hydration needs no module. -
The JSON contract reports of
apigenie-mock --report,GET /__apigenie/contractandapigenie test --reportgive each OpenAPI operation the JSONpointerof the operation in the document (e.g./paths/~1pets/get). -
Internal:
swagger_convertreturns a source map with each conversion, from the JSON pointers of the result to those of the Swagger 2.0 document. -
apigenie-mock --proxy-towith a.protofile logs request violations instead of failing the call with INVALID_ARGUMENT, as the OpenAPI, GraphQL and AsyncAPI proxies do;--strictorgrpc.validation.request: enforcerestores it, and a request is then checked before it reaches the upstream (an always-mode proxy forwarded it first). A mock without--proxy-tokeeps enforcing requests, and now also rejects a request that misses a field marked[(google.api.field_behavior) = REQUIRED]. A non-200 HTTP answer of the upstream ends the call with the status gRPC clients make of it (UNAVAILABLE for 503) instead of always UNAVAILABLE, and an upstream answer withoutgrpc-statusends as UNKNOWN instead of OK. -
--reportand--report-junitofapigenie-mockare accepted for.protofiles, and/__apigenie/contractanswers for them. -
The requester of
generate server aspnet’s Kafka code (AsyncAPI) waits for replies on the runtime’s newKafkaReplies, whichgenerate client csharpshares; its behaviour is unchanged. The OpenAPI configurations ofgenerate client csharpandgenerate server aspnetignore theirasyncapisection instead of rejecting it as an unknown option. -
generate server aspnet(OpenAPI) writes the recursive DataAnnotations validation ofValidationFilterasInfrastructure/ModelValidator.cs, which the AsyncAPI Kafka application uses for its messages too; the behavior is unchanged. -
apigenieandapigenie-mocklink Mbed TLS 3.6 (Apache-2.0), the first runtime library from vcpkg;creditsandlicense --library mbedtlslist it. The release binaries grow by about 0.9 MB (macOS arm64:apigenie+3.5%,apigenie-mock+8.2%). The Docker images now include Alpine’s CA certificates. -
apigenie-mock --proxy-tono longer refuseshttps://andwss://targets with exit status 2, and the 426 answer to gRPC over HTTP/1.1 on a TLS port says to ask forh2by ALPN instead of offeringUpgrade: h2c. -
generate server springis built onopenapi_generator_java, the Java layer it shares withgenerate client java, whose Feign and HTTP interface writers now generate itsspring-cloudandspring-http-interfacelibraries; their output is unchanged. Models getdefaultImplfromdiscriminator.defaultMapping(x-defaultMappingbefore 3.2), name implicit discriminator mappings after theconstvalue of the members (personinstead ofPerson), no longer require the properties ofanyOf/oneOfalternatives (Person and Company required bothemailandphoneof ContactChannels), and treat anallOfwith only a reference to aoneOfinterface as that interface (3.0 documents produced a class extending an interface). -
generate server go: ananyOforoneOfwhose members only list required properties no longer makes a union of untyped values, soallOfschemas holding one are merged into structs (instead ofany) andValidate()checks the alternatives; a discriminator’sdefaultMapping(x-defaultMappingin 3.0 and 3.1) decodes unknown values as its fallback variant, and an implicit mapping also accepts theconstthe variant gives the property. The runtime’s request binders moved fromruntime_params.gen.goto a newruntime_bind.gen.go. -
The OpenAPI library reads a media type’s
itemSchemaand a discriminator’sdefaultMapping(3.2), and theirx-itemSchemaandx-defaultMappingforms in 3.0 and 3.1 documents. -
generate server aspnetandgenerate client csharpshare their C# models (src/openapi_generator_csharp). The server’s output changes in two ways: the implicit discriminator value of aoneOfvariant is theconstof its discriminator property when it has one (e.g."person"instead of the schema name"Person"), and aoneOforanyOfwhose members map to one C# type has that type (e.g.List<string>for strings of theipv4andipv6formats, instead ofList<object>). -
The contract report of
apigenie-mock(gate, JSON and JUnit) moved into themockandopenapi_mocklibraries, shared withapigenie test; its output is unchanged. -
formatkeeps the YAML comments of OpenAPI, Swagger and AsyncAPI documents (at the top and the end of the file, above keys, at the end of lines, inside sequences and between entries); comments move with their keys when properties are sorted. Before, every comment was dropped, andformat --checkreported commented files that were formatted. -
lint --fixkeeps the YAML comments of the OpenAPI, Swagger and AsyncAPI documents it rewrites, including those of the properties a fix replaces, instead of dropping them all. -
formatwrites a Protobuf comment that follows a declaration on the next line (and that protoc attaches to it) on its own line again, instead of moving it to the end of the declaration’s line. -
i18nhas two subcommands:i18n extractextracts texts, asi18ndid, andi18n applyapplies translations.apigenie i18n FILE --output DESTbecomesapigenie i18n extract FILE --output DEST, which also accepts-o. -
apigenie-mock --stubswith a GraphQL schema or an AsyncAPI document exits with status 2 instead of being ignored; stubs answer the calls of a.protofile and, now, the operations of an OpenAPI document. -
lintexits with status 1 when it reports issues of severityerror(those remaining after--fix), so CI jobs fail on them; warnings keep the exit status at 0. -
The CLI binary is about 2.8 MB (14%) smaller: the test-data generator’s string tables (names, addresses, words and the like) are stored compressed and decoded when first used, instead of as pointer tables.
-
The built-in English spelling dictionary is stored compressed (about 290 KB less in the binary) and decoded only when the first spell-check rule runs; the decoded bytes are released once the dictionary is parsed.
-
The third-party license texts shown by
license --libraryare stored compressed and decoded only when one is printed. -
formatreports files that are neither OpenAPI nor AsyncAPI documents and leaves them untouched, instead of writing empty output. -
Parse errors in JSON and YAML input now report why and where the input cannot be parsed.
-
lintwith several input files writes a single report for thejson,checkstyle,junit,html,sarifandtapformatters, instead of one document per file. -
diffexit status and--update-versionconsider all changes, whatever the report shows, and a fixed severity of a node caps the severities of changes inside it (e.g. adding an optional parameter is non-breaking). -
diff --update-versionchanges only the version in the file, keeping its formatting. -
diffconfiguration is validated: unknown node types, properties and severities are reported. -
generate server springbuilds Java sources with a syntax tree instead of text templates. The generated code is the same, but imports are sorted and blank lines and indentation are consistent. -
unused-shared-componentis enabled again in therecommended(warning),strictandallrulesets, and skips documents without operations, which are libraries of components for other documents. -
schema-no-optional-nullable-propertiesrecognizes OpenAPI 3.0nullable: true, referenced nullable schemas andanyOf/oneOfnull branches, findsrequiredinallOfmembers, and skipsPATCHand merge-patch bodies, wherenullclears a value (optionignorePatchBodies). -
spell-check(OpenAPI and AsyncAPI) now checks spelling: it reports each misspelled word of titles, summaries and descriptions with suggested corrections, using a built-in English dictionary, and skips code, URLs, identifiers and acronyms. Optionswords(extra words),suggestionsanddictionary/affix(any Hunspell dictionary) are optional. The rule moved from therecommendedruleset tostrictandall. -
Internal: the Swagger 2.0 to OpenAPI 3.2 conversion behind
convert --to openapi32moved from the CLI into its own library,swagger_convert, which also reports what each conversion approximates or drops (not shown by the CLI yet). The output ofconvertis unchanged. -
Internal: a release gate (
release_gate.yaml) runs every test on Linux, Windows and macOS, the tests that build generated code with Java, Go, .NET and Python, the RealWorld demo, codegen conformance Levels 2 and 3 and the WebAssembly build; acli-*tag only gets its draft release once the gate passed on the tagged commit.
Removed
generate doc html, the Astro documentation project it generated and the@mykulyak/api-genie-doc-html-templatepackage it depended on, with thecommands.generate.doc_htmlconfiguration section (see Upgrading). Internal:openapi_generator_doc_htmlandcode_morph_astro.
Fixed
apigenie-mock --licensewithout a library name prints API Genie’s license instead of the usage: an option whose value is optional counted as given only with a value.generate server springreads request bodies typed by a oneOf without discriminator (JSON Patch operations, Carpet Express’s recursiveSearchFilter): such interfaces had no type information, so Jackson couldn’t instantiate them and the request answered 500. The interface now names aDeserializerbuilt on the generatedOneOfDeserializer, which reads the one member the value matches: a member that declares all its properties (or takes additional properties), reads it and, with Bean Validation, is valid. A value matching no member, or several, is a 400.useDeductionForOneOfInterfaces=truekeeps Jackson’s deduction. Strings withformat: uri(java.net.URI) no longer get@Patternor@Sizeingenerate server springandgenerate client java: Bean Validation can’t check them on a URI and threwUnexpectedTypeException(the server’screateWebhookSubscriptionanswered 500); their pattern isn’t checked.- The Pydantic models of
generate client python,generate server faststreamand the Python Kafka client checkpattern:Annotated[str, matching(r"...")], a validator of_models.pycompiling the pattern with Python’srewhen a value is first checked (Pydantic’s default engine rejects look-arounds, back-references and large\wrepetitions, which would break the model). Named string schemas keep their constraints in their aliases, e.g.ShipmentId: TypeAlias = Annotated[str, matching(r"^shp_[0-9A-Za-z]{24}$")]; a patternrecan’t compile, such as one using\p{L}, isn’t checked. generate server faststream: published messages and replies no longer carrynullfor optional fields that weren’t set, which their schemas don’t allow. The publish functions and the subscribers’ replies send payloads through the runtime’s newencode_message()(the fields that were set), instead of letting FastStream serialize the models with every field. Subscribers of operations with a static reply channel now return aKafkaResponse,RabbitResponseorMQTTResponse, and their reply publisher states the reply’s schema (schema=).- The code of
generate client wsandgenerate server wspasses typescript-eslint’s recommended type-checked rules: options and handler interfaces that would be empty are type aliases, the client runtime’sWebSocketLike.addEventListenerhas one overload per event instead of ananylistener, and the shared AsyncAPI type mapper no longer writesunknowninto unions ([key: string]: unknown) or intersections (ananyOfofrequiredlists). - The validators of
generate server express(validation: functions) importpointerTokenonly when a generated function uses it, so object schemas withadditionalProperties: trueno longer leave an unused import. - Generated values (
apigenie-mockresponses and messages, examples) satisfy an object schema’soneOf/anyOfnext to itsproperties, such asanyOf: [{required: [phone]}, {required: [email]}]: one alternative is picked and what it requires is generated. The alternatives used to be ignored, so such objects could lack every property they ask for. apigenie-mock: the problems of anx-apigenie-mockextension name it by a well-formed JSON pointer (/paths/~1pets/get/x-apigenie-mock, not/paths//pets/get/...), and by its line when the value has no position of its own.apigenie-mockgenerates AsyncAPI messages whose schemas use AsyncAPI’s stringdiscriminatoror anexternalDocsgiven by$ref: such documents made every message without an example fail with “the payload has no schema values can be generated from”. A reply whose message has itscorrelationIdin the payload now carries the request’s value (Carpet Express’srequestPickupgets apickupConfirmedwith itsrequestId).apigenie-mock --proxy-to ws://checks the server’s reply to a client message of a request/reply operation against the reply’s messages and itscorrelationId(message.reply.uncorrelated), instead of reporting it aschannel.send.undeclaredon a channel withoutsendoperations (ADR-110).generate server faststreamno longer fails with “The message schemas can’t be mapped to Python types” when a payload schema’sexternalDocsis a$ref, as the AsyncAPI fixture’sCloudEventhas: the reference is followed.- The AsyncAPI contract report counts the requests of HTTP channels, mocked or proxied, as messages of their operations.
generate client csharp: the generated project compiles again: the client of the API is registered withAddHttpClient<TClient, TClient>(factory), asAddHttpClient<TClient>has no factory overload and bound the lambda to its configuration callback (CS1503). Unions read by the generated discriminator converters no longer carry[JsonPolymorphic]/[JsonDerivedType], which System.Text.Json rejects next to a custom converter (“does not support metadata writes or reads”) when sending or reading aoneOfwith a discriminator.generate client csharpandgenerate server aspnet: properties with a default value are settable instead of init-only, because the System.Text.Json source generator of .NET 8 assigns every init-only property and replaced the default of an absent one with0/false/null; decimals described as strings (type: string, format: decimal) are written as JSON strings.generate client python: form body fields whoseencodingsetsstyleorexplodeare written like query parameters of that style (deepObject:hints[building]=rear) instead of as JSON text. The package passes ruff’s default rules:__all__lists are sorted as RUF022 expects, and the runtime usesX | Yunions andSelf.generate server expresswithvalidation: generated: objects without declared properties (maps), or with onlypatternProperties, no longer get validators with constant conditions (if (!(false)),false || ...), which failed ESLint’sno-constant-condition. The codegen conformance suite’s Level 2 now also type-checks and lints avalidation: generatedbuild.generate client fetch: properties ofapplication/x-www-form-urlencodedbodies whoseencodingsetsstyleorexplodeare written like query parameters of that style (deepObject:hints%5Bbuilding%5D=rear, alsopipeDelimited,spaceDelimitedand unexplodedform) instead of as JSON text; the other object properties stay JSON. The runtime no longer callsString()on values of unknown type, so it passes typescript-eslint’sno-base-to-string.generate server expressroutesQUERYandCOPYoperations (OpenAPI 3.2): their routes were typed with a method the runtime’sHttpMethodtype lacked, so the generated package didn’t compile. An empty form field of a nullable property (location=foroneOf: [GeoPoint, {type: null}]) is read as null instead of failing validation, and the runtime passes typescript-eslint’s recommended type-checked rules.generate client fetch,generate server expressandgenerate server orpc:anyOf/oneOfalternatives that only add constraints (anyOf: [{required: [email]}, {required: [phone]}]) no longer add& unknownto the type.generate server aspnet: an empty form field for an object (location=, how form style writes an empty or null value) is read as null instead of failing to parse as JSON with 400.generate server spring: a@lombok.*annotation in a model’sx-class-extra-annotationdrops the members Lombok generates for that model and adds Lombok to the pom, asadditionalModelTypeAnnotationsdoes; before, the model kept its ownequals/hashCodenext to@lombok.EqualsAndHashCodeand the project didn’t build without Lombok. Deprecated parameters no longer get@Deprecated, which has no effect on parameters (javac -Xlintwarns since JDK 21); the deprecation stays in@Parameter(deprecated = true).generate client pythonandgenerate server faststream:allOfparts withoneOf/anyOfalternatives that only add constraints, such as Carpet Express’sContactChannels(an email or a phone), are merged like other object parts, soPersonandCompanyare models instead ofAny. Discriminated unions dispatch on the mapping, on the schema names of the members it leaves out (the implicit mapping) and on the value each of those members fixes the property to withconstor a singleenumvalue (partyType: personof a schema namedPerson). Values whose tag is missing or unknown are read as thediscriminator.defaultMappingschema (x-defaultMappingin OpenAPI 3.0 and 3.1), which joins the union if it isn’t a member; before, they matched no model. The unions keep their callablepydantic.Discriminator, asField(discriminator=...)can neither fall back nor take several tags per model. The codegen conformance suite checksgenerate client pythonagainst the OpenAPI 3.1 and 3.0 fixtures too.generate server fastapipasses the codegen conformance suite’s mypy, ruff and runtime checks without known failures, on the OpenAPI 3.2, 3.1 and 3.0 fixtures:- Discriminator tags follow the Python models: a member that fixes the property with
constor a singleenumvalue gets that value (party_type: Literal["person"]for a schema namedPerson), where the implicit mapping made itLiteral["Person"]and rejected valid parties. A union with adiscriminator.defaultMapping(x-defaultMappingin OpenAPI 3.0 and 3.1) dispatches with a callableDiscriminator()that reads values with a missing or unknown tag as the default schema, which joins the union if it isn’t a member. RootModelsubclasses have their type argument (RootModel[int | str]; a forward reference in a cycle), whichmypy --strictasked for.- A request body with several JSON media types, such as
application/merge-patch+jsonandapplication/json-patch+json, is validated against the schema of the request’sContent-Type(415 for another one); the route took only the first media type’s schema. The body parameter is the union of the schemas and the served OpenAPI document lists every media type. - Integer enums in parameters and form fields convert the request’s text (
dpi=300);Literal[203, 300, 600]rejected every value. - Multipart file parts described with
contentMediaTypeorcontentEncoding: binary(OpenAPI 3.1 and 3.2), not onlyformat: binary, areUploadFiles, and arrays of themlist[UploadFile]. - Imports are sorted the way Ruff’s isort rules expect (case-insensitive, numbers by value, long single-name imports
wrapped), and the imports at the end of modules of a cycle no longer carry a
# noqa: E402that Ruff reports as unused.
- Discriminator tags follow the Python models: a member that fixes the property with
apigenie test --generatesends multipart file parts (format: binary,contentMediaTypeorcontentEncoding: binary) with a file name and the Content-Type of their encoding, and an array as a part per item; servers that only take parts with a file name as files, such as FastAPI’s and ASP.NET Core’s, rejected the generated requests.generate client pythonandgenerate server faststream: an object schema with properties of its own andoneOforanyOfalternatives that are objects (each narrowing a property or requiring others) becomes a union of one model per alternative with the object’s properties merged in. The properties were lost (a union of classes holding only the alternatives’ properties), andanyOfofrequiredlists, such as “an email or a phone”, wasAny.- The CLI no longer writes
.apigenie.telemetryin the current directory; setAPIGENIE_TELEMETRY_FILEto keep a local telemetry log. apigenie-mockandapigenie testtype the fields of form and multipart bodies (and deepObject and delimited parameters) whose properties come fromallOfmembers, references included:residential=falseof a bodyallOf: [$ref Address, {...}]is the booleanfalse, not the string. They were validated as strings, so valid requests were reported or rejected with 422.- YAML output quotes strings that YAML 1.1 parsers (PyYAML, SnakeYAML, go-yaml v2) read as booleans, numbers or
merge keys:
yes,No,ON,off,y,N,0b101,1_000,1:30,<<and the like. Before,bundle,overlay,lint --fix,convertandformatwith thebest-fitstyle wrote them plain, so other tools readenum: [yes, no]as booleans and1:30as 90. apigenie-mockvalidates requests and responses whose schemas refer to themselves (trees, nested filters, recursiveoneOfs). They were accepted without any check, in OpenAPI and AsyncAPI documents alike. The lint rulesschema-no-contradictionsandschema-oneof-no-overlapnow analyze such schemas too.- OpenAPI 3.2 documents: examples written with
dataValueand media types that refer tocomponents/mediaTypesare read. Generators no longer drop them (Spring rendered such examples asnull, Go typed such problem responses asany), andapigenie-mockanswers with those examples. generate server fastapiroutes the OpenAPI 3.2querymethod and the methods ofadditionalOperations(such asCOPY) with@router.api_route(..., methods=[...]). They were emitted as@router.query/@router.copy, whichAPIRouterdoesn’t have, so a server generated from a document with such an operation failed to import.convert --to openapi32converts the structure of Swagger 2.0 documents, not only their references: body andformDataparameters become request bodies with the media types ofconsumes(files asmultipart/form-data),collectionFormatbecomesstyleandexplode, response schemas, headers and examples move intocontentper media type ofproduces, global body parameters becomecomponents.requestBodies, and references to parameters and responses point intocomponents. Schemas gettype: fileas binary strings, object discriminators, numeric exclusive bounds andx-nullableas anulltype. Before, most converted documents could not be read (2163 of the 2168 Swagger documents of the openapi-directory corpus convert into readable documents now, the others are invalid).- Many memory-safety and robustness issues in the parsers, bundler, reader and linter.
diffchangelog reports rate added, removed and modified operations by their own changes rather than their path’s, report request body changes as such instead of as response codes, and no longer list a removed deprecated operation as deprecated.generate server springnumbers enum constants whose names collide (e.g. the values+1and-1, both named_1), like openapi-generator, so the generated models compile.testreports a rejected certificate as such forwss://,mqtts://andgrpcs://URLs whose host name has several addresses: it stops at the first address that answers instead of trying the others and reporting the last one’s “Connection refused” (on macOS,localhostis 127.0.0.1 and ::1, and a server often listens on one).- The YAML and JSON writers report an alias of a collection that contains it as soon as they meet it again, instead of after expanding it to the depth limit, which overflowed the JavaScript stack of the WebAssembly build.
generate server springwithuseSpringBoot4no longer declaresspring-boot-starter-restclient(or-webclient) twice, which Maven rejects as a duplicate dependency.
0.8.2 (2025-11-13)
Pre-release, published on GitHub.
0.8.0 (2025-07-12)
Pre-release, published on GitHub.