eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

1. Overview

In our RAML tutorial article, we introduced the RESTful API Modeling Language and created a simple API definition based on a single entity called Foo. Now imagine a real-world API in which you have several entity-type resources, all having the same or similar GET, POST, PUT, and DELETE operations. You can see how your API documentation can quickly become tedious and repetitive.

In this article, we show how the use of the resource types and traits features in RAML can eliminate redundancies in resource and method definitions by extracting and parameterizing common sections, thereby eliminating copy-and-paste errors while making your API definitions more concise.

2. Our API

In order to demonstrate the benefits of resource types and traits, we will expand our original API by adding resources for a second entity type called Bar. Here are the resources that will make up our revised API:

  • GET /api/v1/foos
  • POST /api/v1/foos
  • GET /api/v1/foos/{fooId}
  • PUT /api/v1/foos/{fooId}
  • DELETE /api/v1/foos/{fooId}
  • GET /api/v1/foos/name/{name}
  • GET /api/v1/foos?name={name}&ownerName={ownerName}
  • GET /api/v1/bars
  • POST /api/v1/bars
  • GET /api/v1/bars/{barId}
  • PUT /api/v1/bars/{barId}
  • DELETE /api/v1/bars/{barId}
  • GET /api/v1/bars/fooId/{fooId}

3. Recognizing Patterns

As we read through the list of resources in our API, we begin to see some patterns emerge. For example, there is a pattern for the URIs and methods used to create, read, update, and delete single entities, and there is a pattern for the URIs and methods used to retrieve collections of entities. The collection and collection-item pattern is one of the more common patterns used to extract resource types in RAML definitions.

Let’s look at a couple of sections of our API:

[Note: In the code snippets below, a line containing only three dots (…) indicates that some lines are being skipped for brevity.]

/foos:
  get:
    description: |
      List all foos matching query criteria, if provided;
      otherwise list all foos
    queryParameters:
      name?: string
      ownerName?: string
    responses:
      200:
        body:
          application/json:
            type: Foo[]
  post:
    description: Create a new foo
    body:
      application/json:
        type: Foo
    responses:
      201:
        body:
          application/json:
            type: Foo
...
/bars:
  get:
    description: |
      List all bars matching query criteria, if provided;
      otherwise list all bars
    queryParameters:
      name?: string
      ownerName?: string
    responses:
      200:
        body:
          application/json:
            type: Bar[]
  post:
    description: Create a new bar
    body:
      application/json:
        type: Bar
    responses:
      201:
        body:
          application/json:
            type: Bar

When we compare the RAML definitions of the /foos and /bars resources, including the HTTP methods used, we can see several redundancies among the various properties of each, and we again see patterns begin to emerge.

Wherever there is a pattern in either a resource or method definition, there is an opportunity to use a RAML resource type or trait.

4. Resource Types

In order to implement the patterns found in the API, resource types use reserved and user-defined parameters surrounded by double angle brackets (<< and >>).

4.1. Reserved Parameters

Two reserved parameters may be used in resource type definitions:

  • <<resourcePath>> represents the entire URI (following the baseURI), and
  • <<resourcePathName>> represents the part of the URI following the right-most forward slash (/), ignoring any braces { }.

When processed inside a resource definition, their values are calculated based on the resource being defined.

Given the resource /foos, for example, <<resourcePath>> would evaluate to “/foos” and <<resourcePathName>> would evaluate to “foos”.

Given the resource /foos/{fooId}, <<resourcePath>> would evaluate to “/foos/{fooId}” and <<resourcePathName>> would evaluate to “foos”.

4.2. User-Defined Parameters

A resource type definition may also contain user-defined parameters. Unlike the reserved parameters, whose values are determined dynamically based on the resource being defined, user-defined parameters must be assigned values wherever the resource type containing them is used, and those values do not change.

User-defined parameters may be declared at the beginning of a resource type definition, although doing so is not required and is not common practice, as the reader can usually figure out their intended usage given their names and the contexts in which they are used.

4.3. Parameter Functions

A handful of useful text functions are available for use wherever a parameter is used in order to transform the expanded value of the parameter when it is processed in a resource definition.

Here are the functions available for parameter transformation:

  • !singularize
  • !pluralize
  • !uppercase
  • !lowercase
  • !uppercamelcase
  • !lowercamelcase
  • !upperunderscorecase
  • !lowerunderscorecase
  • !upperhyphencase
  • !lowerhyphencase

Functions are applied to a parameter using the following construct:

<<parameterName | !functionName>>

If you need to use more than one function to achieve the desired transformation, you would separate each function name with the pipe symbol (“|”) and prepend an exclamation point (!) before each function used.

For example, given the resource /foos, where <<resourcePathName>> evaluates to “foos”:

  • <<resourcePathName | !singularize>> ==> “foo”
  • <<resourcePathName | !uppercase>> ==> “FOOS”
  • <<resourcePathName | !singularize | !uppercase>> ==> “FOO”

