Skip to content

Latest commit

 

History

History
309 lines (243 loc) · 7.55 KB

MIGRATION_GUIDE.md

File metadata and controls

309 lines (243 loc) · 7.55 KB

Migration Guide: Convention-Based OpenAPI Generation

This guide explains the improvements made to the OpenAPI generation system.

What Changed?

Before: Manual Registration ❌

Previously, you had to manually register every API endpoint in scripts/generate-openapi.ts:

// Had to write this for every endpoint:
registry.registerPath({
	method: 'get',
	path: '/api/users',
	description: 'Get all users',
	summary: 'List all users',
	responses: {
		200: {
			description: 'List of users',
			content: {
				'application/json': {
					schema: z.array(UserSchema)
				}
			}
		}
	}
});

// And this for POST...
registry.registerPath({
	method: 'post',
	path: '/api/users',
	// ... more manual registration
});

// And this for every other endpoint...

Problems:

  • ❌ Lots of duplication
  • ❌ Easy to forget to update when adding new routes
  • ❌ Route structure defined in two places (SvelteKit routes + OpenAPI script)
  • ❌ More code to maintain

After: Convention-Based ✅

Now, routes are automatically discovered from your SvelteKit file structure:

// In your route file: src/routes/api/users/+server.ts
export const _openapi: RouteOpenAPIMetadata = {
	operations: [
		{
			method: 'GET',
			summary: 'List all users',
			tags: ['Users'],
			responses: [
				{ status: 200, description: 'List of users', schema: z.array(UserSchema) }
			]
		},
		{
			method: 'POST',
			summary: 'Create user',
			tags: ['Users'],
			requestBody: { schema: CreateUserSchema },
			responses: [
				{ status: 201, description: 'User created', schema: UserSchema },
				{ status: 400, description: 'Invalid input' }
			]
		}
	]
};

Then just run npm run generate:openapi and it automatically:

  • ✅ Scans all route files
  • ✅ Discovers API paths from folder structure
  • ✅ Converts [id] to {id} automatically
  • ✅ Generates complete OpenAPI spec

Benefits:

  • ✅ Single source of truth (route structure = API structure)
  • ✅ No manual path registration needed
  • ✅ Adding new routes is automatic
  • ✅ Less code, less maintenance

How to Add New Routes

1. Create Your Route File

Follow SvelteKit conventions:

src/routes/api/products/+server.ts        → /api/products
src/routes/api/products/[id]/+server.ts   → /api/products/{id}

2. Add OpenAPI Metadata

Export an _openapi object:

import type { RouteOpenAPIMetadata } from '../../../../scripts/types.js';
import { ProductSchema, CreateProductSchema } from '@zod-schema-example/shared';

export const _openapi: RouteOpenAPIMetadata = {
	operations: [
		{
			method: 'GET',
			summary: 'List products',
			tags: ['Products'],
			responses: [
				{ status: 200, description: 'List of products', schema: z.array(ProductSchema) }
			]
		}
	]
};

export const GET: RequestHandler = async () => {
	// Your implementation
};

3. Regenerate OpenAPI

npm run generate:openapi

That's it! Your new route is automatically included.

Key Concepts

Route Scanning

The system scans src/routes for all +server.ts files:

  • Discovers all API route files automatically
  • Converts SvelteKit paths to OpenAPI format
  • Imports each route file to read metadata
  • Registers all operations found

Path Conversion

SvelteKit Path OpenAPI Path
api/users/+server.ts /api/users
api/users/[id]/+server.ts /api/users/{id}
api/posts/[postId]/comments/[cid]/+server.ts /api/posts/{postId}/comments/{cid}

Type Safety

All metadata is fully typed:

export interface RouteMethodConfig {
	method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
	summary?: string;
	description?: string;
	tags?: string[];
	requestBody?: {
		schema: z.ZodTypeAny;  // Any Zod schema
		contentType?: string;
		description?: string;
	};
	responses: ResponseDefinition[];
}

Examples

Simple GET Endpoint

export const _openapi: RouteOpenAPIMetadata = {
	operations: [
		{
			method: 'GET',
			summary: 'Health check',
			responses: [
				{ status: 200, description: 'Server is healthy' }
			]
		}
	]
};

POST with Request Body

export const _openapi: RouteOpenAPIMetadata = {
	operations: [
		{
			method: 'POST',
			summary: 'Create product',
			tags: ['Products'],
			requestBody: {
				schema: CreateProductSchema,
				description: 'Product data'
			},
			responses: [
				{ status: 201, description: 'Product created', schema: ProductSchema },
				{ status: 400, description: 'Invalid input' },
				{ status: 409, description: 'Product already exists' }
			]
		}
	]
};

Multiple Operations

export const _openapi: RouteOpenAPIMetadata = {
	operations: [
		{
			method: 'GET',
			summary: 'Get product',
			tags: ['Products'],
			responses: [
				{ status: 200, description: 'Product found', schema: ProductSchema },
				{ status: 404, description: 'Product not found' }
			]
		},
		{
			method: 'PUT',
			summary: 'Update product',
			tags: ['Products'],
			requestBody: { schema: UpdateProductSchema },
			responses: [
				{ status: 200, description: 'Product updated', schema: ProductSchema },
				{ status: 404, description: 'Product not found' },
				{ status: 400, description: 'Invalid input' }
			]
		},
		{
			method: 'DELETE',
			summary: 'Delete product',
			tags: ['Products'],
			responses: [
				{ status: 204, description: 'Product deleted' },
				{ status: 404, description: 'Product not found' }
			]
		}
	]
};

Updating Existing Routes

Your existing routes have been updated to use the new system:

  1. src/routes/api/users/+server.ts - Added _openapi export for GET and POST
  2. src/routes/api/users/[id]/+server.ts - Added _openapi export for GET, PUT, DELETE

All route handlers remain unchanged - only metadata was added.

Files Changed

New Files

  • scripts/types.ts - TypeScript types for OpenAPI metadata
  • scripts/route-scanner.ts - Scans and discovers route files
  • scripts/README.md - Documentation for the scripts
  • MIGRATION_GUIDE.md - This file

Modified Files

  • scripts/generate-openapi.ts - Now uses route scanning instead of manual registration
  • src/routes/api/users/+server.ts - Added _openapi export
  • src/routes/api/users/[id]/+server.ts - Added _openapi export

FAQ

Why _openapi instead of openapi?

SvelteKit only allows specific exports in route files (like GET, POST, etc.) or exports prefixed with _. Using _openapi follows SvelteKit conventions and avoids linter warnings.

Do I need to update the script for new routes?

No! Just create your route file with the _openapi export and run npm run generate:openapi. The scanner will automatically discover it.

What if I don't want to document a route?

Simply don't export _openapi from that route file. The scanner will skip it and show a warning.

Can I still use manual registration?

Yes, but it's not recommended. The new system is more maintainable and follows the "convention over configuration" principle.

How do path parameters work?

Path parameters are automatically detected from the route path. For example, [id] in the filename becomes {id} in the OpenAPI spec. Parameter validation schemas are applied in the generator.

Next Steps

  1. ✅ All existing routes have been migrated
  2. ✅ Run npm run generate:openapi to verify it works
  3. ✅ When adding new routes, use the new convention
  4. ✅ Enjoy automated OpenAPI generation!

Need Help?

Check out:

  • scripts/README.md - Detailed documentation on the generation system
  • scripts/types.ts - TypeScript type definitions
  • Existing route files for examples