Skip to content

feat: add NangoConnection<I> extractor for nango-powered integration routes#3962

Merged
yujonglee merged 9 commits intomainfrom
devin/1771044867-nango-connection-extractor
Feb 17, 2026
Merged

feat: add NangoConnection<I> extractor for nango-powered integration routes#3962
yujonglee merged 9 commits intomainfrom
devin/1771044867-nango-connection-extractor

Conversation

@devin-ai-integration
Copy link
Contributor

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

feat: add NangoConnection<I> extractor for nango-powered integration routes

Summary

Introduces a generic NangoConnection<I> axum extractor that resolves a user's Nango connection_id from a Supabase nango_connections table, removing the need for clients to pass connection_id in every request body. The extractor uses marker types for type-safe integration identification.

Key pieces:

  • Supabase migration (nango_connections table) — stores user_id ↔ connection_id per integration, populated by webhook
  • OwnedNangoProxy + OwnedNangoHttpClient in crates/nango — owned variants to solve lifetime issues in extractors; shared header logic extracted to apply_proxy_headers
  • NangoIntegrationId trait + GoogleCalendar/GoogleDrive marker types in crates/api-nango
  • NangoConnection<I> extractor — reads AuthContext for user_id, queries Supabase PostgREST for connection_id, builds ready-to-use HTTP client
  • Webhook handler extended to upsert/delete rows on Nango auth events; returns 500 on transient DB failures (so Nango retries), but gracefully returns 200 when supabase_service_role_key is not configured
  • SupabaseClient extracted into supabase.rs with URL-encoded query params and on_conflict=user_id,integration_id for correct upsert behavior
  • api-calendar and api-storage refactoredconnection_id removed from all request bodies, duplicated config.rs/state.rs deleted

Before → After (handler example):

// Before: 3-line preamble + connection_id in body
pub async fn list_calendars(
    State(state): State<AppState>,
    Json(payload): Json<ListCalendarsRequest>, // had connection_id field
) -> Result<...> {
    let proxy = state.nango.integration("google-calendar").connection(&payload.connection_id);
    let http = NangoHttpClient::new(proxy);
    let client = GoogleCalendarClient::new(http);

// After: extractor does it all
pub async fn list_calendars(
    nango: NangoConnection<GoogleCalendar>,
) -> Result<...> {
    let client = GoogleCalendarClient::new(nango.into_http());

Updates since last revision

  • Fixed supabase_service_role_key serde conflict: The Env struct in apps/api/src/env.rs had an explicit supabase_service_role_key: Option<String> field that shadowed the same field inside #[serde(flatten)] SupabaseEnv, which would have crashed the API server on startup. Removed the duplicate and now uses env.supabase.supabase_service_role_key directly.
  • Webhook gracefully handles missing config: The webhook handler now checks SupabaseClient::is_configured() before attempting DB operations. If supabase_service_role_key is not set, it logs a warning and returns 200 (no Nango retries for a config issue). Transient DB failures still return 500 so Nango retries.
  • Merge conflict resolved: main refactored stt/llm into separate rate-limited route groups; kept that structure alongside this PR's changes (no-arg calendar::router(), nango_connection_state extension layer).
  • Deletion success gate documented: Added comment explaining why we check payload.success before deleting local state on deletion webhooks — avoids removing a valid local record if revocation failed on Nango's side (per Nango webhook docs, successful deletions set success: true).
  • Merged with latest main: Incorporated recent changes from main branch.

Review & Testing Checklist for Human

  • Breaking API change: connection_id is removed from all calendar/storage request bodies. No frontend changes are included in this PR — verify that callers are updated or that this is intentional for the new OAuth flow.
  • Extension layer ordering: Confirm NangoConnectionState is available to calendar/storage routes — the .layer(Extension(...)) is added after .nest("/calendar", ...) and .nest("/nango", ...) in apps/api/src/main.rs, which in axum means it applies to all routes above it in the chain. Verify this is correct.
  • UUID assumption for end_user_id: The webhook handler passes payload.end_user.end_user_id directly to the nango_connections table's user_id uuid column with a FK to auth.users(id). Verify that the upstream Connect Session creation (step 3 of the OAuth milestone) will pass the Supabase user UUID as end_user.id.
  • Test end-to-end: Connect a Google Calendar integration via Nango → verify webhook populates nango_connections → call /calendar/calendars with just auth token → verify it resolves connection and returns data.

Notes

  • The extractor queries Supabase PostgREST using the user's anon key + auth token (not service role key), relying on the RLS select policy for security. Users can only read their own connections.
  • list_calendars is now a POST with no request body (just the extractor).
  • The OwnedNangoProxy duplicates method signatures from NangoProxy — this is a known tradeoff to solve lifetime issues in axum extractors. Future proxy behavior changes need to be made in both places.

Link to Devin run: https://app.devin.ai/sessions/4feb4eb6926b42c5b1c43ccca430802b
Requested by: @yujonglee

…routes

- Add nango_connections supabase migration table
- Add OwnedNangoProxy + OwnedNangoHttpClient to nango crate
- Add NangoIntegrationId trait with GoogleCalendar/GoogleDrive marker types
- Add NangoConnection<I> axum extractor (resolves connection_id from DB)
- Extend webhook handler to upsert/delete nango_connections
- Refactor api-calendar to use NangoConnection<GoogleCalendar>
- Refactor api-storage to use NangoConnection<GoogleDrive>
- Remove connection_id from request bodies (server-side lookup)
- Remove duplicated config.rs/state.rs from api-calendar and api-storage

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

netlify bot commented Feb 14, 2026

Deploy Preview for hyprnote-storybook canceled.

Name Link
🔨 Latest commit 8625fde
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/6993b2f2efd86c0008b3812c

@netlify
Copy link

netlify bot commented Feb 14, 2026

Deploy Preview for hyprnote failed.

Name Link
🔨 Latest commit 8625fde
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/6993b2f204a9c90008f59882

@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

devin-ai-integration[bot]

This comment was marked as resolved.

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration bot and others added 4 commits February 14, 2026 05:57
Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

…cess comment

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration bot and others added 2 commits February 17, 2026 00:12
…ssing config in webhook

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
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 new potential issue.

View 8 additional findings in Devin Review.

Open in Devin Review

@yujonglee yujonglee merged commit 22cb3a7 into main Feb 17, 2026
21 of 25 checks passed
@yujonglee yujonglee deleted the devin/1771044867-nango-connection-extractor branch February 17, 2026 02:13
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