And given the resource /bars/{barId}, where <<resourcePathName>> evaluates to “bars”:

  • <<resourcePathName | !uppercase>> ==> “BARS”
  • <<resourcePathName | !uppercamelcase>> ==> “Bar”

5. Extracting a Resource Type for Collections

Let’s refactor the /foos and /bars resource definitions shown above, using a resource type to capture the common properties. We will use the reserved parameter <<resourcePathName>>, and the user-defined parameter <<typeName>> to represent the data type used.

5.1. Definition

Here is a resource type definition representing a collection of items:

resourceTypes:
  collection:
    usage: Use this resourceType to represent any collection of items
    description: A collection of <<resourcePathName>>
    get:
      description: Get all <<resourcePathName>>, optionally filtered 
      responses:
        200:
          body:
            application/json:
              type: <<typeName>>[]
    post:
      description: Create a new <<resourcePathName|!singularize>> 
      responses:
        201:
          body:
            application/json:
              type: <<typeName>>

Note that in our API, because our data types are merely capitalized, singular versions of our base resources’ names, we could have applied functions to the <<resourcePathName>> parameter, instead of introducing the user-defined <<typeName>> parameter, to achieve the same result for this portion of the API:

resourceTypes:
  collection:
  ...
    get:
      ...
            type: <<resourcePathName|!singularize|!uppercamelcase>>[]
    post:
      ...
            type: <<resourcePathName|!singularize|!uppercamelcase>>

5.2. Application

Using the above definition that incorporates the <<typeName>> parameter, here is how you would apply the “collection” resource type to the resources /foos and /bars:

/foos:
  type: { collection: { "typeName": "Foo" } }
  get:
    queryParameters:
      name?: string
      ownerName?: string
...
/bars:
  type: { collection: { "typeName": "Bar" } }

Notice that we are still able to incorporate the differences between the two resources — in this case, the queryParameters section — while still taking advantage of all that the resource type definition has to offer.

6. Extracting a Resource Type for Single Items of a Collection

Let’s focus now on the portion of our API dealing with single items of a collection: the /foos/{fooId} and /bars/{barId} resources. Here is the code for/foos/{fooId}:

/foos:
...
  /{fooId}:
    get:
      description: Get a Foo
      responses:
        200:
          body:
            application/json:
              type: Foo
        404:
          body:
            application/json:
              type: Error
              example: !include examples/Error.json
    put:
      description: Update a Foo
      body:
        application/json:
          type: Foo
      responses:
        200:
          body:
            application/json:
              type: Foo
        404:
          body:
            application/json:
              type: Error
              example: !include examples/Error.json
    delete:
      description: Delete a Foo
      responses:
        204:
        404:
          body:
            application/json:
              type: Error
              example: !include examples/Error.json

The /bars/{barId} resource definition also has GET, PUT, and DELETE methods and is identical to the /foos/{fooId} definition, other than the occurrences of the strings “foo” and “bar” (and their respective pluralized and/or capitalized forms).

6.1. Definition

Extracting the pattern we just identified, here is how we define a resource type for single items of a collection:

resourceTypes:
...
  item:
    usage: Use this resourceType to represent any single item
    description: A single <<typeName>>
    get:
      description: Get a <<typeName>>
      responses:
        200:
          body:
            application/json:
              type: <<typeName>>
        404:
          body:
            application/json:
              type: Error
              example: !include examples/Error.json
    put:
      description: Update a <<typeName>>
      body:
        application/json:
          type: <<typeName>>
      responses:
        200:
          body:
            application/json:
              type: <<typeName>>
        404:
          body:
            application/json:
              type: Error
              example: !include examples/Error.json
    delete:
      description: Delete a <<typeName>>
      responses:
        204:
        404:
          body:
            application/json:
              type: Error
              example: !include examples/Error.json

6.2. Application

And here is how we apply the “item” resource type:

/foos:
...
  /{fooId}:
    type: { item: { "typeName": "Foo" } }
... 
/bars: 
... 
  /{barId}: 
    type: { item: { "typeName": "Bar" } }

7. Traits

Whereas a resource type is used to extract patterns from resource definitions, a trait is used to extract patterns from method definitions that are common across resources.

7.1. Parameters

Along with <<resourcePath>> and <<resourcePathName>>, one additional reserved parameter is available for use in trait definitions: <<methodName>> evaluates to the HTTP method (GET, POST, PUT, DELETE, etc) for which the trait is defined. User-defined parameters may also appear within a trait definition, and where applied, take on the value of the resource in which they are being applied.

7.2. Definition

Notice that the “item” resource type is still full of redundancies. Let’s see how traits can help eliminate them. We’ll start by extracting a trait for any method containing a request body:

traits:
  hasRequestItem:
    body:
      application/json:
        type: <<typeName>>

Now let’s extract traits for methods whose normal responses contain bodies:

  hasResponseItem:
    responses:
      200:
        body:
          application/json:
            type: <<typeName>>
  hasResponseCollection:
    responses:
      200:
        body:
          application/json:
            type: <<typeName>>[]

