DEV Community

paohaijiao
paohaijiao

Posted on Originally published at paohaijiao.hashnode.dev

REST in Annotation Mode: JSON GET, POST, PUT, and DELETE with @JCurlCommand

After the Hello World in Post 1, it's time for the classic backend bread-and-butter: a full JSON CRUD cycle. In annotation mode each HTTP verb is just a different curl command sitting on its own interface method. We'll build a small user-management demo against the free ReqRes REST API and show four return-type styles: String, a domain object, a collection, and Void.

The curl Commands Behind Each Verb

# Read one resource (returns a JSON object)
curl -X GET https://reqres.in/api/users/2

# Create a resource (sends JSON, returns the created object with a new id)
curl -X POST https://reqres.in/api/users \
  -H 'Content-Type: application/json' \
  -d '{"name":"Ada","job":"Engineer"}'

# Replace / update a resource
curl -X PUT https://reqres.in/api/users/2 \
  -H 'Content-Type: application/json' \
  -d '{"name":"Ada Lovelace","job":"Analyst"}'

# Delete a resource (returns an empty body, HTTP 204)
curl -X DELETE https://reqres.in/api/users/2
Enter fullscreen mode Exit fullscreen mode

Every one of these strings is a valid @JCurlCommand value — no request-builder API involved.

The CRUD Interface

import com.github.paohaijiao.anno.JCurlCommand;
import com.github.paohaijiao.domain.req.JQuickCurlReq;

public interface UserCrudApi {

    @JCurlCommand("curl -X GET 'https://reqres.in/api/users/${id}'")
    String getUser(JQuickCurlReq request);

    @JCurlCommand("curl -X POST 'https://reqres.in/api/users' "
            + "-H 'Content-Type: application/json' "
            + "-d '{\"name\":\"${name}\",\"job\":\"${job}\"}'")
    String createUser(JQuickCurlReq request);

    @JCurlCommand("curl -X PUT 'https://reqres.in/api/users/${id}' "
            + "-H 'Content-Type: application/json' "
            + "-d '{\"name\":\"${name}\",\"job\":\"${job}\"}'")
    String updateUser(JQuickCurlReq request);

    @JCurlCommand("curl -X DELETE 'https://reqres.in/api/users/${id}'")
    Void deleteUser(JQuickCurlReq request);
}
Enter fullscreen mode Exit fullscreen mode

Observations:

  • Method name, HTTP verb, and URL are decoupled — rename methods freely.
  • The return type drives conversion: here String and Void (Void discards the body for DELETE).
  • JSON bodies use the ${...} placeholders from Post 5, so createUser and updateUser share one command shape each.

Driving the Full Cycle

import com.github.paohaijiao.domain.req.JQuickCurlReq;
import com.github.paohaijiao.executor.JCurlInvoker;

public class CrudDemo {

    public static void main(String[] args) {
        UserCrudApi api = JCurlInvoker.createProxy(UserCrudApi.class);

        // CREATE
        JQuickCurlReq create = new JQuickCurlReq();
        create.put("name", "Ada");
        create.put("job", "Engineer");
        String created = api.createUser(create);
        System.out.println("created: " + created);   // JSON incl. new id

        // READ (needs an id — parse it from the create response in real code)
        JQuickCurlReq read = new JQuickCurlReq();
        read.put("id", 2);
        System.out.println("read: " + api.getUser(read));

        // UPDATE
        JQuickCurlReq update = new JQuickCurlReq();
        update.put("id", 2);
        update.put("name", "Ada Lovelace");
        update.put("job", "Analyst");
        System.out.println("updated: " + api.updateUser(update));

        // DELETE
        JQuickCurlReq delete = new JQuickCurlReq();
        delete.put("id", 2);
        api.deleteUser(delete);
        System.out.println("deleted OK");
    }
}
Enter fullscreen mode Exit fullscreen mode

Expected behavior: ReqRes is a mock API, so responses are deterministic — POST /api/users answers with the payload plus an id, and DELETE returns HTTP 204.

Beyond String: Typed Return Values

Declaring the method return type is all it takes to skip manual JSON parsing. The response converter maps the JSON body onto your class — mirror the response keys with your fields:

public class UserInfo {
    private long id;
    private String email;
    private String name;
    private String job;

    // getters and setters omitted for brevity
}
Enter fullscreen mode Exit fullscreen mode
public interface TypedCrudApi {

    @JCurlCommand("curl -X GET 'https://reqres.in/api/users/${id}'")
    UserInfo getUser(JQuickCurlReq request);

    @JCurlCommand("curl -X GET 'https://reqres.in/api/users?page=${page}'")
    java.util.List<UserInfo> listUsers(JQuickCurlReq request);
}
Enter fullscreen mode Exit fullscreen mode

Other supported return types worth remembering:

Return type Result
String Raw response body as text
byte[] Raw body bytes (great for binary endpoints)
Your POJO Body JSON bound to the object
java.util.List<T> JSON array bound to a list of objects
Void Execute and discard the response body
JResult / JQuickCurlResponseBody Keep the raw response with extra accessors

PUT, PATCH, DELETE, HEAD, OPTIONS, TRACE

JQuickCurl's parser and executor cover eight methods today: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, and TRACE. PATCH follows the same pattern as PUT with a smaller payload:

@JCurlCommand("curl -X PATCH 'https://reqres.in/api/users/${id}' "
        + "-H 'Content-Type: application/json' "
        + "-d '{\"job\":\"${job}\"}'")
String patchUser(JQuickCurlReq request);
Enter fullscreen mode Exit fullscreen mode

HEAD and OPTIONS usually return no meaningful body — return Void — and TRACE is a bodyless diagnostic verb on the executor side.

Summary

CRUD in annotation mode is literally your tested curl commands with the volatile values promoted to ${...} placeholders. Method naming, return-type conversion (String, POJO, List, Void, byte[], …), and header handling are all declarative. This is the pattern the rest of the series builds on — XML configuration, conditionals, uploads, and downloads are just more curl commands wearing different clothes.

Everything here runs against the open-source dromara/jquick-curl. Post 7 shows the same idea, but with API definitions lifted out of Java entirely and into XML.

java #springboot #httpclient #opensource #java-library

Top comments (0)