How do I reference nested sub-schemas from RAML?


#1

Hi,

I’d like to have one large schema file containing all my schemas, possibly nested subschemas. Then I’d like to reference the appropriate subschema whenever I need to reference it from RAML in various method calls.

In the RAML header I’d like to have:

schemas:
  - Definitions: !include definitions.json

Then I’d like to reference the appropriate subschema:

/books:   
  get:
    responses:
      200:
        body:
          application/json:
            schema: Definitions.Books

Or as an alternative:

schemas:
  - Definitions: !include definitions.json
  - Books: Definitions.Books
  - Author: Definitions.Author

But neither of this seems to work, I hope only my syntax is wrong. Is it possible to do something like this? How?

Or do I really have to mess up my schema file and put each subschema in its individual file and then make sure I get all referencesright from one schema file to another (sigh!)? Like this:

schemas:
  - Books: !include books.json
  - Author: !include author.json
  - and so on for many subschemas.....

Thanks in advance!
/Tommy


#2

Hi Tommy,

here are some examples of how you can do this.

Assume you’ve got a file with all your schemas called all-schemas.raml (btw, it has to be end with either .raml or .yaml as the content of this file will be another RAML specification) where you could have the following:

song: |
    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-03/schema",
      "id": "http://jsonschema.net",
      "required": true,
      "properties": {
        "songTitle": {
          "type": "string",
          "required": true
        },
        "albumId": {
          "type": "string",
          "required": true,
          "minLength": 36,
          "maxLength": 36
        }
      }
    }
    
book: |
    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-03/schema",
      "id": "http://jsonschema.net",
      "required": true,
      "properties": {
        "bookTitle": {
          "type": "string",
          "required": true
        },
        "ISBN": {
          "type": "string",
          "required": true,
          "minLength": 36,
          "maxLength": 36
        }
      }
    }

Going to your main RAML file api.raml you could have:

#%RAML 0.8
title: Hello

schemas:
 - !include all-schemas.yaml
 - album: |
    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-03/schema",
      "id": "http://jsonschema.net",
      "required": true,
      "properties": {
        "albumTitle": {
          "type": "string",
          "required": true
        }
      }
    }
   
/resource:
  get:
    responses:
      200:
        body:
          application/json:
            schema: song

Hope that helps you! I must admit that we should clarify this in the spec in more details. I will gave a feedback to the group.

Cheers,
Christian


#3

Ok thanks, but what if one of my schemas reference another one (very common)?

For example, in your example, if a song had a book as another property:

song: |  
    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-03/schema",
      "id": "idSong",
      "required": true,
      "properties": {
        "songTitle": {
          "type": "string",
          "required": true
        },
        "albumId": {
          "type": "string",
          "required": true,
          "minLength": 36,
          "maxLength": 36
        },
        "inspirationalBook": {
          "$ref": "idBook"
        }
      }
    }

book: |  
    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-03/schema",
      "id": "idBook",
      "required": true,
      "properties": {
        "bookTitle": {
          "type": "string",
          "required": true
        },
        "ISBN": {
          "type": "string",
          "required": true,
          "minLength": 36,
          "maxLength": 36
        }
      }
    }

