I'm building a todo app with a Spring Boot backend and a React frontend. The backend provides the API, and the project is containerized using Docker.
My @GetMapping requests work correctly, but my @PostMapping method does not seem to be working at all. When I try to add a new todo item, I get a 405 Method Not Allowed error. Additionally, I don't see any logs in Spring, which makes me think the request isn't even reaching the backend.
What could be causing this issue? Any help would be appreciated!
Forum Component:
const handleSubmit = async (e) => {
e.preventDefault();
if (title.trim() === "") return;
try {
const response = await fetch(`${baseUrl}/api/todos/`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ title }),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const newTodo = await response.json();
onAdd(newTodo);
setTitle("");
} catch (error) {
console.error("There was an error adding the todo:", error);
}
};
Controller Mappings:
@RestController
@RequestMapping("/api")
public class TodoController {
private static final Logger logger = LoggerFactory.getLogger(TodoController.class);
@Autowired
private TodoService todoService;
@GetMapping("/todos/")
public ResponseEntity<List<TodoResponse>> getAllTodos() {
logger.info("GET request to /api/todos");
List<TodoResponse> response = todoService.getTodos();
return ResponseEntity.ok(response);
}
@GetMapping("/todos/{id}")
public ResponseEntity<TodoResponse> getTodoById(@PathVariable Long id) {
logger.info("GET request to /api/todos/{}", id);
TodoResponse response = todoService.getToDoById(id);
return ResponseEntity.ok(response);
}
@PostMapping("/todos/")
public ResponseEntity<TodoResponse> addTodo(@RequestBody TodoRequest request) {
logger.info("POST request to /api/todos with body: {}", request);
TodoResponse response = todoService.createToDo(request);
return ResponseEntity.ok(response);
}
@PutMapping("/todos/{id}")
public ResponseEntity<TodoResponse> updateTodo(@PathVariable Long id, @RequestBody TodoRequest request) {
logger.info("PUT request to /api/todos/{} with body: {}", id, request);
TodoResponse response = todoService.updateToDo(id, request);
return ResponseEntity.ok(response);
}
@DeleteMapping("/todos/{id}")
public ResponseEntity<Void> deleteTodo(@PathVariable Long id) {
logger.info("DELETE request to /api/todos/{}", id);
todoService.deleteToDoById(id);
return ResponseEntity.noContent().build();
}
}
Vite proxy config:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import dns from "node:dns";
dns.setDefaultResultOrder("verbatim");
// /config/
export default defineConfig({
plugins: [react()],
server: {
proxy: {
"/api/todos/": {
target: "http://build-api:8080",
changeOrigin: true,
secure: false,
},
},
},
});
Docker Compose file:
services:
# Creates the database service.
db:
# The image being used. Currently the "lastest" postgres image.
image: postgres
# Restart on error unless manually stopped.
restart: unless-stopped
environment:
# Sets the password for the database user.
POSTGRES_PASSWORD: secret
#Exposes port 5432 to the host.
ports:
- 5432:5432
# Use the backend container network.
networks:
- backend
# Create persistent data by saving to a docker volume.
volumes:
- ghg-data:/var/lib/postgresql/data
# For building the api service image
build-api:
# names the service.
image: spring-api
# Where the source code and Dockerfile live.
depends_on:
- db
build:
context: ./api/
dockerfile: ./Dockerfile
ports:
- 8080:8080
# Set this so spring can find the db as it differs from on localhost.
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/postgres
networks:
- backend
client-nginx:
image: client-nginx
build:
context: ./frontend/
dockerfile: Dockerfile
networks:
- backend
ports:
- 8081:80
client-vite:
image: client-vite
build:
context: ./frontend/
dockerfile: Dockerfile-vite
volumes:
- type: bind
source: ./frontend/
target: /usr/src/app/
- type: volume
target: /usr/src/app/node_modules
- type: bind
source: ./frontend/vite.config.js
target: /usr/src/app/vite.config.js
networks:
- backend
ports:
- 5173:5173
# Creates the volume.
volumes:
ghg-data:
# Creates the network. Can later create a frontend network to seperate
# the frontend and backend.
networks:
backend:
frontend: