Skip to content

Commit e791713

Browse files
committed
more content
1 parent 5d55b0e commit e791713

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed

server-actions-for-next-pages/README.md

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,131 @@ export async function failingFunction({}) {
154154
}
155155
```
156156

157+
## Aborting requests with AbortSignal
158+
159+
You can abort ongoing server actions using `AbortController` and `AbortSignal`. Pass an abort signal in your arguments (either directly or as a field in an object parameter), and it will be used to:
160+
161+
- **Client-side**: Abort the fetch request
162+
- **Server-side**: Allow server functions to respond to request cancellations
163+
164+
```ts
165+
// pages/api/server-actions.js
166+
"poor man's use server";
167+
168+
export async function longRunningTask(signal: AbortSignal) {
169+
for (let i = 0; i < 10; i++) {
170+
// Check if the request was aborted
171+
if (signal.aborted) {
172+
throw new Error('Task aborted: ' + signal.reason);
173+
}
174+
await sleep(1000);
175+
}
176+
return { completed: true };
177+
}
178+
179+
// Also works with async generators
180+
export async function* streamData({ signal }: { signal: AbortSignal }) {
181+
for (let i = 0; i < 20; i++) {
182+
if (signal.aborted) {
183+
throw new Error('Stream aborted: ' + signal.reason);
184+
}
185+
await sleep(500);
186+
yield { count: i };
187+
}
188+
}
189+
```
190+
191+
Client usage:
192+
193+
```tsx
194+
// pages/index.tsx
195+
import { longRunningTask, streamData } from './api/server-actions';
196+
197+
export default function Page() {
198+
const handleAbortableTask = async () => {
199+
const controller = new AbortController();
200+
201+
// Abort after 2 seconds
202+
setTimeout(() => controller.abort('Timeout'), 2000);
203+
204+
try {
205+
await longRunningTask(controller.signal);
206+
} catch (error) {
207+
console.log(error); // "Task aborted: Timeout"
208+
}
209+
};
210+
211+
const handleAbortableStream = async () => {
212+
const controller = new AbortController();
213+
const generator = streamData({ signal: controller.signal });
214+
215+
for await (const { count } of generator) {
216+
console.log(count);
217+
if (count > 5) {
218+
controller.abort('User stopped stream');
219+
break;
220+
}
221+
}
222+
};
223+
224+
return <div>...</div>;
225+
}
226+
```
227+
228+
**Note**: You can pass `AbortSignal` or `AbortController` directly as arguments, or as fields within object parameters. The server function will receive the request's abort signal, allowing it to detect when the client cancels the request (e.g., when navigating away).
229+
230+
## Custom fetch function
231+
232+
You can provide a custom `fetch` function to your server actions by including it as a `fetch` field in an object parameter. This is useful for adding custom headers, authentication, or using a custom fetch implementation.
233+
234+
```ts
235+
// pages/api/server-actions.js
236+
"poor man's use server";
237+
238+
export async function fetchExternalData({ url, fetch }) {
239+
// The fetch parameter will be either:
240+
// - The custom fetch function passed from the client
241+
// - The global fetch function (injected automatically on the server)
242+
const response = await fetch(url);
243+
return response.json();
244+
}
245+
```
246+
247+
Client usage with custom fetch:
248+
249+
```tsx
250+
// pages/index.tsx
251+
import { fetchExternalData } from './api/server-actions';
252+
253+
export default function Page() {
254+
const handleFetch = async () => {
255+
// Define a custom fetch with authentication
256+
const customFetch = (url, options) => {
257+
return fetch(url, {
258+
...options,
259+
headers: {
260+
...options?.headers,
261+
'Authorization': 'Bearer my-token',
262+
},
263+
});
264+
};
265+
266+
const data = await fetchExternalData({
267+
url: 'https://api.example.com/data',
268+
fetch: customFetch
269+
});
270+
console.log(data);
271+
};
272+
273+
return <div>...</div>;
274+
}
275+
```
276+
277+
**How it works**:
278+
- **Client-side**: If you provide a `fetch` field in an object parameter, it will be used for the RPC call instead of the global `fetch`
279+
- **Server-side**: The global `fetch` function is automatically injected into object parameters (unless you already provided your own)
280+
- User-provided fetch functions always take precedence over the injected one
281+
157282
## How it works
158283

159284
The plugin will replace the content of files inside `pages/api` with `"poor man's use server"` at the top to make the exported functions callable from the browser.

0 commit comments

Comments
 (0)