Scalar Module

Scalar Module

The Scalar Module is a pre-built SvelteKit route handler that serves your OpenAPI schema and renders beautiful interactive API documentation using Scalar .

What is ScalarModule?

ScalarModule provides two endpoints in one:

  1. OpenAPI JSON - Serves the generated OpenAPI schema
  2. Scalar UI - Renders interactive documentation

It's designed to be dropped into a dynamic route like src/routes/api-docs/[slug]/+server.ts where the slug parameter determines which endpoint to serve.

Basic Setup

Create a docs route in your SvelteKit app:

// src/routes/api-docs/[slug]/+server.ts
import ScalarModule from 'sveltekit-auto-openapi/scalar-module';

export const { GET, _config } = ScalarModule({
  openApiOpts: {
    openapi: '3.0.0',
    info: {
      title: 'My API',
      version: '1.0.0',
      description: 'My awesome API built with SvelteKit'
    },
  },
});
typescript

That's it! Now you have:

  • OpenAPI schema at /api-docs/openapi.json
  • Interactive docs at /api-docs/scalar

How It Works

When a request arrives:

  1. ScalarModule checks the slug parameter
  2. If slug === 'openapi.json':
    • Imports the virtual module with generated schemas
    • Merges with your openApiOpts
    • Returns OpenAPI JSON
  3. If slug === 'scalar':
    • Generates HTML with embedded Scalar UI
    • Configures Scalar to load schema from /api-docs/openapi.json
    • Returns interactive documentation

Configuration Options

ScalarModule accepts an options object with these properties:

disableOpenApi

  • Type: boolean
  • Default: false

Disables the OpenAPI JSON endpoint.

ScalarModule({
  disableOpenApi: true,  // No /api-docs/openapi.json
})
typescript

Use case: When you want to serve the Scalar UI but provide the OpenAPI schema from elsewhere.

openApiPath

  • Type: string
  • Default: "openapi.json"

The slug value that serves the OpenAPI schema.

ScalarModule({
  openApiPath: 'schema.json',  // Serve at /api-docs/schema.json
})
typescript

showDetailedDocsSchema

  • Type: boolean
  • Default: false

Includes detailed validation metadata in the schema (makes it larger).

ScalarModule({
  showDetailedDocsSchema: true,
})
typescript

When enabled, the schema includes:

  • Detailed error messages
  • Validation keywords
  • Internal metadata

Trade-off: More detailed schema but larger payload size.

skipDocsValidation

  • Type: boolean
  • Default: true

Skips runtime validation of the generated OpenAPI schema against the OpenAPI spec.

ScalarModule({
  showDetailedDocsSchema: true,
  skipDocsValidation: false,  // Validate schema
})
typescript

Use case: Enable in development to catch schema errors, disable in production for performance.

openApiOpts

  • Type: Omit<OpenAPIV3.Document, "paths"> & { paths?: OpenAPIV3.PathsObject }
  • Default: { openapi: "3.0.0", info: { title: "API Documentation", version: "1.0.0" } }

OpenAPI document metadata. The paths are automatically generated by the plugin.

ScalarModule({
  openApiOpts: {
    openapi: '3.0.0',
    info: {
      title: 'My API',
      version: '2.0.0',
      description: 'Detailed API description',
      termsOfService: 'https://example.com/terms',
      contact: {
        name: 'API Support',
        email: 'support@example.com',
        url: 'https://example.com/support'
      },
      license: {
        name: 'MIT',
        url: 'https://opensource.org/licenses/MIT'
      }
    },
    servers: [
      {
        url: 'https://api.example.com',
        description: 'Production server'
      },
      {
        url: 'https://staging-api.example.com',
        description: 'Staging server'
      },
      {
        url: 'http://localhost:5173',
        description: 'Development server'
      }
    ],
    tags: [
      {
        name: 'users',
        description: 'User management endpoints'
      },
      {
        name: 'products',
        description: 'Product catalog endpoints'
      }
    ],
    externalDocs: {
      description: 'Find more info here',
      url: 'https://docs.example.com'
    }
  },
})
typescript

mergePaths

  • Type: OpenAPIV3.PathsObject
  • Default: undefined

Merge additional paths into the generated schema or override existing ones.

