Fixing load
, and tightening up SvelteKit's design before 1.0
#5748
Replies: 102 comments 380 replies
-
I appreciate all the work/thought put into this as well as the willingness to make such significant changes for the benefit of all users. First impression is that the plethora of naming conventions related to routing are a step backwards. Svelte usually encourages "using the platform", whether it be the simplistic style tag over something like css-in-js or the lack of unnecessary boilerplate when dealing with reactivity. These design decisions make Svelte easy to pick up, especially by those without experience in other frameworks. The I won't pretend to understand the inner-workings of these changes. If the new naming conventions are necessary on any level to get the technical benefits such as the simpler load function, then so be it. This is less a complaint and more expressing a slight discomfort with the direction compared to what I personally feel Svelte did so well – reducing cognitive load and staying close to the "vanilla" way of doing things. |
Beta Was this translation helpful? Give feedback.
-
While at first this seemed weird, it grew on me while I was reading it. |
Beta Was this translation helpful? Give feedback.
-
Seems like a pretty solid design update to me (from probably the little I understood 😁) Thanks for the in depth update! |
Beta Was this translation helpful? Give feedback.
-
Thank you for your work on SvelteKit Currently I could not find a way to communicate from page to layout. Can we also think of this communication (from page to layout with reactivity) in this new design? |
Beta Was this translation helpful? Give feedback.
-
I think the proposed changes really could add a lot of value to SvelteKit as while they do increase (file-system) verbosity, they also could help adding quite a bit of clarity. I personally am most excited about the new abilities for passing around loaded data: const { number } = await parent(); to access data from upwards the tree and an app wide Does Regarding naming: I think My last question is regarding the auth guard example. Very excited about the future of kit. You maintainers all rock! PS: I feel for early svelte kit content creators... |
Beta Was this translation helpful? Give feedback.
-
hmmmm…the new routing changes don’t feel so great to me…have not thought them through, however…just curious if anybody floated the idea of having different special characters for different types of routes, so as to avoid excessively long files names? maybe:
or something along those lines, so as to avoid writing +page or +layout, etc… will read through the thing again as well… |
Beta Was this translation helpful? Give feedback.
-
That was a lot to take in (especially with so much
I understand it, and now I'm starting to prefer "directory-based" routing, but there's got to be better names for the files. I don't know if it was just me, or its way too confusing. First, not a big fan of Anyway, we shouldn't be using
Also, I'm not sure about labeled files. I think it's wrong to add "comments" to file names in the first place, but that could just be me. Using named layouts, it'll look like
which I much prefer, and it also takes the Other things:
|
Beta Was this translation helpful? Give feedback.
-
Is parent data (await parent()) going to be accessible outside load like |
Beta Was this translation helpful? Give feedback.
-
A lot of exciting stuff! Appreciate your work. Feel a bit unsettled about file naming but I guess it's a matter of getting used to. With good documentation and examples, especially about differences and main use cases for |
Beta Was this translation helpful? Give feedback.
-
Lots of great points in the post. This is just my personal opinion but it feels at this point that routing will be extremely complicated. There are going to be a lot of rules to learn and a lot of "ahhh why isn't this working?!?!" IMO. This to me at least is pretty confusing It almost feels like a non directory based router would be easier rather than having to infer things. |
Beta Was this translation helpful? Give feedback.
-
After reading through a couple times, I think I am beginning to understand it. I won't comment too much on some of decisions made as they are out of the scope of my expertise, but I will say one thing...seeing filenames begin with + hurts me on the inside. it just feels...wrong |
Beta Was this translation helpful? Give feedback.
-
Is file-based routing worth it once your conventions reach Simple file names to page routes are easy enough to grasp. "Create a Some of these changes are trying to introduce flexibility but are spiking the complexity without the advantage of being discoverable. There is no way to know that Obviously a lot of thought has been put into this and I hope it works out for the project. |
Beta Was this translation helpful? Give feedback.
-
IMO, this is not less confusing than Could it be possible to do I can easily imagine downsides to that suggestion. It's just that adding a file extension to the name of a folder seems like a very awkward convention, and not a step in the right direction if the goal is to make endpoint naming more intuitive. |
Beta Was this translation helpful? Give feedback.
-
Lots to like, lots I don't fully understand but sounds sensible. The one change that seems a bit contrived is the labels on filenames. Can see the use for identifying files but without the knowledge that it is just for labelling it would be expected to be something different. Would some way of identifying it as a non-standard part of a file be viable, so
would become:
Yes a bit more work in naming the file when using labels but as it's optional it doesn't add overall and hopefully makes it clearer it's a "special" part of the filename. |
Beta Was this translation helpful? Give feedback.
-
I don't understand the problem of turning a route file into a route folder at all: folder3.mp4In my opinion, TOTALLY route files without folders should be allowed. Changing one into the other is a natural step, and fabulously easy. I mean something like I wrote earlier that _name.svelte should be the route file. |
Beta Was this translation helpful? Give feedback.
-
One thought would be a plugable routing system. But something that can more easily be switched out depending on how someone wants to do the routing One thing I tend to be against from a personal preference is crowding the filenames with metadata or symbols. I think file based routing only works in the context of when it's simple. |
Beta Was this translation helpful? Give feedback.
-
While we're on the idea of simplifying project structure, one suggestion I would add is the ability to have static files in the folders with our source code, instead of a separate |
Beta Was this translation helpful? Give feedback.
-
a mild suggestion for the prefix -
whatever you guys decide i will live with :) its awkward and appreicate the effort spent here. |
Beta Was this translation helpful? Give feedback.
-
from a teaching and DX POV, the number of files does concern me. a big draw of Svelte was single file components, and with this proposal we no longer have single file routes (arguably we never had them... anyway).
that is of course a thing we can let go if we must, but if @pilcrowonpaper's summary is accurate (i couldnt remember the number of new files to know while reading the proposal) then we have at least 8 new files to keep in mind. (and he didnt even include the traditional estimates for people's working memory of cardinality is 3-5. we're looking at 8 or more here and asking the sveltekit developer to get comfortable with a lot of filename syntax. just want to make sure we make this choice consciously - file "api"s are more costly to change than code "api"s i dont have the knowledge/havent spent the time to make a constructive suggestion but
|
Beta Was this translation helpful? Give feedback.
-
While this is all seems like sound architecture the thing that is most interesting to me is "in the future, we plan to introduce a streaming rendering mode that will flush headers and the first bytes of HTML before the page has loaded and rendered". Please tell me that means rendering similar to Marko. On a more related note; You mentioned index.svelte being problematic for having multiple open... It really isn't if you just use ctrl+p even if you have tons of files open. Regardless, +page.svelte and the other + routes doesn't address that concern. But you already knew that. |
Beta Was this translation helpful? Give feedback.
-
This whole discussion has been a bit confusing to me, is this a good summary of what's going on:
|
Beta Was this translation helpful? Give feedback.
-
Thanks for your work, and the opportunity to give feedback. Huge upvote for the way you are throwing errors and redirects. How to do that consistently has been a recurring pain in the neck. I don't have much skin in the game about file naming, just as long as (1) the convention is settled and (2) the debate about naming does not obscure what I'd call -- just my opinion -- More Important Things. For example, my biggest takeaway from this proposal is that SvelteKit is moving full-on toward route middleware. That is, the ability to do data-fetching and business logic in One of the reasons I like Kit now is that it does away with middleware in the classic, inchoate, terrible, Express sense. Kit forces you to answer questions like "is the user logged in?" and "may the user see this kitten's dashboard?" at the route level rather than in some abstraction, most likely premature, residing elsewhere. The details of answering such questions can still be abstracted out, but you still have to ask them (a good thing) in each endpoint. Application-wide things like reading/writing cookies and logging can be handled in the hooks, where you can see at a glance, in one file, what's going on. I realize that I will probably still be able to do this -- I won't be tied to a chair & forced to use
Whole platforms have sprung up to try to make middleware less dicey, more testable, etc. They've had limited success. (Try to do simple email/password authentication in Nest.js/Passport, for example.) The basic problem with middleware -- the implicit, tight coupling it introduces and the intendant confusion -- remains. I'd really like to see SvelteKit go all in the other way, stick a fork in middleware for good, and encourage folks to concentrate on the logic of individual pages. |
Beta Was this translation helpful? Give feedback.
-
Must say, I'm really enthusiastic about these changes. I had only started to programme when Svelte and Sapper arrived on the scene -- it's all I've ever used for designing and creating web applications so I'm not inclined to compare it to other frameworks. This new design makes total sense to me and clears up quite a lot of lingering confusion I had. Can't wait to start implementing it. I've a few apps going into production over the next few weeks so the sooner it's ready the better from my perspective. Good luck with the implementation! |
Beta Was this translation helpful? Give feedback.
-
My first reaction after reading this is a lot of mixed feelings. I love Svelte and SvelteKit and have started a number of professional projects with both of them. The main reason was because of the beautiful simplicity of Svelte and SvelteKit. Of course we often always resist change, and I've tried to really understand the benefits of these changes for my specific projects. But unfortunately I don't really get it - probably my own fault. I don't quite see for example what will be the benefit of having multiple +page.svelte over multiple index.svelte. That seems to be the same thing, except that:
Also putting the load function (or its equivalent) in another file will pretty much double the number of files in my project. So, I don't quite see the benefit there. So, all in all, not particularly excited thus far. |
Beta Was this translation helpful? Give feedback.
-
I do not find the routing changes very cash-money at all. |
Beta Was this translation helpful? Give feedback.
-
Overall sounds like a step in the right direction, especially the simplified load. But I do agree with the rest of the thread that having a bunch of files named the same thing becomes confusing, it is already confusing enough with 5 different Reading something like Could we simply allow for arbitrary prefixes to the files? ex:
The prefixes can be whatever and does not have to match the folder name (but makes sense if they do) |
Beta Was this translation helpful? Give feedback.
-
Sharing my perspective of someone who has been empowered by Svelte and Sveltekit to build things despite having a somewhat medium knowledge of JS. TLDR: The routes change has a big negative impact to beginner devs, are meant to solve very advanced usecases to the detriment of basic experience, and most justifications are debatable. Going over each reasoning:
Not that confusing. You can start with a file and move to a folder as needed, or just go with folders. Besides, “index” is pretty standard and is being dropped in this change.
Editors have solutions for this. Renaming all to +page doesn’t solve the problem.
Do not colocate files. Bad practice. It muddies the route structure. File based routing makes it straightforward to understand the entire route structure by looking at it, specially when you’re coming fresh to an existing codebase. With folder based and collocated files you have to pause and know which files create routes, which are extra things.
That’s very natural, start simple and move to complex slowly. It’s just renaming a file. A this differentiation adds semantics to what’s a basic route and what’s more special. Or just use folders always, make it show a warning if you forget to do it.
I disagree, it’s bad practice. There’s more value in separating components from helpers from routes…
I don’t know if it’s a broad issue, but the folder is called routes. Maybe a better solution is having a default “components” folder to make it clear and unambiguous where components go instead of changing how everything else works.
That might be a personal preference. Having every file start with a + is just as ugly. At least the “__” differentiates and indicates it’s a special case, while +layout and +page feel more like both are routes or on the same level.
That’s actually what I’d expect if I saw a index.json.js not sure what the confusion would be here. I’d expect foo.json.js to create that same route as well.
I have to admit I did not get into typescript yet so have not come across this issue and don’t know what it means for beginners.
Isn’t the answer “because components are not routes”? It’s ok to say “because that’s not how it works”, changing the entire structure of how things work just to say “it still doesn’t work but now people wont ask as much” is not satisfactory.
Not seeing how this changes with the new structure. (Might be above my level of knowledge)
Moving to different file doesn’t make it less boilerplatey in itself, it’s just elsewhere now. — |
Beta Was this translation helpful? Give feedback.
-
Personally, I think the changes look promising. We're already creating folders per route most of the time. Having just one way of doing things will make onboarding much easier. The data/errors props API with auto generated types solves problems I'm experiencing all the time. I don't mind the + prefix. It clearly differentiates files with special meaning. The only situation that feels a tad weird is routes/my-api.json.ts vs routes/my-api.json/server.ts, but given all the advantages and consistency of the new structure I'm sure I'll get used to it. Obviously a lot of thought went into this, so I wanted to balance the (fair) criticism a bit. I'm sure many silent users are looking forward to a more consistent, actual simpler approach to routing/endpoints/load. |
Beta Was this translation helpful? Give feedback.
-
I'm going to save future commenters the time and effort by locking this thread now. The discussion has been had, and the decisions have been made. Thank you to everyone who joined in! I realise a few of you are still sceptical about some of these changes. At this point, there's nothing anyone can say that will convince you except experiencing it, and for that to happen we need to stop yakking and get cracking. I promise you'll come around. In the meantime, I'll leave this here: |
Beta Was this translation helpful? Give feedback.
-
I wanted to take a minute to share an update and say thanks to the entire community here. It was truly amazing to see the passion around Svelte and SvelteKit. We saw people come forth with alternate proposals, graphs/flowcharts, example projects, testing against various tools, and so much more. There was real effort invested into sharing feedback here and while we can't give each individual post the response it deserves, I do want to make it clear that we read and gave real consideration to all of the suggestions here. Design is inherently subjective and varies by usecase. People use SvelteKit to build small static sites and large interactive sites. They use different editors, tooling, libraries, infrastructure, and platforms. They work as individuals, on small teams, and on large teams. They are beginners, intermediate, or advanced users. They are consumers of the technology and/or teachers of it. Some build only their UI in SvelteKit while others build their whole site or applications. And so on... It was very helpful to have all of the input here to ensure we're considering all of the cases and making the best trade-offs for the user base as a whole including those who may adopt Svelte in the future. The Svelte maintainers met for ~3.5 hours over the weekend to discuss the proposal and ensure we had landed on the most preferred design before beginning to implement any changes. The feedback here was taken into account a few days ago in updating the proposal to make changes such as removing labels and introducing a We believe these changes will result in the best overall version of SvelteKit and we look forward to everyone being able to use it when it's available and continue to build it together. SvelteKit is the work not just of the growing group of maintainers, but also the whole community here. We'll be offering a migration guide and tooling to assist with some of the more automatable parts. In the meantime, we'll be largely pausing other development efforts including PR review for the next short while to allow us to focus on and land this large change. Thanks again to everyone who participated in the discussion! |
Beta Was this translation helpful? Give feedback.
-
migration guide now available at #5774!
update: thank you! #5748 (comment)
updated! #5748 (comment)
I've edited this post with the amendments described in that comment.
Alright friends, strap in — this will be a long read, but hopefully one that leaves you as excited about SvelteKit's future as we are. What follows is a proposal for a set of changes that address the wartiest bits of SvelteKit's design, particularly the more confusing aspects of
load
— it should enable us to close #3021, #3751, #4274, #4656, #4801, #4815, #4911, #5218, #5467, #5537, #5633, #5669, #5732, #5750 and #5532, and it will reframe discussions taking place in a couple of dozen other issues.It will involve some migration work proportional to the size of your app. For that, we're sorry. (We're particularly sorry to people who have created educational content around the current design.) Because of the scale of the changes, we're taking the unusual step of building a migration script that will do as much as can be automated and annotate your codebase for the remainder with errors like this:
tl;dr
Please don't comment on the discussion after only reading this section! It's here to prime the pump and to give you something to refer back to once you've digested the entire thing, including the rationale for the various design decisions.
With that caveat, this proposal:
+page.svelte
dictate how those routes behaveload
into a separate+page.js
file along withexport const prerender
etcexport const ssr = false
controlload
APIload
functions to run concurrentlypage
props with a singledata
prop, plus anerrors
prop for validation errors fromPOST
/PUT
/PATCH
handlers, and...Let's dive in.
Why are we doing this?
The current filesystem-based routing design has some weak spots:
src/routes/foo.svelte
andsrc/routes/foo/index.svelte
are equivalent, and having two ways to do things is always a source of confusion. Each has downsides — too manyindex.svelte
files open in your editor gets confusing, butfoo.svelte
makes it awkward to colocate related files. I frequently find myself movingfoo.svelte
tofoo/index.svelte
as the route becomes more complex (e.g. it needs a dedicated error page, or it gains a child route, or I need to break something out into a separate component, or it needs a page endpoint, and so on). These changes are costly and annoying, and I always kick myself for not just always using folders.src/routes/foo/Widget.svelte
, unaware that this creates an unwanted and broken/foo/Widget
route. It's possible to mark files as 'private' withconfig.kit.routes
(the default is to treat_Widget.svelte
, the aesthetics of which make me shudder, as private) but this is a little convoluted.__layout.svelte
and__error.svelte
are just... ugly.src/routes/foo/index.json.js
creates a/foo.json
route. Everything about this is wantonly confusing.src/routes/foo/[id=integer]@somelayout.svelte
must import its generated types from./__types/[id=integer]@somelayout
, and keep it in sync if[id=integer]
is renamed to[id=uuid]
(though after said rename, VSCode will have tried to 'fix' the import, usually to something batshit insane).Meanwhile,
load
has a number of confusing aspects:<script context="module">
. This leads many people to ask 'why can't arbitrary components have aload
function?', to which the unsatisfying answer is 'you just can't, it doesn't work that way'. The data tree and the render tree are separate-but-related concepts, and our current design conflates them.stuff
because we couldn't figure out what else to do with it. We thinkstuff
is unnecessary, for reasons we'll explore below.error
,status
,redirect
,cache
,dependencies
,stuff
,props
) are optional, but some require other properties to make sense (e.g.redirect
needs a 3xxstatus
,status
needs anerror
if it's larger than 400, but anerror
doesn't need astatus
) and others don't make sense in combination (e.g.error
andprops
). Good luck teaching TypeScript about all these quirks, much less humans.status
anderror
arguments are bizarre.status
is usuallynull
, but will have a value if it's an__error.svelte
file of if there's a page endpoint, but also you can return a status code that will override the status code that was passed in, except sometimes it won't, because reasons. This behaviour is confusing enough that I don't think we even have tests for a lot of it, much less documentation.props
and the type of the props in the component itself — you have to redeclare the types, uselessly. (The same goes for page endpoints.)We don't want to throw the baby out with the bathwater.
load
has some great qualities — SvelteKit makes it very easy to use external APIs for your data without going back to your server (which costs money for you and time for your users), or to delay navigation until some work has happened without attempting to render, or populating the data tree with things that can't be serialized (like components!) — and these are worth preserving.But we can definitely do better. The rest of this document will explain how.
Routing
This will seem familiar if you've read #5037, though it has a few small changes.
Instead of defining routes via files and folders, routes are only defined by folders. Layout and page components are defined with
+layout.svelte
and+page.svelte
files — sosrc/routes/about.svelte
would becomesrc/routes/about/+page.svelte
.The
+
prefix marks the file as being a route file. This scheme has a number of benefits:Widget.svelte
) are simply ignored by the router, making colocation easy without workarounds+loading.svelte
), we can do so without it being a breaking change, because+
is a reserved prefixIf a page uses a named layout, it can be expressed as
[email protected]
.Why
+
?We considered a variety of characters (
~
,@
,#
,$
,%
,-
,+
,=
,_
and so on).Some of these don't work well in some contexts (
$
needs to be escaped in the terminal, for example). Others have an existing meaning (_
in particular has a well-established connotation that it makes the thing private, whereas+page.svelte
is defining the public interface of your app) that rules them out.@
and#
feel very 'heavy', as in they occupy a lot of black pixels.We ultimately landed on
+
because you're adding a route to your app (or some behaviour to your route). It's a lightweight, symmetrical character that doesn't have baggage, and doesn't need to be escaped (though vim users will need to dovim ./+page.svelte
instead ofvim +page.svelte
).As to 'why do we need to prefix route files at all?', see the Prefixes section of this comment.
Endpoints
If a page has an endpoint, it lives in the route as
+page.server.js
. We will also introduce layout endpoints —+layout.server.js
. (We're not using+page.js
and+layout.js
for this, as they have another purpose described below.)Standalone endpoints are expressed as
+server.js
. A+server.js
file cannot coexist with+page.svelte
.At present,
src/routes/foo/index.json.js
creates a/foo.json
route. Under this proposal, it will be necessary to create a separate route:The API for endpoints will change, as detailed below.
New
load
APIAs mentioned,
<script context="module">
is a problematic home forload
. As such, it — along with other module exports likeprerender
— will move to new files,+page.js
and+layout.js
, making them siblings of+page.svelte
and+layout.svelte
. (Error components —+error.svelte
— will no longer be able to useload
, as this is a source of bugginess and confusion for the sake of an arguably undesirable feature.)Separating everything out into a separate file gives us three big wins:
load
belongs to the route and not the component, eliminating the 'why can't myWidget.svelte
have aload
?' confusion.export const ssr = false
. This used to live inside the component, and would tell SvelteKit not to SSR a given page, but in order to readssr
SvelteKit had to import the component. Since the most common reason for not wanting to SSR a page was that it used components that couldn't be imported server-side, this blew everything up, and we replaced it with an option inhandle
that is much less ergonomic than a granular, declarative boolean.load
function. I'm very excited about this — it's going to be absolutely huge for ergonomics and productivity — but an explanation is out of scope for this section, so for now just trust me.The function signature will also change.
Inputs
Today,
load
takes a singleLoadEvent
argument — an object withurl
,params
,props
,fetch
,session
,stuff
,status
anderror
. We're removingstuff
,status
anderror
, renamingprops
todata
, and adding three new functions —parent
,depends
andsetHeaders
:stuff
prevents layoutload
functions from running in parallel with the pageload
function.error
andstatus
are redundant (and confusing, as described above) —$page.status
and$page.error
should be used instead.data
is a more agnostic and universal term that can be used more consistently throughout the design. In this case it refers to data served from+page.server.js
, allowing you to intercept/munge/augment/disregard data from the server.parent
(open to bikeshedding) allows aload
function to opt back in to the waterfall in cases where that's necessary for the purposesstuff
is used for today.depends
(again, bikesheddable) allows aload
function to declare a dependency on a specific resource (replacingdependencies
, which is only necessary in niche circumstances)setHeaders
will give pages the ability to set headers during SSR, includingcache-control
. (Unnecessary-for-now but potentially interesting detail: in the future, we plan to introduce a streaming rendering mode that will flush headers and the first bytes of HTML before the page has loaded and rendered. In that mode,setHeaders
would throw an error, since you can't set headers once the response is already on its way to the user.)Outputs
Instead of returning a complicated object as described above,
load
simply returns data:This means we can no longer return
redirect
,status
,error
,cache
,stuff
ordependencies
. Let's address these in reverse order.dependencies: ['/foo', '/bar']
can be replaced with a call todepends('/foo', '/bar')
stuff
is used for two purposes — to cascade things from the top to the bottom, and to pass things like<title>
or breadcrumbs from the bottom of the data tree to the top of the render tree, so it can be used in<svelte:head>
or<nav>
etc. We'll ignore the latter for now and focus on cascading. Using theparent
function, we can capture all the data that has been loaded by parentload
functions (and+page.server.js
files, as appropriate):This prevents the second
load
function from running until we've got data fromrandomnumberapi.com
.Instead of magic
cache
values, we simply make it possible to set headers directly fromload
. Most of the time settingcache-control
headers on HTML is a mistake, I think, but if you know what you're doing then we will no longer stand in your way.There's only one reason to set
status
, and that's if something went wrong. Otherwise it should just be 200. Accordingly, under the new rules the way you set an error status is just by throwing an error. We'll expose a helper function from@sveltejs/kit/data
(bikesheddable) to allow you to throw errors that have a status code attached:With the code above, any
/admin/*
route will error if the user isn't signed in or isn't an admin. We don't need to repeat that logic in childload
functions, becauseawait parent()
will also throw if the right conditions are not met:This next one might feel weird but bear with me — redirects are also thrown. That's partly because a redirect result is semantically closer to an error result than to success, but partly because it's useful if
await parent()
throws in the redirect case as well as the error case:Exposing data from
load
Assuming we have some data from our
load
function in+page.js
or+layout.js
(or from+page.server.js
/+layout.server.js
if we're getting data direct from the server instead of viaload
), the next step is to use it in our components.Today, the component exposes individual props. If you're building an ecommerce site you might have something like this:
But there's a problem here — there's nothing forcing that laboriously-typed
cart
prop to actually correspond to the type of data the component receives. It could be renameditems
, orqty
could becomequantity
. If you're lucky, that'll result in a runtime error you can try and debug, but you're equally likely to shipNaN
andundefined
to production.In the new world we have a single
data
prop, which is automatically typed:Page-level data
The
data
prop contains only what was returned fromload
(or+page.server.js
, if there's noload
). In some cases it's useful for a layout to be able to use data from the pageload
, for example populating<title>
from a single<svelte:head>
rather than requiring each page to add<svelte:head>
separately.We can do that with
$page.data
, which contains everything.Conceptually,
$page.data
looks like this:+page.server.js
and+layout.server.js
APIThe Artist Formerly Known As Endpoints also gets an API facelift. One of the rough edges of the current design is that 'page endpoints' and 'standalone endpoints' are very similar-looking but are in fact entirely different beasts — standalone endpoints can return a
Response
or at least aResponse
-like object with streaming binary data and custom headers etc, while page endpoints can only return a POJO (Plain Old JavaScript Object) so it can be passed directly to the page for SSR then serialized as JSON to travel over the wire. Also,headers
returned from page endpoints are silently ignored, which is weird.This redesign fixes all that.
+page.server.js
and+layout.server.js
have a different API to+server.js
. We'll cover+server.js
in the next section.As with today's page endpoints,
+page.server.js
exports handlers named for the HTTP methods they handle —GET
,POST
,PUT
,PATCH
andDELETE
. (We might addOPTIONS
in time.)+layout.server.js
is not involved in mutations, so it only exportsGET
.GET
Like
load
, aGET
handler just returns data, rather than{ status?, headers?, body }
as now:There's only one useful status for a
GET
request — a 200. Anything else indicates something went wrong, so — as withload
— wethrow
in those cases, using the same helpers as before:Headers (including
cache-control
andset-cookie
) can be set with the samesetHeaders
function passed intoload
.POST
While
+server.js
gives you complete control over theResponse
(see below), in+page.server.js
we can streamline things in a way that satisfies the common case and makes it easy to implement progressive enhancement (I hope to resurrect the<Form>
discussion soon).In the case of
POST
, there are basically three possible outcomes — success, failure due to input validation errors, and failure due to something unexpected that requires showing an error page.Unexpected failures are easy to deal with, as it's the same as
load
andGET
—throw error(status)
.Validation errors cause the page to be re-rendered with the errors, so they can be used for UI that guides the user towards valid inputs:
By default this page will render with a
400
status code, though an optionalstatus
property returned alongsideerrors
can override it if necessary.Redirects
A common pattern with
POST
requests is to redirect to the newly created resource. We can achieve this in SvelteKit by returning alocation
property that indicates where the created resource lives:A native form submission would cause the browser to redirect to
/items/${id}
with a 303; if nolocation
is provided the page would simply reload (perhaps using the PRG pattern?).PUT
andPATCH
These methods are basically the same as
POST
, except that since no new resource is being created, there's no need to return alocation
property.DELETE
DELETE
is simpler still; no data is being submitted, so there's no room forerrors
. If the function succeeds, SvelteKit responds with a 204.+server.js
APIAs mentioned,
+server.js
differs from+page.server.js
— rather than providing an unserialized POJO to the page for SSR,+server.js
is responsible for constructing the entire response. In this new design, we make this responsibility clear — rather than the{ status, headers, body }
return value, handlers must simply return aResponse
.As conveniences (and to keep things consistent), the
error
andredirect
imports and thesetHeaders
helper can be used, though strictly speaking they're not necessary since you could construct your own error/redirectResponse
objects with whateverheaders
you like.+server.js
is useful when you need to expose a public API, or you need to do things that aren't possible with+page.server.js
like streaming binary data.Next steps
We plan to implement this new design (and the migration script) as soon as we're able to, incorporating feedback from this discussion along the way.
I know this all seems like a lot, and you're probably exhausted from reading it. (Imagine how I feel after writing it.) As a result, you might feel like this is a lot of new complexity, just because of the sheer amount of information you've just ingested. In reality, this is a significant simplification, and — when presented in the form of documentation and educational content — will be much easier to learn. I promise.
Beta Was this translation helpful? Give feedback.
All reactions