Skip to main content

createMutation

API reference for creating mutation handlers

createMutation

Creates a mutation handler for modifying data. Mutations are used for create, update, and delete operations.

Import

		import { createMutation } from 'svelte-stache';
	

Signature

		function createMutation<Params, Result>(
	options: MutationOptions<Params, Result>
): (instanceOptions?: MutationInstanceOptions<Params, Result>) => Mutation<Params, Result>;
	

Options

Required Options

mutationFn

Type: (params: Params) => Promise<Result>

The async function that performs the mutation.

		createMutation({
	mutationFn: async (data: CreatePostInput) => {
		const res = await fetch('/api/posts', {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify(data)
		});
		return res.json();
	}
});
	

Optional Options

beforeMutate

Type: (params: Params) => void | Promise<void>

Called before the mutation executes. Useful for optimistic updates.

		createMutation({
	mutationFn: createPost,
	beforeMutate: (data) => {
		console.log('About to create post:', data.title);
	}
});
	

afterMutate

Type: (params: Params) => void | Promise<void>

Called after the mutation completes (success or failure). Useful for cleanup.

		createMutation({
	mutationFn: createPost,
	afterMutate: (data) => {
		console.log('Mutation completed for:', data.title);
	}
});
	

onSuccess

Type: (context: { params: Params; result: Result }) => void

Called when the mutation succeeds. Common use case: invalidate related queries.

		createMutation({
	mutationFn: createPost,
	onSuccess: ({ params, result }) => {
		console.log('Created post:', result.id);
		// Invalidate posts list to refetch
		invalidatePosts();
	}
});
	

onError

Type: (error: unknown) => void

Called when the mutation fails.

		createMutation({
	mutationFn: createPost,
	onError: (error) => {
		console.error('Failed to create post:', error);
		showToast('Failed to create post');
	}
});
	

Return Value

createMutation returns a factory function. Call it to create a mutation instance:

		const useCreatePost = createMutation({ ... });
 
// In a component:
const mutation = useCreatePost();
	

Instance Options

When creating a mutation instance, you can override or extend callbacks:

		const mutation = useCreatePost({
	onSuccess: ({ result }) => {
		// This runs IN ADDITION to the base onSuccess
		goto(`/posts/${result.id}`);
	},
	onError: (error) => {
		// This runs IN ADDITION to the base onError
		showErrorDialog(error);
	}
});
	

Mutation Object

The mutation instance with reactive properties and methods:

Properties

Property Type Description
status 'idle' | 'loading' | 'error' | 'success' Current mutation status
isLoading boolean True while mutation is executing
isError boolean True if mutation failed
isSuccess boolean True if mutation succeeded
data Result | null The mutation result
error unknown Error if mutation failed

Methods

mutate(params)

Type: (params: Params) => Promise<Result>

Execute the mutation. Returns a promise that resolves to the result.

		await mutation.mutate({ title: 'New Post', content: '...' });
	

Examples

Basic Create

		import { createMutation } from 'svelte-stache';
 
interface CreatePostInput {
	title: string;
	content: string;
}
 
interface Post {
	id: string;
	title: string;
	content: string;
	createdAt: string;
}
 
const useCreatePost = createMutation({
	mutationFn: async (data: CreatePostInput): Promise<Post> => {
		const res = await fetch('/api/posts', {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify(data)
		});
		if (!res.ok) throw new Error('Failed to create post');
		return res.json();
	}
});
 
export { useCreatePost };
	

With Cache Invalidation

		import { createMutation } from 'svelte-stache';
import { invalidatePosts } from './posts-query';
 
const useCreatePost = createMutation({
	mutationFn: createPostApi,
	onSuccess: () => {
		// Refetch the posts list after creating a new post
		invalidatePosts();
	}
});
	

Form Component

		<script lang="ts">
	import { useCreatePost } from '$lib/mutations/post';
	import { goto } from '$app/navigation';
 
	const mutation = useCreatePost({
		onSuccess: ({ result }) => {
			goto(`/posts/${result.id}`);
		}
	});
 
	let title = $state('');
	let content = $state('');
 
	const handleSubmit = async (e: Event) => {
		e.preventDefault();
		await mutation.mutate({ title, content });
	};
</script>
 
<form onsubmit={handleSubmit}>
	<div>
		<label for="title">Title</label>
		<input id="title" bind:value={title} required />
	</div>
 
	<div>
		<label for="content">Content</label>
		<textarea id="content" bind:value={content} required></textarea>
	</div>
 
	{#if mutation.isError}
		<div class="error">
			{mutation.error instanceof Error ? mutation.error.message : 'An error occurred'}
		</div>
	{/if}
 
	<button type="submit" disabled={mutation.isLoading}>
		{mutation.isLoading ? 'Creating...' : 'Create Post'}
	</button>
</form>
	

Update Mutation

		const useUpdatePost = createMutation({
	mutationFn: async ({ id, ...data }: UpdatePostInput) => {
		const res = await fetch(`/api/posts/${id}`, {
			method: 'PATCH',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify(data)
		});
		return res.json();
	},
	onSuccess: ({ params }) => {
		// Invalidate the specific post
		invalidatePost(params.id);
		// Also invalidate the list
		invalidatePosts();
	}
});
	

Delete with Confirmation

		<script lang="ts">
	import { useDeletePost } from '$lib/mutations/post';
 
	let { postId } = $props();
 
	const mutation = useDeletePost({
		onSuccess: () => {
			goto('/posts');
		}
	});
 
	const handleDelete = async () => {
		if (confirm('Are you sure you want to delete this post?')) {
			await mutation.mutate(postId);
		}
	};
</script>
 
<button onclick={handleDelete} disabled={mutation.isLoading} class="delete-btn">
	{mutation.isLoading ? 'Deleting...' : 'Delete Post'}
</button>
	

Optimistic Updates

		const useToggleLike = createMutation({
	mutationFn: async (postId: string) => {
		const res = await fetch(`/api/posts/${postId}/like`, { method: 'POST' });
		return res.json();
	},
	beforeMutate: (postId) => {
		// Optimistically update the UI
		// (You might use a store or context for this)
		updatePostLikeStatus(postId, true);
	},
	onError: (error) => {
		// Revert on error
		updatePostLikeStatus('unknown', false);
		showToast('Failed to like post');
	}
});
	

Multiple Mutations in One Component

		<script lang="ts">
	import { useCreatePost, useUpdatePost, useDeletePost } from '$lib/mutations/post';
 
	const createMutation = useCreatePost();
	const updateMutation = useUpdatePost();
	const deleteMutation = useDeletePost();
 
	// Each mutation has independent state
	const isAnyLoading = $derived(
		createMutation.isLoading || updateMutation.isLoading || deleteMutation.isLoading
	);
</script>
	
1 queries 0 paged