This is post one in a tutorial series on how to build a custom Daily app with SvelteKit.
At Daily, we know how important it is to build flexible APIs. We intentionally build our APIs to allow for a variety of video call customizations.
In one of our previous blog posts, we looked at how to embed Daily Prebuilt – Daily’s video chat interface – into a basic Svelte app.
In cases where you’re looking to build a more custom video experience, using Daily’s call object can provide much more flexibility.
In today’s tutorial, we’ll kick off a new Daily SvelteKit series by setting up our routing in a SvelteKit demo app.
By the end of this post, you’ll be able to submit a form from the home screen to create and join a Daily room. Once the room is created from the home screen, we’ll navigate to a path that corresponds with the Daily room name.
Svelte vs. SvelteKit: What’s the difference?
Svelte is an open-source front-end component framework that can be used as an alternative to other front-end frameworks, like React or Vue.
SvelteKit, on the other hand, is a full-stack app framework (or “meta framework”) built on top of Svelte. It is built to handle application routing and custom API routes, in addition to the front-end components offered by your standard Svelte project.
💡 Note: If you’re familiar with React, Svelte is to SvelteKit what React is to Next.js.
For this project, we are using SvelteKit to take advantage of two features that will simplify the demo app we’re building:
- The file-based routing system to navigate between our home screen and Daily call screen.
- The custom route-based API endpoints we can build to create rooms on the fly prior to navigating away from the home screen.
Building our “create room” endpoint directly in this project means we won’t need to build a separate custom API to be used in our Svelte components, which is great!
⚠️ Note: SvelteKit has not hit v1 yet, so keep that in mind while making framework decisions.
Tutorial goals
In today’s tutorial, we’ll only be looking at how to use Daily’s REST API in a SvelteKit project to create new rooms.
We will:
- Create our home page with a form to create and join a call
- Create an in-call page that will eventually contain all our Daily video call UI
- Write a custom endpoint that creates the Daily room
- Set up our navigation to navigate to the new room’s URL once it’s been created
Getting started
The completed version of this custom Daily call project is publicly available on Github. To run this project locally, first clone the repo.
Next, you will need to rename env.example
to .env.local
and add your Daily API key and Daily domain to it. (Visit the project’s README for more information on where to retrieve these values.)
Lastly, from the project’s root directory, run:
npm install
npm run dev
To view the project, open http://localhost:3000
in the browser of your choice.
Planning our file structure
Where we add our files in this project is extremely important, given that the routing is based on our file structure in SvelteKit.
Let’s first determine where we’ll need to have files to cover both our app pages and custom endpoint.
Project_directory
│_ src
│_ lib // UI components
│_ call
│_ forms
|_ RoomForm.svelte
│_ header
│_ routes
│_ room
│_ [roomId].svelte // in-call page
│_ index.json.js // room endpoints (Daily REST API used here)
│_ index.svelte // home page
│_ app.html // where our app will get injected
|_ store.js
A couple things to note before we dive into this:
- The
lib
directory is where we’ll put all our Svelte UI components. The files in every subdirectory aren’t listed since we’re not discussing most of the UI components just yet. - There are many other files in this repo, but these are the ones we’ll focus on today.
You may have noticed there are two types of file extensions in our routes
directory: .svelte
and .json.js
. These represent the two types of routing files we’ll use: our pages (.svelte
) and our endpoints (.json.js
).
Let’s start by taking a look at the home page: /routes/index.svelte
.
Building a home page view
When you visit the app’s base URL (http://localhost:3000
for now), it will render our /routes
directory’s index.svelte
file.
This file is actually quite concise:
In /routes/index.svelte
, we import our RoomForm
component from the lib
directory (plus a header and some text).
Setting up our RoomForm
Before moving on to our custom endpoints, let’s take a quick look at the RoomForm
component so we know how it triggers a navigation change.
The form itself has two inputs:
- The local user’s name
- The Daily room URL they want to join
Each input has its value bound to a variable declared at the top of the file via Svelte’s bind
attribute (e.g. bind:value={dailyName}
.) This means we can access the input values via the associated variables (dailyName
and dailyUrl
).
The form can handle two possible values for the URL input:
- An existing Daily room URL the local user wants to join
- An empty value. We’ll assume this means they want to create and join a new Daily room
In other words, if the local user enters a URL, we’ll use it. Otherwise, we’ll need to create a new Daily room via the Daily REST API.
Submitting the RoomForm
Now that we have the form that lets us join a room, let’s see what actually happens when you submit it.
As mentioned, there are two possible outcomes from submitting the form, depending on whether the local user wants to create a new Daily room or not.
We’ll start with if they already have a room they want to join:
onSubmit
gets called – unsurprisingly – when the form is submitted. In it, we set the name (dailyName
) provided by the user in our app’s store like so:
username.set(dailyName);
Note: Look at store.js
to see how the username
is initialized in our app’s store.
Once the username is set in the store, we can access it from other components in the app. This will help when we’re initializing the call with daily-js
.
Next, we see if there is a Daily room URL provided by the form. If there is, we get the room name from the URL and navigate to that page via Svelte’s goto
function.
const roomName = dailyUrl.split('/').at(-1);
goto(`/room/${roomName}`);
Since Daily room URLs use the following format, we can assume the last value in the URL is the room name:
https://<your-daily-domain>.daily.co/<room-name>
Once the app navigation has updated, you will be viewing http://localhost:3000/room/[room-name]
. This route maps to our /routes
directory like so: /routes/room/[roomId].svelte
.
Before we move on to that view, let’s first cover what happens if a URL is not provided in the form.
Creating a new Daily room via a SvelteKit routing endpoint
If a room URL is not provided, we’ll continue in submitForm
:
async function submitForm() {
…
if (dailyUrl) {
…
return;
}
/**
* If there isn't a Daily URL, we can create a new
* room with a random name
*/
const submit = await fetch('/room.json', {
method: 'POST'
});
const data = await submit.json();
if (data.success && data?.room?.name) {
goto(`/room/${data.room.name}`);
dailyErrorMessage.set('');
} else if (data.status === '400') {
dailyErrorMessage.set('bad request');
} else if (data.status === '500') {
dailyErrorMessage.set('server error :|');
} else {
dailyErrorMessage.set('Oops, something went wrong!');
}
}
Here, we make a POST
request to /room.json
. If the request is successful, we then navigate to that room using the response’s room.name
value via the goto
navigation method again. If there was an issue creating the room, we instead show an error message.
That brings us to the next obvious question: what is the /room.json
endpoint we’re using? Let’s look!
Using SvelteKit’s route-based endpoints
If we go back to our project’s file structure, we’ll recall there are two types of file types in the /routes
directory.
In /routes/room
, there is [roomId].svelte
(where [roomId]
is a dynamic value), and index.json.js
.
In our POST
request above, /room.json
will get mapped to /routes/room/index.json.js
, which will handle any requests matching that path.
Looking at that file, there is one function in it called post
. (You could add get
or delete
, etc., but we just need post
.) When the submit form makes a POST
request to /rooms.json
, it will look for a post
function in /routes/room/index.json.js
to complete the request.
export async function post() {
/**
* Note: You must at your Daily API key to an .env file
* for this request to work. Refer to the README for
* further instructions. :)
*/
const DAILY_API_KEY = import.meta.env.VITE_DAILY_API_KEY;
// add 30min room expiration
const exp = Math.round(Date.now() / 1000) + 60 * 30;
const options = {
properties: {
exp
}
};
try {
const res = await fetch('https://api.daily.co/v1/rooms', {
method: 'POST',
headers: {
Authorization: `Bearer ${DAILY_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(options)
});
if (res.ok) {
const room = await res.json();
return {
status: 200,
body: JSON.stringify({
success: true,
room
})
};
} else {
return {
status: res.status,
body: JSON.stringify({
success: false
})
};
}
} catch (error) {
return {
status: 500,
body: JSON.stringify({
success: false,
message: 'something went wrong with the room submit!'
})
};
}
}
The post function looks similar to how you would typically make a fetch request using a third party API.
- First, we get our Daily API key, which was set in the
.env.local
file while getting our local dev environment set up.
const DAILY_API_KEY = import.meta.env.VITE_DAILY_API_KEY;
- Next, we set our Daily room options. You can use whatever options you want but we’ve just set a 30 minute expiration to avoid long-lasting demo rooms.
- Then, we actually make our
POST
request to Daily’s REST API using our API key to authenticate the request:
const res = await fetch('https://api.daily.co/v1/rooms', {
method: 'POST',
headers: {
Authorization: `Bearer ${DAILY_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(options)
});
- Finally, we return the response from our request, either with a success message or an error message to be handled by our client-side code.
if (res.ok) {
const room = await res.json();
return {
status: 200,
body: JSON.stringify({
success: true,
room
})
};
}
// error handling below
Once this response is received in our RoomForm
’s submitForm
function, it will navigate to the room’s page or show an error, as mentioned before.
Navigating to our room page
Now that we’ve navigated to our room’s page (https://localhost:3000/room/[room-name]
), we’re officially ready to build our video call with daily-js
! 🙌
[roomId].svelte
is where we will build out our call in the next tutorial, including video, audio, chat, and screen sharing.
Wrapping up
We hope this helps you get started on building a custom Daily video chat app with SvelteKit. To get a head start on our next post, check out the completed demo app in Github. 🌟