(Note: Normally the pound symbol (#) is used for references in json-schemas but it does not mix well with yaml/raml’s comment syntax…)


#4

Please see: http://forums.raml.org/t/how-do-you-reference-another-schema-from-a-schema/485/4 That should give you your answer. :slight_smile:


#5

Thanks again :slight_smile: I know how json schema references work, but I have problems getting those references to work with RAML.

Call me stupid but I don’t see how the link you said would give the answer that really helps. I have tried “everything”, still no go :frowning:

Using this simple song-book example above, how should inspirationalBook reference the book schema so that the raml-parser can provide me with the details of the inspirationalBook?


#6

Oh now I get what you want. The RAML parser should be able to link those two schemas right? But I have to stop you here since RAML is only a specification, or a design element for your API - therefore not responsible to logically link schemas. You’re supposed to define/specify everything you need for your schema and thats all. the logic of linking should be on another level (e.g. in your implementation).

Just to be curious, what would you like to achieve? In your implementation you could parse the RAML, get the schema, use a JSON schema parser and get your references, if that is what you want to achieve.


#7

@christian_vogel i think i now what he means and i’m running into the same issues.

Since the schema e defined in a bunch of little pieces (each for an entity) when you reference from one to the other the json-schema parsers don’t know how to do that.

if you say "$ref": "idBook" it tries to include a file called idBook, if you say "$ref": "file.yaml#idBook" it cant parse the yaml syntax. So I have also not found a combination that allows parsers to read both the RAML and the json schemas when there are references to other models.


#8

@rdohms, do you know if there is an issue for that on github?


#9

@christian_vogel haven’t found anything, also don’t know which repo to fire it at, if its a spec problem or a implementation issue.


#10

@rdohms I have been poking around for a satisfactory solution to this for some time as well…
My current workaround involves the following:
1- define each entity in its own file without any extension (song, book),
2- !include each entity in the schemas section of the raml file
3- host the schemas on an http server
4- define the schema id of the roots (song) as absolute URIs to http server
5- the referenced entity schema id (book) will then be resolved against the root entity URI hence able to be located on the http server.
…Works for me on Mule 3.6.1 + ApiKit
but I’m still interested to hear about a solution resolving schemas on local file system


#11

@lco, that sounds like a good workaround for the time being.

@rdohms: i think that is not a problem of the spec itself. it’s more a problem of the parser.


#12

@lco i was thinking something similar, i’ll try both out and see what happens.

@christian_vogel i’m not sure, i’m thinking that the way json-schema is integrated in the RAML spec breaks how json-schema does references, since its usually wrapped in a yaml shell. So it may be an issue with the spec that means regular json schema parsers don’t work with RAML embedded json schemas.

What may be a better solution is to allow you to link to a complete json schema file with all entities and allow RAML to reference the schema id. That way json schema is resolving in its own isolated shell and RAML is capable of reading from it.


#13

I am not really sure if that would be true for the json schema parser going through the value of a schema parameter. But that is something easily to test right :wink:

I like the idea to also have to ability to directly include your json schema, but on the other side I would recommend to always have the actual schema outside your RAML and to do an !include. Unfortunately, the way most of the parser works are to inject the JSON schema back into the RAML, afaik. Maybe in the future there is a more elegant way of doing that.


#14

I’m facing the same issue now.

Has there been any updates? I’ve seen the github issues, and looked all around, but i can’t seem to find a best practice for this.

I was considering writing each entity in its own file, then writing a script to compile it down to one file.

Would that be a better approach or perhaps creating mini raml files with !includes for the dependencies?


#15

Can you post an example, please.


#16

@christian_vogel Thanks for your reply.

Say I have 4 entities:

Users, Dogs, Cats, and Collars.

Each is independent, and have CRUD endpoints. But, Users have Dogs and Cats. Dogs and Cats have Collars.

I want to define each of these entities separately, with independent endpoints for their CRUD functionality.

I also want to return a User with an array of Dogs and Cats and their nested properties.

I want to define each entity’s schema in a separate file and just include them in the responses.

I tried @lco 's solution, but I couldn’t get it working on my local machine.

Thanks again for your responses Christian.


#17

So with RAML 1.0 (current RC1) and types you can do the following:

collar.raml

#%RAML 1.0 DataType

properties:
  name: string 

dog.raml

#%RAML 1.0 DataType

properties:
  name: string
  collars:
    type: array
    items: !include collar.raml

cat.raml

#%RAML 1.0 DataType

properties:
  name: string
  collars:
    type: array
    items: !include collar.raml

user.raml

#%RAML 1.0 DataType

properties:
  dogs:
    type: array
    items: !include dog.raml
  cats:
    type: array
    items: !include cat.raml

api.raml

#%RAML 1.0
title: My API

types:
  User: !include user.raml

/users:
  get:
    responses:
      200:
        body:
          application/json:
            type: User[]

#18

So how does this work if you want to use a “oneOf” type? For example, I want an animal.raml to be a cat or dog type.

This doesn’t seem to work in the API Workbench 0.8.31?

#%RAML 1.0 DataType
---
type: !include cat.raml | !include dog.raml
properties:
  legs: number

#19

Hi the syntax type: !include anothertype.raml basically inlines the type definition and is not intended to be used for complex type expressions like unions. I would strongly recommend to create identifiers for your types using libraries and use the identifiers instead.

lib.raml

#%RAML 1.0 Library

types:
  cat: !include cat.raml
  dog: !include dog.raml 

animal.raml

#%RAML 1.0 DataType
---
uses: 
  lib: lib.raml

type: lib.cat | lib.dog
properties:
  legs: number

#20

Oh I see, that would be an excelent solution indeed. Thanks for the quick reply!