背景とやりたいこと
既存のモノリシックなシステムのDBが持つデータに対して、REST APIでアクセスしたいとき、どうしますか?
そのシステム担当にREST API公開お願いしますと頼もうとすると、ちょっとよく分からないし、基盤影響とか、いろいろ考えないといけないし、それより今忙しいし、、と真面目に掛け合ってくれないということがあるかもしれません。
そんなときに、既存の環境を使いつつすごく簡単にREST形式のWebAPIを生やすことができないか?と考えました。
過去の検討
自身、2017年ごろに一度調べたことがあったのですが、その際の構成は以下の感じです。
- 既存システム構成の例
- REST API生やした後の構成
軽量なSpringBootで作ったツールの構成としては、Spring Boot+Spring Data REST+SpringFox という構成が楽なのでは無いか?と考えました。
当時のSpringコミュニティの中で、それぞれ注目を集めていたプロジェクトだったためです。
Spring Data REST:SpringDataシリーズと連携し、外からのアクセス部分をRESTAPI化してくれる
SpringFox:Springプロジェクト非公式。自分のプロジェクト内のRest APIを自動で探し出し、OpenAPIドキュメントを自動で生成してくれる。また、ドキュメント上からAPIをコールするクライアント(Swagger-UI)を用意してくれる。
少し試したところ、結構これがいい感じに動いていたので、良さげと思っていました。
一方、最近改めて見るとSpringFoxのリリースの最後が2018年の6月で止まっており、
Springのバージョンも4系までの対応。
Spring5系への対応のIssueも切られているが対応がFixしていない状態ということで、
他の代替できるものが無いか調べ、サンプルを作ってみることにしました。
今回の検討
最初にお題となるアプリを作ることにしました。既存のモノリスシステム内でStudentテーブルがあり、それをREST APIで突つきたいという想定です。
アプリ作成
Spring Initializr からアプリを作成するところからスタートです。
以下設定で作りました。
取得したいテーブルのEntityを作ります。キーは必要ですが、取得したいフィールドだけでよいです。
@Entity
public class Student {
@Id
private String id;
private String name;
private String className;
// 以降Getter,Setterなど
次にDBにアクセスするRepositoryを作ります。まずは、Data REST感なく、Controllerから呼べるように普通に作ります。
@Repository
public interface StudentRepository extends PagingAndSortingRepository<Student,String> {
Iterable<Student> findAll();
}
Controllerは以下の実装となります。
@RestController
public class StudentRestController {
@Autowired
private StudentRepository studentRepository;
@GetMapping("/students") Iterable<Student> getStudents() {
return studentRepository.findAll();
}
}
今回はH2を使ってDBアクセスするので、諸々の設定を入れておきます。後で実験しやすいように初期SQLを流したり、アプリ終了時のデータを永続化させる設定などを入れています。
# datasource
spring.datasource.driver-class-name=org.h2.Driver
# DBのファイルとしての永続化先はh2dbフォルダに保存。データ初期化の際にON CONFLICTを使いたかったのでPostgreSQLモードにしておく。
spring.datasource.url=jdbc:h2:./h2db/sandbox;MODE=PostgreSQL
spring.datasource.username=dev
spring.datasource.password=dev
# resources/sdata.sqlを使ったDBのデータ初期化をアプリ立ち上げ都度実施
spring.datasource.initialization-mode=always
# データをファイルに永続化
spring.jpa.hibernate.ddl-auto=update
# h2 for debug tool
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.h2.console.settings.web-allow-others=true
この状態にしておき、resources下に以下ファイルを置いておくとテーブル作成、初期データ投入(あれば何もしない)をやってくれます。Spring Dataの機能でしょうか?便利ですね。
CREATE TABLE IF NOT EXISTS STUDENT (
ID VARCHAR(255) NOT NULL,
CLASS_NAME VARCHAR(255),
NAME VARCHAR(255),
PRIMARY KEY(ID)
);
-- 一意制約発生を避けるため、ON CONFLICTを使用
INSERT INTO STUDENT VALUES ('1','A CLASS','TAKA')
ON CONFLICT DO NOTHING;
INSERT INTO STUDENT VALUES ('2','A CLASS','KASHI')
ON CONFLICT DO NOTHING;
INSERT INTO STUDENT VALUES ('3','B CLASS','KIKUCHI')
ON CONFLICT DO NOTHING;
長かったですが、これでSpringBoot+REST ControllerでのWebAPI開発完了です。
ここまではSpringDataRESTを使ってないことに留意してください。
SpringBootアプリを立ち上げ、Curlでアクセスした結果は以下の通りです。
( 参考: jqはJSONを整形、加工するコマンドラインツールです)
続いて、SpringDataRESTを組み込んで行きます。といいつつ、3ステップ(最短2ステップ)のみなので至極簡単です。
pom.xmlへの依存の追加。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
先ほどのStudentRepositoryクラスへのRepositoryRestResourceアノテーションの追加
@Repository
@RepositoryRestResource
(collectionResourceRel = "students", path = "students")
public interface StudentRepository extends PagingAndSortingRepository<Student,String> {
(任意)SpringDataRESTで自動作成されるAPIの基底パスの追加
# SpringDataRESTで自動作成されるAPIの基底パス
spring.data.rest.basePath=/api
これで完了です。アプリを再立ち上げし、localhost:8080/apiにアクセスしてみましょう。SpringDataRESTによってREST APIが自動生成されたことを確認することができます。
api/studentsというURIのAPIが作成されてますので、アクセスしてみると以下の結果を得ることが出来ます。
先ほど自作したAPIより情報量が多いことに気づくでしょうか?
SpringDataRESTはHATEOASされているので、情報が多く、JavaScriptなどクライアントからAPIを利用するには優位なのです。
ここまでで、DBに対してWebAPIを簡単に(?)作成することが出来ましたので、
最後にOpenAPI、SwaggerUI対応をします。
SpringFoxの代替は?
SpringDataRest,OpenAPIでググって一番先に出てきたSpringdoc OpenAPIを使ってみました。
おそらくSpring公式なんですかね、URL見る限り。
公式が出たのでSpringFoxの開発が下火になったのであれば納得の流れですね。。
導入方法ですが、依存先に追加するだけです。SpringFoxのときはJavaConfig作成が必須だった記憶があるのですが、こちらはStarterを用意してくれているようです。
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.1.44</version>
</dependency>
依存先追加後、アプリを再立ち上げし、http://localhost:8080/swagger-ui/index.html?url=/v3/api-docsにブラウザでアクセスすることで、SwaggerUIを触ることができます!
WebAPIの一覧や、実際にここからWebAPIを実行することもできるので最高です。
とぬか喜びしていましたが、よく見ると、このUIで表示されているのは最初にRESTController
を作ったものしかなく、Spring Data RESTで作ったものは作成してくれてません。
SpringFoxではやってくれていたのですが、、と思い調べていたら、以下のIssueにたどり着きました。
- How does this work with Spring Data Rest ?
Documentation is available on the official page: https://springdoc.github.io/springdoc-openapi-demos/
Spring Data Rest is not a priority. It will be supported on a future release.
このコメントでCloseされてました。。。
さらに下の別の方のコメントでは、SpringFoxからSpringDocに乗り換えようとしているのだけれど、この機能が無いので乗り換えられないよ、的なコメントも。
なんてこった。SpringDataRestなんてものを使って楽しようとした自分が悪いのか。
確かにSpringDataRestを使ってプロジェクトを開始して、途中でやめた事例などもあり、
依存しすぎはNGという理解はあったものの、ちゃちゃっと作る分には良さそうと思ったのですが。。。
結論
ということで、DB直アクセスWebAPI構築をみんな大好きSpringBootを使って簡単に実現することは現時点では難しそうです。
ここまで読み進められた方であれば、RESTControllerから組み上げる形のほうが分かりやすいと思う人もいるでしょう。
現時点では、その方法で地道に作っていくしかなさそうです。
身近にJava実行環境がどこにでもがあるのでSpringBootがお手軽で良いのでは?と思いましたが、その発想に縛られず、今後調べていきます。
検証で作成したソースコード
今後の参考
DB固有になりますが、PostgRESTなるものがあるそうです。
https://qiita.com/kanedaq/items/0c3097604d0e86afd1e3
MSクラウドのDBであるCosmosDBではRESTAPIがサポートされています。
https://docs.microsoft.com/ja-jp/rest/api/cosmos-db/
製品としては多数ありますね。CDATA API Serverとか。でも有償製品ではなくOpenなものでちゃちゃっとやりたいです。なんとか。