Several services

One apigenie-server serves several documents at once: an application’s backends, a gateway’s services, the HTTP and the messaging side of a system. Each document is a service with a name and a mount, where its clients reach it. Several SPECs on the command line are services named after their files (pets.openapi.yaml is pets) and mounted at /<name>; a .proto file gets a port of its own. A single SPEC is served as before, without a prefix. The configuration’s services list says more:

version: "1.0"
server: {port: 4010}
validation: {request: enforce}   # the services' defaults
services:
  - name: pets
    spec: pets.yaml               # mounted at /pets
  - name: orders
    spec: orders.yaml
    mount: /shop/orders
    proxy: {target: "http://localhost:8080", mode: fallback, record_har: session.har}
    scenarios:
      outage: {rules: [{status: 503}]}
  - name: events
    spec: events.asyncapi.yaml
    mount: {host: events.localhost}
  - name: greeter
    spec: greeter.proto
    mount: {port: 50051}
  • What a service sets: name and spec (required), mount, and the settings of its document: proxy, stubs, scenarios, overrides, validation, security, operations, responses, generation, headers, asyncapi, graphql and grpc. These keys at the top of the file are every service’s defaults; a service’s stubs, scenarios, overrides and proxy replace the defaults rather than adding to them. server, cors, simulation, tls, admin, log, contract, scripts, db, request_overrides and asyncapi.mqtt_port are the server’s.
  • Mounts. A path prefix (/shop/orders, default /<name>) on the server’s port: the service gets the requests below it, without the prefix, so its own base path and routes are unchanged. {host: events.localhost} takes the requests whose Host header names that host (any port), and may add a prefix. {port: N} is a listener of the service’s own (0 picks a free one), which a .proto file’s service needs: gRPC clients can’t add a prefix. Requests the server’s port gets go to the services by host first, then to the one with the longest prefix (prefixes end at a /); the others get 404 no-service, which lists the mounts. Two services can’t share a name, nor can one’s mount hide another’s (/shop and /shop/orders), nor can a mount cover the admin API.
  • Proxies forward the path without the mount’s prefix, after their own (--proxy-to’s path). --proxy-to, --stubs, --record-dir and --record-har name one document’s upstream and recordings, so with several services they are refused: set them per service.
  • WebSocket: an AsyncAPI service’s channels and a GraphQL service’s subscriptions share the server’s port, below the service’s mount (ws://127.0.0.1:4010/events/v1/rooms/1).
  • MQTT: one broker for every service (--mqtt-port). Topics are served as the documents name them, unless the MQTT channels of two services can match the same topic (doors/+/open in both, or status and #): then the topics of each of those services start with its name (front/doors/7/open). The startup line shows the prefix (MQTT topics under front/). An mqtt:// upstream goes with one document.
  • The log names the service of each event ("service": "orders" in JSON, [orders] in the stylish log); event paths are within the service, without its prefix. Ids are unique across the services.
  • The admin API (/__apigenie) covers every service: GET /__apigenie/services lists them with their mounts and URLs, the items of /routes (full paths), /scenarios, /overrides, /log and the status’s documents name their service, and ?service=NAME narrows any endpoint to one service. An endpoint of one service’s resources (a scenario of two services, PUT /overrides, /state, /db, /spec) needs it when more than one service could be meant, and answers 400 admin-service-required otherwise; a service’s own endpoints (the gRPC ones, /contract/violations) answer 404 without it. PUT /simulation, POST /seed and POST /reload apply to every service unless one is named.
  • The contract report (--report, GET /__apigenie/contract) has a section per service in sections, each a whole report of its document with its name and mount, and a summary that counts them together: requests, violations and coverage.combined, the units of every service added up (OpenAPI branches, GraphQL fields, gRPC methods, AsyncAPI messages). --fail-on and --min-coverage apply to that summary: a violation of any service fails the run, and the combined coverage must reach the threshold (the reason names the service with the lowest). The JUnit report has a suite per service.
  • The HAR file of the proxies is one per server: every service that records names the same file, and each entry’s _apigenie.service names its service; URLs keep the mount’s prefix.
  • Reloads (--watch): a service reloads when its document, stubs or scripts change; a change of the configuration reloads every service. Adding or removing a service needs a restart.
Was this helpful?