---
title: "Truto vs Nango: Code-First vs Declarative SaaS Integrations"
slug: truto-vs-nango-code-first-vs-declarative-saas-integrations
date: 2026-07-02
author: Roopendra Talekar
categories: [General, Engineering]
excerpt: Compare the architectural differences between Nango's code-first integration framework and Truto's declarative unified API for B2B SaaS engineering teams.
tldr: "Nango requires your team to write and maintain custom TypeScript integration scripts, while Truto uses a declarative, zero-code execution engine driven by JSONata to eliminate technical debt."
canonical: https://truto.one/blog/truto-vs-nango-code-first-vs-declarative-saas-integrations/
---

# Truto vs Nango: Code-First vs Declarative SaaS Integrations


If you are evaluating infrastructure to power your B2B SaaS integrations, you are fundamentally choosing between two architectural philosophies: writing custom code for every integration, or treating integrations entirely as data-driven configuration. 

Nango provides a code-first framework. It handles the OAuth handshakes and provides a hosted environment, but your engineering team must write, deploy, and maintain custom TypeScript functions to dictate how data is fetched and synced. Truto provides a declarative unified API. It relies on a generic execution engine that uses JSONata and configuration blobs to normalize third-party APIs, requiring zero integration-specific code in the platform's core logic.

The stakes for this architectural decision are high. BetterCloud's 2025 State of SaaS report found that organizations now use an average of 106 different SaaS tools. Your enterprise customers expect your product to connect with their existing stack natively. Yet, according to Adalo, only 2% of organizations have successfully integrated more than half their applications. This massive integration gap exists because building and maintaining custom API connections is notoriously difficult, expensive, and prone to breaking.

This guide breaks down exactly how Truto and Nango compare at an architectural level. We will examine how each platform handles custom enterprise fields, rate limits, webhooks, and the long-term technical debt associated with maintaining dozens of third-party API connections.

## The Core Architectural Difference: Code-First vs. Declarative Integrations

