Spring Bootで為替変換APIを作る|外部HTTP通信・JSON解析・REST API

Spring Bootで為替変換APIを作る|外部HTTP通信・JSON解析・REST API

この記事は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. 要件定義:何を作るかを整理する

#要件(やること)理由
1GET /api/exchange?from=USD&to=JPY&amount=100 でリクエストを受け取るクエリパラメータで通貨と金額を柔軟に指定できる
2外部の為替レートAPIにHTTPリクエストを送るリアルタイムのレートを取得するため
3JSONレスポンスを解析してレートの数値を取り出す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);
    }
}

RuntimeException vs Exception 課題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=100200 + 換算結果JSON
?from=EUR&to=JPY(amountなし)200 + 1EURの換算結果
?from=XXX&to=JPY404 + エラーメッセージ

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"
\`\`\`

まとめ:学んだこと

概念内容どこで使ったか
@RequestParamURLクエリパラメータを引数として受け取る?from=USD&to=JPY
@ServiceビジネスロジックをControllerから分離するExchangeService
HttpClient外部URLにHTTPリクエストを送受信する外部APIへのGET
HttpResponseステータスコード・レスポンスボディの取得200/404の判定
Duration.ofSeconds()タイムアウト設定10秒でタイムアウト
Jackson ObjectMapperJSONを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

Members Only

サブスク加入者限定の コンテンツを配信中

  • 毎月のAI・自動化トレンドレポート
  • クリエイター向け業務効率化テンプレート
  • ツール選定・SaaS比較まとめ(限定公開)
Discord サーバー & LINE 公式の両方で通知します
クリエイター・個人事業主向け 受付中

「これ自動化できないかな?」 そのアイデア、ITで実現できます。

業務の自動化・お客様向けツール開発・AI活用まで、クリエイター・個人事業主専門のITコンサルタントが対応します。まずは気軽にご相談ください。

LINEで相談 フォームで相談