Skip to main content

Writing adapters

If an adapter for your preferred environment doesn't yet exist, you can build your own. We recommend looking at the source for an adapter to a platform similar to yours and copying it as a starting point.

Adapter packages implement the following API, which creates an Adapter:

/** @param {AdapterSpecificOptions} options */
export default function (options: any
@param
options
options
) {
/** @type {import('@sveltejs/kit').Adapter} */ const const adapter: Adapteradapter = { Adapter.name: string

The name of the adapter, using for logging. Will typically correspond to the package name.

name
: 'adapter-package-name',
async Adapter.adapt: (builder: Builder) => MaybePromise<void>

This function is called after SvelteKit has built your app.

@param
builder An object provided by SvelteKit that contains methods for adapting the app
adapt
(builder: Builderbuilder) {
// adapter implementation }, async Adapter.emulate?: (() => MaybePromise<Emulator>) | undefined

Creates an Emulator, which allows the adapter to influence the environment during dev, build and prerendering.

emulate
() {
return { async
Emulator.platform?(details: {
    config: any;
    prerender: PrerenderOption;
}): MaybePromise<App.Platform>

A function that is called with the current route config and prerender option and returns an App.Platform object

platform
({ config: anyconfig, prerender: PrerenderOptionprerender }) {
// the returned object becomes `event.platform` during dev, build and // preview. Its shape is that of `App.Platform` } } },
Adapter.supports?: {
    read?: (details: {
        config: Record<string, any>;
        route: {
            id: string;
        };
    }) => boolean;
    instrumentation?: () => boolean;
} | undefined

Checks called during dev and build to determine whether specific features will work in production with this adapter.

supports
: {
read: ({ config: Record<string, any>config,
route: {
    id: string;
}
route
}) => {
// Return `true` if the route with the given `config` can use `read` // from `$app/server` in production, return `false` if it can't. // Or throw a descriptive error describing how to configure the deployment }, instrumentation: () => { // Return `true` if this adapter supports loading `instrumentation.server.js`. // Return `false if it can't, or throw a descriptive error. } },
Adapter.vite?: {
    plugins?: Plugin[];
} | undefined
vite
: {
plugins?: Plugin<any>[] | undefined

Plugins provided by the adapter are placed before any of SvelteKit's own plugins.

@since
3.0.0
plugins
: [
// add plugins here to integrate with Vite ] } }; return const adapter: Adapter
adapter
;
}

Of these, name and adapt are required. emulate, vite.plugins and supports are optional.

Within the adapt method, there are a number of things that an adapter should do:

  • Clear out the build directory
  • Write SvelteKit output with builder.writeClient, builder.writeServer, and builder.writePrerendered
  • Output code that:
    • Imports Server from ${builder.getServerDirectory()}/index.js
    • Instantiates the app with a manifest generated with builder.generateManifest({ relativePath })
    • Listens for requests from the platform, converts them to a standard Request if necessary, calls the server.respond(request, { getClientAddress }) function to generate a Response and responds with it
    • expose any platform-specific information to SvelteKit via the platform option passed to server.respond
  • Bundle the output to avoid needing to install dependencies on the target platform, if necessary
  • Put the user's static files and the generated JS/CSS in the correct location for the target platform

Where possible, we recommend putting the adapter output under the build/ directory with any intermediate output placed under .svelte-kit/[adapter-name].

Configuring the development and preview experience

By default, SvelteKit runs your server code through a Node.js runtime when running vite dev and vite preview. You can change this behaviour by adding a Vite plugin that has a configureServer and configurePreviewServer hook to route requests to a different runtime.

The main Vite server environment SvelteKit uses is named ssr. You can change its settings by referencing it in the config hook of a Vite plugin.

config(userConfig) {


	userConfig.environments = {
		ssr: {}ssr: {
			// ...
		}
	}
}

You can also define a custom server entry during development by adding a Vite plugin which resolves the sveltekit:server-entry ID to your own module.

{
	name: 'vite-plugin-name-it-yourself',
	applyToEnvironment(environment) {
		return environment.name === 'ssr';
	},
	resolveId: {
		filter: {
			id: /^sveltekit:server-entry$/
		},
		handler(id) {
			return this.resolve(import.meta.ImportMeta.resolve(specifier: string, parent?: string | URL): string (+1 overload)

import.meta.resolve is a module-relative resolution function scoped to each module, returning the URL string.

const dependencyAsset = import.meta.resolve('component-lib/asset.css');
// file:///app/node_modules/component-lib/asset.css
import.meta.resolve('./dep.js');
// file:///app/dep.js

All features of the Node.js module resolution are supported. Dependency resolutions are subject to the permitted exports resolutions within the package.

Caveats:

  • This can result in synchronous file-system operations, which can impact performance similarly to require.resolve.
  • This feature is not available within custom loaders (it would create a deadlock).
@since
v13.9.0, v12.16.0
@param
specifier The module specifier to resolve relative to the current module.
@param
parent An optional absolute parent module URL to resolve from. Default: import.meta.url
@returns
The absolute URL string that the specifier would resolve to.
resolve
('./path-to-your-server.js'));
} } }

This module should instantiate the server with your app's manifest, initialise environment variables and the read implementation, and export a fetch handler which receives a Request and returns a Response:

import { const env: Record<string, string>env } from 'sveltekit:env';
import { class ServerServer } from 'sveltekit:server';
import { const manifest: SSRManifestmanifest } from 'sveltekit:server-manifest';

const const server: Serverserver = new new Server(manifest: SSRManifest): ServerServer(const manifest: SSRManifestmanifest);

await const server: Serverserver.Server.init(options: ServerInitOptions): Promise<void>init({
	ServerInitOptions.env: Record<string, string>

A map of environment variables.

env
,
ServerInitOptions.read?: ((file: string) => MaybePromise<ReadableStream<any> | null>) | undefined

A function that turns an asset filename into a ReadableStream. Required for the read export from $app/server to work.

read
: (file: stringfile) => { /* implement how your platform retrieves file contents */ }
}); /** * @param {Request} request * @returns {Promise<Response>} */ export async function function fetch(request: any): Promise<Response>
@param
request
@returns
fetch(request: any
@param
request
request
) {
return await const server: Serverserver.Server.respond(request: Request, options: RequestOptions): Promise<Response>respond(request: any
@param
request
request
, {
RequestOptions.getClientAddress(): stringgetClientAddress: () => { return request: any
@param
request
request
.headers.get('how-your-platform-exposes-the-remote-address')
} }); } // Without this, server file changes will invalidate the entire Vite server module graph: import.meta.ImportMeta.hot?: ViteHotContext | undefinedhot?.ViteHotContext.accept(): void (+3 overloads)accept();

Edit this page on GitHub llms.txt

previous next