SvelteKit serves as our chosen frontend framework or “BFF” (Backend For Frontend). This powerful framework facilitates seamless development of efficient and interactive user interfaces. Leveraging its component-based architecture, SvelteKit allows us to create dynamic web applications with minimal boilerplate code.
Pocketbase plays a crucial role as a single, compact binary file serving as our “BaaS” (Backend as a Service) for the BFF. This innovative solution simplifies backend operations by providing essential functionalities without the need for extensive infrastructure setup. With Pocketbase, we can efficiently manage databases, authentication, and other backend services, allowing us to focus more on building engaging user experiences.
Vercel stands out as our preferred platform for deploying our landing page. Its zero-config requirement for SvelteKit web apps streamlines the deployment process, allowing us to effortlessly showcase our application to the world. Additionally, Vercel’s reliability and scalability make it an excellent choice for hosting our landing page. While Vercel handles the production environment, we also implement a redundant deployment strategy by hosting a version on our homelab, ensuring flexibility and control over our deployment infrastructure.
Our main UI Toolkit, allows us to create accessible interface, with it comes one of its dependency which is floating-ui/dom and tailwindcss. floating-ui/dom is only required if you need the Popup elements, given that tailwind is required by skeleton components extends tailwindscss utility-first classes.
Installation steps are as follows after creating a fresh sveltekit project, you just need to install skeleton , tailwind and floating-ui/dom with the following commands
Installing Skeleton
Install Tailwind with the following command
svelte-add is a handy tool to add more integrations in a svelte or sveltekit project, also supports bootstrap, bulma,mdsvex, imagetools and more! check them out too!
after installing all of that skeleton requires you to modify the tailwind configuration which is located at the root of your project tailwind.config.[ts|js|cjs] we used typescript in our project so we use the .ts file extension here is what our configuration looked like!
These two proved invaluable for dynamically injecting classes into our components based on a given state. This implementation provides a convenient and efficient way to manage dynamic class injections, improving the flexibility and maintainability of our codebase.
prettier-plugin-tailwindcss this sorts our tailwind classes, since we are using prettier by default why not extend it by sorting the classes too!
We’ve incorporated Skeleton’s layouting component, [<AppShell />](https://www.skeleton.dev/components/app-shell) into our design. This integration necessitated updates to both app.html and app.postcss . The final output for these files now resembles the following, Feel free to explore the provided link for a deeper understanding of the features and capabilities offered by Skeleton’s AppShell component.
app.html
app.postcss
Expanding on our root layout, we’ve introduced some significant enhancements. In this updated layout, we incorporate the Inter font and app.postcss Furthermore, we initialize the @floating-ui/dom utilities using stores provided by skeleton. Additionally, we initiate the use of sveltekit-view-transition by @paoloricciuti.
The inclusion of Skeleton adds a layer of utility components and global stores that we initialize at this stage, making them accessible across our various components and pages. Noteworthy additions include:
For detailed information on these utilities, refer to the Skeleton documentation. This refined root layout sets the stage for a more sophisticated and feature-rich user interface throughout our application.
+layout.svelte(click me for code preview)
We’ve implemented group, a remarkable feature that empowers us to break free from a uniform layout structure. This allows us to employ distinct layouts for specific pages throughout our application, providing a versatile and tailored user experience.
Always prioritize security by validating user inputs and never solely rely on client-side data. This crucial practice is exemplified through the use of two powerful tools: sveltekit-superforms created by sveltekit-superforms created by ciscoheat and zod created by collinhacks. These libraries work in tandem to reinforce the integrity of user input, ensuring a robust and reliable validation process.
Zod stands out due to its versatility and adaptability. For instance, in working with the requiredString type, I found that I could leverage Zod’s built-in methods to generate accurate error messages based on the specified field name. This capability proves particularly useful in scenarios involving fields such as firstName and lastName. Additionally, I extended this functionality to the username field by incorporating actions like trimming, converting to lowercase, and implementing a regex pattern to ensure field validity. It’s a niche feature that enhances the overall flexibility and customization provided by Zod.
Carta is a new lightweight, fast and extensible markdown editor created by BearToCode, works like a charm in SvelteKit bonus is that it supports Server Side Rendering(more on this later) check out more of their examples here
We used most of the available plugins of carta available e.g Attachment, Code, emoji and slash. where most of them are useful in editing our profile page.
In action with plugins
Carta plugins specially the attachment part works well with our pocketbase api, where it needs an endpoint that returns a url for the uploaded attachment.
userdetails-form.svelte(click me for code preview)
A featured profile can be previewed at the given link, a co member of the community straightly copied his README.md at github although its not perfectly aligned(we will fix it in future revisions) you have to admire how simple it is implemented on sveltekit and carta. code snippets are located at the pocketbase section.
Pocketbase
SvelteKit seamlessly integrates with various APIs, but when it comes to a unique solution, Pocketbase stands out. What sets Pocketbase apart is its distinctive blend of a Svelte-built admin UI, backed by Go. This choice was deliberate, driven by the rich features it brings to our stack, including:
Realtime Database: embedded sqlite with realtime subscriptions through sdk and rest api.
Authentication: wether its a classic email/username login or oauth2 integrations without worry.
File Storage: store files within the filesystem or to an s3 compatible storage. Explore these features in detail by referring to the PocketBase documentation,
Upon initial inspection, one might question the readiness of pocketbase created by @ganigeorgiev for production use, it is not! he created it intentionally for his other project presentator a design presentation and collaboration platform. One noteworthy aspect is that Pocketbase, designed intentionally as a single binary with SQLite embedded underneath, allows us to opt for breaking changes that align with the version of the SDK we use. Presently, we are utilizing v.20.0, deployed at pockethost an open source project created by @benallfree. Pockethost serves as a cloud hosting platform tailored for Pocketbase, contributing to the robustness and scalability of our chosen stack.
We also hooked our pocketbase file storage to Cloudflare R2 a global object storage by cloudflare with 10gb free storage! all our media files are stored there even the ones that come from carta attachment plugin.
This module, strategically placed as a Server-only module ensures that sensitive information such as credentials is not bundled with the client-side code. This is a security measure provided by SvelteKit, allowing us to safeguard our secrets and maintain a secure development environment.
Usage of the global instance, we can use the pocketbase instance directly on our server side code but we went away and further abstracted the queries
Svelte includes a logic block #await, allowing us to branch on Promise states, pending, fulfilled or rejected. this is comparable to jsx’s(react, solidjs) or vue(experimental) <Suspense /> component.
Most of the code implementation is at EventPaginator component where the input on the search is debounced and appended on the url as query parameters, it searches for the events collection for keywords of the desired event , given that the keywords exist on events.
Data Submission
In the sections aboved, i have written about us utilizing zod binded in our forms and data types from pocketbase to forms, in all of our forms we used the best feature i think that svelte has to offer which is Form Actions,
The superform/client module allows us to provide the form data and bind the value using the bind:value directive from svelte, including the erros and constraints defined in the schema we wrote using zod.
We can also write the endpoint in the same +page.server.ts on the same route but we prefer to declare all our action endpoints at the /api/actions route, form-action api requires method="POST" if you use method="post" it will not work. use:enhance is the key to making the form actions work smoothly, hydrating the data based on the page load and returned data from the action endpoint.
Authentication
Pocketbase Authentication uses JWT, by default admin requests can access its entire API, that is why our global instance is used as server-only-module, with sveltekit server hooks think of it like a middleware or an interceptor, in this case it intercepts all the requests as we have not filtered on a per route basis.
Locals can only be accessed by the server side modules e.g *.server.{ts|js} files, in our root(default) +layout+server.ts we have the following
The following implementation is not safe there is an existing open issue at github about how to safely implement it on child routes check issue here, we need to await the parent data on child routes. e.g:
On all our server action that needs authentication we also access the locals.user context which allows us to easily grab the user’s identity on a secured route / action.
Migrations and Backup
Pocketbase migration is easy as pasting the json file from development to production. check out the files here, it also supports backing up and importing data in its admin ui.
Vercel
Deployment
We did not need to instal lthe vercel adapter as we didn’t feel the need to use most of what their platform offers, e.g analytics, db, kv etc …
Vercel is as straightforward as other cloud platforms we only need to link the repository, set the domains and environment variables, we can do further by auto previews on each PR made, but our repository is on an organization the actual linked repository is at one of our personal github accounts.
Self Hosting and Development
Self Hosting and Development
Development
Install dependencies
Download pocketbase runtime depending on your OS. paste it on the pb directory;
You don’t need to touch that directory.
After downloading try running pnpm run dev:api
If successful initialize your instance by going at http://localhost:8090/_/
Copy the .env.example to .env.development
Whatever you put in the installer setup should also be in your .env.development credentials
Use the sample data just upload the sampledata.zip located at pb directory to your local pb instance at Backups
You local data that you have will stay with you as pb/pb_data is gitignored This will keep our production data pristine
You can now install node dependencies using pnpm install
On a seperate terminal run the ff commands on each one pnpm dev:api and pnpm dev:web
In case you are lazy run both using pnpm dev but some systems might not support closing each child process.