Building a Feedback Form with Spring Boot and Google Sheets API
A feedback form is a useful way to collect comments from customers, members, or website visitors without building a complete administration dashboard. With Spring Boot handling the HTTP request and Google Sheets storing each submission, a small application can be ready quickly and remain easy for a non-technical team to manage.
This approach suits Australian businesses that want practical visibility over customer opinions. A café in Brisbane, a trades company in Perth, or a consultancy serving clients in Sydney can review responses in a familiar spreadsheet while the Java application manages validation, security, and integration details.
Prepare the Google Sheets integration
Create a Google Cloud project and enable the Google Sheets API. Create a service account, download its JSON key, and copy the service account email address. In Google Sheets, share the target spreadsheet with that address as an Editor.
Add a worksheet called Feedback and create a header row such as:
Name | Email | Rating | Message | Submitted At
For a Spring Boot application, include the Google API client dependencies:
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-sheets</artifactId>
<version>v4-rev20230815-2.0.0</version>
</dependency>
<dependency>
<groupId>com.google.auth</groupId>
<artifactId>google-auth-library-oauth2-http</artifactId>
<version>1.23.0</version>
</dependency>
Store the credentials outside source control. A local development setup can use GOOGLE_APPLICATION_CREDENTIALS, while a hosted application should use a secret manager or an injected environment variable.
Configure Spring Boot and Google credentials
The service account needs a Google Sheets scope. The following configuration reads the credentials file and creates a Sheets client:
@Configuration
public class GoogleSheetsConfig {
@Value("${google.sheets.credentials}")
private Resource credentials;
@Bean
Sheets sheetsClient() throws IOException {
GoogleCredentials googleCredentials =
GoogleCredentials.fromStream(credentials.getInputStream())
.createScoped(
Collections.singleton(
SheetsScopes.SPREADSHEETS));
return new Sheets.Builder(
GoogleNetHttpTransport.newTrustedTransport(),
JacksonFactory.getDefaultInstance(),
new HttpCredentialsAdapter(googleCredentials))
.setApplicationName("Feedback Form")
.build();
}
}
Set the spreadsheet ID and credentials path in application.yml. The spreadsheet ID is the value between /d/ and /edit in its Google Sheets URL.
google:
sheets:
spreadsheet-id: ${GOOGLE_SHEET_ID}
credentials: ${GOOGLE_APPLICATION_CREDENTIALS}
Keep the sheet in a controlled Google Workspace account. Australian organisations should consider who can access personal information under the Privacy Act 1988 and the Australian Privacy Principles. A shared sheet containing email addresses should not be left open to everyone with the link.
Create the feedback model and endpoint
A record keeps the request model compact and makes validation clear:
public record FeedbackRequest(
@NotBlank String name,
@Email @NotBlank String email,
@Min(1) @Max(5) int rating,
@NotBlank @Size(max = 2000) String message
) {}
The controller accepts JSON from a web form or JavaScript client. @Valid rejects missing, invalid, or oversized values before they reach Google Sheets.
@RestController
@RequestMapping("/api/feedback")
public class FeedbackController {
private final FeedbackService service;
public FeedbackController(FeedbackService service) {
this.service = service;
}
@PostMapping
public ResponseEntity<Void> submit(
@Valid @RequestBody FeedbackRequest request) {
service.save(request);
return ResponseEntity.status(HttpStatus.CREATED).build();
}
}
A browser form can send a request using fetch:
await fetch("/api/feedback", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({
name: "Taylor Nguyen",
email: "taylor@example.com",
rating: 5,
message: "Great service and a quick response."
})
});
Append responses to the worksheet
The service converts the submitted values into a Google Sheets row. Use an ISO timestamp so entries from Melbourne, Adelaide, or Darwin remain unambiguous when staff review them later.
@Service
public class FeedbackService {
private final Sheets sheets;
private final String spreadsheetId;
private static final String RANGE = "Feedback!A:E";
public FeedbackService(
Sheets sheets,
@Value("${google.sheets.spreadsheet-id}") String spreadsheetId) {
this.sheets = sheets;
this.spreadsheetId = spreadsheetId;
}
public void save(FeedbackRequest feedback) {
List<Object> row = List.of(
feedback.name(),
feedback.email(),
feedback.rating(),
feedback.message(),
OffsetDateTime.now(ZoneOffset.UTC).toString()
);
ValueRange body = new ValueRange()
.setValues(List.of(row));
try {
sheets.spreadsheets().values()
.append(spreadsheetId, RANGE, body)
.setValueInputOption("USER_ENTERED")
.setInsertDataOption("INSERT_ROWS")
.execute();
} catch (IOException exception) {
throw new FeedbackStorageException(
"Unable to save feedback", exception);
}
}
}
Add an exception handler that returns a useful 400 response for validation failures and a controlled 503 response when Google is temporarily unavailable. Logging should include a request identifier, but avoid writing email addresses or message content into application logs.
Harden and operate the form
A public feedback endpoint can attract spam, duplicate submissions, and automated abuse. Add rate limiting, CSRF protection where appropriate, a honeypot field, and a CAPTCHA service if the form becomes a target. Configure CORS narrowly rather than allowing every origin.
For Australian users, display a clear privacy notice explaining why contact details are collected and how long they are retained. Use Australian spelling in the interface, such as “Your feedback has been submitted”, and show dates in a friendly local format while retaining UTC in the stored record. A business serving both Sydney and Perth should avoid relying on the server’s default timezone.
Useful operational safeguards include:
- Keep the Google service-account key in a secret manager, never in Git.
- Share the spreadsheet with the service account only, using the minimum required access.
- Validate names, email addresses, ratings, and message length on the server.
- Add retry handling with backoff for temporary Google API failures.
- Record a submission ID to help identify duplicate requests.
- Back up important feedback because a spreadsheet is not a full database.
- Check Australian privacy, retention, and consent requirements before collecting personal data.
For higher traffic, place submissions into a queue and let a background worker update Sheets. This prevents a slow Google API response from delaying customers who are submitting feedback after a service call, booking, or support interaction.