Full-Stack Application
Learning Objectives
- You know the term “Full-Stack Application”.
- You know how to access a server-side API from a client-side application.
Full-stack applications
A “Full-Stack Application” is an application that includes both the client-side functionality and the server-side functionality. The user sees and interacts with the client-side functionality, while the client-side functionality interacts with the server-side functionality. The server-side application may also e.g. interact with a database.
Similarly, a “Full-Stack Developer” is a developer who is familiar with both client-side and server-side development, and can work on both parts of the application.
At this point, the walking skeleton has (1) client-side functionality, (2) server-side functionality, and (3) a PostgreSQL database. The server-side code already interacts with the database when retrieving todos.
Server-side API location
To allow the client-side application to interact with the server-side application, we need to tell the client-side application where the server-side API is, and add functionality to the client-side application that interacts with the server-side API.
Create a file called .env.development to the root of the Svelte project (the folder client). Add the following line to the file.
PUBLIC_API_URL=http://localhost:8000
The file .env.development is used to define environment variables that are used in the development environment. The variable PUBLIC_API_URL is used to define the address of the API that the Svelte application uses. The prefix PUBLIC_ is used to indicate that the variable is public, meaning that it is available to the client-side code.
The public variables are imported from $env/static/public in the Svelte project.
Once you have created the .env.development file, restart the project by running docker compose up --build in the root folder of the project. After the restart, the client-side application will have access to the PUBLIC_API_URL variable.
Showing the number of visits
To test the connection and to see that the Svelte application can fetch data from the backend, let’s first modify the +page.svelte file in the src/routes folder of the Svelte project to show the number of visits to the page. The number of visits is fetched from the server-side application. Change the content of the file src/routes/+page.svelte to match the following.
<script>
import { PUBLIC_API_URL } from "$env/static/public";
let visits = $state(0);
const fetchVisits = async () => {
const response = await fetch(`${PUBLIC_API_URL}/api/visits`);
const data = await response.json();
visits = data.visits;
};
$effect(() => {
fetchVisits();
})
</script>
<h1>Visit count: {visits}</h1>
Now, when you open up the address http://localhost:5173 in a browser, you should see a page that displays the visit count. The visit count is fetched from the server-side application, which increments the count each time the page is loaded or refreshed. In Figure 1 below, the page has been visited 5 times.

As you might notice when testing locally, on each reload, there is a brief moment where the count is at 0. This is because the Svelte application fetches the visit count from the server-side application, and there is a slight delay before the data is available.
Showing todos
Next, modify the +page.svelte to match the following.
<script>
import { PUBLIC_API_URL } from "$env/static/public";
let todos = $state([]);
const fetchTodos = async () => {
const response = await fetch(`${PUBLIC_API_URL}/api/todos`);
const data = await response.json();
todos = data;
};
$effect(() => {
fetchTodos();
});
</script>
<h1>Todos</h1>
<ul>
{#each todos as todo}
<li>{todo.name}</li>
{/each}
</ul>
Now, when you visit the page at http://localhost:5173, you should see a list of todos fetched from the server-side application. If there are no todos in the database, the list will be empty. In Figure 2 below, the page at http://localhost:5173 shows a list of todos fetched from the server-side application, which retrieves them from the database.

Summary
In summary:
- A full-stack application includes both client-side and server-side functionality.
- We configured the client-side application to know where the server-side API is located.
- We added functionality to the client-side application to fetch data from the server-side application.
- We tested the connection by fetching and displaying the number of visits and a list of todos from the server-side application.
File-wise, the sole change in this step was the introduction of the .env.development file that was added to the client folder. At this point, the structure of the project should be similar to the following (folders like node_modules and files like deno.lock are omitted).
$ tree --dirsfirst
.
├── client
│ ├── src
│ │ ├── lib
│ │ │ └── index.js
│ │ ├── routes
│ │ │ └── +page.svelte
│ │ └── app.html
│ ├── static
│ │ └── favicon.png
│ ├── .env.development
│ ├── package.json
│ ├── Dockerfile
│ ├── README.md
│ ├── svelte.config.js
│ └── vite.config.js
├── database-migrations
│ └── V1__todos.sql
├── server
│ ├── app.js
│ ├── app-run.js
│ └── deno.json
├── compose.yaml
└── project.env