{"id":37022159,"url":"https://github.com/guinetik/rocketrest","last_synced_at":"2026-01-14T02:38:22.720Z","repository":{"id":294401150,"uuid":"986832265","full_name":"guinetik/RocketRest","owner":"guinetik","description":"A small Java 8 library for making HTTP API requests with a clean, fluent interface.","archived":false,"fork":false,"pushed_at":"2025-12-20T20:04:17.000Z","size":237,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-22T11:46:52.491Z","etag":null,"topics":["async","circuitbreaker","http-client","java","jdk8","rest-api"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/guinetik.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-20T07:29:39.000Z","updated_at":"2025-12-20T20:04:11.000Z","dependencies_parsed_at":"2025-05-20T09:27:33.603Z","dependency_job_id":"19e01472-4ac7-4eac-8b67-c24b0957bed7","html_url":"https://github.com/guinetik/RocketRest","commit_stats":null,"previous_names":["guinetik/rocketrest"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/guinetik/RocketRest","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guinetik%2FRocketRest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guinetik%2FRocketRest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guinetik%2FRocketRest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guinetik%2FRocketRest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/guinetik","download_url":"https://codeload.github.com/guinetik/RocketRest/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guinetik%2FRocketRest/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408711,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","circuitbreaker","http-client","java","jdk8","rest-api"],"created_at":"2026-01-14T02:38:21.913Z","updated_at":"2026-01-14T02:38:22.700Z","avatar_url":"https://github.com/guinetik.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RocketRest\r\n\r\nRocketRest is a small Java 8 library for making HTTP API requests with a clean, fluent interface. It provides both synchronous and asynchronous APIs, abstracting away the underlying HTTP client implementation.\r\n\r\n## Features\r\n\r\n- Only 2 Dependencies: Jackson for JSON and SLF4J.\r\n- Default Client implementation using `HttpUrlConnection` as vanilla as we can.\r\n- Abstract architecture allowing different HTTP client implementations.\r\n- Support for both synchronous and asynchronous requests using `CompletableFuture`.\r\n- Automatic JSON serialization/deserialization.\r\n- Fluent API for HTTP requests (GET, POST, PUT, DELETE).\r\n- Result pattern for elegant error handling.\r\n- Pluggable authentication strategies.\r\n- Customizable error handling\r\n- Circuit breaker pattern for resilient API calls\r\n- Factory-based client creation with decorators\r\n- Comprehensive mocking support for testing\r\n- Pluggable credential-refresh hooks (e.g., custom logic for bearer tokens)\r\n\r\n## Motivation\r\n\r\nOver the years, I've accumulated a collection of standards and patterns for making HTTP requests in Java applications. RocketRest is my attempt to bring everything together in a single project that's compatible with JDK 8 with minimal dependencies. Working with legacy Java projects taught me the importance of having a modern, fluent API without requiring the latest JDK or carrying heavy dependency baggage. This library is an attempt to bring balance between contemporary design with legacy compatibility.\r\n\r\n## Quick Start\r\n\r\nAdd RocketRest to your Maven project:\r\n\r\n```xml\r\n\u003cdependency\u003e\r\n    \u003cgroupId\u003ecom.guinetik\u003c/groupId\u003e\r\n    \u003cartifactId\u003erocketrest-core\u003c/artifactId\u003e\r\n    \u003cversion\u003e1.0.0\u003c/version\u003e\r\n\u003c/dependency\u003e\r\n```\r\n\r\nMake your first API call:\r\n\r\n```java\r\n// Create a simple client\r\nRocketRest client = new RocketRest(\"https://api.example.com\");\r\n\r\n// Make a GET request\r\nUser user = client.get(\"/users/1\", User.class);\r\nSystem.out.println(\"Hello, \" + user.getName());\r\n\r\n// Don't forget to shutdown when done\r\nclient.shutdown();\r\n```\r\n\r\n## Usage Example\r\n\r\n```java\r\n// Create configuration with default options\r\nRocketRestConfig config = RocketRestConfig.builder(\"https://api.example.com\")\r\n        .authStrategy(AuthStrategyFactory.createBearerToken(\"your-api-token\"))\r\n        .defaultOptions(options -\u003e {\r\n            options.set(RocketRestOptions.RETRY_ENABLED, true);\r\n            options.set(RocketRestOptions.MAX_RETRIES, 3);\r\n            options.set(RocketRestOptions.LOG_REQUEST_BODY, true);\r\n        })\r\n        .build();\r\n\r\n// Create RocketRest client (inherits default options from config)\r\nRocketRest client = new RocketRest(config);\r\n\r\ntry {\r\n    // Synchronous API\r\n    User user = client.sync().get(\"/users/1\", User.class);\r\n    \r\n    // Asynchronous API with CompletableFuture\r\n    CompletableFuture\u003cUser\u003e futureUser = client.async().get(\"/users/2\", User.class);\r\n    futureUser.thenAccept(u -\u003e System.out.println(\"Received user: \" + u.getName()));\r\n    \r\n    // Fluent API with Result pattern for elegant error handling\r\n    Result\u003cUser, ApiError\u003e result = client.fluent().get(\"/users/3\", User.class);\r\n    result.match(\r\n        user -\u003e System.out.println(\"Success: \" + user.getName()),\r\n        error -\u003e System.err.println(\"Error: \" + error.getMessage())\r\n    );\r\n    \r\n    // Direct access methods (uses sync API internally)\r\n    User createdUser = client.post(\"/users\", new User(\"John\", \"john@example.com\"), User.class);\r\n    \r\n} finally {\r\n    // Shutdown the client to release resources\r\n    client.shutdown();\r\n}\r\n```\r\n\r\n## Overview\r\n```mermaid\r\nflowchart TD\r\n    RocketRest[\"RocketRest (Facade API)\"]:::facade\r\n\r\n    subgraph \"Configuration\"\r\n        Config[\"RocketRestConfig\"]:::core\r\n        Options[\"RocketRestOptions\"]:::core\r\n        SecureConfig[\"RocketRestSecureConfig\"]:::core\r\n    end\r\n\r\n    subgraph \"API Client Layer\"\r\n        AbstractApiClient[\"AbstractApiClient\"]:::core\r\n        DefaultApiClient[\"DefaultApiClient\"]:::core\r\n        AsyncApiClient[\"AsyncApiClient\"]:::core\r\n        FluentApiClient[\"FluentApiClient\"]:::core\r\n    end\r\n\r\n    subgraph \"HTTP Transport \u0026 Decorators\"\r\n        Factory[\"RocketClientFactory\"]:::core\r\n        RCInterface[\"RocketClient (interface)\"]:::transport\r\n        DefaultHttpClient[\"DefaultHttpClient\"]:::transport\r\n        AsyncHttpClient[\"AsyncHttpClient\"]:::transport\r\n        CircuitBreakerClient[\"CircuitBreakerClient\"]:::transport\r\n        MockRocketClient[\"MockRocketClient\"]:::transport\r\n    end\r\n\r\n    subgraph \"Auth Strategies\"\r\n        AuthFactory[\"AuthStrategyFactory\"]:::core\r\n        BasicAuthStrategy[\"BasicAuthStrategy\"]:::strategy\r\n        BearerTokenStrategy[\"BearerTokenStrategy\"]:::strategy\r\n        NoAuthStrategy[\"NoAuthStrategy\"]:::strategy\r\n        AbstractOAuth2[\"AbstractOAuth2Strategy\"]:::strategy\r\n        OAuth2AssertionStrategy[\"OAuth2AssertionStrategy\"]:::strategy\r\n        OAuth2ClientCredStrategy[\"OAuth2ClientCredentialsStrategy\"]:::strategy\r\n        OAuth2PasswordStrategy[\"OAuth2PasswordStrategy\"]:::strategy\r\n    end\r\n\r\n    subgraph \"JSON, Request \u0026 Result Utilities\"\r\n        JsonObjectMapper[\"JsonObjectMapper\"]:::utils\r\n        RequestBuilder[\"RequestBuilder\"]:::utils\r\n        RequestSpec[\"RequestSpec\"]:::utils\r\n        Result[\"Result\"]:::utils\r\n        ApiError[\"ApiError\"]:::utils\r\n        ResponseLogger[\"ResponseLogger\"]:::utils\r\n        StreamUtils[\"StreamUtils\"]:::utils\r\n    end\r\n\r\n    Examples[\"Examples Module\"]:::facade\r\n\r\n    Examples --\u003e|\"uses\"| RocketRest\r\n    RocketRest --\u003e|configures| Config\r\n    RocketRest --\u003e|configures| Options\r\n    RocketRest --\u003e|configures| SecureConfig\r\n    RocketRest --\u003e|delegates to| AbstractApiClient\r\n    RocketRest --\u003e|delegates to| AsyncApiClient\r\n    RocketRest --\u003e|delegates to| FluentApiClient\r\n\r\n    AbstractApiClient --\u003e|\"builds client\"| Factory\r\n    DefaultApiClient --\u003e|\"builds client\"| Factory\r\n    AsyncApiClient --\u003e|\"builds client\"| Factory\r\n    FluentApiClient --\u003e|\"builds client\"| Factory\r\n\r\n    Factory --\u003e|creates| RCInterface\r\n    RCInterface --\u003e|\"executes\"| DefaultHttpClient\r\n    RCInterface --\u003e|\"executes\"| AsyncHttpClient\r\n    RCInterface --\u003e|\"wraps\"| CircuitBreakerClient\r\n    RCInterface --\u003e|\"mockable\"| MockRocketClient\r\n\r\n    CircuitBreakerClient --\u003e|\"decorates\"| DefaultHttpClient\r\n    CircuitBreakerClient --\u003e|\"decorates\"| AsyncHttpClient\r\n\r\n    Factory --\u003e|\"selects strategy\"| AuthFactory\r\n    AuthFactory --\u003e|\"provides\"| BasicAuthStrategy\r\n    AuthFactory --\u003e|\"provides\"| BearerTokenStrategy\r\n    AuthFactory --\u003e|\"provides\"| NoAuthStrategy\r\n    AuthFactory --\u003e|\"provides\"| AbstractOAuth2\r\n    AbstractOAuth2 --\u003e|\"extends\"| OAuth2AssertionStrategy\r\n    AbstractOAuth2 --\u003e|\"extends\"| OAuth2ClientCredStrategy\r\n    AbstractOAuth2 --\u003e|\"extends\"| OAuth2PasswordStrategy\r\n\r\n    DefaultApiClient --\u003e|uses| RequestBuilder\r\n    AsyncApiClient --\u003e|uses| RequestBuilder\r\n    FluentApiClient --\u003e|uses| RequestBuilder\r\n    RequestBuilder --\u003e|defines| RequestSpec\r\n    RequestBuilder --\u003e|injects auth| AuthFactory\r\n\r\n    DefaultHttpClient --\u003e|serializes| JsonObjectMapper\r\n    AsyncHttpClient --\u003e|serializes| JsonObjectMapper\r\n    JsonObjectMapper --\u003e|maps to| Result\r\n    JsonObjectMapper --\u003e|maps errors to| ApiError\r\n\r\n    DefaultHttpClient --\u003e|logs| ResponseLogger\r\n    DefaultHttpClient --\u003e|streams| StreamUtils\r\n\r\n    classDef facade fill:#BBDEFB,stroke:#000;\r\n    classDef core fill:#C8E6C9,stroke:#000;\r\n    classDef transport fill:#FFE0B2,stroke:#000;\r\n    classDef strategy fill:#FFF9C4,stroke:#000;\r\n    classDef utils fill:#CFD8DC,stroke:#000;\r\n\r\n    click RocketRest \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/RocketRest.java\"\r\n    click Config \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/RocketRestConfig.java\"\r\n    click Options \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/RocketRestOptions.java\"\r\n    click SecureConfig \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/RocketRestSecureConfig.java\"\r\n    click Factory \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/http/RocketClientFactory.java\"\r\n    click AbstractApiClient \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/api/AbstractApiClient.java\"\r\n    click DefaultApiClient \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/api/DefaultApiClient.java\"\r\n    click AsyncApiClient \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/api/AsyncApiClient.java\"\r\n    click FluentApiClient \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/api/FluentApiClient.java\"\r\n    click RCInterface \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/http/RocketClient.java\"\r\n    click DefaultHttpClient \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/http/DefaultHttpClient.java\"\r\n    click AsyncHttpClient \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/http/AsyncHttpClient.java\"\r\n    click CircuitBreakerClient \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/http/CircuitBreakerClient.java\"\r\n    click MockRocketClient \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/http/MockRocketClient.java\"\r\n    click AuthFactory \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/auth/AuthStrategyFactory.java\"\r\n    click BasicAuthStrategy \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/auth/BasicAuthStrategy.java\"\r\n    click BearerTokenStrategy \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/auth/BearerTokenStrategy.java\"\r\n    click NoAuthStrategy \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/auth/NoAuthStrategy.java\"\r\n    click AbstractOAuth2 \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/auth/AbstractOAuth2Strategy.java\"\r\n    click OAuth2AssertionStrategy \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/auth/OAuth2AssertionStrategy.java\"\r\n    click OAuth2ClientCredStrategy \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/auth/OAuth2ClientCredentialsStrategy.java\"\r\n    click OAuth2PasswordStrategy \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/auth/OAuth2PasswordStrategy.java\"\r\n    click JsonObjectMapper \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/json/JsonObjectMapper.java\"\r\n    click RequestBuilder \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/request/RequestBuilder.java\"\r\n    click RequestSpec \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/request/RequestSpec.java\"\r\n    click Result \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/result/Result.java\"\r\n    click ApiError \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/result/ApiError.java\"\r\n    click ResponseLogger \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/util/ResponseLogger.java\"\r\n    click StreamUtils \"https://github.com/guinetik/rocketrest/blob/master/rocketrest-core/src/main/java/com/guinetik/rr/util/StreamUtils.java\"\r\n    click Examples \"https://github.com/guinetik/rocketrest/tree/master/examples/src/main/java/com/guinetik/examples/\"\r\n```\r\n\r\n## More Examples\r\n\r\nCheck out the [examples directory](./examples) for more detailed usage examples:\r\n\r\n- [Simple Todo Example](./examples/src/main/java/com/guinetik/examples/JsonTodoExample.java) - Basic GET request with JSON parsing\r\n- [Weather API Example](./examples/src/main/java/com/guinetik/examples/WeatherExample.java) - Using async API with wttr.in\r\n- [PokéAPI Browser](./examples/src/main/java/com/guinetik/examples/PokeApiExample.java) - Working with pagination\r\n- [NASA APOD API](./examples/src/main/java/com/guinetik/examples/NasaApodExample.java) - Using the fluent API with Result pattern\r\n\r\nTo run the examples:\r\n\r\n```bash\r\n# Install the core library\r\ncd rocketrest-core\r\nmvn clean install\r\n\r\n# Run the examples\r\ncd ../examples\r\nmvn compile exec:java\r\n```\r\n\r\n### Creating Basic Clients\r\n\r\n```java\r\n// Create a default HTTP client\r\nRocketClient client = RocketClientFactory.builder(\"https://api.example.com\")\r\n    .withOptions(options)\r\n    .build();\r\n\r\n// Create an async HTTP client\r\nExecutorService executor = Executors.newFixedThreadPool(4);\r\nAsyncHttpClient asyncClient = RocketClientFactory.builder(\"https://api.example.com\")\r\n    .withOptions(options)\r\n    .withExecutorService(executor)\r\n    .buildAsync();\r\n```\r\n\r\n### Creating Clients with Circuit Breaker\r\n\r\n```java\r\n// Create a client with default circuit breaker settings\r\nRocketClient client = RocketClientFactory.builder(\"https://api.example.com\")\r\n    .withCircuitBreaker()\r\n    .build();\r\n\r\n// Create a client with custom circuit breaker settings\r\nRocketClient client = RocketClientFactory.builder(\"https://api.example.com\")\r\n    .withCircuitBreaker(5, 30000)  // 5 failures, 30 second timeout\r\n    .build();\r\n\r\n// Create a client with fully customized circuit breaker\r\nRocketClient client = RocketClientFactory.builder(\"https://api.example.com\")\r\n    .withCircuitBreaker(\r\n        5,                                              // 5 failures before opening\r\n        30000,                                          // 30 second reset timeout\r\n        60000,                                          // 1 minute failure decay\r\n        CircuitBreakerClient.FailurePolicy.SERVER_ERRORS_ONLY  // Only count server errors\r\n    )\r\n    .build();\r\n```\r\n\r\n### Creating from Configuration\r\n\r\n```java\r\n// Create a builder pre-configured from RocketRestConfig\r\nRocketClient client = RocketClientFactory.fromConfig(config)\r\n    .withCircuitBreaker()\r\n    .build();\r\n\r\n// Create a default client directly from config\r\nRocketClient client = RocketClientFactory.createDefaultClient(config);\r\n```\r\n\r\n### Adding Custom Decorators\r\n\r\n```java\r\n// Add a custom decorator to the client\r\nRocketClient client = RocketClientFactory.builder(\"https://api.example.com\")\r\n    .withCustomDecorator(baseClient -\u003e new MyCustomClientDecorator(baseClient))\r\n    .build();\r\n\r\n// Combine circuit breaker with custom decorator\r\nRocketClient client = RocketClientFactory.builder(\"https://api.example.com\")\r\n    .withCircuitBreaker()\r\n    .withCustomDecorator(baseClient -\u003e new MyCustomClientDecorator(baseClient))\r\n    .build();\r\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguinetik%2Frocketrest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fguinetik%2Frocketrest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguinetik%2Frocketrest/lists"}