Links
Links are one of the new features of OpenAPI 3.0. Using links, you can describe how various values returned by one operation can be used as input for other operations. This way, links provide a known relationship and traversal mechanism between the operations. The concept of links is somewhat similar to hypermedia, but OpenAPI links do not require the link information present in the actual responses.
When to Use Links?
Consider the “create user” operation:
1POST /users HTTP/1.12Host: example.com3Content-Type: application/json4
5{6 "name": "Alex",7 "age": 278}9
10which returns the ID of the created user:11
12HTTP/1.1 201 Created13Content-Type: application/json14
15{16 "id": 30517}
This user ID can then be used to read, update or delete the user: GET /users/305
, PATCH /users/305
and DELETE /users/305
. Using links, you can specify that the id
value returned by “create user” can be used as a parameter to “get user”, “update user” and “delete user”. Another example is pagination via cursors, where the response includes a cursor to retrieve the next data set:
1GET /items?limit=1002
3 ⇩4
5{6 "metadata": {7 "previous": null,8 "next": "Q1MjAwNz",9 "count": 1010 },11 ...12}13
14 ⇩15
16GET /items?cursor=Q1MjAwNz&limit=100
However, linking relationships are not necessarily within the same resource, or even the same API specification.
Defining Links
Links are defined in the links
section of each response:
1responses:2 "200":3 description: Created4 content: ...5 links: # <----6 ...7 "400":8 description: Bad request9 content: ...10 links: # <----11 ...
To better understand this, let’s look at a complete example. This API defines the “create user” and “get user” operations, and the result of “create user” is used as an input to “get user”.
1openapi: 3.0.02info:3 version: 0.0.04 title: Links example5
6paths:7 /users:8 post:9 summary: Creates a user and returns the user ID10 operationId: createUser11 requestBody:12 required: true13 description: A JSON object that contains the user name and age.14 content:15 application/json:16 schema:17 $ref: "#/components/schemas/User"18 responses:19 "201":20 description: Created21 content:22 application/json:23 schema:24 type: object25 properties:26 id:27 type: integer28 format: int6429 description: ID of the created user.30 # -----------------------------------------------------31 # Links32 # -----------------------------------------------------33 links:34 GetUserByUserId: # <---- arbitrary name for the link35 operationId: getUser36 # or37 # operationRef: '#/paths/~1users~1{userId}/get'38 parameters:39 userId: "$response.body#/id"40
41 description: >42 The `id` value returned in the response can be used as43 the `userId` parameter in `GET /users/{userId}`.44 # -----------------------------------------------------45
46 /users/{userId}:47 get:48 summary: Gets a user by ID49 operationId: getUser50 parameters:51 - in: path52 name: userId53 required: true54 schema:55 type: integer56 format: int6457 responses:58 "200":59 description: A User object60 content:61 application/json:62 schema:63 $ref: "#/components/schemas/User"64
65components:66 schemas:67 User:68 type: object69 properties:70 id:71 type: integer72 format: int6473 readOnly: true74 name:75 type: string
The links
section contains named link definitions, in this example – just one link named GetUserByUserId. The link names can only contain the following characters:
1A..Z a..z 0..9 . _ -
Each link contains the following information:
operationId
oroperationRef
that specifies the target operation. It can be the same operation or a different operation in the current or external API specification.operationId
is used for local links only, andoperationRef
can link to both local and external operations.parameters
and/orrequestBody
sections that specify the values to pass to the target operation. Runtime expression syntax is used to extract these values from the parent operation.- (Optional) The
server
that the target operation should use, if it is different from the default servers. - (Optional) A
description
of this link. CommonMark syntax can be used for rich text representation.
The rest of this page goes into more detail about these keywords.
operationId
If the target operation has operationId
specified, the link can point to this ID – as in the image above. This approach can be used for local links only, because the operationId
values are resolved in the scope of the current API specification.
operationRef
operationRef
can be used when operationId
is not available. operationRef
is a reference to the target operation using the JSON Reference syntax – same as used by the $ref
keyword. References can be local (within the current API specification):
1operationRef: "#/paths/~1users~1{userId}/get"
or external:
1operationRef: 'https://anotherapi.com/openapi.yaml#/paths/~1users~1{userId}/get'2operationRef: './operations/getUser.yaml'
Here, the string #/paths/~1users~1{userId}/get
actually means #/paths//users/{userId}/get
, but the inner slashes / in the path name need to be escaped as ~1
because they are special characters.
1#/paths/~1users~1{userId}/get2 │ │ │3 │ │ │4paths: │ │5 /users/{userId}: │6 get: ─────────────────┘7 ...
This syntax can be difficult to read, so we recommend using it for external links only. In case of local links, it is easier to assign operationId
to all operations and link to these IDs instead.
parameters and requestBody
The most important part of a link is computing the input for the target operation based on the values from the original operation. This is what the parameters
and requestBody
keywords are for.
1links:2 # GET /users/{userId}3 GetUserByUserId:4 operationId: getUser5 parameters:6 userId: "$response.body#/id"7
8 # POST /users/{userId}/manager with the manager ID in the request body9 SetManagerId:10 operationId: setUserManager11 requestBody: "$response.body#/id"
The syntax is _parameter_name: value_
or requestBody: value
. The parameter names and request body are those of the target operation. There is no need to list all the parameters, just those required to follow the link. Similarly, requestBody
is only used if the target operation has a body and the link purpose is to define the body contents. If two or more parameters have the same name, prefix the names with the parameter location – path, query, header or cookie, like so:
1parameters:2 path.id: ...3 query.id: ...
The values for parameters and requestBody
can be defined in the following ways:
- runtime expressions, such as
$response.body#/id
, that refer to the values in the request or response of the original operation, - strings containing embedded runtime expressions, such as
ID_{$response.body#/id}
, - hard-coded values – strings, numbers, arrays, and so on, such as
mystring
ortrue
.
You would typically use constant values if you need to pass a specific combination of evaluated and hard-coded parameters for the target operation.
1paths:2 /date_ranges:3 get:4 summary: Get relative date ranges for the report.5 responses:6 '200':7 description: OK8 content:9 application/json:10 example: [Today, Yesterday, LastWeek, ThisMonth]11 links:12 ReportRelDate:13 operationId: getReport14 # Call "getReport" with the `rdate` parameter and with empty `start_date` and `end_date`15 parameters:16 rdate: '$response.body#/1'17 start_date: ''18 end_date: ''19
20 # GET /report?rdate=...21 # GET /report?start_date=...&end_date=...22 /report:23 get:24 operationId: getReport25 ...
Runtime Expression Syntax
OpenAPI runtime expressions are syntax for extracting various values from an operation’s request and response. Links use runtime expressions to specify the parameter values to be passed to the linked operation. The expressions are called “runtime” because the values are extracted from the actual request and response of the API call and not, say, the example values provided in the API specification. The following table describes the runtime expression syntax. All expressions refer to the current operation where the links
are defined.
Expression
Description
$url
The full request URL, including the query string.
$method
Request HTTP method, such as GET or POST.
$request.query._param_name_
The value of the specified query parameter. The parameter must be defined in the operation’s parameters
section, otherwise, it cannot be evaluated. Parameter names are case-sensitive.
$request.path._param_name_
The value of the specified path parameter. The parameter must be defined in the operation’s parameters
section, otherwise, it cannot be evaluated. Parameter names are case-sensitive.
$request.header._header_name_
The value of the specified request header. This header must be defined in the operation’s parameters
section, otherwise, it cannot be evaluated. Header names are case-insensitive.
$request.body
The entire request body.
$request.body_#/foo/bar_
A portion of the request body specified by a JSON Pointer.
$statusCode
HTTP status code of the response. For example, 200 or 404.
$response.header._header_name_
The complete value of the specified response header, as a string. Header names are case-insensitive. The header does not need to be defined in the response’s headers
section.
$response.body
The entire response body.
$response.body_#/foo/bar_
A portion of the request body specified by a JSON Pointer.
foo{$request.path.id}bar
Enclose an expression into {}
curly braces to embed it into a string.
Notes:
- The evaluated expression has the same type as the referenced value, unless noted otherwise.
- If a runtime expression cannot be evaluated, no parameter value is passed to the target operation.
Examples
Consider the following request and response:
1GET /users?limit=2&total=true2Host: api.example.com3Accept: application/json
1HTTP/1.1 200 OK2Content-Type: application/json3X-Total-Count: 374
5{6 "prev_offset": 0,7 "next_offset": 2,8 "users": [9 {"id": 1, "name": "Alice"},10 {"id": 2, "name": "Bob"}11 ]12}
Below are some examples of runtime expressions and the values they evaluate to:
Expression | Result | Comments |
---|---|---|
$url | http://api.example.com/users?limit=2&total=true | |
$method | GET | |
$request.query.total | true | total must be defined as a query parameter. |
$statusCode | 200 | |
$response.header.x-total-count | 37 | Assuming X-Total-Count is defined as a response header. Header names are case-insensitive. |
$response.body#/next_offset | 2 | |
$response.body#/users/0 | {"id": 1, "name": "Alice"} | JSON Pointer (the #/... part) uses 0-based indexes to access array elements. There is no wildcard syntax though, so $response.body#/users/*/id is not valid. |
$response.body#/users/1 | {"id": 2, "name": "Bob"} | |
$response.body#/users/1/name | Bob | |
ID_{$response.body#/users/1/id} | ID_2 |
server
By default, the target operation is called against its default servers – either global servers
, or operation-specific servers
. However, the server can be overridden by the link using the server
keyword. server
has the same fields as global servers, but it is a single server and not an array.
1servers:2 - url: https://api.example.com3---4links:5 GetUserByUserId:6 operationId: getUser7 parameters:8 userId: "$response.body#/id"9 server:10 url: https://new-api.example.com/v2
Reusing Links
Links can be defined inline (as in the previous examples), or placed in the global components/links
section and referenced from an operation’s links
section via $ref
. This can be useful if multiple operations link to another operation in the same way – referencing helps reduce code duplication. In the following example, both the “create user” and “update user” operations return the user ID in the response body, and this ID is used in the “get user” operation. The source operations reuse the same link definition from components/links
.
1paths:2 /users:3 post:4 summary: Create a user5 operationId: createUser6 ...7 responses:8 '201':9 description: Created10 content:11 application/json:12 schema:13 type: object14 properties:15 id:16 type: integer17 format: int6418 description: ID of the created user.19 links:20 GetUserByUserId:21 $ref: '#/components/links/GetUserByUserId' # <-------22
23 /user/{userId}:24 patch:25 summary: Update user26 operationId: updateUser27 ...28 responses:29 '200':30 description: The updated user object31 content:32 application/json:33 schema:34 $ref: '#/components/schemas/User'35 links:36 GetUserByUserId:37 $ref: '#/components/links/GetUserByUserId' # <-------38
39 get:40 summary: Get a user by ID41 operationId: getUser42 ...43
44components:45 links:46 GetUserByUserId: # <----- The $ref's above point here47 description: >48 The `id` value returned in the response can be used as49 the `userId` parameter in `GET /users/{userId}`.50 operationId: getUser51 parameters:52 userId: '$response.body#/id'
References
Did not find what you were looking for? Ask the community
Found a mistake? Let us know