Openapi-specification: Support for multiple $refs inside a single object

Created on 17 Jul 2015  Â·  34Comments  Â·  Source: OAI/OpenAPI-Specification

It would be really useful to be able to do something like this:

paths:
    $ref: "/operations/pets.yaml"       # Multiple paths
    $ref: "/operations/stores.yaml"     # Multiple paths
    $ref: "/operations/users.yaml"      # Multiple paths

Where each file includes multiple paths. For example (pets.yaml):

/pets:
  post:
    # ...
/pets/findByStatus:
  get:
    # ...

This would make it easier to organize a large API.

Edit:

It could also be an array, like this:

paths:
    $ref:
        - "/operations/pets.yaml"
        - "/operations/stores.yaml"
        - "/operations/users.yaml"
$ref

Most helpful comment

:+1: for this appraoch:

paths:
    $ref:
        - "/operations/pets.yaml"
        - "/operations/stores.yaml"
        - "/operations/users.yaml"

It's simple and easy to understand.

@IvanGoncharov: I could think of a couple ways to resolve conflicts. Apologies if I'm missing something large here, but it seems like you could do something like this:

  • If the references are both objects, the later values for the keys overwrite the earlier values for the keys. This would work the same way Object.assign() overwrites values.
  • If they're both arrays, they're concatenated.
  • Otherwise, the second reference overwrites the first (strings, booleans, etc.).

All 34 comments

Swagger spec, define '$ref' as follows:
The Reference Object is a JSON Reference that uses a JSON Pointer as its value.

References used through entire spec and most importantly inside Schema object.
What mean if $ref behaviour will change it will totally ruin compatibility with JSON Schema and JSON Reference and all tooling which being build around them.

Another problem is how you would handle merge conflicts?
For example /operations/pets.yaml and /operations/stores.yaml both have same key inside.
Or one contain an object and another an array.

But I totally support your idea about allowing to split paths into separate files.
Instead of reinventing the wheel, we could use RAML approach to this problem. And make paths recursive structure, so your example will look like this:

paths:
  /pets:
    $ref: "/operations/pets.yaml"       # Multiple paths
  /stores:
    $ref: "/operations/stores.yaml"     # Multiple paths
  /users:
    $ref: "/operations/users.yaml"      # Multiple paths

And 'pets.yaml' will looks like this:

post:
  # ...
/findByStatus:
  get:
    # ...

@glen-84 Is this proposal will solve your problem?

P.S. It could also solve path parameters duplication problem.

It's quite surprising to me that the existing standards don't support this. I don't suppose this is likely to change?

As for merge conflicts, if I create two equal paths with the Swagger editor, it just uses the last one. It could be the same, or it could be a setting if you want it to throw an error.

Nested paths might be an option, although it feels a bit weird having the nested paths at the same level as post/get etc.

Could allOf be used at all?

paths:
    allOf:
        - $ref: "/operations/pets.yaml"
        - $ref: "/operations/stores.yaml"
        - /users:
            get:
                # ...

... that may be completely incorrect or make no sense – I don't know very much about JSON schema. :smile:

I don't suppose this is likely to change?

In any case it should be changes to JSON Pointer spec.

Could allOf be used at all?

No, 'allOf' supported only inside Schema Object.
Swagger is very strict format and you can see all supported fields here

+1 for adding allof to paths so multiple paths/APIs can be referenced

+1 I am looking for any work around right now for allowing path urls and their subsequent path objects to be defined in multiple external files. Does anyone have any suggestions?

@wannabesrevenge I'm currently using something like this:

paths:
    # Blogs
    # -- Blogs
    /blogs:
        $ref: "operations/blogs/blogs.yaml#/Blogs"
    /blogs/{id}:
        $ref: "operations/blogs/blogs.yaml#/Blog"
    # -- Blog Posts
    /blogs/{id}/posts:
        $ref: "operations/blogs/blog-posts.yaml#/BlogPosts"
    /blogs/posts/{id}:
        $ref: "operations/blogs/blog-posts.yaml#/BlogPost"
    /blogs/posts/{id}/comments:
        $ref: "operations/blogs/blog-posts.yaml#/BlogPostComments"

I don't think it's possible to move the actual paths to separate files.

That's a shame. I'm hoping that "merge" in JSON schema v5 (i think it's v5) will help. I settled for doing this the same way as you. In one way it's nice because its explicit, but it is definitely not completely DRY in principle. Now if we add a route to one microservice api, we must also add the route to the master api.

+1 for supporting recursive structure for paths

+1 need this

+1

:+1: for this appraoch:

paths:
    $ref:
        - "/operations/pets.yaml"
        - "/operations/stores.yaml"
        - "/operations/users.yaml"

It's simple and easy to understand.

@IvanGoncharov: I could think of a couple ways to resolve conflicts. Apologies if I'm missing something large here, but it seems like you could do something like this:

  • If the references are both objects, the later values for the keys overwrite the earlier values for the keys. This would work the same way Object.assign() overwrites values.
  • If they're both arrays, they're concatenated.
  • Otherwise, the second reference overwrites the first (strings, booleans, etc.).

@LandonSchropp I agree. Would pleasantly mimic familiar Object.assign() behavior here.

Merging objects with same properties

var o1 = { a: 1, b: 1, c: 1 };
var o2 = { b: 2, c: 2 };
var o3 = { c: 3 };

var obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }

The properties are overwritten by other objects that have the same properties later in the parameters order.

Folks, I'm afraid you are pitching to the wrong people. OpenAPI uses $ref as it is defined by JSON Schema. If you want $ref 's behavior to be changed, you need to convince the JSON Schema folks to change it. The alternative is to define a completely new re-use mechanism so there is no confusion between reuse within JSON Schemas and reuse elsewhere.

