Skip to content

add sync module to nango crate#3715

Merged
yujonglee merged 1 commit intomainfrom
devin/1770476323-nango-sync
Feb 7, 2026
Merged

add sync module to nango crate#3715
yujonglee merged 1 commit intomainfrom
devin/1770476323-nango-sync

Conversation

@devin-ai-integration
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot commented Feb 7, 2026

add sync module to nango crate

Summary

Adds sync.rs to crates/nango implementing typed Rust bindings for all 10 Nango Sync API endpoints:

  • GET /recordsget_records
  • PATCH /records/pruneprune_records
  • POST /sync/triggertrigger_sync
  • POST /sync/startstart_sync
  • POST /sync/pausepause_sync
  • GET /sync/statussync_status
  • PUT /sync/update-connection-frequencyupdate_connection_frequency
  • GET /environment-variablesget_environment_variables
  • POST /sync/{name}/variant/{variant}create_variant
  • DELETE /sync/{name}/variant/{variant}delete_variant

Follows existing patterns from connection.rs and integration.rs — uses common_derives!, append_query, check_response/parse_response, #[serde(skip)] on param structs, etc.

Review & Testing Checklist for Human

  • SyncStatus field casing: finishedAt, nextScheduledSyncAt, latestResult use camelCase #[serde(rename)]. If the actual Nango API returns snake_case for these, deserialization will silently produce None. Verify against a real API response.
  • prune_records request shape: Docs page was truncated. Current impl assumes Connection-Id/Provider-Config-Key headers + model query param (mirroring GET /records). Confirm this matches the actual API.
  • ids query param format in get_records: Currently appends multiple ids=X pairs. Verify the Nango API doesn't expect ids[]=X or comma-separated format.
  • NangoRecordMetadata is exported but not referenced in any method signature — confirm it's useful as a downstream helper type or remove it.

Notes

  • SyncStatus.status and SyncStatus.type are String (not enums) — intentional for flexibility since Nango could add new values.
  • GetRecordsResponse.records is Vec<serde_json::Value> since record shapes are user-defined per sync script.
  • No new tests added; existing crate only has #[ignore] integration tests that require live credentials.
  • Cargo check passes locally.

Requested by: @yujonglee
Link to Devin run


Open with Devin

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
@netlify
Copy link

netlify bot commented Feb 7, 2026

Deploy Preview for hyprnote-storybook failed.

Name Link
🔨 Latest commit 47dc6b1
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/69875531a7c04a000884459b

@netlify
Copy link

netlify bot commented Feb 7, 2026

Deploy Preview for hyprnote failed.

Name Link
🔨 Latest commit 47dc6b1
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/69875531b4f5c800080c9a7c

@devin-ai-integration
Copy link
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@yujonglee yujonglee merged commit 1c1d22b into main Feb 7, 2026
7 of 16 checks passed
@yujonglee yujonglee deleted the devin/1770476323-nango-sync branch February 7, 2026 15:08
Copy link
Contributor Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View 5 additional findings in Devin Review.

Open in Devin Review

Comment on lines +344 to +348
let mut url = self.api_base.clone();
url.set_path(&format!("/sync/{}/variant/{}", name, variant));

let response = self.client.delete(url).json(&req).send().await?;
check_response(response).await?;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🔴 delete_variant sends required parameters as JSON body on DELETE instead of query parameters

The delete_variant method sends provider_config_key and connection_id as a JSON body (.json(&req)) on a DELETE request. The Nango API expects these as query parameters for the DELETE /sync/{name}/variant/{variant} endpoint, meaning the server will not receive the required parameters and the call will fail.

Root Cause and Evidence from Existing Patterns

The existing delete_connection method in connection.rs:223-239 demonstrates the correct pattern for DELETE endpoints — it passes parameters as query params via append_query:

pub async fn delete_connection(...) {
    url.set_path(&format!("/connections/{}", connection_id));
    append_query(&mut url, "provider_config_key", &provider_config_key.to_string());
    let response = self.client.delete(url).send().await?;
    ...
}

Similarly, delete_integration in integration.rs:155-165 sends no body at all on DELETE.

In contrast, delete_variant at crates/nango/src/sync.rs:347 uses .json(&req) on a DELETE request:

let response = self.client.delete(url).json(&req).send().await?;

This sends {"provider_config_key":"...","connection_id":"..."} in the request body, but the Nango API expects ?provider_config_key=...&connection_id=... as query parameters. The API will reject the request or behave unexpectedly because the required query parameters are missing.

Impact: Every call to delete_variant will fail because the required provider_config_key and connection_id parameters are not sent where the API expects them.

Suggested change
let mut url = self.api_base.clone();
url.set_path(&format!("/sync/{}/variant/{}", name, variant));
let response = self.client.delete(url).json(&req).send().await?;
check_response(response).await?;
let mut url = self.api_base.clone();
url.set_path(&format!("/sync/{}/variant/{}", name, variant));
append_query(&mut url, "provider_config_key", &req.provider_config_key);
append_query(&mut url, "connection_id", &req.connection_id);
let response = self.client.delete(url).send().await?;
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant