API fragments với RAML

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

Khi định nghĩa API specs với RAML, việc chúng ta định nghĩa tất cả các thông tin chỉ trong một tập tin RAML sẽ gây khó khăn trong việc maintain và sẽ có những phần, những data types được sử dụng đi, sử dụng lại trong nhiều request URI khác nhau, nếu định nghĩa như vậy thì không phải là best practice. Việc chia nhỏ định nghĩa những phần được sử dụng đi sử dụng lại này thành những fragments trong những tập tin RAML khác, sẽ giúp chúng ta quản lý cách định nghĩa API specs tốt hơn, tránh lặp đi lại những thứ không cần thiết. Trong bài viết này, mình sẽ hướng dẫn các bạn cách định nghĩa API specs với các fragments trong RAML các bạn nhé!

  10+ tools và extensions tuyệt vời cho GraphQL APIs
  3 bước tối ưu hiệu năng React App bằng các API mới của React

Xem thêm các việc làm REST API hấp dẫn trên TopDev

Để làm ví dụ cho bài viết này, mình sẽ sử dụng một tập tin RAML định nghĩa API specs dùng để quản lý thông tin sinh viên có nội dung ban đầu như sau:

#%RAML 1.0
baseUri: https://localhost:8081/api
title: Student Information Management System
version: 1.0

types:
Student:
type: object
properties:
id?:
type: integer
example: 1
code:
type: string
example: "001"
name:
type: string
example: "Huong Dan Java"

/students:
get:
responses:
200:
body:
application/json:
type: array
items: Student
example:
[
{ "id": 1, "code": "001", "name": "Khanh" },
{ "id": 2, "code": "002", "name": "Quan" }
]
post:
body:
application/json:
type: Student
example: { "code": "005", "name": "Khanh" }
/{id}:
uriParameters:
id:
description: Id of the Student
type: string
example: "1"
get:
responses:
200:
body:
application/json:
example: { "id": 1, "code": "001", "name": "Khanh" }
put:
body:
application/json:
type: Student
example: { "id": 5, "code": "005", "name": "Khanh Nguyen" }
delete:
responses:
200:
body:
application/json:
example: { "message": "Student deleted!" }

Như các bạn thấy là, tất cả các thông tin về Student object, các example đều được khai báo trong cùng một tập tin RAML. Giả sử sau này ứng dụng của mình được mở rộng để thêm các request URI thêm xoá sửa lớp học, lấy thông tin student của một lớp học:

#%RAML 1.0
baseUri: https://localhost:8081/api
title: Student Information Management System
version: 1.0

types:
Student:
type: object
properties:
id?:
type: integer
example: 1
code:
type: string
example: "001"
name:
type: string
example: "Huong Dan Java"
Class:
type: object
properties:
id?:
type: integer
example: 1
name:
type: string
example: "A"
students?:
type: array
items: Student

/students:
get:
responses:
200:
body:
application/json:
type: array
items: Student
example:
[
{ "id": 1, "code": "001", "name": "Khanh" },
{ "id": 2, "code": "002", "name": "Quan" }
]
post:
body:
application/json:
type: Student
example: { "code": "005", "name": "Khanh" }
/{id}:
uriParameters:
id:
description: Id of the Student
type: string
example: "1"
get:
responses:
200:
body:
application/json:
example: { "id": 1, "code": "001", "name": "Khanh" }
put:
body:
application/json:
type: Student
example: { "id": 5, "code": "005", "name": "Khanh Nguyen" }
delete:
responses:
200:
body:
application/json:
example: { "message": "Student deleted!" }

/classes:
get:
responses:
200:
body:
application/json:
type: array
items: Class
example: [{ "id": 1, "name": "A" }, { "id": 2, "name": "B" }]
post:
body:
application/json:
type: Class
example: { "name": "A" }
/{id}:
uriParameters:
id:
description: Id of the Class
type: string
example: "1"
get:
responses:
200:
body:
application/json:
type: Class
example: { "id": 1, "name": "A" }
put:
body:
application/json:
type: Class
example: { "id": 5, "name": "B" }
delete:
responses:
200:
body:
application/json:
example: { "message": "Class deleted!" }
/students:
get:
responses:
200:
body:
application/json:
type: Class
example:
{
"id": 1,
"name": "A",
"students":
[
{ "id": 1, "code": "001", "name": "Khanh" },
{ "id": 2, "code": "002", "name": "Quan" }
]
}

