On a whim, I decided I want to create a news website to brush up my skills in
Svelte and Pocketbase. Most of this code hand-written and I’m telling you
whenever I use AI (for the code, not for article).
In the end, you’ll have the end repository you can use and self-host yourself.
Setting some goals#
The idea is for us to use G1(very famous news website
in brazil for reference as the starting point. I’ll try to replicate as much
features as possible in as little time as possible
Humble begginings#
First, let’s Setup a SvelteKit project (already convered it before).
but there are a couple differences:
I’m using mise as the version manager and using the
latest version of node.
I wanna use the latest version of yarn, becase it was apparently re-written
in RUST in its
much faster (normally, I would use bun for that).
Creating a project#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
| npx sv create
# options: 1. minimal,
npx sv create
Need to install the following packages:
[email protected]
Ok to proceed? (y) y
┌ Welcome to the Svelte CLI! (v0.12.1)
│
◇ Where would you like your project to be created?
│ ./
│
◇ Directory not empty. Continue?
│ Yes
│
◇ Which template would you like?
│ SvelteKit minimal
│
◇ Add type checking with TypeScript?
│ Yes, using TypeScript syntax
│
◇ What would you like to add to your project? (use arrow keys / space bar)
│ prettier, eslint, tailwindcss
│
◇ tailwindcss: Which plugins would you like to add?
│ none
│
◆ Project created
│
◆ Successfully setup add-ons: prettier, eslint, tailwindcss
│
◇ Which package manager do you want to install dependencies with?
│ yarn
│
│ To skip prompts next time, run:
● npx sv create --template minimal --types ts --add prettier eslint tailwindcss="plugins:none" --install yarn ./
│
◆ Successfully installed dependencies with yarn
│
◇ Successfully formatted modified files
│
◇ What's next? ───────────────────────────────╮
│ │
│ 📁 Project steps │
│ │
│ 1: yarn run dev --open │
│ │
│ To close the dev server, hit Ctrl-C │
│ │
│ Stuck? Visit us at https://svelte.dev/chat │
│ │
├──────────────────────────────────────────────╯
│
└ You're all set!
|
OpenCode Plugins#
I said I would keep the user of AI minimal in this project, right? Yes, but
even still we gotta leave this project “AI-Ready”.
My tool of choice is OpenCode. And luckly enough,
svelte has official support
for it.
I normally only use the plugin, not the MCP its a way to easily reference the
docs to AI without the need to copy/fetch the link, great stuff!
To configure, you just need to create an .opencode folder and put this JSON
in a file
named opencode.json.
1
2
3
4
5
| // opencode.json
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@sveltejs/opencode"]
}
|
Installing Pocketbase#
You can may refer to the official guide
for the latest up to date guide.
Let’s install Pocketbase and the EventSource (for realtime connection).
1
| yarn add pocketbase eventsource --save
|
Configuring Pocketbase with SvelteKit#
Our project will have authentication, and for that we need a way to check (at every
page if the user is authenticated).
We need a way to load the auth data BEFORE every request. There, we also
also would populate the Pocketbase object with the appropriate info of the
logged-in user, check cookies and eveything in between.
Although we can put this logic in a +page.server.ts, in the root layout,
wouldn’t be awesome if we had a “layout-independent” way of doing this?
Well, there is! It’s svelte “middleware-like” strucure, they are called “hooks”,
and are ran at every request, better yet, we can populate a global object over
there by using the locals variables (so we can access it at every page).
Fist off, let’s define the environment variables to make the API calls to Pocketbase dynamic.
1
| PB_API="http://127.0.0.1:8090
|
You need to run the project to generate the variables bindings we gonna use
in the next step.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| import { PB_API } from "$env/static/private";
import type { Handle } from "@sveltejs/kit";
import { sequence } from "@sveltejs/kit/hooks";
import Pocketbase from "pocketbase";
const pocketbase: Handle = async ({ event, resolve }) => {
event.locals.pb = new Pocketbase(PB_API);
const pbAuthCookie = event.cookies.get('pb_auth');
if (!pbAuthCookie) return await resolve(event);
event.locals.pb.authStore.loadFromCookie(pbAuthCookie);
try {
// if our user gets updated in the DB, ensure the user is using
// the latest data
if (event.locals.pb.authStore.isValid)
await event.locals.pb.collection('users').authRefresh();
} catch (_) {
event.locals.pb.authStore.clear();
}
// we must update the cookie with the latest data (including the 'clear' one)
event.cookies.set('pb_auth', event.locals.pb.authStore.exportToCookie(), {
path: '/',
secure: true,
sameSite: 'lax',
httpOnly: true
});
return await resolve(event);
}
export const handle = sequence(pocketbase);
|
We’re almost there. We just need to add the type locals, to have autocomplete.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // src/app.d.ts
import Pocketbase from "pocketbase";
// See https://svelte.dev/docs/kit/types#app.d.ts
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
interface Locals {
pb: Pocketbase
}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}
export { };
|
And we’re done! In the next post, we’ll handle authentication.
See ya!