In this hands-on tutorial, we will delve into using React Query with Hilla to connect to Spring Boot through type-safe backend services. We will be creating a simple Hilla application that integrates React Query on the frontend. As we walk through the process, you'll gain an understanding of how to build fast, efficient, and type-safe applications using Java and TypeScript.
Prerequisites
Before diving in, make sure you have a basic understanding of React, Java, and Spring Boot. A little familiarity with TypeScript will also be helpful.
Step 1: Setting Up the Application
Let's kick things off by setting up our backend. For our demonstration, we'll create a simple Todo
entity and a TodoRepository
to save these entities to a database. Here's what our basic Todo
entity looks like:
@Entity
public class Todo {
@Id
@GeneratedValue
private Long id;
private String task;
// getters, setters
}
And our TodoRepository
is a simple Spring Boot repository:
public interface TodoRepository extends JpaRepository<Todo, Long> {}
Next, we'll create a TodoService
class. This is a browser-callable service that fetches all todos and saves a todo:
@BrowserCallable
@AnonymousAllowed
public class TodoService {
private final TodoRepository todoRepository;
public TodoService(TodoRepository todoRepository) {
this.todoRepository = todoRepository;
}
public List<Todo> getTodos() {
return todoRepository.findAll();
}
public Todo save(Todo todo) {
return todoRepository.save(todo);
}
}
Step 2: Installing React Query
Now, let's integrate React Query into our project. Visit the TanStack React Query website and locate the npm install instruction. Open your terminal, navigate to the root of your project, and run the following command:
npm i @tanstack/react-query
Step 3: Setting Up the Query Client Provider
With React Query installed, we need to set up a query client and wrap our application with it. This is done in the App.tsx
file:
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient();
export default function App() {
return (
<QueryClientProvider client={queryClient}> // Your application code here </QueryClientProvider>
);
}
Step 4: Using React Query and Hilla in Our View
Now that our query client provider is set up, navigate to your view (we'll use HelloWorldView.tsx
for this tutorial).
We'll start by importing the necessary modules:
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { TodoService } from "Frontend/generated/endpoints";
import { useForm } from "@hilla/react-form";
import TodoModel from "Frontend/generated/com/example/application/db/TodoModel";
import { TextField } from "@hilla/react-components/TextField";
import { Button } from "@hilla/react-components/Button";
Next, we'll set up our queries and mutations using React Query hooks and our TodoService
. We'll use the useForm
hook from Hilla to create a form for adding new todos:
export default function HelloWorldView() {
const queryClient = useQueryClient();
const query = useQuery({
queryKey: ["todos"],
queryFn: TodoService.getTodos,
});
const mutation = useMutation({
mutationFn: TodoService.save,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["todos"] });
},
});
const { model, field, submit } = useForm(TodoModel, {
onSubmit: async (todo) => mutation.mutate(todo),
});
Finally, we'll build out our UI. We'll create a form for adding new todos and a list for displaying all existing todos:
return (
<div className="p-m"> <h1>Hilla todo</h1> <div className="flex gap-m"> <TextField {...field(model.task)} /> <Button onClick={submit}>Add todo</Button> </div> <ul> {query.data?.map((todo) => (
<li key={todo.id}>{todo.task}</li>
))} </ul> </div>
);
}
And that's it! You've successfully integrated React Query with Hilla for type-safe backend services. Refresh your browser and test out your new application. Enjoy the full type safety experience from backend to frontend, powered by Java and TypeScript!