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.
- π 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
Demo pages use Tailwind CSS for modern, responsive styling. Package size remains 76KB - Tailwind is a devDependency only, and demo routes are excluded from the npm package.
If you want to use this library's components with Tailwind in your project, add the package to your tailwind.config.js:
export default {
content: [
'./src/**/*.{html,js,svelte,ts}',
'./node_modules/sveltekit-openapi-generator/**/*.{html,js,svelte,ts}'
]
};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-dist<!-- src/routes/docs/+page.svelte -->
<script lang="ts">
import { onMount } from 'svelte';
import spec from 'virtual:openapi-spec';
import 'swagger-ui-dist/swagger-ui.css';
let containerElement: HTMLElement;
onMount(async () => {
if (!containerElement) return;
// @ts-ignore - swagger-ui-dist doesn't have types
const { SwaggerUIBundle, SwaggerUIStandalonePreset } = await import('swagger-ui-dist');
SwaggerUIBundle({
spec,
domNode: containerElement,
deepLinking: true,
presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset]
});
});
</script>
<svelte:head>
<title>API Documentation</title>
</svelte:head>
<div id="swagger-ui-container" bind:this={containerElement}></div>
<style>
/* Hide the default Swagger UI top bar */
:global(.swagger-ui .topbar) {
display: none;
}
</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.
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