DEV Community

Dubaguntla Charan Sai
Dubaguntla Charan Sai

Posted on

Connecting a React Dashboard to a Spring Boot Backend: Lessons from Building CropCare AI

When I set out to build CropCare — a full-stack platform that helps farmers detect crop diseases and get support — the hardest part wasn't the AI or the database. It was something a lot of tutorials skip over: getting a React dashboard to reliably talk to a Spring Boot backend and render live, changing data without breaking.

If you're building your first full-stack app and want your frontend and backend to actually work together smoothly, here's what I learned — and how you can set yours up the same way.

The Stack

  • Backend: Java, Spring Boot, MySQL, REST APIs, JWT authentication
  • Frontend: React.js
  • Goal: Dashboards showing bar charts, pie charts, and analytics reports that update based on real backend data — not hardcoded values

Step 1: Design Your API Before Your UI

The first mistake I almost made was designing the dashboard UI first and figuring out the data later. That's backwards. Before writing a single React component, I mapped out exactly what data each chart needed:

  • Disease detection counts by crop type → needed a GET /api/reports/disease-summary endpoint
  • Support ticket status breakdown → needed GET /api/tickets/status-summary

Each endpoint returned a clean, predictable JSON shape — no nested surprises the frontend would have to untangle.

Lesson: decide your JSON shape first. It saves you from rewriting chart logic later.

@GetMapping("/api/reports/disease-summary")
public ResponseEntity<List<DiseaseSummaryDTO>> getDiseaseSummary() {
    return ResponseEntity.ok(reportService.getDiseaseSummary());
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Secure the Endpoints Without Blocking Yourself

Since CropCare uses JWT authentication with role-based access, every dashboard call needed a valid token attached.

Early on, I kept getting silent failures — the dashboard would just show blank charts with no error.

The fix was to centralize the API calls through a single Axios instance that automatically attaches the token and handles 401s consistently:

const api = axios.create({ baseURL: "/api" });

api.interceptors.request.use((config) => {
  const token = localStorage.getItem("token");

  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }

  return config;
});
Enter fullscreen mode Exit fullscreen mode

Lesson: don't attach auth headers manually in every component. Centralize it once, or you'll spend hours debugging "blank chart" bugs that are actually silent 401 errors.

Step 3: Keep the Frontend Dumb, the Backend Smart

My first version tried to do data aggregation — counting diseases per crop type and calculating percentages — inside React.

It worked, but it was messy and slow to change.

I moved that logic to the backend instead. The API returns data already shaped for the chart library, and React just renders it:

[
  {
    "cropType": "Wheat",
    "diseaseCount": 12
  },
  {
    "cropType": "Rice",
    "diseaseCount": 7
  }
]
Enter fullscreen mode Exit fullscreen mode

Lesson: if your frontend is doing math or aggregation, ask whether that logic belongs on the server instead. It usually does.

Step 4: Handle the Loading/Empty/Error States Explicitly

The dashboards felt broken during demos until I added three explicit UI states for every chart:

  1. Loading — skeleton or spinner
  2. Empty — no data yet, with a friendly message instead of a blank chart
  3. Error — something went wrong, with a retry button instead of a silent failure
if (loading) return <Spinner />;

if (error) {
  return <ErrorMessage onRetry={fetchData} />;
}

if (!data.length) {
  return <EmptyState message="No disease reports yet." />;
}

return <BarChart data={data} />;
Enter fullscreen mode Exit fullscreen mode

This one change made the dashboards feel production-ready instead of like a student project.

What I'd Tell Someone Starting This Today

If you're building something similar:

  1. Design your API response shape before touching the frontend.
  2. Centralize auth handling — don't repeat it per component.
  3. Push aggregation logic to the backend.
  4. Always design for loading, empty, and error states, not just the "happy path."

None of this is complicated once you know it — but I didn't know it until I'd rebuilt the CropCare dashboards twice.

Hopefully this saves you a round trip.


CropCare is a full-stack crop disease detection and farmer support platform built with Java, Spring Boot, React.js, and MySQL.

Live demo

Top comments (0)