server$()
server$()
позволяет вам создать функцию, которая всегда будет выполняться на сервере, что делает её отличным местом для доступа к БД или выполнения действий только на сервере.
server$
- это форма механизма RPC (Remote Procedure Call) между клиентом и сервером, как и традиционная конечная точка HTTP, но сильно типизированная благодаря TypeScript, и более простая в обслуживании.
Ваша новая функция будет иметь следующую сигнатуру:
([AbortSignal, ] ...): Promise<T>
Сигнал AbortSignal
является необязательным и позволяет вам отменить давно выполняющийся запрос, прервав соединение.
Обратите внимание, что в зависимости от среды исполнения вашего сервера, функция на сервере может завершиться или не завершиться немедленно, так как это зависит от того, как клиентские отключения обрабатываются в текущем времени выполнения.
import { component$, useSignal } from '@builder.io/qwik';
import { server$ } from '@builder.io/qwik-city';
// Обернув функцию в `server$()`, мы помечаем её, как всегда
// выполняющуюся на сервере. Это одна из форм механизма RPC.
const serverGreeter = server$((firstName: string, lastName: string) => {
const greeting = `Привет ${firstName} ${lastName}`;
console.log('Вывод на сервере', greeting);
return greeting;
});
export default component$(() => {
const firstName = useSignal('');
const lastName = useSignal('');
return (
<section>
<label>Имя: <input bind:value={firstName} /></label>
<label>Фамилия: <input bind:value={lastName} /></label>
<button
onClick$={async () => {
const greeting = await serverGreeter(firstName.value, lastName.value);
alert(greeting);
}}
>
приветствие
</button>
</section>
);
});
server$
может также читать HTTP-куки, заголовки и переменные окружения, используя this
. В этом случае вам нужно будет использовать обычную функцию вместо стрелочной.
// Обратите внимание, что обёрнутая функция объявлена как `async function`.
const addUser = server$(async function(id: number, fullName: string, address: Address) {
// `this` - это объект `RequestEvent`, который содержит
// HTTP-заголовки, куки и переменные окружения.
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$ может принимать любое количество аргументов и возвращать любое значение, которое может быть сериализовано Qwik, включая примитивы, объекты, массивы, bigint, узлы JSX и даже промисы, и это только некоторые из них.
Потоковые ответы
server$
может возвращать поток данных с помощью асинхронного генератора. Это полезно для потоковой передачи данных от сервера к клиенту.
Завершение работы генератора на стороне клиента (например, вызовом return()
на генераторе, или выходом из вашего async for-loop
) прервёт соединение. Как и в случае с AbortSignal
- как генератор завершится на стороне сервера, зависит от среды исполнения сервера и от того, как обрабатывается отключение клиента.
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}`;
}
}}
>
старт
</button>
<div>{message.value}</div>
</div>
);
});
Этот API фактически используется для реализации потоковых ответов QwikGPT на нашем сайте документации (только на официальном сайте).
server$()
?
Как работает server$()
оборачивает функцию и возвращает асинхронный прокси-объект к функции. На сервере прокси-функция напрямую вызывает обернутую функцию, а конечная точка HTTP автоматически создаётся функцией server$()
.
На клиенте прокси-функция вызывает обёрнутую функцию через HTTP-запрос, используя fetch()
.
Примечание: функция
server$()
должна гарантировать, что на сервере и клиенте выполняется одна и та же версия кода. При наличии перекоса в версии поведение не определено и может привести к ошибке. Если перекос версий является общей проблемой, то следует использовать более формальный механизм RPC, например, tRPC или другую библиотеку.
server$
Промежуточное ПО и При использовании server$
важно понимать, как выполняются функции промежуточного ПО. Функции промежуточного ПО, определённые в файлах layout
, не выполняются для запросов server$
. Это может привести к путанице, особенно когда разработчики ожидают, что определённое промежуточное ПО будет выполняться как для запросов страниц, так и для запросов server$
.
Чтобы гарантировать, что функция промежуточного ПО будет работать для обоих типов запросов, ее следует определить в файле plugin.ts
. Это гарантирует, что промежуточное ПО выполняется последовательно для всех входящих запросов, независимо от того, являются ли они обычными запросами страниц или запросами server$
.
Определив промежуточное ПО в файле plugin.ts
, разработчики могут поддерживать централизованное расположение общей логики промежуточного ПО, обеспечивая согласованность и уменьшая потенциальные ошибки или упущения.