skills/migrating-to-zero-yaml/SKILL.md
Manually migrates legacy `nango.yaml` Nango projects to Zero YAML TypeScript by generating `models.ts`, `index.ts`, package/tsconfig scaffolding, and wrapping legacy scripts in `createSync()`, `createAction()`, or `createOnEvent()`. Use when a Nango repo still has `nango.yaml` and needs manual Zero YAML migration.
npx skillsauth add nangohq/skills migrating-to-zero-yamlInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill replaces the old CLI migrator with an LLM-driven mechanical migration. Keep behavior as close as possible to the existing YAML project, then validate and stop. Do not turn this into a broader cleanup unless the user asks.
nango migrate-to-zero-yaml.lastSyncDate, syncType, trackDeletes, or validation patterns unless the user asks or compilation forces you to touch them.nango.yaml exists.nango.yaml and inventory:
The migrated project should have:
package.jsontsconfig.jsonmodels.tsindex.tscreateSync(), createAction(), or createOnEvent()nangonango.yaml in the final migrated statenango.yaml and build a migration inventory.package.jsontsconfig.jsonmodels.ts from the YAML models.createSync(...).createAction(...).createOnEvent(...).index.ts side-effect imports for every migrated script.nango.yaml only after the replacement project is in place and there is still a rollback path outside the file.package.jsonIf the project does not have a package file, create one. If it already exists, preserve existing fields and add the Zero YAML essentials:
type: "module"engines.node: ">=20.0"compile: "nango compile"dev: "nango dev"devDependencies.nangodevDependencies.zodPrefer preserving the repo's package manager and existing workspace fields.
tsconfig.jsonUse the standard Zero YAML TypeScript config shape:
{
"$schema": "https://json.schemastore.org/tsconfig",
"include": ["index.ts", "**/*.ts"],
"exclude": ["node_modules", "dist", "build", ".nango"],
"compilerOptions": {
"module": "node16",
"target": "esnext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node16",
"exactOptionalPropertyTypes": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"sourceMap": true,
"noEmit": true
}
}
models.tsCreate a root models.ts file that turns YAML model definitions into Zod schemas plus exported inferred types.
Use this pattern:
import * as z from 'zod';
export const Ticket = z.object({
id: z.string(),
title: z.string()
});
export type Ticket = z.infer<typeof Ticket>;
export const models = {
Ticket
};
Conversion rules:
string -> z.string()number -> z.number()boolean -> z.boolean()Date -> z.date()any -> z.any() or z.unknown() if you need a safer fallback.optional()z.array(...)z.object(...)z.union([...])z.record(...) or .catchall(...)Practical rule: if the old YAML model is hard to express exactly, preserve migration momentum with the broadest safe schema that still lets the project compile. Tighten later only if needed.
Define models in dependency order. If you hit circular references, use z.lazy(() => ModelName).
For each YAML sync, keep the existing file path when possible and wrap the current default-exported function in createSync(...).
Map YAML config to Zero YAML config like this:
description -> descriptionversion -> version (default to 0.0.1 if missing)runs -> frequencyauto_start -> autoStartsync_type -> syncTypetrack_deletes -> trackDeletesendpoints -> endpointsscopes -> scopeswebhookSubscriptionsinput model -> metadataoutput models -> modelsexecIf no YAML input model exists for the sync, default metadata to z.object({}) for migration parity.
If the file exports onWebhookPayloadReceived, move that function into the createSync(...) config as onWebhook.
Use this shape:
import { createSync } from 'nango';
import * as z from 'zod';
import { Metadata, Ticket } from '../../models.js';
const sync = createSync({
description: 'Sync tickets',
version: '0.0.1',
frequency: 'every hour',
autoStart: true,
syncType: 'full',
trackDeletes: false,
endpoints: [{ method: 'GET', path: '/tickets', group: 'Tickets' }],
metadata: Metadata,
models: { Ticket },
exec: async (nango) => {
// keep existing logic
}
});
export type NangoSyncLocal = Parameters<typeof sync['exec']>[0];
export default sync;
For each YAML action, wrap the existing default-exported function in createAction(...).
Map YAML config like this:
description -> descriptionversion -> version (default 0.0.1)endpoint -> endpointinput -> inputoutput -> outputscopes -> scopesexecIf the action has no input or output schema, use z.void().
Use this shape:
import { createAction } from 'nango';
import * as z from 'zod';
import { CreateTicketInput, CreateTicketOutput } from '../../models.js';
const action = createAction({
description: 'Create ticket',
version: '0.0.1',
endpoint: { method: 'POST', path: '/tickets', group: 'Tickets' },
input: CreateTicketInput,
output: CreateTicketOutput,
exec: async (nango, input) => {
// keep existing logic
}
});
export type NangoActionLocal = Parameters<typeof action['exec']>[0];
export default action;
For each YAML lifecycle handler, wrap the existing default export in createOnEvent(...).
Map like this:
event<event> event handlerexecUse this shape:
import { createOnEvent } from 'nango';
export default createOnEvent({
event: 'post-connection-creation',
description: 'post-connection-creation event handler',
exec: async (nango) => {
// keep existing logic
}
});
The old migrator also cleaned up imports. Do the same manually.
NangoSync, NangoAction, ProxyConfiguration, and ActionError imports from old models imports.nango instead.../../models.js or the correct relative path..js in index.ts.Important mechanical fixes:
NangoSync parameter annotations with NangoSyncLocal after wrapping the file.NangoAction parameter annotations with NangoActionLocal after wrapping the file.nango.batchSave<T>(...)nango.batchUpdate<T>(...)nango.batchDelete<T>(...)nango.getMetadata<T>()Those generic arguments often compile in the old format but become noisy or unnecessary after migration.
index.tsCreate a root index.ts with side-effect imports for every migrated script. Include the .js extension.
Example:
import './github/syncs/fetch-issues.js';
import './github/actions/create-issue.js';
import './github/on-events/post-connection-creation.js';
Do not use named or default imports here.
Search the rest of the repo for TypeScript helper files outside the migrated integration entrypoints.
If they import Nango runtime types from old model files, move those imports to nango and leave model imports in place only for actual model schemas/types.
Do not rewrite unrelated business logic in helper files.
The manual migration should behave like the old CLI migrator:
syncTypetrackDeletesnango.lastSyncDate usage if it already existsOnly do a second modernization pass if the user explicitly asks.
After the mechanical migration:
nango compilenango dryrun <script-name> <connection-id> --validate -e dev --no-interactive --auto-confirm--input '{...}' or --input '{}'nango dryrun <script-name> <connection-id> --save -e dev --no-interactive --auto-confirmnango generate:tests && npm test*.test.jsonIf a migrated sync/action still needs a true behavioral refactor after this point, stop and treat that as a separate task.
development
Copy-pasteable Nango quickstart prompt for agents. Guides brand-new Nango customers from signup through API key setup, integration setup, connection authorization, and action/sync next steps. Use for first Nango integration walkthroughs.
tools
Builds Nango Function implementation patterns for createAction() and createSync() without choosing a local CLI or remote API workflow. Use only when the user asks to create or update a Nango action or sync and it is unclear whether the work should happen in a checked-out project via CLI or through Nango remote APIs. Do not load when building-nango-functions-locally or building-nango-functions-remotely applies; those skills overlap with this content and add workflow-specific validation and deploy details.
development
Migrates existing Nango TypeScript createSync implementations from nango.lastSyncDate, legacy incremental syncType, and non-resumable full refreshes to checkpoint-based syncs. Use when updating customer Nango sync code to define checkpoint schemas, call getCheckpoint/saveCheckpoint/clearCheckpoint after every batchSave (including inside paginate loops), test dryruns with --checkpoint, and fix deletion handling for checkpointed incremental or full syncs.
tools
Universal gateway for any third-party API or SaaS (Google Calendar, Gmail, Slack, Notion, Linear, HubSpot, etc.). TRIGGER on any request to read or modify data in an external product, even when no matching MCP tool is loaded.