この記事はJava学習シリーズ「課題3」です。まだ環境が整っていない方は Java環境構築ガイド を先に読んでください。課題1(パスワードチェッカー)でSpring Bootの基本を学んでいることを前提とします。
この記事で作るもの
外部の為替レートAPIをリアルタイムで呼び出し、通貨換算結果をJSON形式で返すREST APIです。
APIの使い方(curl):
curl "http://localhost:8080/api/exchange?from=USD&to=JPY&amount=100"
レスポンス:
{
"from": "USD",
"to": "JPY",
"amount": 100.0,
"rate": 153.42,
"converted": 15342.0,
"timestamp": "2026-04-10T10:30:00"
}
クラウドワークス案件では「外部APIを呼び出してデータを返す」処理が頻繁に登場します。この課題でHTTP通信とJSON解析の基礎を身につけます。
この記事で学べること
- Java HttpClient(Java 11以降の標準HTTP通信ライブラリ)
- JSON解析(Jacksonライブラリ)
- クエリパラメータの受け取り(
@RequestParam) - カスタム例外クラス(独自のエラー種別の定義)
@ExceptionHandler(エラーをAPIレスポンスに変換する)
REST APIとは? 「URLにHTTPリクエストを送ると、データをJSON形式で返してくれる仕組み」のことです。今回はLapis Techが提供するAPIが、さらに外部の為替レートAPIを呼び出して結果を返します。このように「APIがAPIを呼ぶ」構造はWebサービス開発で非常によく登場します。
1. 要件定義:何を作るかを整理する
| # | 要件(やること) | 理由 |
|---|---|---|
| 1 | GET /api/exchange?from=USD&to=JPY&amount=100 でリクエストを受け取る | クエリパラメータで通貨と金額を柔軟に指定できる |
| 2 | 外部の為替レートAPIにHTTPリクエストを送る | リアルタイムのレートを取得するため |
| 3 | JSONレスポンスを解析してレートの数値を取り出す | JSONから必要な値だけを取り出すため |
| 4 | 換算結果(レート・換算後金額・取得時刻)をJSON形式で返す | 呼び出し元がデータを使いやすい形にするため |
| 5 | 存在しない通貨コードは404、ネットワークエラーは500で返す | エラーの種類を適切なHTTPステータスコードで伝えるため |
2. プロジェクト作成とJacksonの追加
start.spring.io でプロジェクトを生成します。
Artifact: currency-api
Java: 21
Dependencies:
✓ Spring Web ← HTTP通信とJSONの変換に必要(Jacksonも同梱される)
Jacksonとは? Spring Webに同梱されているJSON変換ライブラリです。JavaのオブジェクトをJSONに変換したり(シリアライズ)、その逆(デシリアライズ)したりします。今回は外部APIのJSONレスポンスを解析するのに使います。
3. 使う外部APIを確認する
今回は無料・登録不要の為替レートAPIを使います。
URL: https://open.er-api.com/v6/latest/USD
ブラウザでこのURLを開くと、以下のようなJSONが返ってきます。
{
"result": "success",
"base_code": "USD",
"rates": {
"USD": 1.0,
"JPY": 153.42,
"EUR": 0.92
}
}
rates.JPY の数値を取り出してレートとして使います。
4. DTOクラスを作る
APIが返すJSONの形を ExchangeResult.java として定義します。
package com.example.currencyapi;
import java.time.LocalDateTime;
public record ExchangeResult(
String from,
String to,
double amount,
double rate,
double converted,
LocalDateTime timestamp
) {}
5. 外部APIを呼び出す仕組みを作る(要件2〜3の実装)
Javaで外部URLにHTTPリクエストを送るには HttpClient が必要です。
HttpClient(HTTPクライアント)とは? Javaが外部のWebサーバーに「GET/POSTリクエストを送って、レスポンスを受け取る」ための道具です。ブラウザがWebサイトにアクセスするのと同じHTTPプロトコルをコードから使います。Java 11で標準ライブラリに追加されました。
タイムアウトとは? サーバーが応答しない場合に「N秒経ったらあきらめる」という設定です。設定しないと永遠に待ち続けることになります。
ExchangeService.java を作成します。fetchRate() メソッドで外部APIを呼び、為替レートを取得します。
package com.example.currencyapi;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
@Service
public class ExchangeService {
private static final String API_BASE = "https://open.er-api.com/v6/latest/";
// ObjectMapper は再利用するためフィールドに持つ(毎回生成は非効率)
private final ObjectMapper mapper = new ObjectMapper();
private final HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
// 要件2〜3:外部APIを呼び出してレートを取得する
public double fetchRate(String from, String to) throws IOException, InterruptedException {
String url = API_BASE + from.toUpperCase();
// リクエストを定義
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.timeout(Duration.ofSeconds(10))
.build();
// リクエストを送信してレスポンスを受け取る
HttpResponse<String> response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofString()
);
// 要件5:存在しない通貨コード(APIが404を返す)
if (response.statusCode() == 404) {
throw new CurrencyNotFoundException("存在しない通貨コード: " + from);
}
if (response.statusCode() != 200) {
throw new IOException("APIエラー(ステータス: " + response.statusCode() + ")");
}
// JSONレスポンスを解析してレートを取り出す
JsonNode root = mapper.readTree(response.body());
JsonNode rates = root.get("rates");
if (rates == null || !rates.has(to.toUpperCase())) {
throw new CurrencyNotFoundException("存在しない通貨コード: " + to);
}
return rates.get(to.toUpperCase()).asDouble();
}
}
Jacksonの JsonNode でJSONを解析する理由
JsonNodeとは? Jacksonが提供するクラスで、JSON文字列をツリー構造のオブジェクトに変換したものです。root.get("rates").get("JPY").asDouble()のようにキーを指定して値を取り出せます。
JsonNode root = mapper.readTree(jsonString);
root.get("result").asText() // "success"
root.get("rates").get("JPY").asDouble() // 153.42
root.get("rates").has("XXX") // false(存在しないキー)
6. カスタム例外クラスを作る(要件5の準備)
「存在しない通貨コード」と「ネットワークエラー」では、ユーザーに返すメッセージが違います。両方を Exception で扱うと種類を区別できないため、専用の例外クラスを作ります。
カスタム例外クラスとは?
Exceptionを継承するだけで作れる「独自のエラー種別」です。catch (CurrencyNotFoundException e)と書けば「存在しない通貨コード」のエラーだけを専用の処理で捕捉できます。
package com.example.currencyapi;
public class CurrencyNotFoundException extends RuntimeException {
public CurrencyNotFoundException(String message) {
super(message);
}
}
RuntimeExceptionvsException課題3(CLI版)ではExceptionを継承しましたが、Spring BootではRuntimeExceptionを継承する方が一般的です。RuntimeExceptionは「チェック例外」ではないためthrows宣言が不要で、Spring の@ExceptionHandlerで自動的に捕捉されます。
7. Controllerを作る(完成版)
要件1〜5をすべて組み合わせた ExchangeController.java の完成版です。
package com.example.currencyapi;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Map;
@RestController
@RequestMapping("/api")
public class ExchangeController {
private final ExchangeService exchangeService;
// @Service のインスタンスをSpringが自動で注入してくれる(DI)
public ExchangeController(ExchangeService exchangeService) {
this.exchangeService = exchangeService;
}
// 要件1:GET /api/exchange?from=USD&to=JPY&amount=100
@GetMapping("/exchange")
public ExchangeResult convert(
@RequestParam String from,
@RequestParam String to,
@RequestParam(defaultValue = "1") double amount
) throws IOException, InterruptedException {
double rate = exchangeService.fetchRate(from, to);
double converted = amount * rate;
return new ExchangeResult(
from.toUpperCase(),
to.toUpperCase(),
amount,
Math.round(rate * 10000.0) / 10000.0,
Math.round(converted * 100.0) / 100.0,
LocalDateTime.now()
);
}
// 要件5:存在しない通貨コード → 404
@ExceptionHandler(CurrencyNotFoundException.class)
public ResponseEntity<Map<String, String>> handleNotFound(CurrencyNotFoundException e) {
return ResponseEntity.status(404)
.body(Map.of("error", e.getMessage()));
}
// 要件5:ネットワークエラー → 503
@ExceptionHandler({IOException.class, InterruptedException.class})
public ResponseEntity<Map<String, String>> handleNetworkError(Exception e) {
return ResponseEntity.status(503)
.body(Map.of("error", "外部APIとの通信に失敗しました: " + e.getMessage()));
}
}
@RequestParamとは? URLのクエリパラメータ(?from=USD&to=JPY)をメソッドの引数として受け取るためのアノテーションです。defaultValue = "1"を指定すると、amountを省略したときに1として扱います。
@ExceptionHandlerとは? このControllerで発生した特定の例外を捕捉して、適切なHTTPレスポンスに変換するメソッドに付けるアノテーションです。CurrencyNotFoundExceptionが発生したら自動的に404レスポンスが返ります。
8. 実行してテストする
起動後、以下のcurlコマンドでテストします。
# USD → JPY 換算
curl "http://localhost:8080/api/exchange?from=USD&to=JPY&amount=100"
# 金額省略(1として計算)
curl "http://localhost:8080/api/exchange?from=EUR&to=JPY"
# 存在しない通貨コード → 404
curl "http://localhost:8080/api/exchange?from=XXX&to=JPY"
テストケース一覧:
| リクエスト | 期待するレスポンス |
|---|---|
?from=USD&to=JPY&amount=100 | 200 + 換算結果JSON |
?from=EUR&to=JPY(amountなし) | 200 + 1EURの換算結果 |
?from=XXX&to=JPY | 404 + エラーメッセージ |
9. GitHubに公開する
git init
git add .
git commit -m "feat: 為替変換REST API(Spring Boot + HttpClient + Jackson)"
git remote add origin https://github.com/YOUR_NAME/currency-api.git
git push -u origin main
README.md に記載する内容例:
# 為替変換 REST API
Spring Boot で作成した為替レート取得・通貨換算REST API。
外部の為替レートAPI(open.er-api.com)をリアルタイムで呼び出して換算結果を返します。
## 技術スタック
- Java 21 / Spring Boot 3.3
- Java HttpClient(外部API通信)/ Jackson(JSON解析)
## エンドポイント
| メソッド | パス | 説明 |
|--------|------|------|
| GET | /api/exchange | 為替レート取得・通貨換算 |
## 使い方
\`\`\`bash
./mvnw spring-boot:run
# USD → JPY で100ドル換算
curl "http://localhost:8080/api/exchange?from=USD&to=JPY&amount=100"
\`\`\`
まとめ:学んだこと
| 概念 | 内容 | どこで使ったか |
|---|---|---|
@RequestParam | URLクエリパラメータを引数として受け取る | ?from=USD&to=JPY |
@Service | ビジネスロジックをControllerから分離する | ExchangeService |
HttpClient | 外部URLにHTTPリクエストを送受信する | 外部APIへのGET |
HttpResponse | ステータスコード・レスポンスボディの取得 | 200/404の判定 |
Duration.ofSeconds() | タイムアウト設定 | 10秒でタイムアウト |
Jackson ObjectMapper | JSONをJsonNodeに変換する | レスポンスのJSON解析 |
JsonNode.get().asDouble() | JSONから数値を取得する | レートの取り出し |
| カスタム例外クラス | extends RuntimeException でエラー種別を分離 | CurrencyNotFoundException |
@ExceptionHandler | 例外をHTTPレスポンスに変換する | 404・503の返却 |
Javaの学習シリーズ: 環境構築 | 課題1・パスワードチェッカー | 課題2・データ分析API | 今ここ(課題3・為替変換API) | 課題4・Spring Boot Todo API | 課題5・家計簿API
関連記事
CHECK IT OUT
サブスク加入者限定の
コンテンツを配信中
- 毎月のAI・自動化トレンドレポート
- クリエイター向け業務効率化テンプレート
- ツール選定・SaaS比較まとめ(限定公開)
「これ自動化できないかな?」
そのアイデア、ITで実現できます。
業務の自動化・お客様向けツール開発・AI活用まで、クリエイター・個人事業主専門のITコンサルタントが対応します。まずは気軽にご相談ください。