Sync Helper

Sync Helper

The Sync Helper automatically injects TypeScript type definitions into SvelteKit's generated type files, providing type-safe access to validated, json(), and error() helpers in your route handlers.

What is generateAutoOpenApiTypes?

generateAutoOpenApiTypes is a function that runs during SvelteKit's sync command to inject type augmentations into .svelte-kit/types files.

Setup in svelte.config.js:

import { generateAutoOpenApiTypes } from 'sveltekit-auto-openapi/sync-helper';

generateAutoOpenApiTypes();  // Call before export

/** @type {import('@sveltejs/kit').Config} */
const config = {
  // ... your config
};

export default config;
javascript

What it does:

  1. Detects when svelte-kit sync runs
  2. Finds all routes with _config exports
  3. Injects InjectedHelpers types into corresponding $types.d.ts files
  4. Provides autocomplete for validated, json(), and error()

Type Injection Mechanism

How It Works

The sync helper modifies SvelteKit's auto-generated type files:

Before sync:

// .svelte-kit/types/src/routes/api/users/$types.d.ts
import type * as Kit from '@sveltejs/kit';

type RouteParams = { };
type RouteId = '/api/users';

export type RequestEvent = Kit.RequestEvent<RouteParams, RouteId>;
export type RequestHandler = Kit.RequestHandler<RouteParams, RouteId>;
typescript

After sync:

// .svelte-kit/types/src/routes/api/users/$types.d.ts
import type * as Kit from '@sveltejs/kit';

type RouteParams = { };
type RouteId = '/api/users';

// AUTO-GENERATED by sveltekit-auto-openapi - DO NOT MODIFY
import type { _config } from './+server';
import type { InjectedHelpers } from 'sveltekit-auto-openapi/types';

type AugmentedRequestEvent = Kit.RequestEvent<RouteParams, RouteId> & (
  | InjectedHelpers<typeof _config, 'POST'>
  | InjectedHelpers<typeof _config, 'GET'>
);

export type RequestEvent = AugmentedRequestEvent;
export type RequestHandler = (event: AugmentedRequestEvent) => Response | Promise<Response>;
// END AUTO-GENERATED
typescript

What Gets Injected

The injected code adds three typed helpers to your RequestEvent:

1. validated Property

Contains all validated request data:

{
  validated: {
    body: T;         // Validated request body
    query: T;        // Validated query parameters
    pathParams: T;   // Validated path parameters
    headers: T;      // Validated headers
    cookies: T;      // Validated cookies
  }
}
typescript

Types are extracted from your _config schemas.

2. json() Helper

Type-safe JSON response function:

json(data: SuccessResponseType): Response
typescript

The data parameter is type-checked against your 2XX response schemas.

3. error() Helper

Type-safe error function:

error(status: number, body: ErrorResponseType): never
typescript

The body parameter is type-checked against your 4XX/5XX response schemas.

How Sync Works

On Initial Build

When you run bunx svelte-kit sync:

  1. Discover routes: Finds all +server.ts files with _config exports
  2. Map to types: Locates corresponding .svelte-kit/types/.../$types.d.ts files
  3. Inject types: Modifies each types file to add InjectedHelpers
  4. Write files: Saves modified types back to disk

On File Changes

When you modify a route file:

  1. Detect change: Plugin's HMR hook detects file modification
  2. Check for _config: Determines if route has _config export
  3. Update types: Re-runs sync for just that file
  4. Restore if removed: If _config is removed, restores original types

Type Safety Flow

Your _config export

RouteConfig type validates structure

Sync helper reads _config

Extracts HTTP methods

Generates InjectedHelpers<typeof _config, Method>

Injects into $types.d.ts

TypeScript provides autocomplete
null

Internal API

_syncAllTypes()

Syncs all routes with _config exports:

export async function _syncAllTypes(): Promise<void> {
  // Find all +server.ts files
  const serverFiles = await glob('src/routes/**/+server.ts');

  for (const file of serverFiles) {
    // Check if has _config
    const hasConfig = await checkForConfig(file);

    if (hasConfig) {
      await _syncFileTypes(file);
    }
  }
}
typescript

_syncFileTypes(filePath)

Syncs a specific route file:

export async function _syncFileTypes(filePath: string): Promise<void> {
  // Extract methods from _config
  const methods = await extractMethods(filePath);

  // Find corresponding types file
  const typesPath = getTypesPath(filePath);

  // Modify types file
  await modifyTypesFile(typesPath, methods);
}
typescript

modifyTypesFile(typesPath, methods)

Uses ts-morph to modify the TypeScript file:

