Skip to main content

Introduction to RAPID Schema Definition Language (RSDL)

RAPID Schema Definition Language (RSDL) is a language to define Web APIs.

RSDL is based on the RAPID Profile of the OData specification. RAPID provides an easy way to envision, create, and consume a Web API that is compatible with the OData Standard and can evolve over time to support more advanced scenarios.

Introductory Example

RAPID APIs are defined by a schema, which can easily be specified using RSDL.

Defining a Structured Type

Let's say that we wanted our API to deal with information about a company. We could specify the properties of a company as follows:

type Company
{
stockSymbol: String
name: String
incorporated: Date
}

Our company has three properties: a stockSymbol, a name, and the date of incorporation.

A property in RAPID can be an Integer, String, Boolean, DateTime, Date, Double, Decimal, TimeOfDay, Duration built-in types, an enum type, a structured type, or a collection of any of the former.

Defining a Service

Now we can create a service that returns information about our company:

service {
company: Company
}

This allows us to make simple requests against our company:

RequestComment
GET http://rapid-pro.org/company?select=stockSymbol,nameget the stock symbol and name of the company
PATCH http://rapid-pro.org/company
{ "name":"Spacely's Space Sprockets" }
update the company name

Defining a Structured Type Property

Now let's say that we wanted to add employees to our company.

First, we would define the employee type:

type Employee
{
key id: Integer
firstName : String
lastName : String
title: String
}

The id property is identified as a key, meaning that instances of employees within a collection can be referenced by their id.

Now we can add employees to our company:

type Company
{
stockSymbol: String
name: String
incorporated: Date
employees: [Employee]
}

The employees property is a collection of our employee type. Because it is a collection, the type is enclosed in square brackets [...].

Now we can get employees for our company:

RequestComment
GET http://rapid-pro.org/company/employees?select=lastName,titlelist the last name and title for all employees for the company
GET http://rapid-pro.org/company/employees/1?select=lastName,titleget the last name and title of the employee with id=1
GET http://rapid-pro.org/company?select=name&expand=employees(select=lastName)get the company name and the last names of all of its employees
POST http://rapid-pro.org/company/employees
{ "firstName": "Cosmo","lastName": "Spacely","title": "CEO" }
add a new employee
PATCH http://rapid-pro.org/company/employees/1 {"title": "Assistant to the Assistant Regional Manager"}change the title of the employee with id=1
DELETE http://rapid-pro.org/company/employees/1delete the employee with id=1

Defining a Top-Level Collection

Our service exposes a single top-level company instance.

We could also add a top-level collection.

For example, we could reuse the same company type to create a collection of companies that are competitors.

service {
company: Company
competitors: [Company]
}

Because company is now part of a collection, if we want to reference individual companies within the collection we would define a key. In this case, we use stockSymbol as the key:

type Company
{
key stockSymbol: String
name: String
incorporated: Date
employees: [Employee]
}

Now we can request individual companies within the competitors collection:

RequestComment
POST http://rapid-pro.org/competitors
{ "stockSymbol":"cgswl", "name":"Cogswell's Cosmic COGs" "incorporated":"2054-10-04T00:00:00Z" }
create a new competitor
GET http://rapid-pro.org/competitors?select=namelist the names of all of the competitors
GET http://rapid-pro.org/competitors/cgswl?select=nameget the name of the competitor with the stock symbol cgswl
GET http://rapid-pro.org/competitors/cgswl/employees?select=lastNameget the last name of employees for the competitor with the stock symbol cgswl
DELETE http://rapid-pro.org/competitors/cgswldelete the competitor with the stock symbol cgswl

Defining an Enum

Enumerations allow us to define a string-valued property with a fixed set of values.

Let's say that we wanted to define an employmentType enumeration, with possible values "salaried" and "hourly". We could do so as follows:

enum EmploymentType
{
salaried
hourly
}

Now we could use that employmentType enum in our employees example:

type Employee
{
key id: Integer
firstName: String
lastName: String
title: String
employeeType: EmploymentType
}

Defining a Structured Property without Identity

Our employee has first name and last name properties. We could define a "fullName" type to group those properties together:

type FullName
{
firstName: String
lastName: String
}

and then use that type in our employee:

type Employee
{
key id: Integer
name: FullName
title: String
}

Grouping these properties together keeps them organized and makes them easy to use in various structured types.

Defining Methods

RAPID supports actions and functions.

Actions

An action takes zero or more input parameters and may or may not return a value. Actions may have side effects.

We can define a "youreFired" action on our company that takes a string parameter "reason":

type Company
{
key stockSymbol: String
name: String
incorporated: Date
employees: [Employee]
topEmployees(num: Integer): [Employee]
action youreFired(reason: String)
}

youreFired has the action attribute to show that it is an action and may have side-affects. It does not return a value.

Because actions may have side-affects, they are invoked using POST. Their parameters are passed in the body of the request.

RequestComment
POST http://rapid-pro.org/company/employees/1/youreFired
{ "reason": "Embezzlement" }
invoke the youreFired action on employee with id = 1

Actions and functions may also be defined on the service.

service {
company: Company
competitors: [Company]
currentStockPrice(stockSymbol: String): Decimal
}
RequestComment
GET http://rapid-pro.org/currentStockPrice?stockSymbol=cgswlget the current stock price for cgswl

Functions

A function takes zero or more input parameters, and returns a value. Functions must not have side effects.

We can define a "topEmployees" function on our company:

type Company
{
key stockSymbol: String
name: String
incorporated: Date
employees: [Employee]
topEmployees(num: Integer) : [Employee]
}

topEmployees takes a single Integer parameter "num" and returns a collection of employees.

Functions are invoked using a GET request. Function parameters are passed in the URL.

RequestComment
GET http://rapid-pro.org/company/topEmployees?num=10get the company's top 10 employees

Explicit Paths

The set of paths supported by a RAPID service generally do not need to be explicitly enumerated in the RSDL because they are defined by the model. However, in some cases you may want to be explicit about the set of paths supported, either because the service supports only a subset of paths or because you want to specify capabilities that are specific to that path.

The set of paths supported by a RAPID service can be explicitly enumerated in the RSDL specification:

paths :
{
/company
/company/employees
/company/employees/{id}
/competitors
/competitors/{stockSymbol}
/competitors/{stockSymbol}/employees
/competitors/{stockSymbol}/employees/{id}
/company/topEmployees(num={num})
/company/topEmployees(num={num})/{id}
/company/youreFired
}

Paths that are omitted are not supported, even if valid according to the model.

A nice way of documenting that a path is not supported is by it by adding it as a comment. From a syntax perspective this is equivalent to omitting that path:


paths :
{
/company
/company/employees
/company/employees/{id}
/competitors
/competitors/{stockSymbol}
# /competitors/{stockSymbol}/employees
# /competitors/{stockSymbol}/employees/{id}
/company/topEmployees(num={num})
# /company/topEmployees(num={num})/{id}
/company/youreFired
}