Skip to content

Migrate from ureq to reqwest with async support#92

Open
weppos wants to merge 6 commits intomainfrom
migrate-to-reqwest-async
Open

Migrate from ureq to reqwest with async support#92
weppos wants to merge 6 commits intomainfrom
migrate-to-reqwest-async

Conversation

@weppos
Copy link
Member

@weppos weppos commented Jan 23, 2026

Summary

  • Migrates the HTTP client from synchronous ureq to asynchronous reqwest with Tokio runtime support
  • Converts all 23 service modules and their methods to async/await
  • Updates all 24 test files to use async mockito server and #[tokio::test]

Closes #42
Closes #76

Breaking Changes

All API methods are now async and require .await. Example:

// Before
let response = client.identity().whoami().unwrap();

// After
let response = client.identity().whoami().await.unwrap();

Dependency Changes

  • Removed: ureq
  • Added: reqwest = "0.12" with json feature
  • Added: tokio = "1" with rt-multi-thread and macros features

Test plan

  • All 127 tests pass (4 unit tests, 84 integration tests, 39 doc tests)
  • cargo test runs successfully
  • All service modules compile with async methods

This is a breaking change that migrates the HTTP client from synchronous
ureq to asynchronous reqwest with Tokio runtime support.

Key changes:

- Replace ureq with reqwest + tokio in dependencies
- Convert all HTTP methods (get, post, put, patch, delete) to async
- Update all 23 service modules to use async/await
- Update error handling to support reqwest error types
- Migrate test infrastructure to use mockito async server
- Convert all test functions to async with #[tokio::test]

This addresses issues #42 and #76, providing modern async HTTP support
for the DNSimple Rust client.

BREAKING CHANGE: All API methods are now async and require .await
@weppos weppos self-assigned this Jan 23, 2026
@weppos weppos added the enhancement New feature, enhancement or code changes, not related to defects label Jan 23, 2026
Remove unnecessary borrows when passing URLs to reqwest methods.
Copy link
Member

@onlyhavecans onlyhavecans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some small style/rust notes and a few blocking things.

in this change we are forcing a change from the rusttls to native-tls (which even reqwest has changed in 0.13, also defaulting to rusttls) so even if we are committing to reqwest 0.12 (which I don't understand why we are doing it in this change) we should also keep "rusttls"

Also choosing to force rt-multi-thread has downstream implementations on people who use our library and we shouldn't force it and let the implementer chose multi or single thread

considering how massively breaking change this is, and how it will require a major release and overhaul by anybody who uses it I highly recommend we also move to edition 2024 and remove all the [#allow(depreciated)] decorators (which will likely be required anyways to update code patterns).

Comment on lines +439 to 444
if response.status().is_success() {
Self::build_dnsimple_response::<E>(response).await
} else {
let body = response.json::<Value>().await.ok();
Err(DNSimpleError::parse_response(status, body))
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this no longer a match?

Comment on lines +458 to +462
if response.status().is_success() {
Self::build_empty_dnsimple_response(response).await
} else {
let body = response.json::<Value>().await.ok();
Err(DNSimpleError::parse_response(status, body))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this still functions as a match


[dependencies]
ureq = { version = "2.6", features = ["json"] }
reqwest = { version = "0.12", features = ["json"] }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is an old version of reqwest? why

Comment on lines +54 to +55
pub fn parse_reqwest_error(error: reqwest::Error) -> DNSimpleError {
Self::Transport(error.to_string(), "Request error".to_string())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this really isn't a good pattern and we should probably use thiserror better by defining it above in the DNSimpleError like we do all the others.

see https://docs.rs/thiserror/latest/thiserror/

#[error("Request error: {0}")]
Reqwest(#[from] reqwest::Error),

user_agent: String,
auth_token: String,
pub _agent: ureq::Agent,
pub _client: reqwest::Client,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we intentionally expose this directly? we might want to drop the underscore unless that's intentional

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature, enhancement or code changes, not related to defects

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update or move away from ureq Implement async variants of methods that make HTTP requests

2 participants