Migrating to Zero YAML
Guide on how to migrate to Zero YAML in Tenklo.
The nango.yaml configuration file is no longer supported. Since September 15, 2026, Tenklo rejects deployments that use it. This guide helps you migrate your existing integrations from the YAML-based approach to Zero YAML, which uses pure TypeScript with modern tooling.
What is Zero YAML?
Zero YAML is Tenklo's new approach to building integrations that eliminates the need for separate nango.yaml configuration files. Instead, everything is defined in TypeScript using a configuration-as-code approach with full type safety.
Key benefits:
- Type Safety: Full TypeScript support with Zod schema validation
- Better Developer Experience: Modern tooling, enhanced CLI output, and better error messages
- Portability: Self-contained files that can be easily shared and version controlled
- No Custom Syntax: Pure TypeScriptβno need to learn YAML-specific conventions and our previous custom model syntax
Here's a quick example of the new syntax:
import { createSync } from 'nango';
import * as z from 'zod';
const issueSchema = z.object({
id: z.string(),
title: z.string(),
state: z.string()
});
const sync = createSync({
description: 'Fetches GitHub issues',
frequency: 'every hour',
models: { GithubIssue: issueSchema },
exec: async (nango) => {
await nango.batchSave([{ id: 'foobar' }], 'GithubIssue');
}
});How Zero YAML Works
The new system is built around simplification
Simplified file Structure
Like a regular TypeScript codebase
βββ index.ts # Declare what's deployed
βββ package.json # Dependency management and types
βββ github/ # An integration
βββ syncs/
β βββ fetchIssues.ts # A sync
βββ actions/
βββ createIssue.ts # An actionSimplified configuration
Three core functions that replace YAML configuration. You'll benefit from IntelliSense and type safety, directly in your IDE. No need to jump between files to find the right configuration.
createSync(): Defines data synchronization jobscreateAction(): Defines one-off jobscreateOnEvent(): Defines event-based jobs
Quick Migration Guide
Automatic Migration
The easiest way to migrate is using our automated migration command:
nango migrate-to-zero-yamlThis command will:
- Analyze your existing
nango.yamland TypeScript files - Generate new self-contained TypeScript files
- Create the required
package.jsonif it doesn't exist - Set up the
index.tsentry point - Preserve your existing logic and configuration
Post-Migration Steps
The migration command should automatically transform all your files. However, you should review the changes and make sure everything is correct.
Migration Plan
We recommend:
- Back up your current nango integrations folder
- Use your dev env to migrate to test the new format
- Deploy to prod env after you have assessed that everything works properly
What Has Changed?
Configuration: YAML β TypeScript Objects
Before (nango.yaml + separate .ts file):
# nango.yaml
integrations:
github:
issues:
runs: every 1h
sync_type: full
endpoint: GET /issues
description: Fetches GitHub issues
models:
GithubIssue:
id: string
title: stringAfter (single self-contained file):
const sync = createSync({
description: 'Fetches GitHub issues',
frequency: 'every hour',
models: { GithubIssue: issueSchema }
});Models: YAML Definitions β Zod Schemas
Before:
models:
GithubIssue:
id: string
title: string
state: stringAfter:
const issueSchema = z.object({
id: z.string(),
title: z.string(),
state: z.string()
});Before and After Examples
Sync Example
Before (issues.ts + nango.yaml excerpt):
// issues.ts
import type { NangoSync, GithubIssue } from '../../models';
export default async function fetchData(nango: NangoSync) {
// Sync logic here
}# nango.yaml excerpt
integrations:
github:
issues:
runs: every 1h
sync_type: full
endpoint: GET /issuesAfter (self-contained fetchIssues.ts):
import { createSync } from 'nango';
import * as z from 'zod';
const issueSchema = z.object({
id: z.string(),
title: z.string(),
state: z.string()
});
const sync = createSync({
description: 'Fetches GitHub issues',
frequency: 'every hour',
models: { GithubIssue: issueSchema },
exec: async (nango) => {
// Your existing sync logic here
}
});
export default sync;Action Example
Before (separate files):
// create-issue.ts
export default async function runAction(nango: NangoAction, input: CreateIssueInput) {
// Action logic
}After (self-contained):
import { createAction } from 'nango';
import * as z from 'zod';
const inputSchema = z.object({
title: z.string(),
body: z.string()
});
const action = createAction({
description: 'Create a GitHub issue',
input: inputSchema,
output: z.void(),
exec: async (nango, input) => {
// Your action logic here
}
});
export default action;Index File Structure
The new index.ts file imports all your integrations:
// index.ts
import './github/syncs/fetchIssues.js';
import './github/actions/createIssue.js';
// Add more imports as neededMigration Gotchas & FAQ
Gotchas
err - github/syncs/issues.ts:57:43
Argument of type '{ id: number; owner: string; repo: string; issue_number: number; title: string; author: string; author_id: string; state: string; date_created: Date; date_last_modified: Date; body: string; }[]' is not assignable to parameter of type 'RawModel[]'.
Type '{ id: number; owner: string; repo: string; issue_number: number; title: string; author: string; author_id: string; state: string; date_created: Date; date_last_modified: Date; body: string; }' is not assignable to type 'RawModel'.
Types of property 'id' are incompatible.
Type 'number' is not assignable to type 'string'.Previously you were setting the ID property of a sync to a number when it should always be a string.
err - 1password-scim/actions/create-user.ts:26:45
Property 'errors' does not exist on type 'ZodError<{ firstName: string; lastName: string; email: string; active?: boolean | undefined; externalId?: string | undefined; phoneNumbers?: { type: "work" | "mobile" | "other"; value: string; }[] | undefined; photos?: { ...; }[] | undefined; addresses?: { ...; }[] | undefined; title?: string | undefined; }>'.Previously you were validating an input using Zod in this way
const parsedInput = scimCreateUserSchema.safeParse(input);
if (!parsedInput.success) {
for (const error of parsedInput.error.errors) {
await nango.log(
`Invalid input provided to create a user: ${error.message} at path ${error.path.join(".")}`,
{ level: "error" },
);
}
throw new nango.ActionError({
message: "Invalid input provided to create a user",
});
}Instead of this, you can use a Tenklo helper:
const parsedInput = await nango.zodValidateInput({ zodSchema: scimCreateUserSchema, input });Nango.yaml EOL
Q: Is nango.yaml still supported?
A: No. Support ended on September 15, 2026. Deployments that use a nango.yaml are rejected with an error that points to this guide. Functions deployed before then keep running until you redeploy them, which requires migrating first.
Package.json Requirement
Q: Why is package.json now mandatory?
A: The new system uses modern JavaScript tooling that requires proper dependency management. The package.json ensures:
- Consistent dependency versions across environments
- Proper module resolution
- Better integration with modern development tools
If you don't have one, the migration command will create it automatically.
Backward Compatibility
Q: What still works the same?
A: Your core integration logic remains unchanged. No new features or breaking changes. All CLI commands are still available.
Testing Migrated Integrations
Q: How do I test after migration?
A: Use the same testing commands:
# Test a specific sync
nango dryrun <scriptName> <connectionId>Rollback Process
Q: Can I rollback if something goes wrong?
A: Not to nango.yaml, since deployments that use it are rejected. Validate the migrated integration before it reaches production: run nango dryrun against a test connection, then deploy to a development environment with nango deploy dev. Keep a copy of your original files as a reference until you have validated the migration.
The Zero YAML approach represents a significant step forward in Tenklo's developer experience. By eliminating custom syntax and embracing pure TypeScript, we've made integrations more maintainable, shareable, and enjoyable to work with. Many more features will be possible thanks to this new syntax.
If you encounter any issues during migration, our support team is ready to help. Happy integrating! π
Questions, problems, feedback? Please reach out in the Slack community.
Related guides
- Functions guide - understand the current TypeScript-first project layout.
- Functions CLI - deploy and dry-run migrated functions.
- Testing - verify migrated integrations before rollout.