Add the PocketBase backend to a SvelteKit project that has none — one scaffolded from the static template, or one the backend was removed from. Installs @velastack/pocketbase, pocketbase-sveltekit and @sveltejs/adapter-node, composes handlePocketbase into src/hooks.server.ts alongside the handles already there, scaffolds the data directory the database runs from with fixtures, hooks and seeds inside, and writes the test/setup.ts that gives server tests their PocketBase clients. Run through the CLI it also asks for an admin email and password, creates that superuser and records it in .env, so the database is usable the moment vela dev starts it. Everything else that needs a database — auth, the API, scaffolds, seeds and fixtures — builds on this.
Sample data for development
# Hooks
Add \*.pb.js hooks here to extend PocketBase: https://pocketbase.io/docs/js-overview/
Essential system-level data (e.g., roles, locales)
import {
POCKETBASE_URL,
POCKETBASE_SUPERUSER_EMAIL,
POCKETBASE_SUPERUSER_PASSWORD,
} from "$app/env/private";
import { handlePocketbase } from "@velastack/pocketbase";
export const handle = handlePocketbase({
pocketbaseUrl: POCKETBASE_URL,
superuserEmail: POCKETBASE_SUPERUSER_EMAIL,
superuserPassword: POCKETBASE_SUPERUSER_PASSWORD,
});
import { defineEnvVars } from "@sveltejs/kit/env";
/**
* Every environment variable the app reads. SvelteKit exposes only what is
* declared here, through `$app/env/private` (and `$app/env/public` for
* `public: true`).
*
* Each schema turns a missing value into `''`, so the app builds and starts
* with an empty `.env`, and a default belongs at the call site as `X || fallback`.
*/
export const variables = defineEnvVars({
POCKETBASE_URL: {
schema: (value) => value ?? "",
description:
"Where PocketBase listens. `vela dev` and `vela deploy` set it.",
},
POCKETBASE_SUPERUSER_EMAIL: {
schema: (value) => value ?? "",
description:
"Superuser email the server signs in with for admin features and workflows.",
},
POCKETBASE_SUPERUSER_PASSWORD: {
schema: (value) => value ?? "",
description: "Password for POCKETBASE_SUPERUSER_EMAIL.",
},
});
import { beforeEach, afterEach, beforeAll } from "vitest";
import supertest, { type Agent } from "supertest";
import PocketBase from "pocketbase-sveltekit";
import type { TestContext } from "@velastack/pocketbase/testing";
const admin = new PocketBase(
process.env.POCKETBASE_URL!,
) as App.Locals["admin"];
const pb = new PocketBase(process.env.POCKETBASE_URL!) as App.Locals["pb"];
const testUserPassword = "password";
async function authenticateUser(agent: Agent, user: { email?: string }) {
// Logs in with the password, so it needs the email (made optional by
// enable-whatsapp, for accounts that sign in with a phone number).
if (!user.email) {
throw new Error("authenticateUser needs a user with an email");
}
await agent.post("/login").type("form").send({
type: "password",
email: user.email,
password: testUserPassword,
});
}
beforeAll(async () => {
await admin
// @ts-ignore
.collection("_superusers")
.authWithPassword(
process.env.POCKETBASE_SUPERUSER_EMAIL!,
process.env.POCKETBASE_SUPERUSER_PASSWORD!,
);
});
beforeEach(async (context: TestContext) => {
context.request = supertest(process.env.VITE_TEST_URL!);
context.agent = supertest.agent(
process.env.VITE_TEST_URL!,
) as TestContext["agent"];
context.admin = admin;
context.pb = pb;
const email = `test-${Math.random().toString(36).slice(2)}@example.com`;
context.user = await context.admin.collection("users").create({
email,
password: testUserPassword,
passwordConfirm: testUserPassword,
});
context.agent.authenticateUser = () =>
authenticateUser(context.agent, context.user);
await context.pb
.collection("users")
.authWithPassword(email, testUserPassword);
});
afterEach(async (context: TestContext) => {
await context.pb.authStore.clear();
await context.admin.collection("users").delete(context.user.id);
});
import { defineConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";
import { sveltekit } from "@sveltejs/kit/vite";
import adapter from "@sveltejs/adapter-node";
export default defineConfig({
plugins: [
tailwindcss(),
sveltekit({
compilerOptions: {
runes: ({ filename }) =>
filename.split(/[/\\]/).includes("node_modules") ? undefined : true,
},
adapter: adapter(),
// The public origin, which SvelteKit checks form posts against and uses
// for prerendered pages. `vela deploy` sets VELA_ORIGIN, and the value is
// baked in at build time. It is left unset for a deploy that serves more
// than one host, where each request's own origin is used instead.
...(process.env.VELA_ORIGIN
? { paths: { origin: process.env.VELA_ORIGIN } }
: {}),
}),
],
});