SvelteKit has something called hooks. A file which exports a set of functions which run on every request to the server. It's really useful!
Introduction
A SvelteKit hook is a file which exports four functions which you can use to modify data from your server. It is not compulsory for your app to contain a hook file but one is implemented by default if you don't create one. It exports four optional functions handle
, handleError
, getSession
and externalFetch
. With hooks, you can extend SvelteKit to suit your needs😉.
Use Cases of Hooks
A list of possible things which you can do with SvelteKit hooks.
- Redirecting a user
- Authenticating a user
- Minifying HTML, CSS and JavaScript response
- Getting the user session
- Setting cookies
- Fetching data from a database
- Handling errors
- Modifying fetch response
- Modifying HTML returned from the server
Creating a Hook
The default location for the hook file is src/hooks.js
or if you prefer to use a folder, you can store your functions in an index.js
file inside src/hooks/
which will then make the path src/hooks/index.js
.
Also, you can change the default path to the file inside your svelte.config.js
file by editing the config.kit.files.hooks
value.
Listed below are the list of functions which are exported by hooks in SvelteKit.
handle
The handle hook is a function which runs on every request to the server and also during prerendering. It allows us to access the request and modify the response. Inside the handle function, we can add custom data to the request.locals
which will then be accessible to all the endpoints.
To simply put, the handle function acts like a middle-man who sits between the client and the server and intercepts the request before taking it back to the client. This allows us to perform a variety of actions to the response before sending it back. For example, we can decide to minify the HTML response before sending it back to the server or redirect a user who is not logged-in to the login page.
export const handle = async ( { event, resolve })=> { return await resolve(event)}
The snippet above shows the bare minimum of how the handle
hook works. It receives an event
object which contains the request and an asynchronous resolve
function which generates the response by taking in the event as parameter. Also included in the event object is the clientAddress
(a getter function which returns the user's I.P address), locals
(An object which allows you to pass custom data to your endpoints), params
(an object which contains the value of the dynamic path), platform
(a variable which contains the system's platform. It is usually set by the adapter), routeId
and url
.
To add custom data to the request, just populate the event.locals
with your data before returning the response. You can also minify your HTML generated by SvelteKit using hooks since it doesn't do that by default. I have written a guide on how to do it.
The resolve
function also supports a second optional parameter that also gives us more control over the response.
- ssr
This allows us to disable server-side rendering. By default, it is set to true
. If set to false. It renders an empty shell page.
/** @type {import('@sveltejs/kit').Handle} */
export const handle = async ( { event, resolve })=>{
const response = await resolve(event,{
ssr: false
});
return response;
}
- transformPage
This function allows you to apply custom transforms to the HTML document.
/** @type {import('@sveltejs/kit').Handle} */
export const handle = async ( { event, resolve })=>{
const response = await resolve(event,{
transformPage: ({ html })=>{
return html.replace("a","b")
}
});
return response;
}
Multiple handle functions
SvelteKit allows you to chain multiple handle
functions with the use of the sequence
helper function. What the function does is that, it takes an array of functions and iterates through them and apply the handle function to each of them.
Let's look at an example.
import { sequence } from "@sveltejs/kit/hooks";
const first = async ( { event, resolve })=>{
console.log("First handle start");
const result = await resolve(event);
console.log("First handle stop")
return result;
}
const second = async ( { event, resolve })=>{
console.log("Second handle start");
const result = await resolve(event);
console.log("Second handle stop");
return result;
}
export const handle = sequence(first, second)
The resulting response will be:
First handle start
Second handle start
Second handle stop
First handle stop
As you can see from the resulting output, the first
function will be called first followed by the second
function but the second
function will finish before the first
function. Well, I still don't know the reason why it happens this way🤷♂️.
Fixing Common Errors Whiles Using The Sequence Helper
- Cannot find package "@sveltejs/kit" on production build
This problem is mostly found for those who use adapter-node
whiles using the sequence
helper function. The reason for this error is that, adapter-node
doesn't bundle @sveltejs/kit
with the build output so it doesn't find the sequence
function when running the application in production.
Solution
-
Create a file called
sequence.js
and paste the sequence function code inside.You can get the source code from
node_modules/@sveltejs/kit/dist/hooks.js
. -
Then you import the sequence function from the file you just created.
example below
import { sequence } from "./sequence.js";
handleError
This is the hook responsible for handling errors in your application. Whenever an error is thrown when rendering your app, this function will be called with the error
and the corresponding event
that caused it. This function actually doesn't do much apart from reporting the errors in your application. It is mostly useful during production where you might want to get notified of every error that happens in your application. For example, you might want to write in a code that sends you an email each time your app receives an error.
During development, a frame will be displayed on your screen highlighting the cause of the error.
The handleError function will not be called when pages and endpoints explicitly respond with 4xx and 5xx status codes
getSession
Since HTTP is a stateless protocol, it means the client and server forget about each other at the end of every request. In order to keep track of user's state, we use sessions to store data about the user. The data is stored in the server's memory.
The getSession
hook allows you to access the session
object. The function takes an event object and returns a session object. This function is run whenever SvelteKit server-renders a page. If it's unimplemented, the session is an empty object {}
.
Apart from accessing the session object, you can also set the session inside this hook. Let's look at an example.
export const getSession = (event)=>{
return event.locals.user = {
name: "John Doe",
age: "20",
id: 112244233434343
}
}
In the example above, we set the session object with some values. In order to access the session in our page, we just need to retrieve it inside our load function.
<script context="module">
export const load = async ({ session })=>{
return {
props: {
session
}
}
}
</script>
Also, the data inside our session object will be accessible by endpoint if they access event.request.locals
.
externalFetch
This hook simply allows you to modify a fetch request that happens inside a load
function that runs on the server. Any fetch to any external resource than runs on the server is handled by this function. Actually, it doesn't do much, the only important use case I found for it was making it hit API's directly instead of passing through proxies. This doesn't do much but it helps increase the speed of your fetch requests. Also, it allows you to modify the headers of your fetch request.
Conclusion
This is a much detailed explanation of hooks in SvelteKit. If any information is missing out, please do write it in the comment section.
Top comments (2)
I just dont get why they would call middleware - hooks. And use a word that is already know in React as some other thing.
being able to HOOK into requests already happening makes a lot of sense. Not everything in web dev is inspired by React, thank god.