When evaluating [Truto vs Nango: Why Code-First Integration Platforms Don't Scale](https://truto.one/truto-vs-nango-why-code-first-integration-platforms-dont-scale/), the discussion always traces back to where the business logic lives.

Code-first platforms operate on the assumption that API integrations are too complex to be fully abstracted. They provide the scaffolding - typically managed authentication, token refresh schedulers, and a basic job queue - but leave the actual data transformation and sync logic to the developer. You write a script that says "fetch this endpoint, map these fields, and push to this database."

Declarative platforms operate on the assumption that 95% of REST and GraphQL APIs behave in predictable patterns that can be abstracted. Instead of writing imperative instructions, you provide a configuration file. You define the shape of the upstream data, the desired shape of your unified data, and the transformation rules. A generic execution engine reads this configuration and performs the work.

Both approaches can successfully pull a list of contacts from Salesforce. The divergence happens six months later, when you have 40 integrations running, 15 enterprise customers demanding custom field mappings, and upstream APIs deprecating endpoints without warning.

## Nango's Approach: The Hidden Cost of Owning Integration Code

Nango positions itself as a developer-friendly integration framework. The initial developer experience is highly appealing to engineering teams. You get a familiar TypeScript environment, an SDK to handle the OAuth dance, and the freedom to write whatever logic you want.

Behind the scenes, Nango acts as a managed runtime for your integration scripts. When a sync job triggers, Nango spins up a worker, injects the correct OAuth tokens, and executes your custom TypeScript function. 

This architecture creates immediate velocity but guarantees long-term technical debt. Hyper.io reports that developers spend an average of 41% of their time on bugs, maintenance, and technical debt. When you choose a code-first integration platform, you are actively adding to that percentage.

Consider what happens when you need to support five different CRMs. In a code-first environment, you are responsible for writing five distinct synchronization scripts. 

```typescript
// Conceptual representation of a code-first sync script
export default async function syncContacts(context: SyncContext) {
  const { provider, credentials } = context;
  
  if (provider === 'salesforce') {
    // Write SOQL query to fetch contacts
    // Handle Salesforce's specific pagination cursor
    // Map Salesforce fields to your internal model
  } else if (provider === 'hubspot') {
    // Call HubSpot's CRM API
    // Handle HubSpot's specific pagination cursor
    // Map HubSpot fields to your internal model
  } else if (provider === 'pipedrive') {
    // Call Pipedrive API
    // Map Pipedrive fields
  }
}
```

You own this code. When Salesforce updates their API version, you must update your script. When HubSpot changes their rate limit headers, you must implement the new backoff logic. When a specific enterprise customer needs a custom field mapped from Pipedrive, you have to add conditional logic to your script to handle that specific tenant's edge case.

As you scale to 50 or 100 integrations, your integration repository becomes a sprawling, fragile codebase. Adding a new integration is no longer a quick task; it requires a code review, a staging deployment, and a production release. You are forced to dedicate engineering headcount simply to keep the existing integrations functioning.

## Truto's Approach: Zero Integration-Specific Code

Truto takes a radically different approach to the same problem. The entire platform contains zero integration-specific code. 

There is no `if (provider === 'hubspot')` logic anywhere in Truto's execution path. There are no provider-specific database tables. The platform is designed as a generic execution pipeline that treats integrations entirely as data operations.

When you use Truto, the behavior of an integration is defined by a declarative configuration blob and a set of JSONata expressions. The runtime engine reads this configuration and executes it without any awareness of which third-party API it is interacting with. 

To understand [Zero Integration-Specific Code: How to Ship API Connectors as Data-Only Operations](https://truto.one/zero-integration-specific-code-how-to-ship-new-api-connectors-as-data-only-operations/), look at the architecture of the execution pipeline:

```mermaid
flowchart TD
    A["Incoming API Request<br>(e.g. GET /unified/contacts)"] --> B["Generic Execution Engine"]
    B --> C{"Load Provider Config"}
    C --> D["Inject Auth Credentials"]
    C --> E["Load JSONata Mapping Expressions"]
    D --> F["Execute Upstream HTTP Request"]
    E --> F
    F --> G["Normalize Response Shape<br>via JSONata"]
    G --> H["Return Standardized JSON"]
```

When a request enters the system, the engine queries the database for the mapping configuration associated with that specific resource. It uses JSONata - a lightweight query and transformation language for JSON data - to translate the unified request into the provider-specific format. It executes the HTTP request using the stored OAuth tokens, receives the raw payload, and uses another JSONata expression to transform the response back into the unified schema.

This architectural pattern fundamentally changes how integrations are maintained. Adding support for a new CRM does not require writing new code or deploying the platform. It simply requires adding a new JSON configuration record to the database. 

If an upstream API changes its response shape, you update the JSONata mapping string in the configuration interface. The core execution engine remains untouched. This separation of concerns allows engineering teams to ship integrations at massive scale without inflating their codebase or dedicating sprints to integration maintenance.

## Handling Custom Fields and Enterprise Edge Cases

Standardized data models work perfectly for small businesses. They break immediately when you move upmarket. Enterprise customers heavily customize their SaaS instances. A standard `Contact` object in Salesforce might have 40 custom fields specific to that company's internal sales process.

In a code-first platform like Nango, handling per-customer custom fields requires modifying your TypeScript sync scripts. You typically end up writing complex conditional logic to check the tenant ID, fetch their specific custom field schema, and map the data dynamically. As the number of enterprise customers grows, this code path becomes incredibly difficult to test and maintain safely.

Truto solves this problem without requiring a single line of custom code through its configuration override architecture. 

If you want to understand [Per-Customer Data Model Customization Without Code: The 3-Level JSONata Architecture](https://truto.one/per-customer-data-model-customization-without-code-the-3-level-jsonata-architecture/), you have to look at how configuration is resolved at runtime. Truto resolves JSONata mappings through a strict three-level hierarchy:

**Level 1: Platform Defaults**
Truto provides the base JSONata mapping for the integration. This covers the standard fields (name, email, company) that apply to 90% of use cases. 

**Level 2: Environment Overrides**
You can define your own JSONata mapping at the environment level. If your application specifically requires the `lead_score` field from HubSpot, you can override the default mapping for your entire production environment to always include that field.

**Level 3: Linked Account Overrides**
This is where enterprise customization happens. You can define a JSONata override for a single, specific end-user (a Linked Account). If Enterprise Customer A has a custom field called `cf_internal_routing_id`, you can add a mapping specifically to their Linked Account record.

When the execution engine runs, it merges these configurations, with the Linked Account taking highest precedence. 

```json
{
  "name": "$$.properties.firstname & ' ' & $$.properties.lastname",
  "email": "$$.properties.email",
  "custom_routing": "$$.properties.cf_internal_routing_id"
}
```

This approach isolates enterprise edge cases. You do not have to fork your integration logic or pollute your codebase with tenant-specific conditionals. The customization lives as pure data, attached directly to the customer's connection record.

## Infrastructure: Rate Limits, Pagination, and Webhooks

Connecting to an API is easy. Handling the infrastructure realities of working with third-party systems at scale is where engineering teams lose months of development time. 

### Rate Limiting Reality

Every third-party API handles rate limits differently. Some use standard headers, some return custom error payloads, and some simply drop connections. 

Many integration platforms obscure rate limits, attempting to automatically retry failed requests behind the scenes. This is a dangerous architectural pattern. If a platform silently absorbs HTTP 429 (Too Many Requests) errors and retries them, your application loses visibility into the actual state of the request. You end up with stalled queues, timeout errors, and unpredictable sync delays.

Truto takes a highly objective, transparent approach to rate limiting. Truto does not retry, throttle, or apply backoff on rate limit errors. When an upstream API returns an HTTP 429, Truto passes that error directly back to the caller.

However, Truto normalizes the rate limit metadata. Regardless of whether the upstream provider uses `X-RateLimit-Remaining`, `Rate-Limit-Left`, or a custom JSON error body, Truto extracts that data and normalizes it into standardized IETF headers:

*   `ratelimit-limit`: The total request quota.
*   `ratelimit-remaining`: The number of requests left in the current window.
*   `ratelimit-reset`: The timestamp when the quota resets.

This design gives your engineering team full control. You receive standard, predictable headers that allow you to implement intelligent backoff and circuit breaker patterns in your own application architecture, exactly where that state belongs.

### Webhook Normalization and Delivery

Managing incoming webhooks from 50 different providers requires significant infrastructure. Each provider signs payloads differently, structures events differently, and delivers them with varying degrees of reliability.

Nango allows you to write webhook handler functions, but you are still responsible for writing the logic to parse the specific provider payloads and map them to your internal schema.

Truto unifies webhook ingestion. The platform supports both account-specific webhooks and environment-integration fan-out patterns. When a webhook arrives at Truto's edge, the system verifies the provider's signature, identifies the associated Linked Account, and uses JSONata to normalize the incoming payload into a standard event contract.

For outbound delivery to your application, Truto utilizes a highly resilient queue and object-storage claim-check pattern. Instead of attempting to push massive payloads directly to your endpoint (which often causes timeout failures), Truto stores the normalized payload securely and sends your endpoint a lightweight, signed notification containing a reference ID. Your system can then pull the full payload when it has capacity, guaranteeing delivery even during massive traffic spikes.

### Pagination Abstraction

If you want to know [How to Normalize API Pagination and Error Handling Across 50+ APIs Without Building It Yourself](https://truto.one/how-to-normalize-api-pagination-and-error-handling-across-50-integrations/), you have to look at how pagination is abstracted.

Some APIs use cursor-based pagination. Others use offset/limit. Some return a `next_page_url`. In a code-first platform, you have to write `while` loops to handle these specific mechanics for every single integration.

Truto handles pagination declaratively. The provider configuration defines how pagination works for that specific API. When you request data through Truto's unified API, you use a standard set of cursor parameters. The execution engine translates your unified cursor into the provider-specific pagination format, fetches the data, and returns a unified `next_cursor` string. You never have to write a custom pagination loop again.

## Which Integration Platform Should You Choose?

The choice between Nango and Truto comes down to how you want to allocate your engineering resources over the next three years.

Choose Nango if you have a dedicated integrations team that prefers to retain total control over the execution logic. If you view integrations as a core engineering competency and are willing to absorb the technical debt of maintaining a growing repository of custom TypeScript scripts, a code-first framework provides a familiar environment to build in.

Choose Truto if you want to ship enterprise-grade integrations without expanding your engineering headcount. By abstracting the execution logic into a generic pipeline and treating data mapping as a declarative configuration exercise, Truto eliminates the technical debt associated with third-party APIs. You gain the ability to handle complex enterprise customizations through JSONata overrides, standardized rate limit headers, and normalized webhooks, allowing your team to focus entirely on building your core product.

> Stop writing custom integration scripts. See how Truto's declarative architecture can help your engineering team ship and maintain 100+ integrations without the technical debt.
>
> [Talk to us](https://cal.com/truto/partner-with-truto)
