server$()
server$()
allows you to create a function that always execute on the server, making it a great place to access the DB or perform server-only actions.
server$
is a form of RPC (Remote Procedure Call) mechanism between the client and server, just like a traditional HTTP endpoint but strongly typed thanks to Typescript, and easier to maintain.
Your new function will have the following signature:
([AbortSignal, ] ...): Promise<T>
AbortSignal
is optional, and allows you to cancel a long running request by terminating the connection.
Please note that depending on your server runtime, the function on the server may or may not terminate immediately as it depends on how client disconnections are handled by said runtime.
import { component$, useSignal } from '@builder.io/qwik';
import { server$ } from '@builder.io/qwik-city';
// By wrapping a function with `server$()` we mark it to always
// execute on the server. This is a form of RPC mechanism.
const serverGreeter = server$((firstName: string, lastName: string) => {
const greeting = `Hello ${firstName} ${lastName}`;
console.log('Prints in the server', greeting);
return greeting;
});
export default component$(() => {
const firstName = useSignal('');
const lastName = useSignal('');
return (
<section>
<label>First name: <input bind:value={firstName} /></label>
<label>Last name: <input bind:value={lastName} /></label>
<button
onClick$={async () => {
const greeting = await serverGreeter(firstName.value, lastName.value);
alert(greeting);
}}
>
greet
</button>
</section>
);
});
server$
can also read the HTTP cookies, headers, or environment variables, using this
. In this case you will need to use a function instead of an arrow function.
// Notice that the wrapped function is declared as a `async function`
const addUser = server$(async function(id: number, fullName: string, address: Address) {
// The `this` is the `RequestEvent` object, which contains
// the HTTP headers, cookies, and environment variables.
const db = createClient(this.env.get('DB_KEY'));
if (this.cookie.get('user-session')) {
await db.from('users').insert({
id,
fullName,
address
});
return {
success: true,
}
}
return {
success: false,
}
})
Server$ can accept any number of arguments and return any value that can be serialized by Qwik, that includes primitives, objects, arrays, bigint, JSX nodes and even Promises, just to name a few.
Streaming responses
server$
can return a stream of data by using an async generator. This is useful for streaming data from the server to the client.
Terminating the generator on the client side (e.g. by calling return()
on the generator, or by breaking out from your async for-loop) will terminate the connection. As with AbortSignal
-
how the generator will terminate on the server side depends on the server runtime and how client disconnects are handled.
import { component$, useSignal } from '@builder.io/qwik';
import { server$ } from '@builder.io/qwik-city';
const stream = server$(async function* () {
for (let i = 0; i < 10; i++) {
yield i;
await new Promise((resolve) => setTimeout(resolve, 1000));
}
});
export default component$(() => {
const message = useSignal('');
return (
<div>
<button
onClick$={async () => {
const response = await stream();
for await (const i of response) {
message.value += ` ${i}`;
}
}}
>
start
</button>
<div>{message.value}</div>
</div>
);
});
This API is actually used to implement QwikGPT streaming responses in our docs site.
server$()
work?
How does A server$()
wraps a function and returns an async proxy to the function. On the server, the proxy function directly calls the wrapped function, and a HTTP endpoint is automatically created by the server$()
function.
On the client, the proxy function invokes the wrapped function via an HTTP request, using fetch()
.
Note: The
server$()
function must ensure that the server and client have the same version of the code executing. If there is a version skew the behavior is undefined and may result in an error. If version skew is a common problem then a more formal RPC mechanism should be used such as a tRPC or other library.