Reading Time: 14 minutes

In the first article in this series, RESTful API Modeling Language 101, we saw how to design basic RAML and we had banking API as a use case. Here we will walk through some of the advanced RESTful API Modeling Language (RAML) to achieve code reusability and better code readability.

Several RESTFul API Modeling Language components, such as Traits, Resource Types, Data Types, and Security Schemes are used to enable stronger code reusability. Code reusability and readability are essential and allow us to write modular code and promote reuse while avoiding redundancy. Reusability and readability also enable us to design more efficient APIs and speed up the API design and creation processes.

latest report
Learn why we are the Leaders in API management and iPaaS

Simplifying the API design and improving readability with Traits

A Trait, like a method, can provide method-level nodes such as description, headers, query string parameters, and responses. Methods that use one or more Traits inherit nodes of those Traits. Usage of Traits is optional but is highly encouraged since it simplifies the API design and improves readability. Traits should be created as separate files and included at the start of the API RAML file.

For this example, we will define error response for each method within resources, meaning we will keep writing code duplication for each method within resources. Instead, the error response is defined in Traits and reused across any method within resources.

#%RAML 1.0 Trait
 
responses:
  400:
    body:
      application/json:
        example: |
          {"errorMessage":"Bad Request", "errorCode":"400"}
  404:
    body:
      application/json:
        example: |
          {"errorMessage":"Not Found", "errorCode":"404"}
  401:
    body:
      application/json:
        example: |
          {"errorMessage":"Unauthorized", "errorCode":"401"}
  500:
    body:
      application/json:
        example: |
          {"errorMessage":"Internal Server Error", "errorCode":"500"}

The Traits node within the API RAML is used to refer to external Traits.

traits:
  errorTraits: !include traits/errorTrait.raml


Traits can be mapped in the API RAML by referencing the assigned Trait.

/accounts:
  get:
    description: Fetch all the accounts for customers.
    is: [errorTraits]
    headers:
      customerId:
        required: true
        type: string
        example: "323232"
        maxLength: 20
    responses:
      200:
       body:
        application/json:
          example: |
            [{
              "accountId": "32323232",
              "accountType": "Current",
              "accountName": "Mr. James John",
              "currency": "USD",
              "status": "Active"
            }]

Reduce API design complexity with Resource Types

Resource Types are a powerful way to reuse patterns across multiple resources and methods. The patterns provided by Resource Types encourage consistency and reduce complexity for API designers and consumers. Usage of Resource Types is optional but is highly encouraged since it simplifies the API design and improves readability. Resource Types should be created as separate files and included at the start of the API RAML file.

Resource Types are like a resource as they can specify the descriptions, methods, and parameters. Resources that use Resource Types can inherit its nodes, while Resource Types can use and inherit from other Resource Types.

Resource Types is a template used to define the descriptions, methods, and parameters that multiple resources can use without writing duplicate or repeating code.

In our use case, the pattern remains the same for each method within resources, but only examples change for each method. But with Resource Types, we can define the patterns for the HTTP method and can be re-used within root RAML across multiple methods within resources.

We can define Resource Types in two ways, and one way is to create RAML of type Resource Types as shown below directly. For the following example, we will create Resource Types where we will define patterns for the GET and POST methods which are reusable across various resources.

#%RAML 1.0 ResourceType
 
get?:
  description: Fetch the <<resourcePathName>>
  responses:
      200:
        body:
          application/json:
            example: |
              <<exampleReference1>>
            
  post?:
    description: Create the <<resourcePathName>>
    body:
      application/json:
        example: |
          <<exampleReference2>>
      
    responses:
      200:
        body:
          application/json:
            example: |
              <<exampleReference3>>

The Resource Types can be referred to in root RAML using the syntax below.

resourceTypes:
  accountRespTypes: !include resourceTypes/resourceType.raml

Resource Types can be mapped in the API RAML by referencing the assigned type.

accounts:
  type:
    accountRespTypes:
      exampleReference1: !include examples/accounts.json
      exampleReference2: !include examples/accounts.json
      exampleReference3: !include examples/accountResp.json
  get: 
  is: [errorTraits]
  post:
  /{accountId}:
    type:
      accountRespTypes:
        exampleReference1: !include examples/accounts.json
    get:
 
    /balances:
      type:
        accountRespTypes:
          exampleReference1: !include examples/balances.json
      get:
 
    /transactions:
      type:
        accountRespTypes:
          exampleReference1: !include examples/transactions.json
      get: 
      /{transactionId}:
        type:
          accountRespTypes:
            exampleReference1: !include examples/transactions.json
        get:

To define the Resource Type via another method, we will create a library of type Resource Types where we will define patterns for the GET and POST methods which can be reused across various resources.

