sveltejs / kit

web development, streamlined
https://svelte.dev/docs/kit
MIT License
18.73k stars 1.94k forks source link

Allow Scripts That Only Run On the Server ("Micro" Endpoints For Routes) #3483

Closed Acmion closed 2 years ago

Acmion commented 2 years ago

Describe the problem

I am often building applications where the server file system is an integral aspect, but this also applies to anything that requires execution on the server, such as database calls. Thus I often have components or routes of the following format:

<script lang="ts">
    import fs from "fs"

    var entries = fs.readdirSync(".")
</script>

{#each entries as e}
    {e}<br>
{/each}

The component is SSR rendered just fine, but when the hydration kicks in things start to fail. The reason in this case is that client side code can not access fs. In old school MVC projects the example above is essentially the way similar things were handled.

Blazor Server, which is a relatively new web framework project by Microsoft, is great for use cases just like these. You just develop your components and routes without worrying about creating api endpoints.

Describe the proposed solution

I would like to somehow guarantee that certain code is only executed on the server. For example:

<script context="server">
     // Top level code would get executed every time

     import fs from "fs"

     var data = fs.readDirSync(".")
</script>

A better solution would probably be "micro" endpoints, which would be available for routes. This is actually a quite common approach in many MVC frameworks. For example:

<script context="endpoint">
    // This works almost as normal SvelteKit endpoints, but returning a response is not needed
    // Top level code would get executed every time and appropriate functions called depending on the request type

    import fs from "fs"

    var data = []

    export function get()
    {
        data = fs.readDirSync(".")
    }
</script>

Alternatives considered

Checking the environment does not really work, because you can not conditionally import modules.

import { browser} from '$app/env'

if(!browser)
{
    // Does not work, because imports need to be top level statements
    import fs from "fs"
}

Conditional dynamic imports could work, but they are somewhat of a hassle.

Disabling route hydration works in certain cases, but not generally.

Creating an api endpoint works fine, but then you get the overhead of first developing that endpoint and then creating an appropriate request to it. Because endpoints are not statically typed, this means that the developers must remember the correct url, the correct method and the correct payload. This is significantly harder to maintain than the proposed solutions.

Svemix is a vite plugin that essentially enables this behavior.

Importance

would make my life easier

Additional Information

The proposed solutions are probably easier to implement for routes than components. Additionally, not sure whether changing the context of a script is the appropriate solution.

pixelmund commented 2 years ago

You might want to check out svemix which solves basically what you described with a vite plugin.

dodiameer commented 2 years ago

I was just typing my comment about svemix when @pixelmund replied — I haven't used it yet but I have used Remix and the ideas of Remix were borrowed by svemix so I assume they function similarly and solve the issue you described

Acmion commented 2 years ago

Awesome! Svemix seems just like the thing I need!

I'll add Svemix to the original issue under "Alternatives considered", but I'll leave the issue open, just in case this is every directly implemented in SvelteKit.

Rich-Harris commented 2 years ago

We've considered this idea in the past and decided against it. You might be interested in #3532 though

Acmion commented 2 years ago

Could something like <svelte:static>HTML HERE</svelte:static> be considered?

The <svelte:static> tags could be rendered only on the server and not in the client. Some treeshaking algorithm could be utilized to drop specific code from the client side.