Skip to content

Publish a schema

Publishing is the privileged half. It has a separate API and a separate HTTP handler from reading, so that whoever wires authentication cannot give one the other's policy by accident.

In Go

reg := schema.New(schema.NewMemoryStore(),
    jsonschema.Checker{},
    protobuf.Checker{},
)

err := reg.Publish(ctx, schema.Schema{
    URN:      urn.Schema{Name: "orders.created", Version: 3},
    Language: schema.JSONSchema,
    Bytes:    document,
}, schema.CompatibilityNone)

Over HTTP

$ curl -X PUT https://schema.internal.example/publish/schemas/orders.created/3 \
    -H 'X-Schema-Language: jsonschema' \
    --data-binary @schemas/orders.created.v3.json

For a protobuf descriptor set, add the root message:

$ curl -X PUT https://schema.internal.example/publish/schemas/orders.shipped/1 \
    -H 'X-Schema-Language: protobuf' \
    -H 'X-Schema-Root: uk.phpboyscout.OrderShipped' \
    --data-binary @schemas/orders.shipped.v1.binpb

A descriptor set holds every message the target file depends on and nothing in it says which one a payload is, so the root is required rather than inferred.

The compatibility argument

reg.Publish(ctx, s, schema.CompatibilityNone)      // accepted
reg.Publish(ctx, s, schema.CompatibilityBackward)  // ErrUnsupportedCompatibility

Nothing checks compatibility yet, so asking for a guarantee gets a refusal rather than silence. Over HTTP that is ?compatibility=backward and a 400.

The argument exists from the first release so that no call site changes signature when a checker arrives. See The compatibility-shaped gap.

Republishing

What you publish What happens
The same identifier, identical bytes Accepted. A deployment that registers on every start must be idempotent
The same identifier, different bytes ErrImmutable409 Conflict over HTTP
The same identifier, different language or root ErrImmutable. Same bytes with a different meaning is still a different schema

To change a schema, publish the next version. That is what versions are for, and the version is an integer precisely so it claims nothing about compatibility.

The version in the URL must be canonical: /3, never /03 or /+1. Two spellings of one version would be two identifiers for one schema, and immutability is stated in terms of the identifier.

From CI

Publishing from a pipeline is the usual arrangement, and it makes the idempotency above load bearing: a re-run of the same pipeline must be a no-op rather than a conflict.

publish-schemas:
  script:
    - |
      for f in schemas/*.json; do
        name=$(basename "$f" .json | cut -d. -f1-2)
        version=$(basename "$f" .json | sed 's/.*\.v//')
        curl --fail -X PUT "$REGISTRY/publish/schemas/$name/$version" \
          -H "X-Schema-Language: jsonschema" \
          -H "Authorization: Bearer $REGISTRY_TOKEN" \
          --data-binary "@$f"
      done

--fail matters. Without it a 409 is a silent success, and the pipeline reports that it published a schema it did not.