RAML libraries are pre-defined sets of Data Types, Resource Types, Traits, Security Schemes, and reusable assets — all in a namespaced environment. One of the main advantages of Libraries is modularization and they can enable reusability. It is a kind of “typed fragment,” and you can define multiple types in one library, and that can be referred from RAML. It is also possible to define a library as an inline.

#%RAML 1.0 Library
 
resourceTypes:
  accountCollection:
    get?:
      description: Fetch the <<resourcePathName>>
      responses:
        200:
          body:
            application/json:
              example: |
                <<exampleReference1>>
            
    post?:
      description: Create the <<resourcePathName>>
      body:
        application/json:
          example: |
           <<exampleReference2>>
      
      responses:
        200:
          body:
            application/json:
              example: |
                <<exampleReference3>>
  • ?” means when you inherit the library of type Resource Types for any resources in your root RAML, you don’t have to implement all the methods for that resource.
  • <<exampleReference1>> or <<exampleReference2>> or <<exampleReference3>> is a parameter that can be passed from root RAML as response or request example may vary depending on resources.
  • <<resourcePathName>> is the keyword to read resource name dynamically.

The library of Resource Types can be referred to in root RAML using the syntax below.

uses:
  accountResType: library/accountResourceTypes.raml

The library of Resource Types can be mapped in the API RAML by referencing the assigned type.

/accounts:
  type: {accountResType.accountCollection: {exampleReference1: !include examples/accounts.json,exampleReference2: !include examples/accounts.json,exampleReference3: !include examples/accountResp.json}}
  get:
    is: [errorTraits]
  post:
  /{accountId}:
    type: {accountResType.accountCollection: {exampleReference1: !include examples/accounts.json}}
    get:   
    /balances:
      type: {accountResType.accountCollection: {exampleReference1: !include examples/balances.json}}
      get:
    /transactions:
      type: {accountResType.accountCollection: {exampleReference1: !include examples/transactions.json}}
      get:
        is: [tranTraits]
      /{transactionId}:
        type: {accountResType.accountCollection: {exampleReference1: !include examples/transactions.json}}
        get:

Configure security with Security Schemes

Each authentication pattern supported by the API must be expressed as an element of the Security Schemes node value. The Security Schemes should be created under a separate folder and included within the API RAML file. 

Supported Security Schemes are defined below.

  • OAuth 1.0: API authentication requires using OAuth 1.0
  • OAuth 2.0: API authentication requires using OAuth 2.0 
  • Basic authentication: API authentication relies on using basic authentication, and will need to pass username and password checks.
  • Digest authentication: API authentication relies on using digest authentication.
  • Pass-through: Headers or query parameters are passed through to the API based on a defined mapping.
  • x-{other}: API authentication relies on another authentication mechanism. You can define custom Security Schemes prefixed with “x-”. Please refer to the below example for custom Security Schemes.
#%RAML 1.0 SecurityScheme
type: x-custom-security
description: Custom Security Headers
describedBy:
  headers:
    client_id:
      description: Client Id for Authentication
      type: string
    client_secret:
      description: Client Secret for Authentication
      type: string
  
  responses:
    401:
      description: "Unauthorized Access"

The Security Schemes node within the API RAML refers to external Security Schemes.

securitySchemes:
  clientSecurity: !include security-schemes/banking-fragment.raml
 
securedBy:
  - clientSecurity

Validate data against a type declaration with Data Types

RAML Data Types provide a concise and powerful way of describing the information within the API. Additionally, Data Types add rules for validating data against a type declaration. Data Types should be created as separate files and included at the start of the API RAML file.

#%RAML 1.0 DataType
 
type:
  properties:
    firstName:
      type: string
      example: "James"
      minLength: 1
      maxLength: 10
    lastName:
      type: string
      example: "John"
    age:
      type: number
      example: 30
    address:
      type: string
      example: "404 Oxford"
    city:
      type: string
      example: "Mumbai"
    postalCode:
      type: number
      example: 43432432
    state:
      type: string
      example: "Maharashtra"
    country:
      type: string
      example: "India"
      minLength: 1
      maxLength: 15

The types node within the API RAML refers to external Data Types.

types:
  custDataTypes: !include dataTypes/customerDataTypes.raml

Conclusion

In this RESTFul API Modeling Language 201 article, we have seen various components that enable API security, data validation, and better code reusability and readability. It is always recommended to use Traits and Resource Types to promote code reusability and modularization and help write less code.

To learn more about RESTful API Modeling Language, you can watch the recording session from the recent Surat MuleSoft meetup.

Jitendra Bafna is one of our MuleSoft Ambassadors. If you would like to connect, you can find him on LinkedIn.