Skip to main content

Server-only modules

Like a good friend, SvelteKit keeps your secrets. When writing your backend and frontend in the same repository, it can be easy to accidentally import sensitive data into your front-end code (environment variables containing API keys, for example). SvelteKit provides a way to prevent this entirely: server-only modules.

Private environment variables

The $app/env/private module can only be imported into modules that only run on the server, such as hooks.server.js or +page.server.js.

Server-only utilities

The $app/server module, which contains a read function for reading assets from the filesystem, can likewise only be imported by code that runs on the server.

Your modules

You can make a module server-only in two ways:

  • Add a server segment to the filename, e.g. server.js or secrets.server.ts. This works for any file in the project directory.
  • Place it in a server directory anywhere in your project except inside src/routes or the static directory, e.g src/lib/server/config.js or src/lib/data/server/user/profile.js.
Legacy mode

In SvelteKit 2, server directories were only recognised in the src/lib folder.

Modules outside your working directory and those inside node_modules (e.g. packages from npm) are not subject to these rules. If you want to publish a package with a server-only module, add import '$app/server' to the top of that file.

How it works

Any time you have public-facing code that imports server-only code (whether directly or indirectly)...

#lib/server/secrets
export const atlantisCoordinates = [/* redacted */];
src/routes/utils
export { export atlantisCoordinatesatlantisCoordinates } from '#lib/server/secrets.js';

export const const add: (a: any, b: any) => anyadd = (a, b) => a: anya + b: anyb;

src/routes/+page
<script>
	import { add } from './utils.js';
</script>

...SvelteKit will error:

Cannot import #lib/server/secrets.ts into code that runs in the browser, as this could leak sensitive information.

 src/routes/+page.svelte imports
  src/routes/utils.js imports
   #lib/server/secrets.ts

If you're only using the import as a type, change it to `import type`.

Even though the public-facing code — src/routes/+page.svelte — only uses the add export and not the secret atlantisCoordinates export, the secret code could end up in JavaScript that the browser downloads, and so the import chain is considered unsafe.

This feature also works with dynamic imports, even interpolated ones like await import(`./${foo}.js`).

Unit testing frameworks like Vitest do not distinguish between server-only and public-facing code. For this reason, illegal import detection is disabled when running tests, as determined by process.env.TEST === 'true'.

Further reading

Edit this page on GitHub llms.txt