背景
最近は変化し続ける要件に対応するために、システムも柔軟であることが求められています。
そのため、部分的に変更やスケールの可能なシステムを構築し、API経由で連携するマイクロサービス的アーキテクチャが増えてきています。
そういった設計の中で問題になっていくのが、従来のモノリシックなアプリケーションではIDEやコンパイラなどで行っていた、機能間のインターフェイスをどう管理するかという部分です。
Swaggerとは?
SwaggerとはRESTful APIのドキュメントや、サーバ、クライアントコード、エディタ、またそれらを扱うための仕様などを提供するフレームワークです。
公式サイトでは、The World's Most Popular Framework for APIs
と謳っています。
その理由は、マイクロソフト、Google、IBM、SmartBearなどを大手の企業を含む「Open API Initiative(RESTful APIのインターフェイスを記述するための標準フォーマットを推進する団体)」がLinux Foundationの協力のもとで結成され、APIの記述のために採用したのが「Swagger」です。
Open API InitiativeはSwaggerをベースに、より充実した標準にしていくと説明しています。
しかし実際にSwaggerについて最初に調べたときに悩むのが、関連用語が多く用途と使い分けが分からないことです。
ここではその中でもよりSwaggerの中心になっている用語を説明し、後半ではそれらを使用しどのように開発を進めていくのかをまとめたいと思います。
Swagger関連用語
用語 | 説明 |
---|---|
Swagger Specification | Swaggerを扱う上で中心となる概念で、Swaggerの仕様に準じた、RESTful APIインターフェイスを記述するためのフォーマット(YAML/JSON) Swagger Editorなどで編集するときには、YAMLを用いる方が扱いやすいが、外部のライブラリやSwagger UIと連携する場合にはJSON形式で使用 |
OpenAPI Specification | Swaggerをベースに作られたRESTful APIインターフェイスを記述するためのフォーマット(JSON) |
swagger-core | JavaのコードからSwagger Specification を生成するためのJavaライブラリ |
swagger-node | node.jsのコードからSwagger Specification を生成するためのnode.jsライブラリ |
swagger-js |
Swagger Specification を扱うためのクライアントJavaScriptライブラリ |
Swagger Editor | ブラウザ上で動くSwagger Specification のエディタ、リアルタイムで構文チェック、可視化 |
Swagger Codegen |
Swagger Specification からクライアントコード生成するコマンドラインツール |
Swagger UI |
Swagger Specification から動的にドキュメントを生成するツール |
その他、公式ページのリンクから関連ツールを確認することができます。
Swaggerツールの関係性
Swaggerには公式、コミュニティを含め多くのツールやライブラリ、サービスが存在します。
ここではその中で主要な使い方になる2つを紹介します。
トップダウン形式
- Swagger Editorで
Swagger Specification
を編集、定義 - Swagger Codegenで
Swagger Specification
からソースコードを生成
ボトムアップ形式
- 既に存在するREST APIのソースコードからSwagger Coreのアノテーションなどを使用し
Swagger Specification
定義 - 生成された
Swagger Specification
をもとにSwagger UIによって、REST APIをドキュメント化
Swaggerの使い方
ここからは、トップダウン、ボトムアップそれぞれの具体的な開発の流れをまとめたいと思います。
トップダウン形式
トップダウンの場合には、まず"Swagger Editor"を使用してYAMLかJSONのSwagger Specification
を作成していきます。
Webから直接アクセスできる、http://editor.swagger.io/ が楽ですが、業務的に外に出せない情報を扱う場合も多いので、各環境にインストールすることも出来ます。もしもDocker環境があるのであれば環境を汚さず手軽に試せるので https://hub.docker.com/r/swaggerapi/swagger-editor/ がおすすめです。
左側が実際に編集出来るYAMLまたはJSONファイル、右側がその内容をもとに可視化されたUIになっています。
ここからは下記の記述をもとに少しずつ解説していきます。
(Swagger Specification
の詳細はこちらから確認できます。)
ml
# Example YAML to get you started quickly.
# Be aware that YAML has indentation based scoping.
# Code completion support is available so start typing for available options.
swagger: '2.0'
# This is your document metadata
info:
version: "0.0.0"
title: <enter your title>
# Describe your paths here
paths:
# This is a path endpoint. Change it.
/persons:
# This is a HTTP operation
get:
# Describe this verb here. Note: you can use markdown
description: |
Gets `Person` objects.
Optional query param of **size** determines
size of returned array
# This is array of GET operation parameters:
parameters:
# An example parameter that is in query and is required
-
name: size
in: query
description: Size of array
required: true
type: number
format: double
# Expected responses for this operation:
responses:
# Response code
200:
description: Successful response
# A schema describing your response object.
# Use JSON Schema format
schema:
title: ArrayOfPersons
type: array
items:
title: Person
type: object
properties:
name:
type: string
single:
type: boolean
apiinfo
version情報やタイトルなど
swagger: '2.0'
# This is your document metadata
info:
version: "0.0.0"
title: <enter your title>
paths
エンドポイントのルートを表します。paths
以下各エンドポイントを記述します。
paths:
/persons:
また、RESTなどでよく利用するリソースIDなどの変数形式なデータは以下のように記述します。
paths:
/persons/{id}:
get, post
HTTPメソッドを指定します
paths:
/persons:
get:
description
エンドポイントの説明を記述します。
get:
# Describe this verb here. Note: you can use markdown
description: |
Gets `Person` objects.
Optional query param of **size** determines
size of returned array
parameters
HTTPパラメータを記述します。
parameters:
-
name: size
in: query
description: Size of array
required: true
type: number
format: double
名前 | 説明 |
---|---|
name | パラメータの名前 |
in | queryを指定すると/persons?id=xx のようにパラメータで値を送る(postの場合はbody)。pathを指定すると/persons/{id} のようにURLでパラメータを送るように設定する |
description | パラメータの説明 |
required | 必須要素か否か |
type | パラメータの型。string, boolean, numberなど |
responses
APIのレスポンス情報
responses:
200:
description: Successful response
schema:
title: ArrayOfPersons
type: array
items:
title: Person
type: object
properties:
name:
type: string
single:
type: boolean
パラメータ名 | 概要 |
---|---|
schema | レスポンスの値 |
type | 型を指定する。arrayやobjectなど |
items | 配列などの中身 |
properties | プロパティ情報。itemsを入れ子にすることもできる |
definitions
APIの記述をおこなっていくと、同じ要素が複数箇所で登場する場合がある。
そういった場合には別箇所で要素の定義を行い再利用したほうが便利な場合も多い。
definitionsはこういった、要素を定義し利用することができる。
Locationはオブジェクトの名前
definitions:
Location:
type: object
properties:
id:
type: string
name:
type: string
latitude:
type: number
longitude:
type: number
上記を定義しておくとpropertiesで下記のように$ref
で呼び出せる
responses:
200:
description: OK
schema:
type: object
properties:
data:
$ref: '#/definitions/Location'
また、下記のように入れ子も可能。
definitions:
Media:
type: object
properties:
id:
type: integer
location:
$ref: '#/definitions/Location'
comments:
type: object
properties:
data:
type: array
items:
$ref: '#/definitions/Comment'
Location:
type: object
properties:
id:
type: string
name:
type: string
latitude:
type: number
longitude:
type: number
Comment:
type: object
properties:
id:
type: string
created_time:
type: string
text:
type: string
セキュリティ
oauthなどのセキュリティ関連も設定も可能。
securityDefinitions:
oauth:
type: oauth2
flow: implicit
authorizationUrl: https://twitter.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=REDIRECT-URI&response_type=token
scopes:
basic: |
to read any and all data related to twitter
security:
- oauth:
- basic
メソッド内でoauthを指定する。
paths:
/statuses/mentions_timeline:
get:
description: Returns the 20 most recent mentions for the authenticating user
security:
- oauth:
- basic
Swagger Specification
からコードを生成
Swagger EditorによってSpecificationは出来上がったので、ここからはソースコードを生成してみます。
といっても、Swagger Editorを使用している場合には非常に簡単でGenerate Server
かGenerate Client
から生成したいコードの種類を選ぶだけです。
選択したコードの種類にもよりますが実行環境があれば、多くのものはそのままか、ビルドをすれば使えるので、開発プロジェクトのベースやモックサーバといて活用することが出来ます。
また、Swagger Editorではなくコマンドラインから実行する、Swagger Codegenを使用してソースを生成することもできます。
コマンドツールをダウンロードして、例えば以下のようなコマンドを実行するとSpringプロジェクトのソースコードが生成されます。
java -jar swagger-codegen-cli.jar generate swagger.json -l spring
Swagger Codegenで生成されるコードはSwagger Editorのコードと同じものになるので、Swagger Specification
とコードを生成する人が違っていたり、ビルドツールやCIなどのコマンドライン環境から使用したい場合に役立ちます。
ボトムアップ形式
実際の開発ではトップダウンで生成したソースコードがそのまま扱えない場合も多いと思います。
また、既に開発が始まっているシステムなどはボトムアップ形式でのSwaggerを扱う方が有効な場合も多いと思います。
例ではSpringで作られたアプリケーションから、Swagger APIとUIを提供するSpringfoxを使用します。
説明のために以下のような簡単な機能を持ったAPIを考えてみます。
- 全ユーザ検索と、ユーザIDを指定した検索が可能
- ユーザ登録と削除は管理者機能として提供
コードは以下のような形。
@Entity
class User {
@Id
@GeneratedValue
Long id;
String name;
int age;
Long getId() { return id; }
String getName() { return name; }
int getAge() { return age; }
void setId(Long id) { this.id = id; }
void setName(String name) { this.name = name; }
void setAge(int age) { this.age = age; }
}
interface UserRepository extends JpaRepository<User, Long> {}
@RestController
@AllArgsConstructor
class SampleController {
UserRepository repository;
@GetMapping("/users/")
List<User> users() {
return repository.findAll();
}
@GetMapping("/users/{id}")
User user(@PathVariable Long id) {
return repository.findOne(id);
}
@PostMapping("/admin/")
void create(@RequestBody User user) {
repository.save(user);
}
@DeleteMapping("/admin/{id}")
void delete(@PathVariable Long id) {
repository.delete(id);
}
}
UserクラスとSpring Data JPAのRepositoryを使用したシンプルなAPIです。
このソースから、まず最小構成でSwagger環境を構築します。
Dependencyの追加
dependencies {
compile "io.springfox:springfox-swagger2:2.6.0"
}
springfox-swagger2はSpringソースからSwagger Coreの機能を利用し、Swagger Specification
に従ったJSON APIを作成するためのライブラリです。
Swaggerの有効化
Swaggerのdependencyを追加した後、@Configrationなクラスに@EnableSwagger2を追加して起動します。
@Configuration
@EnableSwagger2
class Config {}
http://localhost:8080/v2/api-docs にアクセスすると、以下のようなSwagger Specification
に従ったJSONが取得できます。
{
"swagger": "2.0",
"info": {
"description": "Api Documentation",
"version": "1.0",
"title": "Api Documentation",
"termsOfService": "urn:tos",
"contact": {},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0"
...
次にSwagger UIを有効にします。
Dependencyの追加
dependencies {
compile "io.springfox:springfox-swagger2:2.6.0"
compile "io.springfox:springfox-swagger-ui:2.6.0" // 追加
}
springfox-swagger-uiはSwagger Specification
形式のAPIを呼び出し、Swagger UIを構築するためのHTMLやJavaScriptを提供します。
Springfoxはこの2つのライブラリにより、ソースからSwagger Specification
+ Swagger SpecificationからSwagger UI
のボトムアップ形式の開発を容易に可能にします。
起動するとデフォルトでは、http://localhost:8080/swagger-ui.htmlのような{アプリケーションのルートパス}/swagger-ui.htmlがSwagger UIへのパスとなります。
このようにdependencyの追加と設定クラスを作るだけでソースコードから、閲覧用のSwagger UIを簡単に作成することができます。
また、ソースコードを修正すれば自動的に反映されるので、動作しているアプリケーションからRESTfulなAPIの情報を常時確認することができるようになります。
また、Swagger UIは単純なAPIドキュメントとしてだけでなく、APIを直接呼び出すことのできるインタラクティブな機能を提供します。
必要なParameters
などを入力してTry it out!
ボタンを選択すると実際にAPIを呼び出した結果も確認することが出来ます。
Swagger UIのカスタマイズ
特に設定を行わない状態でも多くのAPI情報を得ることができますが、もっと細かく情報を追加したくなる場合も多いと思います。
そういった場合には、SwaggerのConfigクラスの修正やソースの中にアノテーションを付加することで追加出来ます。
Configクラスによるカスタマイズ
Configクラスによるカスタマイズは主にSwagger UI(Swagger Specification API)の全体に関わる設定です。
ここでは例として、先程のConfigクラスに設定を追加し、User用とAdmin用にグループを作成、表示項目を分割してみます。
@Configuration
@EnableSwagger2
class Config {
@Bean
public Docket user() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("user")
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(GetMapping.class))
.paths(PathSelectors.regex("/users.*"))
.build()
.apiInfo(apiinfo());
}
@Bean
public Docket admin() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("admin")
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(apiinfo());
}
private ApiInfo apiinfo() {
return new ApiInfoBuilder()
.title("User API")
.description("User情報を扱うためのAPIをです。")
.version("1.0")
.contact(new Contact("disc99","http://disc99.com", "disc99@mail.com"))
.build();
}
}
Docketを@Beanによりコンテナに登録することで、Swagger Specificationのメイン情報を設定することが出来ます。
メソッド | 説明 |
---|---|
getName | グループ名を指定することで、複数のDocketを登録することができ、登録するとSwagger UI上部のセレクトボックスからプルダウンでグループを選択が可能 |
select | Swagger Specificationの対象を選択 |
apis | 使用するAPIの設定 |
paths | 使用するAPIのパスを設定 |
apiInfo | APIの基本情報を設定 |
Swagger UIを確認すると、設定項目が反映されています。
さらに詳しい情報はSpringfoxのドキュメントから確認できます。
アノテーションによるカスタマイズ
アノテーションによるカスタマイズは主に各エントリポイント単位の設定です。
こちらも、先程のAPIに詳細情報を追加します。
@Entity
class User {
@Id
@GeneratedValue
Long id;
String name;
int age;
@ApiModelProperty(value = "ユーザID", required = true)
Long getId() { return id; }
@ApiModelProperty(value = "ユーザ名", required = true)
String getName() { return name; }
@ApiModelProperty(value = "ユーザ年齢")
int getAge() { return age; }
void setId(Long id) { this.id = id; }
void setName(String name) { this.name = name; }
void setAge(int age) { this.age = age; }
}
@RestController
@AllArgsConstructor
class SampleController {
//...
@ApiOperation(value = "ユーザ情報取得", notes = "指定されたユーザの情報を取得します。", response = User.class, tags = {"User",})
@ApiResponses(value = {
@ApiResponse(code = 200, message = "指定されたユーザ情報", response = User.class)})
@GetMapping("/users/{id}")
User user(@ApiParam(value = "ユーザID", required=true) @PathVariable Long id) {
return repository.findOne(id);
}
//...
}
今回は以下のアノテーションを使用し、情報を追加しています。
こちらは、Swaggerが提供しているアノテーションで、より詳しい情報はこちらから確認することができます。
アノテーション | 説明 |
---|---|
@ApiOperation | APIの操作や、パスなどの情報を設定 |
@ApiResponses, @ApiResponse | APIのレスポンスに関する情報を設定 |
@ApiParam | APIのパラメータに関する情報を設定 |
@ApiModelProperty | APIのモデルに関する情報を設定 |
Swaggerからドキュメント生成
Swagger UIを用いれば、動的にUIドキュメンテーションを作ることは出来ますが、そのためには動作させるためのサーバが必要になったり、オフライン環境やブラウザを使わなくても確認できるドキュメントが欲しくなる場合もあると思います。
そのような場合に使えるのが、Swagger2Markupです。
このライブラリはSwagger Specificationから、AsciiDocやHTML、PDFなどのドキュメントを作成することができます。
今回は、Springアプリケーション用にSwagger2Markupと統合されたspringfox-staticdocsを使用します。
Dependencyの追加
先程まで作成していたプロジェクトに以下の依存性を追加します。
dependencies {
compile "io.springfox:springfox-swagger2:2.6.0"
compile "io.springfox:springfox-swagger-ui:2.6.0"
testCompile "io.springfox:springfox-staticdocs:2.6.0" // 追加
}
ドキュメント生成コードを記述
ドキュメントを作成するためにはSwagger Specificationを取得するためにテストコードからを記述し作成します。
@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Application.class, loader = SpringApplicationContextLoader.class)
public class Swagger2MarkupTest {
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
@Test
public void convertSwaggerToAsciiDoc() throws Exception {
this.mockMvc.perform(get("/v2/api-docs")
.accept(MediaType.APPLICATION_JSON))
.andDo(Swagger2MarkupResultHandler.outputDirectory("src/docs/asciidoc/generated").build())
.andExpect(status().isOk());
}
}
このテストを実行することで、src/docs/asciidoc/generated
配下にdefinitions.adoc
、overview.adoc
、paths.adoc
などのasciidocファイルが生成されます。
これらを、Spring REST Docsと組み合わせて使用することで、実際に動作しているAPIから簡単に今風なドキュメントを作成することが出来ます。
Swaggerの競合
最後にSwaggerの他にどのような競合があるかも上げておきます。
-
API Blueprint
MarkdownファイルからAPIドキュメントやサンプルデータになるJSONを生成するための仕様で、apiary.ioと合わせて使用するこで、REST APIの一括して管理することができる。 -
RAML
RESTful API Modeling Languageの略で、YAMLをベースにしたREST API定義のための言語。チャットワークのAPIドキュメントなどにも採用されている。
トレンド
Swaggerは固有名詞ではないのでノイズが入っている可能性もありますが、ここ3年のトレンドを見る限り、Swaggerが優勢なようです。
まとめ
Swaggerは Swagger Specification
を中心にトップダウン、ボトムアップなどSwaggerを使用することで一貫してRESTful APIを扱うことができます。
使い勝手の面ではまだ、競合するツールと迷う部分もありますが、OpenAPI Specificationのベースにもなっているなどの将来性も踏まえると有力な選択肢になると思います。