ScalarModule({
  mergePaths: {
    '/custom/endpoint': {
      get: {
        summary: 'Custom endpoint',
        description: 'Not auto-generated',
        responses: {
          '200': {
            description: 'Success',
            content: {
              'application/json': {
                schema: {
                  type: 'object',
                  properties: {
                    message: { type: 'string' }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
})
typescript

Use cases:

  • Add endpoints not managed by the plugin
  • Override auto-generated schemas
  • Document external APIs your app proxies

disableScalar

  • Type: boolean
  • Default: false

Disables the Scalar documentation UI endpoint.

ScalarModule({
  disableScalar: true,  // No /api-docs/scalar
})
typescript

Use case: When you only want the OpenAPI JSON for other tools (Postman, Insomnia, etc.)

scalarDocPath

  • Type: string
  • Default: "scalar"

The slug value that serves the Scalar UI.

ScalarModule({
  scalarDocPath: 'docs',  // Serve at /api-docs/docs
})
typescript

scalarOpts

  • Type: HtmlRenderingConfiguration
  • Default: undefined

Options for customizing the Scalar UI. See Scalar documentation for all options.

ScalarModule({
  scalarOpts: {
    theme: 'purple',
    darkMode: true,
    layout: 'modern',
    hideDownloadButton: false,
    hideModels: false,
    hideTestRequestButton: false,
  },
})
typescript

Complete Examples

Minimal Setup

Just the basics:

// src/routes/api-docs/[slug]/+server.ts
import ScalarModule from 'sveltekit-auto-openapi/scalar-module';

export const { GET, _config } = ScalarModule({
  openApiOpts: {
    info: {
      title: 'My API',
      version: '1.0.0'
    }
  }
});
typescript

Production Configuration

With full metadata:

// src/routes/api-docs/[slug]/+server.ts
import ScalarModule from 'sveltekit-auto-openapi/scalar-module';

const isProd = import.meta.env.PROD;

export const { GET, _config } = ScalarModule({
  openApiOpts: {
    openapi: '3.0.0',
    info: {
      title: 'E-Commerce API',
      version: '2.1.0',
      description: `
# E-Commerce Platform API

This API provides access to our e-commerce platform's core functionality.

## Authentication

All endpoints require an API key passed in the \`x-api-key\` header.

## Rate Limiting

Requests are limited to 100 per minute per API key.
      `,
      termsOfService: 'https://example.com/terms',
      contact: {
        name: 'API Team',
        email: 'api@example.com'
      },
      license: {
        name: 'Proprietary',
      }
    },
    servers: isProd ? [
      {
        url: 'https://api.example.com',
        description: 'Production'
      }
    ] : [
      {
        url: 'http://localhost:5173',
        description: 'Development'
      },
      {
        url: 'https://staging-api.example.com',
        description: 'Staging'
      }
    ],
    tags: [
      { name: 'auth', description: 'Authentication endpoints' },
      { name: 'users', description: 'User management' },
      { name: 'products', description: 'Product catalog' },
      { name: 'orders', description: 'Order processing' },
      { name: 'payments', description: 'Payment processing' }
    ]
  },
  scalarOpts: {
    theme: 'default',
    darkMode: true,
    layout: 'modern',
  }
});
typescript

Custom Paths with Merge

Adding manual endpoints:

// src/routes/api-docs/[slug]/+server.ts
import ScalarModule from 'sveltekit-auto-openapi/scalar-module';

export const { GET, _config } = ScalarModule({
  openApiOpts: {
    info: {
      title: 'My API',
      version: '1.0.0'
    }
  },
  mergePaths: {
    '/health': {
      get: {
        summary: 'Health check',
        description: 'Returns server health status',
        tags: ['system'],
        responses: {
          '200': {
            description: 'Server is healthy',
            content: {
              'application/json': {
                schema: {
                  type: 'object',
                  properties: {
                    status: { type: 'string', enum: ['ok'] },
                    uptime: { type: 'number' },
                    timestamp: { type: 'string', format: 'date-time' }
                  }
                }
              }
            }
          }
        }
      }
    },
    '/metrics': {
      get: {
        summary: 'Prometheus metrics',
        description: 'Returns application metrics in Prometheus format',
        tags: ['system'],
        responses: {
          '200': {
            description: 'Metrics data',
            content: {
              'text/plain': {
                schema: { type: 'string' }
              }
            }
          }
        }
      }
    }
  }
});
typescript

Environment-Specific Configuration

Different settings per environment:

// src/routes/api-docs/[slug]/+server.ts
import ScalarModule from 'sveltekit-auto-openapi/scalar-module';

const isDev = import.meta.env.DEV;

export const { GET, _config } = ScalarModule({
  showDetailedDocsSchema: isDev,     // Detailed schemas in dev
  skipDocsValidation: !isDev,         // Validate in dev only

  openApiOpts: {
    info: {
      title: 'My API',
      version: '1.0.0'
    },
    servers: isDev ? [
      { url: 'http://localhost:5173', description: 'Local' }
    ] : [
      { url: 'https://api.example.com', description: 'Production' }
    ]
  },

  scalarOpts: {
    darkMode: isDev,  // Dark mode in development
    hideDownloadButton: !isDev,  // Hide download in production
  }
});
typescript

Customization

Custom Theme

Scalar supports theming:

scalarOpts: {
  theme: 'purple',  // 'default', 'alternate', 'moon', 'purple', 'solarized', 'bluePlanet'
  darkMode: true,
}
typescript

Custom Layout

Choose between layouts:

scalarOpts: {
  layout: 'modern',  // 'modern' or 'classic'
}
typescript

Hide UI Elements

Control what's visible:

scalarOpts: {
  hideDownloadButton: true,      // Hide OpenAPI download
  hideTestRequestButton: false,   // Show "Try it" buttons
  hideModels: false,              // Show schema models
  hideDarkModeToggle: false,      // Show dark mode toggle
}
typescript

Custom CSS

Inject custom styles:

scalarOpts: {
  customCss: `
    .scalar-api-reference {
      --scalar-font: 'Inter', sans-serif;
      --scalar-border-radius: 8px;
    }
  `
}
typescript

Advanced Usage

Multiple Documentation Routes

Serve different docs for different APIs:

// src/routes/api/v1/docs/[slug]/+server.ts
export const { GET, _config } = ScalarModule({
  openApiOpts: {
    info: { title: 'API v1', version: '1.0.0' }
  }
});

// src/routes/api/v2/docs/[slug]/+server.ts
export const { GET, _config } = ScalarModule({
  openApiOpts: {
    info: { title: 'API v2', version: '2.0.0' }
  }
});
typescript

Protected Documentation

Add authentication:

// src/routes/api-docs/[slug]/+server.ts
import ScalarModule from 'sveltekit-auto-openapi/scalar-module';
import { error } from '@sveltejs/kit';

const { GET: scalarGET, _config } = ScalarModule({
  openApiOpts: {
    info: { title: 'Internal API', version: '1.0.0' }
  }
});

export { _config };

export const GET = async (event) => {
  // Check authentication
  const apiKey = event.request.headers.get('x-api-key');
  if (!apiKey || apiKey !== process.env.DOCS_API_KEY) {
    error(401, 'Unauthorized');
  }

  // Serve docs
  return scalarGET(event);
};
typescript

Custom Response Headers

Add CORS or other headers:

const { GET: scalarGET, _config } = ScalarModule({
  // ... config
});

export { _config };

export const GET = async (event) => {
  const response = await scalarGET(event);

  // Add custom headers
  response.headers.set('Access-Control-Allow-Origin', '*');
  response.headers.set('Cache-Control', 'public, max-age=3600');

  return response;
};
typescript

Troubleshooting

Schema not updating

Problem: Changes to routes don't appear in documentation

Solution:

  • Restart dev server (virtual module is cached)
  • Check HMR is working
  • Verify routes are in src/routes/ directory

Scalar UI not rendering

Problem: Blank page at /api-docs/scalar

Solution:

  • Check browser console for errors
  • Verify OpenAPI JSON is valid at /api-docs/openapi.json
  • Try skipDocsValidation: false to catch schema errors

404 on documentation routes

Problem: Documentation routes return 404

Solution:

  • Check route file is src/routes/api-docs/[slug]/+server.ts
  • Verify the slug parameter is dynamic [slug], not static
  • Ensure you're using the correct paths (openapi.json and scalar by default)

Scalar showing wrong schema

Problem: Documentation shows outdated schema

Solution:

  • Clear browser cache
  • Hard refresh (Ctrl+Shift+R)
  • Check openapi.json directly to verify it's updated

Related Documentation