npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

sveltekit-openapi-generator

v0.1.5

Published

Generate OpenAPI 3.0 specifications from SvelteKit server endpoints using JSDoc @swagger annotations

Readme

SvelteKit OpenAPI Generator

npm version npm downloads License: MIT GitHub stars

Automatically generate OpenAPI 3.0 specifications from your SvelteKit server endpoints using JSDoc @swagger annotations.

This Vite plugin scans your SvelteKit +page.server.{js,ts} and +server.{js,ts} files, extracts @swagger JSDoc blocks, and produces a unified OpenAPI spec with Hot Module Replacement (HMR) support.

📋 Table of Contents

🔗 Links

✨ Features

  • 🔥 Hot Module Replacement - Specs update live as you edit JSDoc
  • 📦 Virtual Module - Import the spec directly: import spec from 'virtual:openapi-spec'
  • 🛠️ Dev Middleware - Access spec at /openapi-spec.json during development
  • 🏗️ Build Support - Generate static spec files during build
  • 🔗 Smart Merging - Combines multiple specs using openapi-merge
  • 📝 TypeScript Support - Full type support with automatic type stripping for .ts files
  • 🎯 SvelteKit Native - Handles route parameters, groups, and optional segments
  • 🧩 Shared Schemas - Centralize component definitions to avoid duplication
  • 📖 Swagger UI Ready - Easy integration with Swagger UI for interactive docs

🔄 Migration from SwaggerUI-Svelte

If you're migrating from the deprecated SwaggerUI-Svelte project, this library offers more control and features for generating OpenAPI specifications from your SvelteKit endpoints.

Key Changes

  • JSDoc Annotations: Instead of automatic scanning, use @swagger JSDoc blocks above your endpoint handlers for precise documentation.
  • Plugin-Based Generation: Configure the Vite plugin to generate specs with HMR support.
  • Swagger UI Integration: Use swagger-ui-dist for displaying the generated spec, as detailed in the Integration with Swagger UI section below.

Migration Steps

  1. Install Dependencies:

    npm install -D sveltekit-openapi-generator swagger-ui-dist
  2. Configure the Plugin: Add the plugin to your vite.config.js before the SvelteKit plugin:

    import { sveltekit } from '@sveltejs/kit/vite';
    import { defineConfig } from 'vite';
    import openapiPlugin from 'sveltekit-openapi-generator';
    
    export default defineConfig({
    	plugins: [
    		openapiPlugin({
    			// Optional: path to shared schema definitions
    			baseSchemasPath: 'src/lib/schemas.js'
    		}),
    		sveltekit()
    	]
    });
  3. Document Your Endpoints: Add @swagger JSDoc to your server files:

    /**
     * @swagger
     * /api/users:
     *   get:
     *     summary: Get all users
     *     responses:
     *       200:
     *         description: Success
     */
    export async function GET({}) {
    	// Your implementation
    }
  4. Integrate Swagger UI: Follow the steps in the Integration with Swagger UI section to display your API documentation.

For more details, refer to the Quick Start guide.

🎨 Styling

Demo pages use Tailwind CSS for modern, responsive styling. Package size: 14.80KB packed / 59.96KB unpacked - Tailwind is a devDependency only, and demo routes are excluded from the npm package.

This project is compatible with Tailwind CSS v4. If upgrading to v4, follow the official upgrade guide, which includes using @import "tailwindcss"; instead of @tailwind directives and configuring themes in CSS with @theme.

Since the package does not ship pre-built components with Tailwind classes, no additional Tailwind configuration is required for this library.

📦 Installation

npm install -D sveltekit-openapi-generator

🚀 Quick Start

1. Configure the Plugin

Add the plugin to your vite.config.js before the SvelteKit plugin:

import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import openapiPlugin from 'sveltekit-openapi-generator';

export default defineConfig({
	plugins: [
		openapiPlugin({
			// Optional: path to shared schema definitions
			baseSchemasPath: 'src/lib/schemas.js',

			// Optional: external YAML files to include
			yamlFiles: ['src/specs/external.yaml'],

			// Optional: prepend to all paths (useful for /api prefix)
			prependPath: '/api',

			// Optional: output file during build
			outputPath: 'static/openapi.json',

			// Optional: debounce delay for HMR (ms)
			debounceMs: 100
		}),
		sveltekit()
	]
});

2. Document Your Endpoints

Add @swagger JSDoc blocks to your server files:

// src/routes/api/users/+server.js

