ReactとSpringBootを用い、NASA NeoWs APIを使用して作っているwebアプリで発生したエラーに関して
Q&A
Closed
Spring BootとReactでNeoWs APIを使用したアプリケーション開発を開発しています。
1日模索しましたが自力で解決出来なかったので質問させてください。
実装したい機能
NASAのNeoWs API を使用し、以下の機能を実装したいです:
1.	開始日と終了日を指定して地球近傍小天体のデータを取得
•	APIの仕様上、最大7日間のデータ取得が可能。
2.	取得したデータを整形し、サイズや速度の分類結果を返す
•	サイズカテゴリ例: 小 (0.5km未満)、中 (0.5〜1km)、大 (1km以上)。
•	速度カテゴリ例: 低速 (10k km/h未満)、中速 (10k〜30k km/h)、高速 (30k km/h以上)。
3.	フロントエンドでデータを可視化
•	Reactで取得したデータを棒グラフとして描画。
発生している問題
現在以下の問題が発生しており、解決できません:
- バックエンドのAPIエンドポイントにアクセスすると404エラーが発生
• エラーメッセージ: 
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
There was an unexpected error (type=Not Found, status=404).
No static resource api/neows.
- フロントエンドでAPIにリクエストを送ると「SyntaxError: The string did not match the expected pattern」というエラーが発生
 
解決してほしい内容
- バックエンドの404エラーを解消し、正しくエンドポイントが動作するようにしたい。
 - フロントエンドからAPIにリクエストを送信し、正常にデータを取得できるようにしたい。
 
日付の形式はYYYY-MM-DDがNeoWsの方で指定されているのでそれに沿って作っている筈なのですが、どうもエラーしか吐かず。エラーを吐かないようにプログラムを変更してもそもそもバックエンドからのデータ受け渡しがうまくいってないような。調べても解決できなかったので、お助けください。よろしくお願いしますどm(__)m
使用中のソースコード
バックエンド: NasaController.java
@RestController
@RequestMapping("/api")
public class NasaController {
    private final NasaService nasaService;
    public NasaController(NasaService nasaService) {
        this.nasaService = nasaService;
    }
    @GetMapping("/neows")
    public Mono<Map<String, Object>> getNeoWsData(
            @RequestParam("start_date") String startDate,
            @RequestParam("end_date") String endDate) {
        return nasaService.getNeoWsData(startDate, endDate);
    }
}
バックエンド: NasaService.java
@Service
public class NasaService {
    private static final Logger logger = LoggerFactory.getLogger(NasaService.class);
    private final WebClient webClient;
    @Value("${nasa.api.key}")
    private String apiKey;
    public NasaService(WebClient.Builder builder) {
        this.webClient = builder.baseUrl("https://api.nasa.gov").build();
    }
    public Mono<Map<String, Object>> getNeoWsData(String startDate, String endDate) {
        logger.info("Fetching NEO WS data from {} to {}", startDate, endDate);
        return webClient.get()
                .uri(uriBuilder -> uriBuilder
                        .path("/neo/rest/v1/feed")
                        .queryParam("start_date", startDate)
                        .queryParam("end_date", endDate)
                        .queryParam("api_key", apiKey)
                        .build())
                .retrieve()
                .bodyToMono(JsonNode.class)
                .map(this::processNeoData);
    }
    private Map<String, Object> processNeoData(JsonNode data) {
        logger.info("Processing NEO WS data...");
        if (!data.has("near_earth_objects")) {
            throw new IllegalArgumentException("Invalid API response: Missing 'near_earth_objects'");
        }
        Map<String, Integer> sizeCategories = new HashMap<>();
        Map<String, Integer> speedCategories = new HashMap<>();
        data.get("near_earth_objects").fields().forEachRemaining(entry -> {
            entry.getValue().forEach(neo -> {
                double minDiameter = neo.get("estimated_diameter").get("kilometers").get("estimated_diameter_min").asDouble();
                double speed = neo.get("close_approach_data").get(0).get("relative_velocity").get("kilometers_per_hour").asDouble();
                // サイズ分類
                if (minDiameter < 0.5) sizeCategories.merge("Small (<0.5km)", 1, Integer::sum);
                else if (minDiameter < 1) sizeCategories.merge("Medium (0.5-1km)", 1, Integer::sum);
                else sizeCategories.merge("Large (>1km)", 1, Integer::sum);
                // 速度分類
                if (speed < 10000) speedCategories.merge("Slow (<10k km/h)", 1, Integer::sum);
                else if (speed < 30000) speedCategories.merge("Moderate (10k-30k km/h)", 1, Integer::sum);
                else speedCategories.merge("Fast (>30k km/h)", 1, Integer::sum);
            });
        });
        Map<String, Object> result = new HashMap<>();
        result.put("sizes", Map.of(
                "labels", sizeCategories.keySet(),
                "values", sizeCategories.values()
        ));
        result.put("speeds", Map.of(
                "labels", speedCategories.keySet(),
                "values", speedCategories.values()
        ));
        return result;
    }
}
フロントエンド: NeoWs.js
import React, { useState } from 'react';
const NeoWs = () => {
    const [startDate, setStartDate] = useState('');
    const [endDate, setEndDate] = useState('');
    const [chartData, setChartData] = useState(null);
    const fetchNeoData = async () => {
        if (!startDate || !endDate) {
            alert("Please enter both start and end dates.");
            return;
        }
        const url = `/api/neows?start_date=${startDate}&end_date=${endDate}`;
        console.log("Requesting URL:", url);
        try {
            const response = await fetch(url);
            if (!response.ok) {
                throw new Error(`HTTP error: ${response.status}`);
            }
            const data = await response.json();
            setChartData(data);
        } catch (error) {
            console.error("Error fetching data:", error);
            alert("Error fetching NEO data.");
        }
    };
    return (
        <div>
            <h1>NEO Classification</h1>
            <input type="date" value={startDate} onChange={(e) => setStartDate(e.target.value)} />
            <input type="date" value={endDate} onChange={(e) => setEndDate(e.target.value)} />
            <button onClick={fetchNeoData}>Fetch Data</button>
        </div>
    );
};
export default NeoWs;
補足情報
•	開発環境: DockerでSpring BootとReactを使用。