Finally, here’s a trait for any method that could return a 404 error response:

  hasNotFound:
    responses:
      404:
        body:
          application/json:
            type: Error
            example: !include examples/Error.json

7.3. Application

We then apply this trait to our resource types:

resourceTypes:
  collection:
    usage: Use this resourceType to represent any collection of items
    description: A collection of <<resourcePathName|!uppercamelcase>>
    get:
      description: |
        Get all <<resourcePathName|!uppercamelcase>>,
        optionally filtered
      is: [ hasResponseCollection: { typeName: <<typeName>> } ]
    post:
      description: Create a new <<resourcePathName|!singularize>>
      is: [ hasRequestItem: { typeName: <<typeName>> } ]
  item:
    usage: Use this resourceType to represent any single item
    description: A single <<typeName>>
    get:
      description: Get a <<typeName>>
      is: [ hasResponseItem: { typeName: <<typeName>> }, hasNotFound ]
    put:
      description: Update a <<typeName>>
      is: | [ hasRequestItem: { typeName: <<typeName>> }, hasResponseItem: { typeName: <<typeName>> }, hasNotFound ]
    delete:
      description: Delete a <<typeName>>
      is: [ hasNotFound ]
      responses:
        204:

We can also apply traits to methods defined within resources. This is especially useful for “one-off” scenarios where a resource-method combination matches one or more traits but does not match any defined resource type:

/foos:
...
  /name/{name}:
    get:
      description: List all foos with a certain name
      is: [ hasResponseCollection: { typeName: Foo } ]

8. Conclusion

In this tutorial, we’ve shown how to significantly reduce or, in some cases, eliminate redundancies from a RAML API definition.

First, we identified the redundant sections of our resources, recognized their patterns, and extracted resource types. Then we did the same for the methods that were common across resources to extract traits. Then we were able to eliminate further redundancies by applying traits to our resource types and to “one-off” resource-method combinations that did not strictly match one of our defined resource types.

As a result, our simple API with resources for only two entities, was reduced from 177 to just over 100 lines of code.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.

Here is our final RAML API in its entirety:

#%RAML 1.0
title: Baeldung Foo REST Services API
version: v1
protocols: [ HTTPS ]
baseUri: http://rest-api.baeldung.com/api/{version}
mediaType: application/json
securedBy: basicAuth
securitySchemes:
  basicAuth:
    description: |
      Each request must contain the headers necessary for
      basic authentication
    type: Basic Authentication
    describedBy:
      headers:
        Authorization:
          description: |
            Used to send the Base64 encoded "username:password"
            credentials
            type: string
      responses:
        401:
          description: |
            Unauthorized. Either the provided username and password
            combination is invalid, or the user is not allowed to
            access the content provided by the requested URL.
types:
  Foo:   !include types/Foo.raml
  Bar:   !include types/Bar.raml
  Error: !include types/Error.raml
resourceTypes:
  collection:
    usage: Use this resourceType to represent a collection of items
    description: A collection of <<resourcePathName|!uppercamelcase>>
    get:
      description: |
        Get all <<resourcePathName|!uppercamelcase>>,
        optionally filtered
      is: [ hasResponseCollection: { typeName: <<typeName>> } ]
    post:
      description: |
        Create a new <<resourcePathName|!uppercamelcase|!singularize>>
      is: [ hasRequestItem: { typeName: <<typeName>> } ]
  item:
    usage: Use this resourceType to represent any single item
    description: A single <<typeName>>
    get:
      description: Get a <<typeName>>
      is: [ hasResponseItem: { typeName: <<typeName>> }, hasNotFound ]
    put:
      description: Update a <<typeName>>
      is: [ hasRequestItem: { typeName: <<typeName>> }, hasResponseItem: { typeName: <<typeName>> }, hasNotFound ]
    delete:
      description: Delete a <<typeName>>
      is: [ hasNotFound ]
      responses:
        204:
traits:
  hasRequestItem:
    body:
      application/json:
        type: <<typeName>>
  hasResponseItem:
    responses:
      200:
        body:
          application/json:
            type: <<typeName>>
  hasResponseCollection:
    responses:
      200:
        body:
          application/json:
            type: <<typeName>>[]
  hasNotFound:
    responses:
      404:
        body:
          application/json:
            type: Error
            example: !include examples/Error.json
/foos:
  type: { collection: { typeName: Foo } }
  get:
    queryParameters:
      name?: string
      ownerName?: string
  /{fooId}:
    type: { item: { typeName: Foo } }
  /name/{name}:
    get:
      description: List all foos with a certain name
      is: [ hasResponseCollection: { typeName: Foo } ]
/bars:
  type: { collection: { typeName: Bar } }
  /{barId}:
    type: { item: { typeName: Bar } }
  /fooId/{fooId}:
    get:
      description: Get all bars for the matching fooId
      is: [ hasResponseCollection: { typeName: Bar } ]
Next »
Modular RAML Using Includes, Libraries, Overlays and Extensions
« Previous
Introduction to RAML – The RESTful API Modeling Language
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – LS – NPI (cat=REST)
announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)