How to Share Code with a SvelteKit Monorepo

Updated

4 min read

Photo by Chris Liverani

SvelteKit, being SSR-first and having API endpoints, comes with fullstack capabilities. But there are cases where you may require a more robust backend than what SvelteKit provides, in which case a monorepo is a great choice.

In this short writeup, I'm going to give a tutorial on how to create a SvelteKit monorepo, so that you can share code (like Typescript types) between your SvelteKit project and other Node.js apps (an Express.js server in this example).

Why Use a Monorepo with SvelteKit?

Here's an example of why you might want to use a monorepo with SvelteKit:

When I was working on a SvelteKit project that requires file uploading, I found that SvelteKit is a little lackluster regarding multipart/form-data handling.

As of writing, SvelteKit cannot handle streaming of request/response bodies.

But Express.js alongside busboy for example, can do this no problem. This is one example of why having a separate backend for SvelteKit may be a good idea.

Monorepo Structure

For sake of simplicity, let's assume a simple monorepo with a www project housing your SvelteKit app and backend housing an Express.js server.

markdownproject
    - www # Your SvelteKit app
    - backend # An Express.js app, for example

Creating a Common Module

To share code between our www and backend projects, we'll need a "common" module, or shared folder.

To do this, create a new Node app that is siblings with www.

markdownproject
    - www
    - backend
    - common # Where we'll put shared code

Setting Up the Common Module

Initialize a new npm package in common:

bashcd common
npm init

Then, install some basic dependencies for Typescript:

bashnpm install -D typescript @types/node prettier

Most importantly, you need to install tsconfig-paths, which will be important later:

bashnpm install -D tsconfig-paths

Configure tsconfig.json

Create a tsconfig.json file in your common directory.

Then, here's a basic setup you can copy:

json{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "esModuleInterop": true,
    "strict": false,
    "noImplicitAny": false,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "moduleResolution": "node",
    "baseUrl": "./",
    "paths": {
      "www/*": ["../www/src/*"], # Path alias to SvelteKit project 
      "backend/*": ["../backend/src/*"] # Path alias to Express.js project
    }
  },
  "include": [
    "../www/src/**/*.ts",
    "../backend/src/**/*.ts",
    "**/*.ts"
  ],
  "exclude": ["node_modules"],
  "ts-node": {
    "require": ["tsconfig-paths/register"]
  }
}

This lets you access www and backend files from your common directory.

For example, in common, you could now do:

javascriptimport { Schema } from "www/src/types/schema";
// ... 

Configure SvelteKit "tsconfig.json"

Lastly, we need to do something similar with our www SvelteKit project.

In your SvelteKit project, open up tsconfig.json.

Copy Existing Includes/Excludes

Note: SvelteKit warns that "If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes from the referenced tsconfig.json — Typescript does not merge them."

Basically, you want to open ./.svelte-kit/tsconfig.json and copy include and exclude into your root tsconfig.json.

Add Includes for Common Module

From there, you can add in the include for the common Typescript files.

json"include": [
    "../common/**/*.ts",
    // ... rest of includes copied over
]

In the end, your new SvelteKit tsconfig.json should look something like:

json{
	"extends": "./.svelte-kit/tsconfig.json",
	"compilerOptions": {
		"allowJs": true,
		"checkJs": true,
		"esModuleInterop": true,
		"forceConsistentCasingInFileNames": true,
		"resolveJsonModule": true,
		"skipLibCheck": true,
		"sourceMap": true,
		"strict": true,
		"moduleResolution": "bundler"
	},
	
	// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
	//
	// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
	// from the referenced tsconfig.json - TypeScript does not merge them in
	"include": [
		"../common/**/*.ts", // Include files from common module
		"ambient.d.ts",
		"non-ambient.d.ts",
		"./types/**/$types.d.ts",
		"../vite.config.js",
		"../vite.config.ts",
		"../src/**/*.js",
		"../src/**/*.ts",
		"../src/**/*.svelte",
		"../tests/**/*.js",
		"../tests/**/*.ts",
		"../tests/**/*.svelte"
	],
	"exclude": [
		"../node_modules/**"
	]
}

Configure SvelteKit "svelte.config.js"

Lastly, we just need to add a path alias to our SvelteKit config so we can access the common files.

To do that, open up svelte.config.js and add this block under kit:

javascriptconst config = {
	preprocess: vitePreprocess(),

	kit: {
		adapter: adapter(),
		alias: {
			$common: "../common/src", # Add this path alias
		},
	}
};

Use Shared Code with SvelteKit

Now you're all set! In your SvelteKit project, you can now import code from your common module like so:

svelte<script lang="ts">
    import { Schema } from "$common/types/schema";
    import { SCHEMA_DATA_TYPES } from "$common/static/constants";
</script>

Hope this short tutorial helps someone. I couldn't find a specific writeup on how to share types between a SvelteKit frontend and a backend server.

Let me know if you have any suggestions or improvements to make.

Ryan Chiang

Meet the Author

Ryan Chiang

Hello, I'm Ryan. I build things and write about them. This is my blog of my learnings, tutorials, and whatever else I feel like writing about.
See what I'm building →.

Thanks for reading! If you want a heads up when I write a new blog post, you can subscribe below:

2024

2023

© 2023 Ryan Chiang|ryanschiang.com