async function modifyTypesFile(
  typesPath: string,
  methods: string[]
): Promise<void> {
  const project = new Project();
  const sourceFile = project.addSourceFileAtPath(typesPath);

  // Remove previous AUTO-GENERATED section
  removePreviousInjection(sourceFile);

  // Find RouteId type
  const routeIdType = sourceFile.getTypeAlias('RouteId');

  if (routeIdType) {
    // Add imports
    sourceFile.addImportDeclaration({
      moduleSpecifier: './+server',
      namedImports: [{ name: '_config' }],
      isTypeOnly: true
    });

    sourceFile.addImportDeclaration({
      moduleSpecifier: 'sveltekit-auto-openapi/types',
      namedImports: [{ name: 'InjectedHelpers' }],
      isTypeOnly: true
    });

    // Build union type for all methods
    const unionParts = methods.map(
      m => `InjectedHelpers<typeof _config, '${m}'>`
    );

    // Add augmented type
    sourceFile.addTypeAlias({
      name: 'AugmentedRequestEvent',
      type: `Kit.RequestEvent<RouteParams, RouteId> & (${unionParts.join(' | ')})`
    });

    // Replace exports
    replaceRequestEventExport(sourceFile);
  }

  await sourceFile.save();
}
typescript

Troubleshooting

Types Not Updating

Problem: Changes to _config don't reflect in types

Solutions:

  1. Run sync manually:

    bunx svelte-kit sync
    bash
  2. Restart TypeScript server in your IDE:

    • VS Code: Cmd+Shift+P → "TypeScript: Restart TS Server"
  3. Clear and rebuild:

    rm -rf .svelte-kit
    bunx svelte-kit sync
    bash

Types Not Found

Problem: validated, json(), error() not available

Solutions:

  1. Check _config is exported:

    export const _config = { /* ... */ } satisfies RouteConfig;
    typescript
  2. Verify generateAutoOpenApiTypes() is in svelte.config.js

  3. Check types file exists:

    ls .svelte-kit/types/src/routes/your-route/\$types.d.ts
    bash

Type Conflicts

Problem: TypeScript errors about conflicting types

Solutions:

  1. Check for multiple _config exports in the same file

  2. Ensure _config uses satisfies RouteConfig:

    export const _config = { /* ... */ } satisfies RouteConfig;
    typescript
  3. Remove manual type augmentations that might conflict

Sync Not Running

Problem: Types never get injected

Solutions:

  1. Check svelte.config.js has the call:

    generateAutoOpenApiTypes();  // Before export
    javascript
  2. Verify you're running svelte-kit sync:

    bunx svelte-kit sync
    bash
  3. Check console for errors during sync

Advanced Usage

Custom Type Augmentation

If you need additional type augmentations:

// src/app.d.ts
import type { InjectedHelpers } from 'sveltekit-auto-openapi/types';

declare global {
  namespace App {
    interface Locals {
      user?: User;
    }
  }
}
typescript

Conditional Sync

Only sync specific routes:

// Custom sync helper
import { _syncFileTypes } from 'sveltekit-auto-openapi/sync-helper';

const routesToSync = [
  'src/routes/api/users/+server.ts',
  'src/routes/api/posts/+server.ts'
];

for (const route of routesToSync) {
  await _syncFileTypes(route);
}
typescript

Manual Sync Trigger

Trigger sync programmatically:

import { _syncAllTypes } from 'sveltekit-auto-openapi/sync-helper';

// After modifying routes
await _syncAllTypes();
console.log('Types synced!');
typescript

Performance

Build Time Impact

  • First sync: 100-500ms depending on route count
  • Incremental sync: 10-50ms per file
  • No runtime cost: Types are erased at runtime

When Sync Runs

Sync only runs when:

  • svelte-kit sync command executes
  • Route files with _config change (HMR)
  • SvelteKit build process runs

It does NOT run:

  • On every file save
  • For routes without _config
  • At runtime

Best Practices

Always Use satisfies

Ensure type checking with satisfies:

// ✅ Good
export const _config = {
  openapiOverride: { /* ... */ }
} satisfies RouteConfig;

// ❌ Bad - no type checking
export const _config = {
  openapiOverride: { /* ... */ }
};
typescript

Don't Modify Generated Types

Never manually edit .svelte-kit/types files:

// ❌ Don't do this - will be overwritten
// .svelte-kit/types/src/routes/api/users/$types.d.ts
export type RequestEvent = MyCustomType;
typescript

Keep _config in Same File

Don't import _config from elsewhere:

// ❌ Bad - sync won't detect
import { _config } from './config';
export { _config };

// ✅ Good - define in route file
export const _config = { /* ... */ } satisfies RouteConfig;
typescript

Related Documentation