In this part of the series, we will implement our backend service's WebSocket handler and begin building the frontend. This will enable real-time communication between the backend and frontend, allowing us to display analysis results as they become available.

The main prerequisite is that you have gone through the previous articles in this series. This ensures you have the necessary context and environment set up.

Sirneij
Sirneij/finance-analyzer
00

An AI-powered financial behavior analyzer and advisor written in Python (aiohttp) and TypeScript (ExpressJS & SvelteKit with Svelte 5)

sveltetypescriptpythonjavascriptcss3html5

To provide real-time updates for financial data analysis and summaries, we use WebSockets, a bidirectional communication protocol, instead of traditional HTTP requests that require constant refreshing. The TransactionWebSocketHandler function manages WebSocket connections:

src/websockets/transaction.websocket.ts
ts

Using the ws module (installed in the previous article), this function sets up the WebSocket connection and defines handlers for incoming messages, connection close, and errors. As soon as it receives a message, the callback for the message event listener gets fired and it works as follows:

  • Parses incoming messages as JSON, expecting an array of actions.
  • Validates the format of each action, ensuring it contains action and userId.
  • Validates the userId to ensure it is a valid Mongoose ObjectId.
  • Uses a switch statement to handle different actions (analyze and summary), and
  • Calls the handleAction function to process each valid action.

The handleAction processes specific actions requested by the client by retrieving transactions for the specified userId using TransactionService.findTransactionsByUserId static method discussed in the previous article. Any transactions retrieved are sent via the TransactionService.connectToUtilityServer method to the utility server for analysis or summary.

Let's register this handler with our app:

src/app.ts
ts

With that, we conclude the backend service. Now it's time to set SvelteKit up with TailwindCSS.

To setup a SvelteKit project with TailwindCSS, we will refer to the official TailwindCSS guide with some modifications from the migration guide I previously wrote.

First, create a new SvelteKit project via the Svelte 5 sv CLI:

sh

You will be prompted to install sv which you should accent to. Your interaction with the CLI should look like this:

sh

Feel free to modify any of the steps as you like. You can change directory into the newly created project and install the dependencies.

For some reasons, the tailwindcss installed by sv was version 3.4.17. However, at the time of writting this, TailwindCSS is already at version 4.0.2. So we need to migrate. To incept the migration steps, run this command:

sh

You should get something like this:

sh

It will magically modify your src/app.css to look like:

css

tailwind.config.ts will be removed and postcss.config.js will be modified. We need to make a slight change to src/app.css and vite.config.js:

src/app.css
css

We need line 6 so that we can leverage classes (and later, prefers-color-scheme) to dynamically switch themes.

Next is vite.config.js:

vite.config.js
js

That concludes the initial setup.

Many modern operating systems (OS) have made dark mode a first-class feature, and tuning your web application to honor the user's OS theme preference provides a better user experience. The CSS prefers-color-scheme media feature makes this easy to implement. Here's how to leverage it:

src/app.html
html

This short HTML code does a lot of things. First, the theme-color meta tags adapt the browser's UI (e.g., address bar) to match the theme. prefers-color-scheme is used to set different colors for light and dark modes. Then, in the script, we first checks if the user has a saved theme preference in localStorage. If not, it checks the OS-level dark mode setting using window.matchMedia('(prefers-color-scheme: dark)'). It then applies the appropriate theme by adding or removing the dark class to the <html> element. This class is used to toggle CSS styles (e.g., using dark:bg-gray-900 in the <body> class). Finally, it listens for changes in the OS-level dark mode setting and updates the theme accordingly (but only if the user hasn't explicitly set a preference). We also used the <body> element to set the background and text colors based on the presence of the dark class using Tailwind CSS classes.

Before we proceed to creating the authentication/login page, let's make a simple ThemeSwitcher.svelte component:

src/lib/components/reusables/ThemeSwitcher.svelte
html

Since this app will be fully powered by Svelte 5, we are using the $props() rune to accept any attributes passed to the component as props, and the spread operator helps expand these attributes. We also declared a reactive variable with the $state rune, and it gets updated in the $effect rune and in the toggleTheme function. The $effect rune runs once on component initialization and whenever its dependencies change. In this case, it checks if the dark class is present on the <html> element and updates the isDark state accordingly. This ensures the component's initial state matches the current theme. As for the toggleTheme function, it gets called when the button is clicked and toggles the isDark state, saves the selected theme ("dark" or "light") to localStorage, and toggles the dark class on the <html> element. The <button> element calls the toggleTheme function on click and passes any additional props to the button. Inside the button, the {#if isDark}...{:else}...{/if} block conditionally renders either the Sun or Moon component based on the isDark state.

Note: Events are properties in Svelte 5

The on: directive was used in Svelte 4 to attach events to HTML elements. However, Svelte 5 changed that narrative by being more natural with HTML, which sees events as simply properties.

The sun and moon icons are simply svgs that have become svelte components:

src/lib/components/icons/Sun.svelte
html
src/lib/components/icons/Moon.svelte
html

This is a nifty way to make svgs flexible as you can pass styles are other attributes and they will be reflected.

Now, let's see what the login page will look like:

Authentication page dark

Authentication page light

src/routes/finanalyzer/auth/login/+page.svelte
html

Though this code seems long, the only really important part is:

html

These are just <a> elements whose URLs link directly to the API endpoints (Google yet to be implemented). The BASE_API_URI is exported by src/lib/utils/constants.ts:

src/lib/utils/constants.ts
ts

It necessitates that you set either VITE_BASE_API_URI_DEV or VITE_BASE_API_URI_PRODas environment variables pointing to your server's base URL. In development, I set VITE_BASE_API_URI_DEV=http://localhost:3030/api in a .env file at the root of my SvelteKit project.

Tip: Environment Variables in Production

When deploying to a production server, configure the environment variable VITE_BASE_API_URI_PROD to point to your backend's production URL, including the /api path (e.g., https://your-production-url.com/api). Most deployment platforms offer a way to set these variables.

Back in our +page.svelte, we also retrieve the next page, which specifies where a user should be redirected after successful authentication. This is configured on a per-route basis. In addition to these core features, the page includes visual elements such as infinitely animating floating icons. These icons—AiNode (AI integration), FinChart (finance), and Calculator (analysis)—represent the application's key themes. Custom styles are applied to create a continuous vertical translation from 0px to -20px and back, combined with a simultaneous rotation from 0deg to 360deg. We could have used tailwind styles directly.

This article is getting long, so we will defer the user dashboard to the next article. However, there's a little safekeeping that needs to be done. Authenticated users should not be allowed to access this login page since they're already authenticated. To achieve this, we'll have a +page.server.ts file whose aim is to redirect any authenticated user from coming here:

src/routes/finanalyzer/auth/login/+page.server.ts
ts

See you in the next one!

Enjoyed this article? I'm a Software Engineer, Technical Writer and Technical Support Engineer actively seeking new opportunities, particularly in areas related to web security, finance, healthcare, and education. If you think my expertise aligns with your team's needs, let's chat! You can find me on LinkedIn and X. I am also an email away.

If you found this article valuable, consider sharing it with your network to help spread the knowledge!