# Spring Boot REST API tutorial, part 2: Add GET and DELETE endpoints

> For the complete documentation index, see [llms.txt](https://www.jetbrains.com/help/idea/2026.3/llms.txt).

* [1: Create and run a Spring Boot project](spring-boot-tutorial-create-project.html)

* [2: Add REST endpoints]()

* [3: Upload and download files](spring-boot-tutorial-file-upload-download.html)

* [4: Store data in H2 and package a JAR](spring-boot-tutorial-h2-database-jar.html)

Continue the project from [Spring Boot REST API tutorial, part 1: Create and run a project](spring-boot-tutorial-create-project.html). In this part, add a photo model, a service that stores photos in memory, and endpoints to list, retrieve, and delete them.

Procedure: Create the Photo model class

The greeting endpoint returns a string, but the photo endpoints need to work with structured data. A `Photo` represents that data in Java. It starts with an identifier and a file name; the image bytes arrive in the next part, when you add uploads.

1. In the `model` package, create a class named `Photo` with an identifier, a file name, and two constructors:

```JAVA
public class Photo {

    private String id;
    private String fileName;

    public Photo() {
    }

    public Photo(String id, String fileName) {
        this.id = id;
        this.fileName = fileName;
    }

}
```

The two-argument constructor creates the example photo used below. The no-argument constructor is used when adding uploaded photos in the next part.

2. Generate accessors for both fields: place the caret inside the class, press `Alt+Insert` (Windows), `⌘ N` (macOS), `⌃ N` (IntelliJ IDEA Classic (macOS)), `⌘ N` (macOS System Shortcuts), `Alt+Insert` (XWin), `Alt+Insert` (GNOME), `Alt+Insert` (KDE), `Alt+Insert` (Emacs), `Alt+Insert` (Sublime Text), `⌘ N` (Sublime Text (macOS)), `Alt+Insert` (Visual Studio), `⌘ ⌃ N` (Visual Studio (macOS)), `Alt+Insert` (NetBeans), `Alt+Insert` (Eclipse), `⌘ N` (Eclipse (macOS)), select Getter and Setter, then select both fields, and click OK.

![Generate popup open in the Photo class with Getter and Setter selected to create accessor methods for the id and fileName fields.](https://resources.jetbrains.com/help/img/idea/2026.3/sb_generate_getter_setter.png)

Spring uses Jackson to convert the photos returned by your endpoints into JSON. Jackson reads the photo properties through the getters, so the JSON response includes `id` and `fileName`. The setters let you assign these values when creating photos.

> **Tip:**
> For everything IntelliJ IDEA can generate, refer to [Generate code](generating-code.html).

Procedure: Create and inject a photo service

A controller translates an HTTP request into a call, and a result back into a response. Create a service to store the photos that `PhotosController` will return.

In the next part, you will add a second controller to serve image downloads. Both controllers will need the same photos. Giving each its own map would leave the application with two separate sets of photos, so both will use the same service.

The service also becomes the place for logic that is not HTTP translation, such as building a photo out of an uploaded file. Both controllers can use this service, and the service can switch from a map to a database without putting database operations in either controller.

1. In the `service` package, create `PhotosService`:

```JAVA
@Service
public class PhotosService {

    private final Map<String, Photo> db = new HashMap<>();

    public PhotosService() {
        db.put("1", new Photo("1", "hello.jpg"));
    }

    public Collection<Photo> get() {
        return db.values();
    }

    public Photo get(String id) {
        return db.get(id);
    }

    public Photo remove(String id) {
        return db.remove(id);
    }

}
```

Use the `java.util` imports for `Map`, `HashMap`, and `Collection`, and import `Photo` from your `model` package that IntelliJ IDEA suggests.

`@Service` tells Spring to create an instance at startup, the same way `@RestController` does. The map stores photos by identifier, so retrieving or removing one does not require searching a list. It starts with one example, `hello.jpg`, to give the endpoints something to return. This is metadata only, there is no image file yet.

2. In `PhotosController`, add a field and a constructor for the service. Keep the `hello()` method:

```JAVA
private final PhotosService photosService;

public PhotosController(PhotosService photosService) {
    this.photosService = photosService;
}
```

Nothing calls this constructor in your code. At startup Spring sees that `PhotosController` requires a `PhotosService`, finds the class annotated with `@Service`, creates one instance, and passes it in. This is constructor injection, and one instance is created and shared — which is what lets the second controller see the same photos in the next part.

> **Note:**
> Spring supports other injection styles. Annotating the field with `@Autowired` and dropping the constructor has the same effect, and `@Autowired` on a  constructor is allowed but unnecessary, since a single constructor is used automatically.
>
>
>
> Constructor injection is used throughout this tutorial because it allows the field to be `final` and makes the dependency visible in the signature: the class cannot be constructed without a service, so it cannot exist in a half-initialized state.

Procedure: Add a GET endpoint for all photos

The model and storage are in place. The first photo endpoint connects them to the browser by asking the service for all stored photos and returning the result.

1. In `PhotosController`, add a method for GET requests mapped to `/photos`:

```JAVA
@GetMapping("/photos")
public Collection<Photo> get() {
    return photosService.get();
}
```

2. Restart the application: click ![the Rerun button](https://resources.jetbrains.com/help/img/idea/2026.3/app.expui.run.rerun.svg) Rerun in the [Run](run-tool-window.html) tool window. Then open `http://localhost:8080/photos`. The browser shows a JSON array holding the one photo:

```JSON
[{"fileName":"hello.jpg","id":"1"}]
```

![Browser at localhost:8080/photos showing a JSON array with one photo object that has the identifier 1 and the file name hello.jpg.](https://resources.jetbrains.com/help/img/idea/2026.3/sb_return_list_of_photos.png)

Nothing in the method explicitly creates JSON. It returns a Java collection, and because the class is a `@RestController`, Spring converts the return value into the response. The getters you generated provide the photo properties that appear in the JSON array.

Procedure: Add a GET endpoint for one photo

1. Add another `get()` method to `PhotosController`:

```JAVA
@GetMapping("/photos/{id}")
public Photo get(@PathVariable String id) {
    Photo photo = photosService.get(id);
    if (photo == null) {
        throw new ResponseStatusException(HttpStatus.NOT_FOUND);
    }
    return photo;
}
```

The braces in `/photos/{id}` identify a variable part of the path. `@PathVariable` passes it into the parameter with the same name, so a request for `/photos/1` looks up the photo with identifier `1`.

The service returns `null` when it finds no photo. The controller turns that absence into `404 Not Found` by throwing `ResponseStatusException`. This keeps the HTTP response handling in the controller.

2. Restart the application and open `http://localhost:8080/photos/1` to see the photo as a JSON object. Open `http://localhost:8080/photos/2` to check the 404 response. Because there is no photo with this identifier, the browser shows the default Spring Boot Whitelabel Error Page with the `type=Not Found, status=404` message.

![Whitelabel Error Page at localhost:8080/photos/2 with a Not Found error and status 404 because no photo with this identifier exists.](https://resources.jetbrains.com/help/img/idea/2026.3/sb_404_error.png)

Procedure: Add a DELETE endpoint

1. Add a method for DELETE requests to `PhotosController`:

```JAVA
@DeleteMapping("/photos/{id}")
public void delete(@PathVariable String id) {
    Photo photo = photosService.remove(id);
    if (photo == null) {
        throw new ResponseStatusException(HttpStatus.NOT_FOUND);
    }
}
```

`@DeleteMapping` handles a different HTTP verb on the same path. The service returns the removed photo, or `null` if it was absent, which the controller turns into a 404.

2. Restart the application and open `http://localhost:8080/photos`.

Open the browser developer tools, and switch to the Console tab.

Send a DELETE request for the photo that exists:

```JAVASCRIPT
(async function deletePhoto(id) {
    await fetch("http://localhost:8080/photos/" + id, {
        method: "DELETE"
    })
})("1")
```

![Browser developer tools Console tab with an async fetch call that sends a DELETE request to http://localhost:8080/photos/1.](https://resources.jetbrains.com/help/img/idea/2026.3/sb_console_delete_1.png)

Reload `http://localhost:8080/photos`. The list is now empty: `[]`.

![Browser at http://localhost:8080/photos showing an empty JSON array after the DELETE request removed photo 1.](https://resources.jetbrains.com/help/img/idea/2026.3/sb_localhost_empty_array.png)

[Next part](spring-boot-tutorial-file-upload-download.html)

