Skip to content

Instantly share code, notes, and snippets.

@nickludlam
Last active February 19, 2024 17:54
Show Gist options
  • Save nickludlam/fb890c316625af3ea0da5740d0d33242 to your computer and use it in GitHub Desktop.
Save nickludlam/fb890c316625af3ea0da5740d0d33242 to your computer and use it in GitHub Desktop.
A Rails guy learns SvelteKit

My experience coming to SvelteKit from Rails and Ruby

This is specifically because my experience in webdev is very informed by Ruby on Rails. I've done plenty of work with strongly typed languages, but never webdev. My experience is also from 2018 and older, so there may well be modern solutions that achieve parity with the clever things I'm finding in SvelteKit that I feel were not as present in Rails work. Don't at be bro!

Strong types are extraordinary at catching inter module communication issues. Any time you’re putting polymorphic messages into Redis, etc. Also it's very nice having a strongly typed, schema-aware database client. The auto-completion is very useful.

Idea: make some playgrounds to show examples live

Frustrations

Sometimes the VSCode plugins don’t update when you push prisma schema changes So many VSCode plugins! So many code warnings. Most things seem to have a Typescript option though. Sometimes you have to faff around with type definitions, and that's pain. Interpreting objects using ‘as’ doesn’t necessarily do what I thought! There is no enforcement in Typescript land, as it disappears at runtime!

It’s not a kitchen sink framework like Rails or Django, so you need to do a lot of discovery for the best ecosystem extensions to use. UI frameworks, form frameworks, validation, auth, database ORM, background jobs. Gah!

Swift with Codables is better than Svelte + Zod, as it's just part of the language. More hoops to jump through to perform adequate validation. There is however a LOT of discussion online so it's easy to find discussion around things you need to solve.

Async/Await is just cool. Very elegant.

Svelte is great. Where Rails is good at the core, but gets sketchy with hotwire, it’s the inverse for Svelte. You stay in one language front and back. There's no swapping of languages, only swapping of execution context. I think it's very slick.

Swift observables and data binding is clever, but I've seen it misused for things like tracking server connections to URLs. Maybe not misused, but I wouldn't use it for everything, and people reach for it more than I would. Tasks like tracking the number of people connecting to a specific URL. It just won't scale unless you're only ever on one single server host. There's perhaps a lack of enterprise or scale thinking with much of the svelte examples. They are small-time.

Not at all clear how to set up VSCode with the terminal running the dev server, dev syntax checker, etc. I need to make a specific section about this. Get THESE extensions, add THESE npm run ... commands to package.json, configure VSCode to show you THIS view. No pluralisation functionality in the template language like Rails ActionView. I'd LOVE a nice way to display 'Deleted 3 rows', instead of the crass 'Deleted 3 row(s)', or worse, entity/entities.

Promise programming gets complex where you need to handle promise rejections. Needs a section.

Patterns like establishing type safety that works both at compile time and runtime is not obvious. Be aware of things like interface being TS only, and by the time your code runs, the interface has vanished, it's NOT runtime.

Understand that export let data; is the way that incoming data comes into the frontend. This is the equivalent of any template @variables you provide. Only here, they are kept as variables, so you're doing more of a binding operation. You also have events on the client side. onMount and onDestroy. Need to explain page lifecycle.

Stores

I wish I had paid more attention here, as the syntax in Svelte 4 is very terse, and very very magic. I should map it to Swift UI ObservableObject / StateObject macros. The $ dereference operator isn't like old jQuery or whatever. I initially thought it was some kind of scoping operator! I should use my form snap slider example to show it off. Svelte 5 Runes makes this more explicit, so it will be better for newcomers.

Testing

The vitest setup out the box was extremely minimal, and I wish it did more. I had to:

  • Write my own db helpers to push, clear and remove the db via vitest hooks
  • Configure setupFiles: ["./src/tests/setup.ts"] to point to a file where I can define beforeAll(), beforeEach(), afterAll() and clean up the db.
  • Also use globalSetup to define a beforeAll() that really does run once per test invocation, because setupFiles runs before and after each test file. I was initially mistaken in thinking I only needed setupFiles.
  • I create and nuke (rm) the DB after each test to keep things tidy
  • I had to disable parallelism with fileParallelism: false in the vite.config.ts file to prevent DB trampling, because it (not unreasonably) wants to run things in parallel.

dotenv vars

Coming from Rails I really wanted the equivalent of RAILS_ENV=test, which was the highest level control of what mode you were running in.

With dotenv you multiple levels in the hierarchy. You can have .env, .env.local, .env.production. But you also get to override them with pre-existing environment variables done at the process level (naturally).

Vite ALSO has its own way of doing dotenv interpretation. https://vitejs.dev/guide/env-and-mode

Vite recommends: To prevent accidentally leaking env variables to the client, only variables prefixed with VITE_ are exposed to your Vite-processed code. e.g. for the following env variables: This is also to help with intellisense.

Svelte THEN layers on https://kit.svelte.dev/docs/configuration#env Which says $env/static/public vars should start with PUBLIC_ and privatePrefix defaults to ..... empty string!! HAHAHA

IDE

For someone who is used to running their own long running processes in a customised iTerm2, the idea of invoking the dev server inside VSCode felt weird, but it's useful when error messages come up from run dev or check:watch as it turns a file and line number into a hyperlink which takes you to that place in the document immediately, no change of focus.

Promise rejctions

For any long running process like subscription functionality, you must remember to use return to pass back a rejection function. That way, when you navigate away from the page, anything done in onMount can be stopped gracefully.

onMount(() => {
  // Must return the result of this subscribe as a reject function
  return flash.subscribe(($flash) => {
  ...
  }
})

You will get cryptic messages like: Unhandled Promise Rejection: TypeError: Importing a module script failed.

Assignment

Don't accidentally do this:

export let data: PageData;
let ct = data.myCoolThing;

As a way to prevent you from needing to type data.myCoolThing.someProp in the template over and over, and you can just do ct.someProp. It does work to begin with, but it's not correct.

This will decouple your ct var from being an observable state changing object like data is. You will start noticing funny things like programatic use of goto() to move people around your site will result in pages not rendering with up-to-date state. Even though you're on /page/2 you'll still be seeing data for /page/1 unless you do a full browser reload. Svelte wants to keep full page refreshes to a minimum.

Instead, do this:

export let data: PageData;
$: ct = data.myCoolThing; // or const ct = $derived(data.myCoolThing) in Svelte5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment