This chapter completes the front-end functionality to create, read, update, and delete (CRUD) shop data in Fauna.
Create a new page to add new stores. Create a file src/routes/store/new.svelte
and add the following code. On form submit, you create a new store for the logged-in user.
<script lang="js">
import Cookies from 'js-cookie';
import { setClient, mutation } from '@urql/svelte';
import { clientWithCookieSession } from '../../client';
import { goto } from '$app/navigation';
let cookies = Cookies.get('fauna-session');
if(cookies) {
const { secret, email } = JSON.parse(cookies);
console.log('secret', secret);
setClient(clientWithCookieSession(secret));
}
const newStore = mutation({
query: `
mutation CreateNewStore(
$name: String!
$email: String!
$ownerId: ID!
$categories: [String]
$paymentMethods: [String]
) {
createStore(data: {
email: $email,
name: $name,
owner: {
connect: $ownerId
},
categories: $categories,
paymentMethods: $paymentMethods
}) {
_id
name
}
}
`,
});
async function onSubmit(e) {
const formData = new FormData(e.target);
const data = {};
for (let field of formData) {
const [key, value] = field;
data[key] = value;
}
const { email, name, paymentMethods, categories } = data;
console.log('datadata ', categories.split(','));
try {
const { ownerId } = JSON.parse(cookies);
if(!ownerId) {
alert('You must be logged in to create a post');
return;
}
console.log('--->', ownerId);
const resp = await newStore({
name,
email,
categories: categories.split(','),
paymentMethods: paymentMethods.split(','),
ownerId
});
console.log('resp', resp);
if(resp.data.createStore) {
alert('Store created successfully')
goto('/')
}
} catch (error) {
console.log(error);
}
}
</script>
<div class="uk-container">
<h3>Create a new store</h3>
{#if !cookies}
<p class="login-promt">You must be logged in to create a post</p>
{/if}
<form on:submit|preventDefault={onSubmit} >
<div class="input-blocks">
<label for="name">Store Name</label>
<input
class="uk-input"
type="text"
name="name"
value=""
/>
</div>
<div class="input-blocks">
<label for="email">Email</label>
<input
class="uk-input"
type="text"
name="email"
value=""
/>
</div>
<div class="input-blocks">
<label for="paymentMethods">Payment methods (seperated by commas)</label>
<input
class="uk-input"
type="text"
name="paymentMethods"
value=""
/>
</div>
<div class="input-blocks">
<label for="categories">Categories (seperated by commas)</label>
<input
class="uk-input"
type="text"
name="categories"
value=""
/>
</div>
<button class="uk-input btn" type="submit">Submit</button>
</form>
</div>
<style>
.input-blocks {
display: flex;
flex-direction: column;
max-width: 300px;
margin-bottom: 1em;
}
.login-promt {
color: coral;
}
.btn {
max-width: 300px;
}
</style>
Create a new page to show each store. Create a new file src/routes/store/[id].svelte
and add the following code. Notice in the following code findStoreByID
is used to retrieve store information.
<script lang="js">
import { operationStore, query, setClient } from '@urql/svelte';
import { page } from '$app/stores';
import Cookies from 'js-cookie';
import {clientWithCookieSession} from '../../client'
let cookies = Cookies.get('fauna-session');
if(cookies) {
const { secret } = JSON.parse(cookies);
console.log('secret', secret);
setClient(clientWithCookieSession(secret));
}
const currentStore = operationStore(`
query GetStore($id: ID!) {
findStoreByID(id: $id) {
_id
name
email
paymentMethods
categories
owner {
name
}
}
}
`,
{ id: $page.params.id },
{ requestPolicy: 'network-only' }
)
query(currentStore)
export let store = null;
currentStore.subscribe(({data}) => {
if(data) {
store = data.findStoreByID;
}
})
</script>
{#if !cookies}
<p class="login-promt">You must be logged in to create a post</p>
{/if}
{#if $currentStore.fetching}
<p>Loading...</p>
{:else}
<div class="uk-container">
<div class="uk-card uk-card-default card">
<h3>{$currentStore.data.findStoreByID.name}</h3>
<p><b>Categories</b>{$currentStore.data.findStoreByID.categories.join(',')}</p>
<p><b>Payment</b>{$currentStore.data.findStoreByID.paymentMethods.join(',')}</p>
</div>
</div>
{/if}
<style>
.login-promt {
color: coral;
}
.card {
padding: 40px;
}
</style>
Create a new form component to edit stores. Create a new file src/lib/EditStore.svelte
and add the following code.
<script>
import Cookies from 'js-cookie';
import { setClient, mutation } from '@urql/svelte';
import { clientWithCookieSession } from '../client';
import { goto } from '$app/navigation';
let userSession = Cookies.get('fauna-session');
if(userSession) {
const { secret } = JSON.parse(userSession);
setClient(clientWithCookieSession(secret));
}
const updateStore = mutation({
query: `
mutation UpdateStore(
$id: ID!,
$name: String!,
$email: String!,
$categories: [String!],
$paymentMethods: [String!])
{
updateStore(id: $id, data: {
name: $name,
email: $email,
categories: $categories,
paymentMethods: $paymentMethods
}) {
_id
}
}
`
})
export let selectedStore;
let isEdit = false;
let name = '';
let email = '';
let categories = [''];
let paymentMethods = [''];
let errorMessage = '';
function toggleEdit() {
isEdit = !isEdit;
if(isEdit) {
name = selectedStore.name;
email = selectedStore.email;
categories = selectedStore.categories.join(',');
paymentMethods = selectedStore.paymentMethods.join(',');
}
}
async function onSubmit(e) {
const response = await updateStore({
id: selectedStore._id,
name,
email,
categories,
paymentMethods
})
console.log('updatedPost', response);
const { data, error } = response;
if(error) {
errorMessage = error.message;
}
if(data) {
alert('Store updated');
goto(`/`);
}
}
</script>
{#if isEdit}
<div class="uk-card uk-card-default wrap">
<h5>Edit Store</h5>
{#if errorMessage}
<p class="error">{errorMessage}</p>
{/if}
<form on:submit|preventDefault={onSubmit} >
<div class="input-blocks">
<label for="name">Store Name</label>
<input
class="uk-input"
type="text"
name="name"
bind:value={name}
/>
</div>
<div class="input-blocks">
<label for="email">Email</label>
<input
class="uk-input"
type="text"
name="email"
bind:value={email}
/>
</div>
<div class="input-blocks">
<label for="paymentMethods">Payment methods (seperated by commas)</label>
<input
class="uk-input"
type="text"
name="paymentMethods"
bind:value={paymentMethods}
/>
</div>
<div class="input-blocks">
<label for="categories">Categories (seperated by commas)</label>
<input
class="uk-input"
type="text"
name="categories"
bind:value={categories}
/>
</div>
<button class="update uk-button" type="submit" disabled={!userSession}>Update</button>
</form>
</div>
{/if}
<button on:click={toggleEdit} class="update uk-button" disabled={!userSession}>Edit</button>
<style>
.error {
color: coral;
}
.update {
margin-bottom: 10px;
margin-top: 10px;
}
.wrap {
margin-top: 20px;
padding: 40px;
}
</style>
Next, add this component to view store page. Make the following changes to src/routes/store/[id].svelte
file.
// src/routes/store/[id].svelte
<script lang="js">
...
import EditStore from '$lib/EditStore.svelte';
...
<div class="uk-container">
...
<EditStore selectedStore={store}/>
</div>
Ideally, you only allow the data owner to make any changes. For instance, in this application, only the store owner should update their store information. Fauna allows fine-grained access control. You can set access rules (predicates) so that users can only modify their data and not others.
Head over to the Fauna dashboard. Navigate to Security > Roles > AuthRole. Expand the Store collection. Choose the code icon (</>
) underneath Write and add the following rules.
Lambda(
["oldData", "newData"],
And(
Equals(Identity(), Select(["data", "owner"], Var("oldData"))),
Equals(
Select(["data", "owner"], Var("oldData")),
Select(["data", "owner"], Var("newData"))
)
)
)
The rule defines that only a store’s owner can update that store.
Similarly, add the following predicate for Delete. This rule defines that only a store’s owner can delete a store.
Lambda("ref", Equals(
Identity(), // logged in user
Select(["data", "owner"], Get(Var("ref")))
))
Add a predicate for Create as well. The following rule ensures that logged-in users can add store and owner id is associated with a store when it is created.
Lambda("values", Equals(Identity(), Select(["data", "owner"], Var("values"))))
Select Save to save your access control rules. Now, users can only modify or delete their data from your front end.
In this chapter, you learned how to apply fine-grained access control to your data. In the next chapter, you learn more about custom resolvers and Fauna Query Language (FQL).
📙 Get the final code for this section here