/**
 * @swagger
 * /api/users:
 *   get:
 *     summary: Get all users
 *     tags:
 *       - Users
 *     parameters:
 *       - in: query
 *         name: limit
 *         schema:
 *           type: integer
 *           default: 10
 *     responses:
 *       200:
 *         description: Success
 *         content:
 *           application/json:
 *             schema:
 *               type: array
 *               items:
 *                 $ref: '#/components/schemas/User'
 */
export async function GET({ url }) {
	const limit = Number(url.searchParams.get('limit') || 10);
	// Your implementation
	return json({ users: [] });
}

/**
 * @swagger
 * /api/users:
 *   post:
 *     summary: Create a user
 *     tags:
 *       - Users
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             $ref: '#/components/schemas/UserInput'
 *     responses:
 *       201:
 *         description: Created
 */
export async function POST({ request }) {
	const data = await request.json();
	// Your implementation
	return json(data, { status: 201 });
}

3. Define Shared Schemas (Optional)

Create a file for shared component schemas:

// src/lib/schemas.js

/**
 * @swagger
 * components:
 *   schemas:
 *     User:
 *       type: object
 *       required:
 *         - id
 *         - email
 *       properties:
 *         id:
 *           type: string
 *           format: uuid
 *         email:
 *           type: string
 *           format: email
 *         name:
 *           type: string
 *         createdAt:
 *           type: string
 *           format: date-time
 *     UserInput:
 *       type: object
 *       required:
 *         - email
 *       properties:
 *         email:
 *           type: string
 *           format: email
 *         name:
 *           type: string
 */

4. Use the Spec

In Your Svelte Components

<script lang="ts">
	import spec from 'virtual:openapi-spec';

	console.log('Available paths:', Object.keys(spec.paths));
</script>

<h1>API Documentation</h1>
<pre>{JSON.stringify(spec, null, 2)}</pre>

Via Dev Middleware

During development, access the spec at:

http://localhost:5173/openapi-spec.json

After Build

If you set outputPath, the spec will be written to that location:

static/openapi.json

📚 Documentation

Plugin Options

| Option | Type | Default | Description | | ----------------- | ---------- | ----------- | ----------------------------------------------------- | | info | object | undefined | OpenAPI info section (title, version, description) | | servers | array | undefined | OpenAPI servers configuration | | baseSchemasPath | string | undefined | Path to file with shared @swagger component schemas | | yamlFiles | string[] | [] | Additional YAML files to merge into the spec | | prependPath | string | '' | Prefix to prepend to all paths (e.g., /api) | | include | string[] | ['src/routes/**/{+server,+page.server}.{js,ts}'] | Glob patterns to include | | exclude | string[] | ['**/node_modules/**', '**/.svelte-kit/**'] | Glob patterns to exclude | | failOnErrors | boolean | false | Whether to fail on JSDoc parsing errors | | outputPath | string | undefined | File path to write spec during build | | debounceMs | number | 200 | Debounce delay for HMR regeneration |

SvelteKit Route Mapping

The plugin automatically converts SvelteKit route conventions to OpenAPI paths:

| SvelteKit Route | OpenAPI Path | Notes | | --------------------------------- | ------------------- | ----------------------------------------- | | /api/users/+server.js | /api/users | Standard route | | /api/users/[id]/+server.js | /api/users/{id} | Path parameter | | /api/posts/[[page]]/+server.js | /api/posts/{page} | Optional parameter (document as optional) | | /api/(admin)/logs/+server.js | /api/logs | Route groups ignored | | /api/files/[...path]/+server.js | /api/files/{path} | Rest parameters |

TypeScript Support

The plugin fully supports TypeScript files! It automatically strips type annotations before parsing JSDoc, so you can write endpoints in TypeScript without issues.

Example TypeScript endpoint:

// src/routes/api/posts/[id]/+server.ts
import type { RequestHandler } from '@sveltejs/kit';
import { json, error } from '@sveltejs/kit';

/**
 * @swagger
 * /api/posts/{id}:
 *   get:
 *     summary: Get post by ID
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: string
 *     responses:
 *       200:
 *         description: Post found
 *       404:
 *         description: Not found
 */
export const GET: RequestHandler = async ({ params }: { params: { id: string } }) => {
	const post = await getPost(params.id);
	if (!post) throw error(404, 'Post not found');
	return json(post);
};

The plugin handles TypeScript syntax internally using the TypeScript compiler API to ensure @swagger JSDoc blocks are properly extracted.

Type definitions for the virtual module are automatically available. If you need to explicitly reference them:

/// <reference types="sveltekit-openapi-generator/ambient" />

Path Parameters Example

// src/routes/api/users/[id]/+server.js