thì như các bạn thấy: nội dung của tập tin RAML này sẽ phình ra nhiều hơn gây khó khăn trong việc chỉnh sửa, các example không được reusable, … Sử dụng các fragments sẽ giúp chúng ta giải quyết những bất cập này.

Để sử dụng các fragments, điều các bạn cần phải làm đó là chia nhỏ tập tin RAML của chúng ta (mình sẽ gọi tập tin RAML này là tập tin root RAML các bạn nhé!) bằng cách định nghĩa fragment cho các data types, các thông tin chung giữa các request URI sử dụng các tập tin RAML khác và khai báo để sử dụng những tập tin RAML này trong tập tin root RAML sử dụng từ khoá “!include”.

Trong ví dụ của mình ở trên thì việc đầu tiên chúng ta có thể làm là định nghĩa fragment cho các data type object trong các tập tin RAML khác.

Mình sẽ tạo mới 2 tập tin student.raml và class.raml nằm trong thư mục data-types/objects:

để định nghĩa data type cho các object Student, Class.

Để định nghĩa fragment cho các data type, chúng ta cần khai báo dòng “#%RAML 1.0 DataType” ở đầu của mỗi tập tin .raml. Ví dụ nội dung của tập tin student.raml sẽ như sau:

#%RAML 1.0 DataType
displayName: Student

properties:
id?:
type: integer
example: 1
code:
type: string
example: "001"
name:
type: string
example: "Huong Dan Java"

Trong tập tin class.raml, chúng ta sẽ sử dụng từ khoá “!include” để khai báo cho Student object như sau:

#%RAML 1.0 DataType
displayName: Class

properties:
id:
type: integer
example: 1
name:
type: string
example: "A"
students:
type: array
items: !include student.raml

Bây giờ thì trong tập tin root RAML, chúng ta không cần khai báo các data type object nữa. Chúng ta chỉ cần sử dụng từ khoá “!include”, ví dụ như sau:

types:
Student: !include data-types/objects/student.raml
Class: !include data-types/objects/class.raml

Cho các example thì chúng ta có thể tạo thư mục examples để chứa các example này:

Lúc này thì các bạn có thể khai báo example trong tập tin root RAML ví dụ như sau:

/students:
...
/{id}:
uriParameters:
id:
description: Id of the Student
type: string
example: "1"
get:
responses:
200:
body:
application/json:
type: Student
example: !include examples/student.json

Nội dung tập tin root RAML của chúng ta sau khi sử dụng fragment chỉ còn lại như sau:

#%RAML 1.0
baseUri: https://localhost:8081/api
title: Student Information Management System
version: 1.0

types:
Student: !include data-types/objects/student.raml
Class: !include data-types/objects/class.raml

/students:
get:
responses:
200:
body:
application/json:
type: array
items: Student
example: !include examples/students.json

post:
body:
application/json:
type: Student
example: !include examples/post-student.json
/{id}:
uriParameters:
id:
description: Id of the Student
type: string
example: "1"
get:
responses:
200:
body:
application/json:
type: Student
example: !include examples/student.json
put:
body:
application/json:
type: Student
example: !include examples/student.json
delete:
responses:
200:
body:
application/json:
example: { "message": "Student deleted!" }

/classes:
get:
responses:
200:
body:
application/json:
type: array
items: Class
example: !include examples/classes.json
post:
body:
application/json:
type: Class
example: !include examples/post-class.json
/{id}:
uriParameters:
id:
description: Id of the Class
type: string
example: "1"
get:
responses:
200:
body:
application/json:
type: Class
example: !include examples/class.json
put:
body:
application/json:
type: Class
example: !include examples/class.json
delete:
responses:
200:
body:
application/json:
example: { "message": "Class deleted!" }
/students:
get:
responses:
200:
body:
application/json:
type: Class
example: !include examples/class-with-students.json