Skip to content

Commit d4d0818

Browse files
authored
docs(server$): RequestEvent info (#6421)
* docs(server$): RequestEvent info * Update index.mdx
1 parent f409f7a commit d4d0818

File tree

1 file changed

+142
-54
lines changed
  • packages/docs/src/routes/docs/(qwikcity)/server$

1 file changed

+142
-54
lines changed

packages/docs/src/routes/docs/(qwikcity)/server$/index.mdx

Lines changed: 142 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,27 @@ created_at: '2023-03-29T02:35:29Z'
2222

2323
`server$()` allows you to define functions that execute exclusively on the server, making it ideal for server-only operations and database access. It functions as an RPC (Remote Procedure Call) mechanism between the client and server, similar to a traditional HTTP endpoint, but strongly typed with TypeScript and easier to maintain.
2424

25-
Your new function will have the following signature:
26-
`([AbortSignal, ] ...): Promise<T>`
25+
> `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.
26+
2727

28-
`AbortSignal` is optional, and allows you to cancel a long running request by terminating the connection.
29-
Please note that depending on your server runtime, the function on the server may not terminate immediately. It depends on how client disconnections are handled by the runtime.
28+
`AbortSignal` is optional, and allows you to cancel a long running request by terminating the connection.
29+
Your new function will have the following signature:
30+
`([AbortSignal, ...yourOtherArgs]): Promise<T>`
31+
> Please note that depending on your server runtime, the function on the server may not terminate immediately. It depends on how client disconnections are handled by the runtime.
3032
3133
```tsx
3234
import { component$, useSignal } from '@builder.io/qwik';
3335
import { server$ } from '@builder.io/qwik-city';
3436

3537
// By wrapping a function with `server$()` we mark it to always
3638
// execute on the server. This is a form of RPC mechanism.
37-
const serverGreeter = server$((firstName: string, lastName: string) => {
38-
const greeting = `Hello ${firstName} ${lastName}`;
39-
console.log('Prints in the server', greeting);
40-
return greeting;
41-
});
39+
export const serverGreeter = server$(
40+
function (firstName: string, lastName: string) {
41+
const greeting = `Hello ${firstName} ${lastName}`;
42+
console.log('Prints in the server', greeting);
43+
return greeting;
44+
}
45+
);
4246

4347
export default component$(() => {
4448
const firstName = useSignal('');
@@ -50,10 +54,12 @@ export default component$(() => {
5054
<label>Last name: <input bind:value={lastName} /></label>
5155

5256
<button
53-
onClick$={async () => {
54-
const greeting = await serverGreeter(firstName.value, lastName.value);
55-
alert(greeting);
56-
}}
57+
onClick$={
58+
async () => {
59+
const greeting = await serverGreeter(firstName.value, lastName.value);
60+
alert(greeting);
61+
}
62+
}
5763
>
5864
greet
5965
</button>
@@ -62,73 +68,152 @@ export default component$(() => {
6268
});
6369
```
6470

65-
`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.
71+
## Accessing Request Information with `RequestEvent`
72+
73+
When using `server$`, you have access to the `RequestEvent` object through `this`. This object provides useful information about the HTTP request, including environment variables, cookies, URL, and headers. Here's how you can use it:
74+
75+
### Environment Variables
76+
77+
You can access environment variables using `this.env.get()`.
6678

6779
```tsx
68-
// Notice that the wrapped function is declared as an `async function`
69-
const addUser = server$(async function(id: number, fullName: string, address: Address) {
70-
// The `this` is the `RequestEvent` object, which contains
71-
// the HTTP headers, cookies, and environment variables.
72-
const db = createClient(this.env.get('DB_KEY'));
73-
if (this.cookie.get('user-session')) {
74-
await db.from('users').insert({
75-
id,
76-
fullName,
77-
address
78-
});
79-
return {
80-
success: true,
80+
export const getEnvVariable = server$(
81+
function () {
82+
const dbKey = this.env.get('DB_KEY');
83+
console.log('Database Key:', dbKey);
84+
return dbKey;
85+
}
86+
);
87+
```
88+
89+
### Cookies
90+
91+
You can read cookies using `this.cookie.get()` and `this.cookie.set()`.
92+
93+
> When using `handleCookies` (in our example below) if it's used within a `useTask$` function that runs during the initial request, setting cookies won’t work as expected. This is because, during server-side rendering (SSR), the response is streamed, and HTTP requires all headers to be set before sending the first response. However, if handleCookies is used in useVisibleTask$, this issue doesn’t occur. If you need to set cookies for the initial document request you can use `plugin@<name>.ts` or Middleware.
94+
95+
```tsx
96+
export const handleCookies = server$(
97+
function () {
98+
const userSession = this.cookie.get('user-session')?.value;
99+
if (!userSession) {
100+
this.cookie.set('user-session', 'new-session-id', { path: '/', httpOnly: true });
81101
}
102+
return userSession;
82103
}
83-
return {
84-
success: false,
104+
);
105+
```
106+
107+
### URL
108+
109+
You can access the request URL and its components using `this.url`.
110+
111+
```tsx
112+
export const getRequestUrl = server$(
113+
function () {
114+
const requestUrl = this.url;
115+
console.log('Request URL:', requestUrl);
116+
return requestUrl;
85117
}
86-
})
118+
);
87119
```
88120

89-
> 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.
121+
### Headers
90122

123+
You can read headers using `this.headers.get()`.
124+
125+
```tsx
126+
export const getHeaders = server$(
127+
function () {
128+
const userAgent = this.headers.get('User-Agent');
129+
console.log('User-Agent:', userAgent);
130+
return userAgent;
131+
}
132+
);
133+
```
134+
135+
### Using Multiple RequestEvent Information
136+
137+
Here's an example that combines environment variables, cookies, URL, and headers in a single function.
138+
139+
```tsx
140+
export const handleRequest = server$(
141+
function () {
142+
// Access environment variable
143+
const dbKey = this.env.get('DB_KEY');
144+
145+
// Access cookies
146+
const userSession = this.cookie.get('user-session')?.value;
147+
if (!userSession) {
148+
this.cookie.set('user-session', 'new-session-id', { path: '/', httpOnly: true });
149+
}
150+
151+
// Access request URL
152+
const requestUrl = this.url;
153+
154+
// Access headers
155+
const userAgent = this.headers.get('User-Agent');
156+
157+
console.log('Environment Variable:', dbKey);
158+
console.log('User Session:', userSession);
159+
console.log('Request URL:', requestUrl);
160+
console.log('User-Agent:', userAgent);
161+
162+
return {
163+
dbKey,
164+
userSession,
165+
requestUrl,
166+
userAgent
167+
};
168+
}
169+
);
170+
```
91171

92172
## Streaming Responses
93173

94-
`server$` can return a stream of data by using an async generator. This is useful for streaming data from the server to the client.
174+
`server$` can return a stream of data by using an async generator function, which is useful for streaming data from the server to the client.
95175

96-
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` -
97-
how the generator will terminate on the server side depends on the server runtime and how client disconnects are handled.
176+
Terminating the generator on the client side (e.g., by calling `.return()` on the generator or by breaking out from your async for-of loop) will terminate the connection. Similar to `AbortSignal`, how the generator terminates on the server side depends on the server runtime and how client disconnects are handled.
98177

99178
```tsx
100179
import { component$, useSignal } from '@builder.io/qwik';
101180
import { server$ } from '@builder.io/qwik-city';
102181

103-
const stream = server$(async function* () {
104-
// Creation of an array with 10 undefined values
105-
const iterationRange = Array(10).fill().entries();
106-
107-
for (const [index] of iterationRange) {
108-
// Yield returns the index during each iteration
109-
yield index;
110-
111-
// Waiting for 1 second before the next iteration
112-
// This simulates a delay in the execution
113-
await new Promise((resolve) => setTimeout(resolve, 1000));
182+
export const streamFromServer = server$(
183+
// Async Generator Function
184+
async function* () {
185+
// Creation of an array with 10 undefined values
186+
const iterationRange = Array(10).fill().entries();
187+
188+
for (const [value] of iterationRange) {
189+
// Yield returns the array value during each iteration
190+
yield value;
191+
192+
// Waiting for 1 second before the next iteration
193+
// This simulates a delay in the execution
194+
await new Promise((resolve) => setTimeout(resolve, 1000));
195+
}
114196
}
115-
});
197+
);
116198

117199

118200
export default component$(() => {
119201
const message = useSignal('');
120202
return (
121203
<div>
122204
<button
123-
onClick$={async () => {
124-
// call the async stream function and wait for the response
125-
const response = await stream();
126-
// use a for-await-of loop to asynchronously iterate over the response
127-
for await (const index of response) {
128-
// add each index from the response to the message value
129-
message.value += ` ${index}`;
205+
onClick$={
206+
async () => {
207+
// call the async stream function and wait for the response
208+
const response = await streamFromServer();
209+
// use a for-await-of loop to asynchronously iterate over the response
210+
for await (const value of response) {
211+
// add each value from the response to the message value
212+
message.value += ` ${value}`;
213+
}
214+
// do anything else
130215
}
131-
}}
216+
}
132217
>
133218
start
134219
</button>
@@ -163,3 +248,6 @@ When using `server$`, it's important to understand how [middleware functions](/d
163248
To ensure that a middleware function runs for both types of requests, it should be defined in the `plugin.ts` file. This ensures that the middleware is executed consistently for all incoming requests, regardless of whether they are normal page requests or `server$` requests.
164249

165250
By [defining middleware in the `plugin.ts`](/docs/advanced/plugints) file, developers can maintain a centralized location for shared middleware logic, ensuring consistency and reducing potential errors or oversights.
251+
252+
253+

0 commit comments

Comments
 (0)