JSON, records and the request body
A REST API usually exchanges data as JSON. The quarkus-rest-jackson extension turns Java objects into JSON and back through the Jackson library. You do not write the conversion by hand: a method returns an object, and the client receives JSON. The handiest way to describe data is records from Java 16+: short immutable classes whose fields Jackson can see.
The post model
// src/main/java/com/example/blog/Post.java
package com.example.blog;
public record Post(long id, String title, String body) {
}The Post record describes a blog post: its number, title and text.
A list of posts as JSON
Until a database is connected, we keep posts in memory, in a field of the resource. The resource is created once, so the data lives while the application runs. Rewrite PostResource like this:
// src/main/java/com/example/blog/PostResource.java
package com.example.blog;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
@Path("/posts")
public class PostResource {
private final Map<Long, Post> posts = new ConcurrentHashMap<>();
private final AtomicLong nextId = new AtomicLong(1);
public PostResource() {
long id = nextId.getAndIncrement();
posts.put(id, new Post(id, "Hello, Quarkus", "The first post of the blog."));
}
@GET
public List<Post> list() {
return List.copyOf(posts.values());
}
@GET
@Path("/{id}")
public Post get(@PathParam("id") long id) {
Post post = posts.get(id);
if (post == null) {
throw new NotFoundException();
}
return post;
}
}ConcurrentHashMap and AtomicLong are safe when requests arrive at the same time. There is no @Produces: for objects Quarkus returns JSON by itself. The NotFoundException exception becomes a 404 response.
curl http://localhost:8080/posts
curl http://localhost:8080/posts/1[{"id":1,"title":"Hello, Quarkus","body":"The first post of the blog."}]
{"id":1,"title":"Hello, Quarkus","body":"The first post of the blog."}
Accepting JSON in a POST request
For the data the client sends we create a separate record — without an id, because the server assigns it:
// src/main/java/com/example/blog/NewPost.java
package com.example.blog;
public record NewPost(String title, String body) {
}A method parameter without an annotation is the request body. Quarkus reads the JSON and creates a NewPost from it:
// src/main/java/com/example/blog/PostResource.java
@POST
public Response create(NewPost newPost) {
long id = nextId.getAndIncrement();
Post post = new Post(id, newPost.title(), newPost.body());
posts.put(id, post);
return Response.created(URI.create("/posts/" + id))
.entity(post)
.build();
}Add the imports jakarta.ws.rs.POST, jakarta.ws.rs.core.Response and java.net.URI. The Response class lets you set the status and headers: created(...) gives the 201 Created status and a Location header with the address of the new post, and entity(...) sets the response body.
curl -i -X POST http://localhost:8080/posts \
-H "Content-Type: application/json" \
-d '{"title":"Second post","body":"Text"}'HTTP/1.1 201 Created
Content-Type: application/json;charset=UTF-8
Location: http://localhost:8080/posts/2
{"id":2,"title":"Second post","body":"Text"}
Deleting and a response without a body
// src/main/java/com/example/blog/PostResource.java
@DELETE
@Path("/{id}")
public Response delete(@PathParam("id") long id) {
if (posts.remove(id) == null) {
throw new NotFoundException();
}
return Response.noContent().build();
}curl -i -X DELETE http://localhost:8080/posts/2 returns HTTP/1.1 204 No Content — a successful answer without a body. The import is jakarta.ws.rs.DELETE.
Common mistake
If you send JSON without the Content-Type header, the server does not accept it:
curl -i -X POST http://localhost:8080/posts -d '{"title":"x","body":"y"}'HTTP/1.1 415 Unsupported Media Type
content-length: 0
With the -d flag curl sends application/x-www-form-urlencoded by default, while the method expects JSON. Add -H "Content-Type: application/json". If the JSON itself is broken, for example a brace is missing, the answer is 400 Bad Request.