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.
- π Links
- β¨ Features
- π Migration from SwaggerUI-Svelte
- π¨ Styling
- π¦ Installation
- π Quick Start
- π Documentation
- π οΈ Integration with Swagger UI
β οΈ Limitations & Best Practices- π§ Troubleshooting
- π€ Contributing
- π License
- π Links (External)
- π¬ Support
- π Documentation & Demo: https://sveltekit-openapi-generator.netlify.app
- π¦ npm Package: https://www.npmjs.com/package/sveltekit-openapi-generator
- π GitHub Repository: https://github.com/Michael-Obele/sveltekit-api-gen
- π₯ 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.jsonduring 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
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.
- JSDoc Annotations: Instead of automatic scanning, use
@swaggerJSDoc 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-distfor displaying the generated spec, as detailed in the Integration with Swagger UI section below.
-
Install Dependencies:
npm install -D sveltekit-openapi-generator swagger-ui-dist
-
Configure the Plugin: Add the plugin to your
vite.config.jsbefore 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() ] });
-
Document Your Endpoints: Add
@swaggerJSDoc to your server files:/** * @swagger * /api/users: * get: * summary: Get all users * responses: * 200: * description: Success */ export async function GET({}) { // Your implementation }
-
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.
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.
npm install -D sveltekit-openapi-generatorAdd 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()
]
});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 });
}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
*/<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>During development, access the spec at:
http://localhost:5173/openapi-spec.json
If you set outputPath, the spec will be written to that location:
static/openapi.json
| Option | Type | Default | Description |
|---|---|---|---|
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) |
outputPath |
string |
undefined |
File path to write spec during build |
debounceMs |
number |
100 |
Debounce delay for HMR regeneration |
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 |
β 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" />// 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
}You can easily add Swagger UI to visualize and test your API interactively:
npm install swagger-ui-distHere'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>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.
- Manual Documentation Required - The plugin does not infer types from your code; you must write
@swaggerJSDoc blocks - No Runtime Validation - The spec is generated at build/dev time and does not validate actual responses
- Client Bundle Size - Importing the spec client-side adds to your bundle (~10-50KB gzipped)
- SvelteKit Actions - Form action names must be manually documented
β
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
- Check that files match the pattern:
src/routes/**/+{page.server,server}.{js,ts} - Verify
@swaggerblocks are present (not@openapi) - Check browser console for plugin warnings
// Add to src/app.d.ts
declare module 'virtual:openapi-spec' {
import type { OpenAPIV3 } from 'openapi-types';
const spec: OpenAPIV3.Document;
export default spec;
}- Ensure
prependPathmatches your actual route structure - Check that path parameters use curly braces:
{id}not[id] - Verify the
@swaggerblock is directly above the export function
Contributions are welcome! Please feel free to submit a Pull Request.
MIT Β© Michael Obele