:: krowemoh

Friday | 27 DEC 2024
Posts Links Other About Now

previous
next

SvelteKit Cheatsheet

2023-01-14
cheatsheet

Instructions to get started with sveltekit.

pnpm create svelte@latest project-name
cd project-name
pnpm install
pnpm install -D tailwindcss postcss autoprefixer svelte-preprocess
pnpm install -D @tailwindcss/forms
pnpm install @sveltejs/adapter-node@next
pnpm install dotenv
pnpm install svelte-kit-cookie-session
pnpm install better-sqlite3
pnpm install bcrypt

Now we have sveltekit, tailwind and the node adapter installed.

Update vite.config.js to set up the host and port to run the application on.

import { sveltekit } from '@sveltejs/kit/vite';

const config = {
    server: {
        host: '0.0.0.0',
        port: 7091,
    },
	plugins: [sveltekit()]
};

export default config;

Now update svelte.config.js:

import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/kit/vite';
import preprocess from "svelte-preprocess";

/** @type {import('@sveltejs/kit').Config} */
const config = {
    preprocess: vitePreprocess(),
    kit: {
        adapter: adapter({ out: 'out' })
    },
    preprocess: [
        preprocess({
            postcss: true,
        }),
    ],
};

export default config;

Now we can create the tailwind config file, tailwind.config.cjs:

npx tailwindcss init tailwind.config.cjs -p

Then update the file with:

/** @type {import('tailwindcss').Config} */
export default {
    content: ['./src/**/*.{html,js,svelte,ts}'],
    theme: {
        extend: {},
    },
    plugins: [
        require('@tailwindcss/forms'),
    ],
}

Update src/app.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

Add a global stylesheet:

touch src/global.css

Update src/routes/+layout.svelte:

<script>
    import "../app.css";
    import "../global.css";
</script>

<slot />

Update src/routes/+page.svelte:

<h1 class="text-3xl font-bold underline">
  Hello world!
</h1>

Start the dev server:

npm run dev

We should now see the home page with the hello world styled.

Create .env to hold the secret, host and port. The host and port will be used by pm2 when going live but during development it will be from the vite config file.

SECRET="super-secret-key"
HOST=127.0.0.1
PORT=6109
BODY_SIZE_LIMIT=0

Create a database folder in the current directory:

mkdir db

Create src/hooks.server.js to hold the database and sessions logic:

import * as dotenv from 'dotenv'
dotenv.config();

import { redirect, error } from '@sveltejs/kit';
import { handleSession } from '../node_modules/svelte-kit-cookie-session';

import Database from 'better-sqlite3';
const db = new Database('db/kk.db');

export const handle = handleSession(
    {
        secret: process.env.SECRET
    }, 
    ({ event, resolve }) => {
        const path = event.url.pathname;
        const session = event.locals.session.data;

        if (path.startsWith("/admin") && !session.loggedIn) {
            throw redirect(302, "/");
        }

        event.locals.db = db;
        return resolve(event);
    }
);

This can then be used in a route, src/routes/+page.server.js like:

export async function load({ locals }) {
    const db = locals.db;
    let items = db.prepare(`SELECT * FROM news`).all();

    return {
        items
    }
}

A user can be logged in, this would be +page.sever.js:

import { redirect } from '@sveltejs/kit';
import bcrypt from "bcrypt";
 
export const actions = {
    default: async ({ locals, request }) => {
        const db = locals.db;

        const values = await request.formData();

        const username = values.get('username');
        const password = values.get('password');

        const row = db.prepare('SELECT * FROM users WHERE username = ?').get(username);

        if (!row) {
            return { error: 'Invalid username.' };
        }

        let valid = await bcrypt.compare(password, row.password);
        if (!valid) {
            return { error: 'Invalid password.' };
        }

        await locals.session.set({ loggedIn: true, username: username });
        throw redirect(302,"/");
    }
};

The frontend login page:

<script>
    export let form;
</script>

{#if form !== null }
    {form.error}
{/if}
 
<form method="POST" action="">
    <label for="username">Username</label>
    <input type="text" name="username">
    
    <label for="password">Password</label>
    <input type="password" name="password">
    
    <button>Login</button>
</form>