Description
Describe the problem
Thanks for building kit and all the heroic efforts in dealing with this amount of issues!
As the load function runs in both client & server, we always find ourselves creating an extra endpoint for fetching data with server credentials. This is laborious and tedious, leads to duplication of error handling and makes it harder to reason about data flow.
In the example below, notice how /recipes/[slug].svelte
and /api/recipes/[slug].js
have a lot of duplication:
<!-- /recipes/[slug].svelte -->
<script context="module">
export const load = async ({ page: { params }, fetch }) => {
const { slug } = params
if (!slug || typeof slug !== 'string') {
return {
status: 400,
error: 'Recipe not found',
}
}
// No way to click to open endpoint definition
const recipe = await (await fetch(`/api/recipes/${slug}`)).json()
if (!recipe?._id) {
return {
status: 404,
error: 'Recipe not found',
}
}
return {
props: {
recipe,
},
}
}
</script>
// /api/recipes/[slug].js
import getFullRecipe from '$lib/queries/getFullRecipe'
export const get = async (request) => {
const { slug } = request.params
// Duplicated error handling
if (!slug || typeof slug !== 'string') {
return {
status: 400,
error: 'Recipe not found',
}
}
const recipe = await getFullRecipe(slug)
if (!recipe?._id) {
return {
status: 404,
error: 'Recipe not found',
}
}
return {
status: 200,
body: recipe,
}
}
Describe the proposed solution
Ideally, Kit's adapters would automatically transform server-only functions into their own endpoints, without us having to think about it. This would be similar to Next's getServerSideProps.
To annotate a function as server only, we could pass a context="server"
tag to the module <script>
tag. Here's the example above with this approach:
<script context="module" context="server">
import getFullRecipe from '$lib/queries/getFullRecipe'
export const load = async ({ page: { params }, fetch }) => {
const { slug } = params
if (!slug || typeof slug !== 'string') {
return {
status: 400,
error: 'Receita não encontrada',
}
}
const recipe = await getFullRecipe(slug)
if (!recipe?._id) {
return {
status: 404,
error: 'Receita não encontrada',
}
}
return {
props: {
recipe,
},
}
}
</script>
Single file, ~50% code reduction (29 LOC vs. 55 in the example above), one less file to manage ✨
Alternatives considered
I'm considering creating my own abstractions to make this slightly easier to reason about. For example, I could condense all data-fetching server endpoints into a single get
function that takes whatever query I need to perform and returns the data accordingly.
The issue here is that it still wouldn't be as simple as a native solution and we'd be opening up a lot of dissonance between different codebases.
I understand this is potentially a very hard issue to tackle, but if doable it feels like a pre-v1 feature to make dynamic routes easier to build & onboard new people.
Importance
would make my life easier
Additional Information
Sorry if I double posted this - searched hard through issues and couldn't find another. So many issues coming in, thanks for putting in the work to deal with all this pressure 🙏