/**
 * @swagger
 * /api/users/{id}:
 *   get:
 *     summary: Get user by ID
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: string
 *     responses:
 *       200:
 *         description: User found
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/User'
 *       404:
 *         description: User not found
 */
export async function GET({ params }) {
	// Implementation
}

🛠️ Integration with Swagger UI

You can easily add Swagger UI to visualize and test your API interactively:

Installation

npm install swagger-ui-dist

Create a Docs Route

Here's a production-ready example with reactive server URLs and robust error handling:

<!-- src/routes/docs/+page.svelte -->
<script lang="ts">
	import { onMount } from 'svelte';
	import { page } from '$app/state';
	import { dev } from '$app/environment';
	import 'swagger-ui-dist/swagger-ui.css';

	let containerElement: HTMLElement | undefined;
	let spec: any = $state();

	// Get the current server URL reactively
	let currentOrigin = $derived(page.url.origin);

	// Create a modified spec with the current server URL
	let specWithServer = $derived({
		...spec,
		servers: [
			{
				url: currentOrigin,
				description: dev ? 'Development server' : 'Production server'
			}
		]
	});

	async function initializeSwaggerUI() {
		if (!containerElement) return;

		try {
			// Attempt to load a virtual spec module (Vite plugin) first
			try {
				// @ts-ignore - virtual import may not exist in all environments
				const virtualSpec = await import('virtual:openapi-spec');
				spec = virtualSpec?.default ?? virtualSpec;
			} catch (e) {
				// Fallback: fetch the openapi spec from the dev middleware
				try {
					const res = await fetch('/openapi-spec.json');
					if (res.ok) spec = await res.json();
					else spec = { openapi: '3.0.0', info: { title: 'API' }, paths: {} };
				} catch (fetchErr) {
					spec = { openapi: '3.0.0', info: { title: 'API' }, paths: {} };
				}
			}

			// @ts-ignore - swagger-ui-dist doesn't have types
			const { SwaggerUIBundle, SwaggerUIStandalonePreset } = await import('swagger-ui-dist');

			SwaggerUIBundle({
				spec: specWithServer,
				domNode: containerElement,
				deepLinking: true,
				presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset]
			});
		} catch (error) {
			console.error('Failed to initialize Swagger UI:', error);
		}
	}

	onMount(() => {
		initializeSwaggerUI();
	});
</script>

<svelte:head>
	<title>API Documentation</title>
</svelte:head>

<div class="swagger-container">
	<div id="swagger-ui-container" bind:this="{containerElement}"></div>
</div>

<style>
	.swagger-container {
		min-height: 600px;
		padding: 2rem;
	}

	/* Hide the default Swagger UI top bar */
	:global(.swagger-ui .topbar) {
		display: none;
	}

	/* Dark mode support for Swagger UI */
	@media (prefers-color-scheme: dark) {
		:global(.swagger-ui) {
			filter: invert(0.9) hue-rotate(180deg);
		}
	}
</style>

Access Your Docs

Navigate to /docs in your browser to see the interactive API documentation!

The Swagger UI will automatically stay in sync with your spec changes during development thanks to HMR.

⚠️ Limitations & Best Practices

Known Limitations

  1. Manual Documentation Required - The plugin does not infer types from your code; you must write @swagger JSDoc blocks
  2. No Runtime Validation - The spec is generated at build/dev time and does not validate actual responses
  3. Client Bundle Size - Importing the spec client-side adds to your bundle (~10-50KB gzipped)
  4. SvelteKit Actions - Form action names must be manually documented

Best Practices

Centralize Schemas: Use baseSchemasPath to define shared types once
Reference Components: Use $ref: '#/components/schemas/User' instead of inline schemas
Dev-Only Imports: Consider only importing the spec in development mode
Security: Don't expose sensitive internal API details in public builds
Route Groups: Use (groups) for organization without affecting paths

🔧 Troubleshooting

Spec not updating?

  • Check that files match the pattern: src/routes/**/+{page.server,server}.{js,ts}
  • Verify @swagger blocks are present (not @openapi)
  • Check browser console for plugin warnings

TypeScript errors on import?

// Add to src/app.d.ts
declare module 'virtual:openapi-spec' {
	import type { OpenAPIV3 } from 'openapi-types';
	const spec: OpenAPIV3.Document;
	export default spec;
}

Paths not appearing?

  • Ensure prependPath matches your actual route structure
  • Check that path parameters use curly braces: {id} not [id]
  • Verify the @swagger block is directly above the export function

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

📄 License

MIT © Michael Obele

🔗 Links

💬 Support