S
SolidJS•9mo ago
binajmen

How do you consume server actions response?

It might look as a silly question, but it is unclear for me how you must consume the response (either is it result or error).
export default function Users() {
const users = useRouteData<typeof routeData>();

const [creating, { Form }] = createServerAction$(
async (formData: FormData) => {
await new Promise((resolve, reject) => setTimeout(resolve, 1000));
const email = String(formData.get("email"));

if (email.length < 10) {
return json({ success: false, fields: { email: "too small" } }); // <-- will end up in result
// throw json({ success: false, fields: { email: "too small" } }); // <-- will end up in error
}

await db.insert(usersTable).values({ email });

return json({ success: true });
}
);

createEffect(() => console.log("effect:", creating, creating.pending));
console.log(creating);

return (
<div>
<h1>Users list</h1>
<ul>
<For each={users()}>{(user) => <li>{user.email}</li>}</For>
</ul>
<Form>
<label for="email">Email:</label>
<input type="email" name="email" />
<Show when={creating.error}>Error: {creating.error/*???*/}</Show>
<input type="submit" value="submit" />
</Form>
</div>
);
}
export default function Users() {
const users = useRouteData<typeof routeData>();

const [creating, { Form }] = createServerAction$(
async (formData: FormData) => {
await new Promise((resolve, reject) => setTimeout(resolve, 1000));
const email = String(formData.get("email"));

if (email.length < 10) {
return json({ success: false, fields: { email: "too small" } }); // <-- will end up in result
// throw json({ success: false, fields: { email: "too small" } }); // <-- will end up in error
}

await db.insert(usersTable).values({ email });

return json({ success: true });
}
);

createEffect(() => console.log("effect:", creating, creating.pending));
console.log(creating);

return (
<div>
<h1>Users list</h1>
<ul>
<For each={users()}>{(user) => <li>{user.email}</li>}</For>
</ul>
<Form>
<label for="email">Email:</label>
<input type="email" name="email" />
<Show when={creating.error}>Error: {creating.error/*???*/}</Show>
<input type="submit" value="submit" />
</Form>
</div>
);
}
If the action failed (form validation, db error, etc.), I'd like to retrieve this info in the response. In the network tab, the result as the type Response (it makes sense). However, how am I suppose to consume the response and extract the JSON I'm sending from the action?
1 Reply
binajmen
binajmen•9mo ago
I could have an effect to extract the response data:
createEffect(async () => {
if (creating.result) {
const res = await creating.result.json();
console.log({ res });
// do stuff with the response
}
});
createEffect(async () => {
if (creating.result) {
const res = await creating.result.json();
console.log({ res });
// do stuff with the response
}
});
And maybe use a signal to set the response, but it seems very verbose. Is there a wrapper for automating that? The "end" result would be:
export default function Users() {
const users = useRouteData<typeof routeData>();
const [error, setError] = createSignal<{ email?: string }>({});

const [creating, { Form }] = createServerAction$(
async (formData: FormData) => {
const email = String(formData.get("email"));

if (email.length < 10) {
return json(
{ success: false, fields: { email: "too small" } },
{ status: 400 }
); // <-- will end up in result
// throw json({ success: false, fields: { email: "too small" } }); // <-- will end up in error
}

await db.insert(usersTable).values({ email });

return json({ success: true });
}
);

createEffect(async () => {
if (creating.result && creating.result.status > 399) {
const res = (await creating.result.json()) as {
success: boolean;
fields: { email?: string };
};
setError(res.fields);
}
});

return (
<div>
<h1>Users list</h1>
<ul>
<For each={users()}>{(user) => <li>{user.email}</li>}</For>
</ul>
<Form>
<label for="email">Email:</label>
<input type="email" name="email" />
<Show when={error().email}>Error: {error().email}</Show>
<input type="submit" value="submit" />
</Form>
</div>
);
}
export default function Users() {
const users = useRouteData<typeof routeData>();
const [error, setError] = createSignal<{ email?: string }>({});

const [creating, { Form }] = createServerAction$(
async (formData: FormData) => {
const email = String(formData.get("email"));

if (email.length < 10) {
return json(
{ success: false, fields: { email: "too small" } },
{ status: 400 }
); // <-- will end up in result
// throw json({ success: false, fields: { email: "too small" } }); // <-- will end up in error
}

await db.insert(usersTable).values({ email });

return json({ success: true });
}
);

createEffect(async () => {
if (creating.result && creating.result.status > 399) {
const res = (await creating.result.json()) as {
success: boolean;
fields: { email?: string };
};
setError(res.fields);
}
});

return (
<div>
<h1>Users list</h1>
<ul>
<For each={users()}>{(user) => <li>{user.email}</li>}</For>
</ul>
<Form>
<label for="email">Email:</label>
<input type="email" name="email" />
<Show when={error().email}>Error: {error().email}</Show>
<input type="submit" value="submit" />
</Form>
</div>
);
}
Of course, things should be generic and stuff, but you get the gist. Am I missing a library that does the job? Anyone? 🙂