FWIW, JSON References (and JSON Pointers) are actually not part of JSON Schema, but JSON Schema uses them. They were both originally written by the same people (iirc), but that doesn't mean the current JSON Schema folks actually maintain JSON Reference as well, but we'll see their response.

@webron You are correct that there is a distinct spec for JSON Reference, but for some reason the wording describing $ref was duplicated in the JSON Schema spec https://tools.ietf.org/html/draft-wright-json-schema-00#section-7 which leaves us in an ambiguous state. Considering there are people actually working on the JSON Schema now, it might actually be the best place to get change to happen.

@darrelmiller - right, only you're not looking at draft 4 which is our official reference for the spec, where JSON References explicitly point to JSON Reference.

Of course, hopefully by the time official word on the next draft/version of JSON Schema is out, it will be clarified better than what you've shown. And who knows, maybe we'll drop JSON Schema and move to something... better.

@webron there is nothing better, there are _no known valid problems_ with JSON schema 😆

@glen-84 can you explain how your abovementioned solution works?

paths:
# Blogs
# -- Blogs
/blogs:
$ref: "operations/blogs/blogs.yaml#/Blogs"
/blogs/{id}:
$ref: "operations/blogs/blogs.yaml#/Blog"
# -- Blog Posts
/blogs/{id}/posts:
$ref: "operations/blogs/blog-posts.yaml#/BlogPosts"
/blogs/posts/{id}:
$ref: "operations/blogs/blog-posts.yaml#/BlogPost"
/blogs/posts/{id}/comments:
$ref: "operations/blogs/blog-posts.yaml#/BlogPostComments"

when i try to replicate it, it always inserts "Blogs" node below the /blogs
i end up with

paths:
    # Blogs
    /blogs:
        Blogs:  #this shouldnt be here. How to get rid of it?
           get: ....

@alexander-larchenko,

Are you using the #/Blogs reference?

The target file should be structured as follows:

Blogs:
    get:
        summary: Get a list of blogs
        tags:
            - Blogs
        responses:
            etc.

I'm using swagger-parser (v3.4.1) to dereference the file.

@glen-84,

Are you using the #/Blogs reference?

Yes, I tried to replicate your example fully, just to see how it goes.
But I'm using Swagger CLI for bundling and it provides a different result for this way of referencing. That's why you have swagger-parser to handle those references correctly. Right?

That's why you have swagger-parser to handle those references correctly. Right?

Most likely, yes.

I may have started using swagger-parser because of https://github.com/apigee-127/swagger-tools/issues/227.

After reading through the entire conversation it looks like we do not have any way to do something similar to this or any variation similar to the below

paths:

  • $ref: yaml1.yml
  • $ref: yaml2.yml

Please correct me if I am wrong. How are other folks serving a single yml file which serves multiple paths spread across children ymls?

@rayarikar in my case I'm using the discriminator object; for merging values the one I'm using is allOf:

~yaml
components:
schemas:
First:
type: object
items:
first:
type: string
Second:
type: object
items:
second:
type: string
paths:
/:
post:
requestBody:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/First'
- $ref: '#/components/schemas/Second'
~

You mean oneOf? allOf would probably make it not match anything?

allOf will take all the definitions. I want all the definitions to be merged, not just one of the specified definitions.

Would YAML’s own pointers solve your issue? I’m not sure how merging happens though but that could be a solution usable today

Who are you asking to? BTW, I don't think that using a file format specific solution is a good idea here when OpenAPI spec allows other file formats (like json).

This does it https://github.com/WindomZ/swagger-merger . Would love to see this type of support in the official tooling

example:

paths:
  /path1:
    $ref: https://someurl/openapi.yaml
  /path2:
    $ref: https://someurl2/openapi.json

@chrisdostert The example you are showing is actually valid OpenAPI. This is a exceptional usage of $ref in the PathItemObject where $ref is a fixed field. It doesn't behave the same way as the other reference objects in an OpenAPI document.

However, this only allows referencing a single path at a time. There is no way to reference a document that contains a set of paths.

@glen-84 I suggest building a file using command line toolkit that links multiple YAML files with $include tag. Look this https://github.com/javanile/yamlinc add-in project task using gulp or grunt. Any considerations @IvanGoncharov @kulkarniankita @wannabesrevenge @BabuGiri

This is working fine for me

 "requestBody": {
      "required": True,
      "content": {
        "application/json": {
          "schema": {
            "allOf": [{
              "$ref": "#/definitions/First"
            }, {
              "$ref": "#/definitions/Second"
            }]
          }
        }
      }
    }

@waseem-khan-dev yes, that works fine in the Schema Object, but this was reported against the Path Object where allOf is not available.

@webron @MikeRalphson @whitlockjc this is another Path Object $ref issue, but a rather specific one. I don't recall any drive to try to have a multi-$ref feature in Path Objects. Is this worth keeping open for 4.0, or should we close it?

It will definitely not be further addressed in JSON Schema, because allOf handles things there.

Why not use arrays?

| .. | .. |
| ---- | ---- |
| paths | Paths Object \| [Paths Object with path] |

Path Item Object With Path

| .. | .. |
| ---- | ---- |
| path | /{path} |
| ...Paths Object | ... |

...

paths:
  - $ref: "/pets.yaml"
  - $ref: "/users.yaml"
  - path: /stores
    post: 
      ...
...
Was this page helpful?
0 / 5 - 0 ratings

Related issues

kolisko picture kolisko  Â·  4Comments

slinkydeveloper picture slinkydeveloper  Â·  4Comments

niquola picture niquola  Â·  5Comments

ePaul picture ePaul  Â·  5Comments

Prasanthmv picture Prasanthmv  Â·  4Comments