Ukrainian Flag
Russia is still destroying lives. Click here to help.
Ukrainian Flag
me
Jakub Koleżyński
Blog Talks Apps
Vite server and static blobs

Vite server and static blobs

23.05.2023

What are the ways to get your files into a Vite server like SvelteKit? There is static asset hosting or blob databases. But if the files don't need to change in runtime, you can always inline them. And with Vite it's super easy!

Rejecting alternatives 🪄

Static Assets

Static assets are the easiest way to host blobs. The files are then accessible by a url. But they are easily scrapeable, and we don't always want that. For instance, you might want a static asset to be downloadable only when the user is logged in.

Blob database

As soon as you need the files to be editable in runtime, an external database is pretty much the only option. But not all files need to be dynamic, and adding a database always increases the infrastructure complexity and latency.

Setup

Folder structure

Let's look at the following folder structure:

src
├───documents
│   ├───terms and conditions.pdf
│   └───email template.xlsx
├───routes
│   ├───documents
│   │   └───+server.ts
│   └───+page.svelte
└───lib
    └───document-contents.server.ts

While I am using Vite, I'm specifically working with SvelteKit on top of it, which maks the code run both on the server and in the browser. Here's a quick summary of what's happening:

  • Files with server in their names will not accessible in the browser code.
  • Folder routing means directories in src/routes/ by convention correspond to routes. (e.g. www.example.com/documents)
  • +page.svelte is the GUI code written in svelte.
  • +server.ts is responsible for handling REST requests.

Sveltekit file download

Let's assume for quick simplicity that we inlined the whole file content manually. I know, bear with me.

// src/lib/document-contents.server.ts
export const termsPdfContent = `%PDF-1.4\r\n3 0 obj\r\n<<\r\n  /Type /XObject\r\n  /Subtype /Image\r\n
...

Having that string, we can set up a simple GET handler in the /documents route

// src/routes/+server.ts
import { termsPdfContent } from '$lib/document-contents.server';
export async function GET({ setHeaders }) {
	setHeaders({
		'Content-Disposition': `attachement; filename="Terms & Conditions.pdf"`,
	});

	return new Response(termsPdfContent);
}

Single hardcoded file

In Vite, you can leverage the standard ESM import syntax, modified with a ?raw suffix. That will provide you with a string variable that contains the full content of the file. You can put it straight into the GET response.

// src/lib/document-contents.server.ts
import termsPdfContent from './documents/terms and conditions.pdf?raw';

export termsPdfContent;

Multiple dynamicaly imported files

If you want a dynamic list of files, you will need to use glob import. With it, you can import multiple files at a time using wildcards, like so:

const modules = import.meta.glob('./document/*.pdf');

This creates an object, where the property names are file paths and the values are async import() functions for the modules it expects within the files. That's because the main purpose of glob import is importing modules from .js files.

The import functions from this specific object will fail though, because a .pdf file is not a valid module. The glob import, however, when provided with a couple of parameters, will make the values be raw string contents:

// src/lib/document-contents.server.ts
const modules = import.meta.glob('./document/*.pdf', { as: 'raw', eager: true });
// ...

Seeing we don't want to hardcode the property names as well, we will need to map the object into an array, like so:

// src/lib/document-contents.server.ts
// ...
const entries = Object.entries(modules);
export const documents = entries.map(([path, content]) => ({ path, content }));

The resulting documents array will look something like this:

[
	{
		"content": "{PDF raw content}",
		"path": "./documents/terms and conditions.pdf"
	},
	{
		"content": "{Excel raw content}",
		"path": "./documents/email template.xlsx"
	}
]

This array can now be imported to the +server.ts.

If you're constructing the GUI by looping over the files, like I am, you will be able to just drop a new file into the codebase and watch it automatically appear on the website. 🧑‍🍳




Back to list

Comments:

Sign In to comment

Be the first to comment!