# Connectors
URL: https://www.quiltt.dev/get-started/tutorials/connectors
Description: Learn to implement a multi-aggregator connector with Quiltt for robust financial data coverage and reliability. Complete Next.js tutorial with code.
Navigation: get-started → tutorials → connectors
Tags: Tutorials
Content Length: 22k characters

Build a production-ready connector that uses multiple data aggregators (Finicity, MX, Plaid, Akoya) for better reliability and coverage.

**Time:** ~30 minutes
**Level:** Intermediate
**Framework:** Next.js

## Why Multiple Aggregators?

Single-aggregator approaches have limits:
- Service outages disrupt all users
- Coverage gaps for certain banks
- Regional availability issues
- Vendor lock-in risks

Quiltt's unified API lets you use multiple aggregators while writing code once.

## Prerequisites

- [Quiltt Dashboard](https://dashboard.quiltt.dev) account
- Basic GraphQL knowledge ([see guide](/api/graphql))
- Node.js 18+ installed

New to financial data aggregation? We'll explain key concepts as we go.

## Step 1: Understand Available Aggregators

Quiltt supports four major aggregators, each with different strengths:

| Aggregator | Strengths |
|------------|----------|
| **Finicity** | Most OAuth connections |
| **MX** | Broadest coverage |
| **Plaid** | Strong fintech coverage |
| **Akoya** | OAuth-only coverage |

**Key differences:**
- **Coverage:** Regional vs national banks
- **Products:** Account verification, transactions, investments
- **Features:** Real-time updates, categorization accuracy

You don't need to pick one - Quiltt automatically chooses the best aggregator for each connection attempt based on the products you need to connect, and your code stays the same.

## Step 2: Set Up Your Project

Create a Next.js project:

```bash
pnpm create next-app my-quiltt-app --typescript --tailwind --app
cd my-quiltt-app
```

Install dependencies:

```bash
pnpm add @quiltt/react
```

Add credentials from the [Dashboard](https://dashboard.quiltt.dev):

```yaml
# .env.local
NEXT_PUBLIC_QUILTT_CONNECTOR_ID=your_connector_id_here
QUILTT_API_KEY_SECRET=your_api_key_secret_here
```

Info - Authentication Required:
This tutorial requires Session tokens for GraphQL queries. See the [Authentication Tutorial](/get-started/tutorials/authentication) for setup.

Initialize in your root layout:

```typescript
// src/app/layout.tsx

  return (
    

Html:

Body:

QuilttProvider:
[content]

  )
}
```

## Step 3: Implementing Multi-Aggregator Connector

### 3.1 Setting Up Your Connector

Before writing any code, you'll need to configure your connector in the Quiltt Dashboard. This will determine which aggregators and products are available to your users.

1. Visit the [Quiltt Dashboard](https://dashboard.quiltt.dev)
2. Select your environment (sandbox or production)
3. Create a new connector or edit an existing one
4. Configure the connector:
   1. Set up your `Authenticate` configuration
   2. Set up your `Enroll` configuration
   3. In the `Connect` section:
      - Enable at least two aggregators (e.g., MX and Plaid)
      - Select at least one required product (e.g., ACCOUNT_BALANCES_AND_TRANSACTIONS)
   4. For the purposes of this tutorial, set up the `Exit Redirect` section to:
      - Set the `Redirect URL` to `http://localhost:3000/login`
      - Enable the option for `Send Token`

![Connector Connect settings in the Quiltt Dashboard with MX and Plaid providers enabled and the Transactions feature set to Required](./connect-settings.png)

![Connector Exit Redirect settings in the Quiltt Dashboard with the Redirect URL set to http://localhost:3000/login and the Send Token toggle enabled](./exit-redirect-settings.png)

### 3.2 Add the Connector to your Project

Let's implement the core connection components:

The `ConnectAccounts` component provides a general-purpose button that launches the Quiltt connector dialog, allowing users to connect any supported financial institution:

```typescript
// src/components/ConnectAccounts.tsx
'use client'

  const handleLoad: ConnectorSDKOnLoadCallback = (metadata) => {
    console.log(`Connector $[metadata.connectorId] loaded!`)
  }

  const handleSuccess: ConnectorSDKOnExitSuccessCallback = (metadata) => [console.log('Connection established:', metadata.connectionId)]

  return (
    

QuilttButton:
Connect Account

  )
}
```

The `PrefilledConnector` component demonstrates how to pre-select a specific institution (Chase in this example) for a more streamlined connection experience:

```typescript
// src/components/PrefilledConnector.tsx
'use client'

  return (
    

QuilttButton:
[console.log('Connection established:', metadata.connectionId)]}
      className="rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700"
    >
      Connect Chase Account

  )
}
```

### 3.3 Handling Authentication

After a successful connection, the Quiltt Connector redirects users to your login page with an authentication token. Let's implement the login handler:

```typescript
// src/app/login/page.tsx
'use client'

  const router = useRouter()
  const searchParams = useSearchParams()
  const {importSession} = useQuilttSession()

  useEffect(() => {
    const token = searchParams.get('token')
    if (!token) [console.error('No authentication token found')
      return]

    const processToken = async () => {
      try {
        const success = await importSession(token)
        if (success) [router.push('/connections')] else [console.error('Failed to import session')]
      } catch (error) [console.error('Error processing authentication token:', error)]
    }

    processToken()
  }, [importSession, router, searchParams])

  return (
    

Processing Authentication...

        Please wait while we complete your login.

  )
}
```

After implementing this login page:
1. The connector redirects to `http://localhost:3000/login?token={authToken}`
2. `useQuilttSession` processes and stores the token
3. Users are redirected to `/connections`
4. Subsequent API requests use this token for authentication

This completes the connection flow and prepares us for accessing financial data in the next section.

## Step 4: Accessing Data

The journey from initiating a connection to accessing financial data involves several key steps. Let's break this down into a clear process.

### 4.1 Connection Events

The Quiltt Connector SDK provides a comprehensive callback system that lets you track every step of the connection process. These events are crucial for implementing a robust multi-aggregator strategy, as they allow you to:

1. Monitor which aggregator was selected for the connection
2. Track the success rate of different aggregators
3. Handle connection failures gracefully
4. Manage the user experience throughout the connection flow

Here's a robust implementation of a connection manager:

```typescript
// src/components/ConnectionManager.tsx
'use client'

interface ConnectionManagerProps [conditional content]

  const [connectionState, setConnectionState] = useState<[conditional content]>([status: 'idle'])

  // Comprehensive event handler for all connector events
  const handleEvent = (type: ConnectorSDKEventType, metadata: ConnectorSDKCallbackMetadata) => {
    console.log(`Connector event: ${type}`, metadata)

    switch (type) {
      case ConnectorSDKEventType.Open:
        setConnectionState([status: 'connecting',
          message: 'Connector opened',])
        break

      case ConnectorSDKEventType.Load:
        setConnectionState({
          status: 'connecting',
          message: `Connector $[metadata.connectorId] loaded`,
        })
        break

      case ConnectorSDKEventType.ExitSuccess:
        if (metadata.connectionId) {
          setConnectionState({
            status: 'connected',
            message: `Connection $[metadata.connectionId] established`,
          })
          onConnectionEstablished?.(metadata.connectionId)
        }
        break

      case ConnectorSDKEventType.ExitAbort:
        setConnectionState([status: 'idle',
          message: 'Connection process aborted',])
        break

      case ConnectorSDKEventType.ExitError:
        setConnectionState([status: 'error',
          message: 'Error establishing connection',])
        break
    }
  }

  return (
    [/* Standard Connection Flow */]
        

QuilttButton:
Connect Account

        [/* Pre-selected Institution Flow */]
        

QuilttButton:
Connect Chase Account

      [/* Connection State Display */]
      {connectionState.message && (
        [connectionState.message]
      )}
  )
}
```

The key events you'll want to handle are:

- `Open`: Triggered when the connector modal opens
- `Load`: Fired when an aggregator is selected and loaded
- `ExitSuccess`: Indicates a successful connection, providing the `connectionId`
- `ExitAbort`: Triggered if the user cancels the process
- `ExitError`: Fired if there's an error during connection

### 4.2 Account Data

The `@quiltt/react` package provides hooks for handling data fetching and caching through GraphQL. The primary hooks you'll use are:

- `useQuery`: For fetching data
- `useSubscription`: For real-time updates
- `useQuilttClient`: For direct cache access and modifications

These hooks provide several powerful features:
- Automatic retry logic for failed requests
- Request batching for better performance
- Built-in error handling
- Real-time updates via GraphQL subscriptions
- Smart request caching

Info - Server-Side Data Fetching:
While this tutorial uses React hooks for client-side data fetching, you can also implement your own server-side GraphQL client using `fetch`.
This might be preferred if you need to keep sensitive data server-side or want to implement your own caching strategy.
Check out our [API Reference](/api-reference) for details on implementing a custom client.

### 4.3 Displaying Accounts

Now we can create our accounts list component that handles both initial data loading and real-time updates:

```typescript
// src/components/AccountsList.tsx
'use client'

const GET_ACCOUNTS = gql`
  query GetAccounts($connectionId: ID!) {
    connection(id: $connectionId) {
      accounts {
        id
        name
        balance {current}
        kind
      }
    }
  }
`

const CONNECTION_SYNCED = gql`
  subscription OnConnectionSynced($connectionId: ID!) {
    connectionSynced(connectionId: $connectionId) {
      connection {
        id
        accounts {
          id
          name
          balance {current}
          kind
        }
      }
    }
  }
`

interface AccountsListProps [connectionId: string]

  const [data, loading, error] = useQuery(GET_ACCOUNTS, {
    variables: {connectionId},
  })

  // Subscribe to connection syncs. Apollo's normalized cache merges the
  // returned accounts by ID, so displayed balances stay current automatically.
  useSubscription(CONNECTION_SYNCED, {
    variables: {connectionId},
  })

  if (loading) [return Loading accounts...]

  if (error) {
    return Error loading accounts: [error.message]
  }

  return (
    {data?.connection?.accounts.map((account) => (
        

    [account.name]

          $[account.balance?.current?.toFixed(2) ?? 'N/A']

          [account.kind]

          Updates automatically

      ))}
  )
}
```

### 4.4 Real-Time Updates

Quiltt provides real-time updates through GraphQL subscriptions. Here's how to implement comprehensive real-time functionality:

```typescript
// src/components/ConnectionMonitor.tsx
'use client'

const CONNECTION_SYNCED = gql`
  subscription OnConnectionSynced($connectionId: ID) {
    connectionSynced(connectionId: $connectionId) {
      connection [id
        status]
    }
  }
`

interface ConnectionMonitorProps [conditional content]

  const quilttClient = useQuilttClient()

  // Monitor connection syncs and status changes
  useSubscription(CONNECTION_SYNCED, {
    variables: {connectionId},
    onData: ({data}) => {
      const connection = data.connectionSynced?.connection
      if (!connection) return

      // Update connection status in cache
      quilttClient.cache.modify({
        id: quilttClient.cache.identify([__typename: 'Connection', id: connectionId]),
        fields: [status: () => connection.status,],
      })
      onStatusChange?.(connection.status)
    },
  })

  return null // This is a monitoring component with no UI
}
```

This implementation provides several benefits for a multi-aggregator strategy:

- Real-Time Updates: Get immediate notifications when a connection finishes syncing
- Connection Monitoring: Track connection health across all aggregators
- Automatic Cache Updates: Keep UI in sync with backend data
- Resource Optimization: No polling required

Info - Alternative Real-Time Updates:
While GraphQL subscriptions provide the most seamless real-time experience, you can also implement real-time updates using webhooks.
This approach might be preferred for server-side architectures or when you need to integrate with existing event-processing systems.
See our [Webhooks Documentation](/webhooks) for details on setting up webhook-based updates.

### 4.5 Best Practices

When working with multi-aggregator connections, follow these best practices:

1. **Cache Appropriately**: Each aggregator has different rate limits and update frequencies
2. **Handle Throttling**: Implement exponential backoff for retries
3. **Optimize Batch Operations**: Group requests when possible
4. **Monitor Performance**: Track response times and success rates per aggregator

By following this structured approach to data access, you'll be able to maintain reliable connections across multiple aggregators while providing a smooth user experience.

### 4.6 Integration Pages

The home page (`./src/app/page.tsx`) serves as the entry point for users to initiate new connections:

```typescript
// src/app/page.tsx

    

Connect Your Financial Accounts

        

  Connect Any Account

            [ConnectAccounts]

          

  Quick Connect: Chase

            [PrefilledConnector]
  )]
```

The connections page (`./src/app/connections/page.tsx`) provides a dashboard view of all connected accounts and their current status:

```typescript
// src/app/connections/page.tsx
'use client'

const GET_CONNECTIONS = gql`
  query GetConnections {
    connections {
      id
      status
      institution {name}
      accounts {
        id
        name
        balance {current}
        kind
      }
    }
  }
`

  const [data, loading, error] = useQuery<[connections: Array<Connection>]>(GET_CONNECTIONS)

  if (loading)
    return (
      

Your Connected Accounts

        Loading connections...
    )

  if (error)
    return (
      

Your Connected Accounts

        Error loading connections: [error.message]
    )

  console.log(data)

  return (
    

Your Connected Accounts

      {data?.connections.map((connection) => (
          

  [connection.institution.name]

              [ConnectionStatusIndicator]

            [AccountsList]
        ))}
  )
}
```

The connection details page (`./src/app/connections/[id]/page.tsx`) shows detailed information for a specific connection, including all linked accounts and repair options if needed:

```typescript
// src/app/connections/[id]/page.tsx
'use client'

const GET_CONNECTION = gql`
  query GetConnection($id: ID!) {
    connection(id: $id) {
      id
      status
      institution {name}
    }
  }
`

interface ConnectionPageProps {
  params: [id: string]
}

  const [data, loading, error] = useQuery(GET_CONNECTION, {
    variables: [id: params.id],
  })

  if (loading)
    return (
      Loading connection details...
    )

  if (error)
    return (
      Error loading connection: [error.message]
    )

  if (!data?.connection) return notFound()

  const {connection} = data

  return (
    

[connection.institution.name] Details

          [ConnectionStatusIndicator]

        

Suspense:
Loading accounts...}>
          [AccountsList]

      </div>
  )
}
```

## Step 5: Error Handling and Connection Maintenance

Financial data connections can become disrupted for various reasons:

1. **Authentication Changes**
   - Password updates
   - Multi-factor authentication changes
   - Security question modifications
   - Token expiration

2. **Institution Changes**
   - API updates
   - Security protocol changes
   - Service migrations
   - Backend system maintenance

3. **Aggregator Issues**
   - Service disruptions
   - API deprecations
   - Rate limiting
   - Data format changes

Maintaining reliable connections requires robust error handling and repair capabilities. Let's implement comprehensive error handling:

### 5.1 Connection Status Types

First, we'll define our connection status handling using the types generated from our GraphQL schema:

Info - ConnectionStatus:
The `ConnectionStatus` type is generated using `@graphql-codegen/typescript`.

For detailed instructions on setting up GraphQL Code Generator with Quiltt, see our [GraphQL Tooling Tutorial](/get-started/tutorials/graphql-tooling).

```typescript
// src/lib/connection-status.ts

interface StatusHandler [message: string
  severity: 'success' | 'info' | 'warning' | 'error'
  action: 'repair' | 'retry' | 'reconnect' | 'check_logs' | 'check_status' | 'contact_support' | null]

  switch (status) {
    case 'SYNCED':
      return [message: 'Connected and up to date',
        severity: 'success',
        action: null,]

    case 'SYNCING':
    case 'INITIALIZING':
    case 'UPGRADING':
      return [message: 'Updating connection...',
        severity: 'info',
        action: null,]

    case 'ERROR_REPAIRABLE':
      return [message: 'Connection needs repair',
        severity: 'warning',
        action: 'repair',]

    case 'ERROR_INSTITUTION':
      return [message: 'Institution temporarily unavailable',
        severity: 'error',
        action: 'retry',]

    case 'ERROR_PROVIDER':
      return [message: 'Provider error - check remote data',
        severity: 'error',
        action: 'check_logs',]

    case 'ERROR_SERVICE':
      return [message: 'Service disruption - check status page',
        severity: 'error',
        action: 'check_status',]

    case 'DISCONNECTED':
      return [message: 'Connection disconnected',
        severity: 'error',
        action: 'reconnect',]

    default:
      return [message: 'Unknown status',
        severity: 'error',
        action: 'contact_support',]
  }
}
```

### 5.2 Status Display Component

Let's create a reusable component to display connection status:

```typescript
// src/components/ConnectionStatusIndicator.tsx
'use client'

interface ConnectionStatusIndicatorProps [status: ConnectionStatus
  connectionId: string]

  const [message, severity, action] = handleConnectionStatus(status)

  const getSeverityStyle = () => {
    switch (severity) [case 'success':
        return 'bg-green-100 text-green-800'
      case 'info':
        return 'bg-blue-100 text-blue-800'
      case 'warning':
        return 'bg-yellow-100 text-yellow-800'
      case 'error':
        return 'bg-red-100 text-red-800']
  }

  const renderAction = () => {
    switch (action) {
      case 'repair':
        return (
          

QuilttButton:
[console.log('Connection repaired:', metadata.connectionId)]}
            className="rounded bg-yellow-600 px-4 py-2 text-white hover:bg-yellow-700"
          >
            Repair Connection

        )
      case 'reconnect':
        return (
          

QuilttButton:
[console.log('Connection reconnected:', metadata.connectionId)]}
            className="rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700"
          >
            Reconnect

        )
      case 'check_status':
        return (
          Check Status Page (https://status.quiltt.io)
        )
      // Add other action handlers as needed
      default:
        return null
    }
  }

  return (
    {message}
      [renderAction()]
  )
}
```

### 5.3 Best Practices for Error Handling

1. **Proactive Monitoring**: Monitor connection statuses and handle repairs before users encounter issues
2. **Clear Communication**: Always show current connection status and provide clear next steps
3. **Automatic Recovery**: Implement automatic retry logic for transient failures
4. **State Management**: Track repair attempts and connection history
5. **Error Logging**: Log errors with proper context for debugging and support
6. **User Guidance**: Include clear instructions for user-actionable items

## Conclusion

Implementing a multi-aggregator strategy with Quiltt is like building a resilient financial data infrastructure. By following this guide, you've created:

- A smart routing system for optimal aggregator selection
- Resilient connection management across providers
- Adaptive feature support that maximizes capabilities

The implementation provides:

- A clear entry point for connecting accounts
- A dashboard view of all connections
- Detailed views of individual connections
- Automatic error detection and repair flows
- Real-time connection status monitoring

## Next Steps

**Continue Learning:**
- [GraphQL Tooling Tutorial](/get-started/tutorials/graphql-tooling) - Set up type-safe GraphQL with code generation
- [Account Statements Tutorial](/get-started/tutorials/statements) - Fetch and display account statements

**Related Tutorials:**
- [Authentication Tutorial](/get-started/tutorials/authentication) - Session token setup and management

**Reference Documentation:**
- [Connector SDK documentation](/connector/sdk) - Complete SDK reference for all platforms
- [Connector Reconnect flow](/connector/reconnect) - Repair broken connections
- [Webhooks documentation](/webhooks) - Real-time event notifications
- [Integration guides](/integrations) - Provider-specific configurations