How To Generate Types And Use API Rules In Pocketbase
Do you want to learn how to generate types for your Pocketbase collections and how to protect them using API rules? In this post, we will do this in preparation for the next post, where we will build all the components of a CRUD application.
This is the fifth part of my free Pocketbase course, check the introduction and a list of all posts here.
- Update collections with API access rules in Pocketbase
- Create Pocketbase types
- Create application header
- Load the Pocketbase user everywhere in the app
- Conclusion
Update collections with API access rules in Pocketbase
First we will do some updates to the collections in our backend. This means start your pocketbase instance (./pocketbase serve
), go to http://127.0.0.1:8090/_ and open the expenses collection.
In the collection click on Edit collection, add a new field of type relation and then call it user, select the users collection and use type single. We might need to update the type later on if we want to share expenses with others.
Next, we will set some access rules for our categories and expenses. With the access rules, we define which user or also not user can access what data. In our case, we want any user to create new expenses, but we only want that the user can access his or her own expenses.
Meaning we will create the following API rules under the edit tab API rules:
- List rule: @request.auth.id = user.id
- View rule: @request.auth.id = user.id
- Create rule: @request.auth.id = “”
- Update rule: @request.auth.id = user.id
- Delete rule: @request.auth.id = user.id
The rules basically mean is the requestors id the same as the expenses user id, or in case of create does a auth id exist. With this, we protect our expenses, and even with a basic select, the users are only able to see their own expenses.
Create Pocketbase types
Next up we will create types for our collections itself. The pocketbase client in SvelteKit already provides types, but only for things like the users collection that are always there.
Need help or want to share feedback? Join my discord community!
For us to also have types for our self-generated collections, we will use the pocketbase-typegen
package. We install it with pnpm add -D pocketbase-typegen
. Then we create a new directory to store our types called src/lib/types
. To actually generate the types, we will add a new script to our package.json
. The script will define the output directory and which database to use for the type generation.
"typegen": "pocketbase-typegen --out ./src/lib/types/pocketbase.d.ts --db ../backend/pb_data/data.db"
And then, finally, to generate the types, we will run pnpm typegen
. To then use the types in the application, we can, for example, use ExpensesRecord
to type a variable or use the type in our requests to pocketbase. You will see exactly how it works in another part.
If this guide is helpful to you and you like what I do, please support me with a coffee!
Create application header
Now we will create the header of our application. This will be visible in all parts of the app besides the landing page. To do so, we will create a new route group to scope the layout. A route group is not visible inside the actual URL, it is only to order your routes inside the editor.
We will call the group src/routes/(app)
and then move the dashboard route inside of it. Additionally, we will create a new +layout.svelte
file for the header. Inside we will create a new Skeleton app shell. Inside of it, we will use the header slot with a Skeleton app bar. The app bar consists of our logo and a profile image with a dropdown when clicking on it. Inside the dropdown the user is able to navigate to a not yet created settings page and to logout.
<script lang="ts">
import { AppBar, AppShell } from '@skeletonlabs/skeleton';
import type { LayoutData } from './$types';
export let data: LayoutData;
let profileMenuVisible = false;
</script>
<AppShell>
<svelte:fragment slot="header">
<AppBar slotTrail="relative">
<svelte:fragment slot="lead">💵</svelte:fragment>
<svelte:fragment slot="trail">
<button on:click={() => profileMenuVisible = true}>
<span class="sr-only">Your profile</span>
<img
class="h-8 w-8 rounded-full bg-white"
src="{'https://ui-avatars.com/api/?name=' + data.user.email}"
alt=""
/>
</button>
<div
class="origin-top-right absolute top-full right-0 mt-2 w-48 btn-group-vertical variant-filled"
role="menu"
aria-orientation="vertical"
aria-labelledby="user-menu"
style="display: {profileMenuVisible ? 'block' : 'none'}"
>
<a
href="/settings"
role="menuitem"
class="w-full"
>
Settings
</a>
<form action="/login?/logout" method="post" class="w-full">
<button
type="submit"
role="menuitem"
class="w-full"
>
Log Out
</button>
</form>
</div>
</svelte:fragment>
</AppBar>
</svelte:fragment>
<slot />
</AppShell>
Load the Pocketbase user everywhere in the app
Next, we will make the user available everywhere in our route group, called (app)
. Currently, the user is only available inside the dashboard, but we will need him or her everywhere later on. For example, if we want to display a profile image in the app bar or create a dedicated settings screen.
To do so, we will create src/routes/+layout.server.ts
and load the user in here.
import { serializeNonPOJOs } from '$lib/utils';
import type { LayoutServerLoad } from './$types';
export const load = (async ({locals}) => {
const user = locals.pb.authStore.model;
return {user: serializeNonPOJOs(user)};
}) satisfies LayoutServerLoad;
With that, it is available in all routes below this one. This also means we can now remove the user loading from the +page.server.ts
inside the dashboard
route.
You can also access the current state of the project in this repository and the branch called “course-5”.
Conclusion
In this post, you learned how to generate types for your Pocketbase collections and how to protect them with API rules. In the next post those will have the benefit of restricting which user can see which expenses. Additionally, we made the user available everywhere and added a header to our app layout.
I hope you liked the course so far. See you in the next part!
And as always, if you have any questions… ask! 😀
In case you have any questions, feel free to ask them, and if you want to stay up to date with all my posts, consider subscribing to my monthly newsletter!
[convertkit form=2303042]