Skip to content

Commit 8d475dc

Browse files
author
Rich Harris
committed
POST/PUT/DELETE
1 parent 0994534 commit 8d475dc

File tree

12 files changed

+525
-6
lines changed

12 files changed

+525
-6
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
title: POST handlers
3+
---
4+
5+
You can also add handlers that mutate data, such as `POST`. In most cases, you should use [form actions](the-form-element) instead — you'll end up writing less code, and it'll work without JavaScript, making it more resilient.
6+
7+
Inside the `keydown` event handler of the 'add a todo' `<input>`, let's post some data to the server:
8+
9+
```svelte
10+
/// file: src/routes/+page.svelte
11+
<input
12+
type="text"
13+
autocomplete="off"
14+
on:keydown={async (e) => {
15+
if (e.key === 'Enter') {
16+
const input = e.currentTarget;
17+
const description = input.value;
18+
19+
+++ const response = await fetch('/todo', {
20+
method: 'POST',
21+
body: JSON.stringify({ description }),
22+
headers: {
23+
'Content-Type': 'application/json'
24+
}
25+
});+++
26+
27+
input.value = '';
28+
}
29+
}}
30+
/>
31+
```
32+
33+
Here, we're posting some JSON to the `/todo` API route — using a `userid` from the user's cookies — and receiving the `id` of the newly created todo in response.
34+
35+
Create the `/todo` route by adding a `src/routes/todo/+server.js` file with a `POST` handler that calls `createTodo` in `src/lib/server/database.js`:
36+
37+
```js
38+
/// file: src/routes/todo/+server.js
39+
import { json } from '@sveltejs/kit';
40+
import * as database from '$lib/server/database.js';
41+
42+
export async function POST({ request, cookies }) {
43+
const { description } = await request.json();
44+
45+
const userid = cookies.get('userid');
46+
const { id } = await database.createTodo({ userid, description });
47+
48+
return json({ id }, { status: 201 });
49+
}
50+
```
51+
52+
The `request` is a standard [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object; `await request.json()` returns the data that we posted from the event handler.
53+
54+
We're returning a response with a [201 Created](https://httpstatusdogs.com/201-created) status and the `id` of the newly generated todo in our database. Back in the event handler, we can use this to update the page:
55+
56+
```svelte
57+
/// file: src/routes/+page.svelte
58+
<input
59+
type="text"
60+
autocomplete="off"
61+
on:keydown={async (e) => {
62+
if (e.key === 'Enter') {
63+
const input = e.currentTarget;
64+
const description = input.value;
65+
66+
const response = await fetch('/todo', {
67+
method: 'POST',
68+
body: JSON.stringify({ description }),
69+
headers: {
70+
'Content-Type': 'application/json'
71+
}
72+
});
73+
74+
+++ const { id } = await response.json();
75+
76+
data.todos = [...data.todos, {
77+
id,
78+
description
79+
}];+++
80+
81+
input.value = '';
82+
}
83+
}}
84+
/>
85+
```
86+
87+
> You should only mutate `data` in such a way that you'd get the same result by reloading the page.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const database = new Map();
2+
3+
export function getTodos(userid) {
4+
if (!database.has(userid)) {
5+
createTodo({ userid, description: 'Learn about API routes' });
6+
}
7+
8+
return Array.from(database.get(userid).values());
9+
}
10+
11+
export function createTodo({ userid, description }) {
12+
if (!database.has(userid)) {
13+
database.set(userid, new Map());
14+
}
15+
16+
const todos = database.get(userid);
17+
18+
const id = crypto.randomUUID();
19+
20+
todos.set(id, {
21+
id,
22+
description,
23+
done: false
24+
});
25+
26+
return {
27+
id
28+
};
29+
}
30+
31+
export function toggleTodo({ userid, id, done }) {
32+
const todos = database.get(userid);
33+
todos.get(id).done = done;
34+
}
35+
36+
export function deleteTodo({ userid, id }) {
37+
const todos = database.get(userid);
38+
todos.delete(id);
39+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import * as database from '$lib/server/database.js';
2+
3+
export function load({ cookies }) {
4+
let userid = cookies.get('userid');
5+
6+
if (!userid) {
7+
userid = crypto.randomUUID();
8+
cookies.set('userid', userid, { path: '/' });
9+
}
10+
11+
return {
12+
todos: database.getTodos(userid)
13+
};
14+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<script>
2+
export let data;
3+
</script>
4+
5+
<div class="centered">
6+
<h1>todos</h1>
7+
8+
<label>
9+
add a todo:
10+
<input
11+
type="text"
12+
autocomplete="off"
13+
on:keydown={async (e) => {
14+
if (e.key === 'Enter') {
15+
const input = e.currentTarget;
16+
const description = input.value;
17+
18+
// TODO handle submit
19+
20+
input.value = '';
21+
}
22+
}}
23+
/>
24+
</label>
25+
26+
<ul class="todos">
27+
{#each data.todos as todo (todo.id)}
28+
<li>
29+
<label>
30+
<input
31+
type="checkbox"
32+
checked={todo.done}
33+
on:change={async (e) => {
34+
const done = e.currentTarget.checked;
35+
36+
// TODO handle change
37+
}}
38+
/>
39+
<span>{todo.description}</span>
40+
<button
41+
aria-label="Mark as complete"
42+
on:click={async (e) => {
43+
// TODO handle delete
44+
}}
45+
/>
46+
</label>
47+
</li>
48+
{/each}
49+
</ul>
50+
</div>
51+
52+
<style>
53+
.centered {
54+
max-width: 20em;
55+
margin: 0 auto;
56+
}
57+
58+
label {
59+
display: flex;
60+
width: 100%;
61+
}
62+
63+
input[type="text"] {
64+
flex: 1;
65+
}
66+
67+
span {
68+
flex: 1;
69+
}
70+
71+
button {
72+
border: none;
73+
background: url(./remove.svg) no-repeat 50% 50%;
74+
background-size: 1rem 1rem;
75+
cursor: pointer;
76+
height: 100%;
77+
aspect-ratio: 1;
78+
opacity: 0.5;
79+
transition: opacity 0.2s;
80+
}
81+
82+
button:hover {
83+
opacity: 1;
84+
}
85+
</style>
86+
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<script>
2+
export let data;
3+
</script>
4+
5+
<div class="centered">
6+
<h1>todos</h1>
7+
8+
<label>
9+
add a todo:
10+
<input
11+
type="text"
12+
autocomplete="off"
13+
on:keydown={async (e) => {
14+
if (e.key === 'Enter') {
15+
const input = e.currentTarget;
16+
const description = input.value;
17+
18+
const response = await fetch('/todo', {
19+
method: 'POST',
20+
body: JSON.stringify({ description }),
21+
headers: {
22+
'Content-Type': 'application/json'
23+
}
24+
});
25+
26+
const { id } = await response.json();
27+
28+
data.todos = [...data.todos, {
29+
id,
30+
description
31+
}];
32+
33+
input.value = '';
34+
}
35+
}}
36+
/>
37+
</label>
38+
39+
<ul class="todos">
40+
{#each data.todos as todo (todo.id)}
41+
<li>
42+
<label>
43+
<input
44+
type="checkbox"
45+
checked={todo.done}
46+
on:change={async (e) => {
47+
const done = e.currentTarget.checked;
48+
49+
// TODO handle change
50+
}}
51+
/>
52+
<span>{todo.description}</span>
53+
<button
54+
aria-label="Mark as complete"
55+
on:click={async (e) => {
56+
// TODO handle delete
57+
}}
58+
/>
59+
</label>
60+
</li>
61+
{/each}
62+
</ul>
63+
</div>
64+
65+
<style>
66+
.centered {
67+
max-width: 20em;
68+
margin: 0 auto;
69+
}
70+
71+
label {
72+
display: flex;
73+
width: 100%;
74+
}
75+
76+
input[type="text"] {
77+
flex: 1;
78+
}
79+
80+
span {
81+
flex: 1;
82+
}
83+
84+
button {
85+
border: none;
86+
background: url(./remove.svg) no-repeat 50% 50%;
87+
background-size: 1rem 1rem;
88+
cursor: pointer;
89+
height: 100%;
90+
aspect-ratio: 1;
91+
opacity: 0.5;
92+
transition: opacity 0.2s;
93+
}
94+
95+
button:hover {
96+
opacity: 1;
97+
}
98+
</style>
99+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { json } from '@sveltejs/kit';
2+
import * as database from '$lib/server/database.js';
3+
4+
export async function POST({ request, cookies }) {
5+
const { description } = await request.json();
6+
7+
const userid = cookies.get('userid');
8+
const { id } = await database.createTodo({ userid, description });
9+
10+
return json({ id }, { status: 201 });
11+
}

content/tutorial/03-sveltekit/05-api-routes/02-post-put-patch-delete/README.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

content/tutorial/03-sveltekit/05-api-routes/02-post-put-patch-delete/app-a/src/routes/+page.svelte

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)