Okay, so we've finished part 3, the tutorial.
We've learned all of the basics of SvelteKit, and it's now time to move on to
part 4 where we're gonna learn some advanced techniques.
We're gonna begin with hooks.
SvelteKit provides several hooks,
which are ways to intercept the frameworks default behavior.
The most elementary of these hooks is the handle hook lives in a file
called hooks.server.js, and it receives an event object along
with a resolve function, and it returns a response object.
Now, resolve here, this is where the magic happens.
This is where SvelteKit will match the incoming request URL to a root of
your app.
It'll import the relevant code, which means the page server.js, and
the page SvelteKit files and so on.
It loads the data needed by the route, and then it generates the response.
And so, this is what the default handle hook looks like if you don't
have a hook.server.js file, this is essentially what SvelteKit is,
is stubbing in on your behalf.
And when you're rendering a page, you can modify the HTML that gets generated.
So here, I'm gonna add an option to resolve.
I'm gonna use the transform page chunk method,
which is gonna accept some HTML, and
it's gonna return the HTML with some modifications.
Refresh the page now.
The body tag has now been manipulated by transformPageChunk.
So that's use useful for things like internationalization you
can specify the lang attribute of your HTML element for
example, but you can also use this hook to create entirely
new routes that don't exist in your directory structure.
For example, we create a new route called ping.
We can return a response, pong.
Now, if we navigate to that URL, we're not gonna be able to navigate to it
using the client side router because it doesn't exist as a client side route,
but it will go to the server and return that response from handle.
Now that event object that we were looking at, this thing here, is the same object
which is an instance of something called a RequestEvent which you can read about in
the documentation that is passed into API roots in server.js files,
form actions in page.server.js files, and
also your load functions in page.server.js and layout.server.js.
The event object contains a number of useful properties and methods,
some of which you've already encountered in this workshop, the cookies API,
a fetch an implementation of the fetch API that comes with some additional powers.
The function for getting the user's client address, which is useful for
things like geolocation.
And is DataRequest object, which is true if we're hitting a load
function as a result of a client-side navigation,
as opposed to rendering an entire HTML document.
Event.locals is a place to put arbitrary data.
Params is the current root parameters.
Root is an object with an ID property representing the root that
was matched, if any.
We've got the set headers function that we saw before, and we have that URL object
with properties like origin, host name, path name, search params, and so on.
So a useful pattern that you'll often use in SvelteKit
is to add some data to event.locals inside handle so
that it can be read in subsequent load functions.
So in our hook, we're gonna do event.locals.answer = 42,
and then we can read that data inside a load function.
Gonna add the event here, and
then we're gonna put that data inside this template string.
Okay, in a real application, this is the sort of thing that you might use
with information about the current user.
You'll do your authentication in the handle hook,
grab an object that represents the current user and put that on event dot local,
so that all of your server load functions can access it.
And one of the things that we haven't seen yet
is the fetch method that exists on the event object.
So you've probably used Fetch in the browser, if not, again,
MDN has all the information that you need to know how to use Fetch.
The Fetch that we get on the event object is a little bit different because it can
be used to make credential requests on the server, inherits the cookies and
the authorization header,
that were passed from the original request from the browser.
That's not something that you get if you're using something like NodeFetch.
It can make relative requests on the server, whereas normally in Node or
server environments, Fetch requires a fully qualified URL.
And for internal requests, for example, if inside your load function you make a fetch
call to one of your own API routes, that doesn't issue an HTTP request,
it just calls the function directly, so it's a lot more efficient.
And its behavior can be modified with the handleFetch hook,
which by default looks like this.
It's very similar to the handle hook in that it takes the event object and
an implementation of fetch.
And by default, all we are doing is fetching the request that was passed in,
but we can change that.
For example, we could respond to requests for this file,
with responses from this file instead,
just by changing the behavior of event.fetch.
So, open up hooks.server.js and then, inside here,
we're gonna find out what the requested URL is.
To create a new URL object from request to that URL, request dot URL is a string.
It's a fully qualified URL.
But when we call new URL, we get that nice object with the path name and
the origin and all of that other useful stuff.
So if the path name that's been requested is /a,
then we're gonna return a response by fetching b instead.
Okay, so in our root page server.js, we are fetching /a, that isn't being
redirected inside handle fetch, and we're getting data from slash b instead.
Now according to everything we've seen so far, that's probably not very useful,
but later we're gonna to learn about something called universal load functions,
and that's what this feature is really designed for.
In that scenario, handleFetch is useful if you are making
requests to a public URL, like api.yourapp.com.
From the browser, you wanna go to that public URL, but if you're making the fetch
from the server, then you don't wanna go via the public Internet.
You don't wanna go through the load balancers and proxies and
whatever between the server that's rendering the HTML, and the server that's
serving your API, because they can just talk directly to each other.
So you can use handle fetch in that context to change requests for
api.yourapp.com, to local host whatever
The last hook that we're gonna learn
about is the handle error hook.
This lets you intercept those unexpected errors that we talked about earlier and
trigger some behavior, maybe you wanna ping a Slack channel, or
you wanna send some data to an error logging service.
So, to recap, an unexpected error is one that was
not created with the error helper from SvelteKit.
And generally, when you see an unexpected error,
it means that something in your app needs fixing.
Default behavior is just to log the error.
So if you haven't defined handleError,
then it basically behaves a little bit like this.
And so in this app, if we navigate to the bad place,
we'll see an internal error message, but we're not seeing the error that was
actually thrown from our load function, which says, this is the bad place.
But if we open up the terminal, then we will see that error message logged,
which will help us diagnose the problem and hopefully fix it.
So once again, the reason that we do this is because error messages by their very
nature can contain unexpected information.
You don't control it.
It could be coming from a third party library or something else, and
it could contain sensitive information that could reveal
information about your setup to people who wish you harm.
And so the default behavior in SvelteKit is to redact that information and
just show a generic internal error message instead.
So the error object that is available to your application, whether that's
represented as the page.error, property in your error.svelte pages or
the sveltekit.error property inside your source/error.html,
is just this object here.
A plain object with a message that says internal error or
in the case of a 404, it'll say not found.
In some situations, you may want to customize a subject.
So in here, instead of allowing SvelteKit to generate that
internal error object, we could return an object of our own.
Say everything is fine, refresh the page, and
you can see that is now being shown, and we could add a code that goes with it.
We can essentially add any serializable data in here.
And so we can now reference properties other than the message inside our custom
error page.
We'll create a custom error page,
click the button here, plus error.svelte.
We begin by importing the page store again.
We'll show the page status.
It's gonna be 500.
We'll print the error message that was included on that object,
but now we can also add the code property that we added.
So that's the sort of thing that might be useful to show to your users so
that they can quote that in correspondence with your customer service people or
something like that.
It's also something that you might use when you're
sending details of the error to an error logging service.
So those are the three hooks that we have available handle, handleFetch and
handleerror, and
they allow us pretty broad array of customizations of the application.