Skip to Content
Paywall Client-Side SDKAbort API provider request

Abort API Provider Request

Learn how to cancel ongoing API provider requests using abort signals. This is useful for stopping long-running requests when users navigate away or want to cancel operations.

This feature only available when paywall is in client mode and has tokenization enabled. Learn more about paywall tokenization.

Prerequisites: You must have an API provider created before making requests that can be aborted.

Overview

Both paywall.makeRequest() and paywall.makeStreamRequest() support request cancellation through abort signals. This allows you to:

  • Cancel long-running requests - Stop requests that are taking too long
  • Handle navigation - Cancel requests when users leave the page
  • Improve UX - Allow users to stop operations they no longer need
  • Prevent memory leaks - Clean up resources from cancelled requests
  • Save tokens - Avoid token deduction for cancelled requests (if cancelled before processing)

Basic Usage

Adding Abort Signal

Pass an abort signal as the third parameter to enable request cancellation:

import { useRef } from 'react'; const abortSignal = useRef({}); const response = await paywall.makeRequest( "https://onlineapp.pro/api/v1/api-gateway/999?paywall_id=100", { method: 'POST', body: JSON.stringify({ messages: [{ role: "user", content: "Long processing task" }] }) }, abortSignal // Third parameter for abort control );

Aborting the Request

To cancel the request, call the abort method:

const abortRequest = () => { abortSignal.current?.abort(); };

Complete Examples

React Component with Abort

import { useState, useRef } from 'react'; function AbortableAPICall() { const [response, setResponse] = useState(''); const [loading, setLoading] = useState(false); const abortSignal = useRef({}); const makeAPICall = async () => { setLoading(true); setResponse(''); try { const result = await paywall.makeRequest( "https://onlineapp.pro/api/v1/api-gateway/999?paywall_id=100", { method: 'POST', body: JSON.stringify({ messages: [ { role: "user", content: "Generate a long story" } ] }) }, abortSignal // Enable abort functionality ); if (result.ok) { const data = await result.json(); setResponse(data.choices[0].message.content); } } catch (error) { if (error.name === 'AbortError') { console.log('Request was cancelled'); setResponse('Request cancelled by user'); } else { console.error('Request failed:', error); } } finally { setLoading(false); } }; const cancelRequest = () => { abortSignal.current?.abort(); }; return ( <div> <button onClick={makeAPICall} disabled={loading}> {loading ? 'Processing...' : 'Start API Call'} </button> {loading && ( <button onClick={cancelRequest}> Cancel Request </button> )} {response && <div>{response}</div>} </div> ); }

Streaming Request with Abort

import { useState, useRef } from 'react'; function AbortableStreaming() { const [answer, setAnswer] = useState(''); const [isStreaming, setIsStreaming] = useState(false); const abortSignal = useRef({}); const startStream = async () => { setIsStreaming(true); setAnswer(''); try { const requestUrl = "https://onlineapp.pro/api/v1/api-gateway/999?paywall_id=100"; for await (const chunk of paywall.makeStreamRequest( requestUrl, { method: 'POST', body: JSON.stringify({ messages: [ { role: "user", content: "Write a detailed explanation" } ], stream: true }) }, abortSignal // Enable abort for streaming )) { const lines = chunk .split('\n') .filter((line) => line.startsWith('data: ')); lines.forEach((line) => { const data = line.replace('data: ', '').trim(); if (data !== '[DONE]') { try { const parsed = JSON.parse(data); if (parsed.choices?.[0]?.delta?.content) { setAnswer(prev => prev + parsed.choices[0].delta.content); } } catch (error) { console.error('Parse error:', error); } } }); } } catch (error) { if (error.name === 'AbortError') { console.log('Streaming was cancelled'); setAnswer(prev => prev + '\n\n[Stream cancelled by user]'); } else { console.error('Streaming error:', error); } } finally { setIsStreaming(false); } }; const stopStream = () => { abortSignal.current?.abort(); }; return ( <div> <button onClick={startStream} disabled={isStreaming}> {isStreaming ? 'Streaming...' : 'Start Stream'} </button> {isStreaming && ( <button onClick={stopStream}> Stop Stream </button> )} <div>{answer}</div> </div> ); }

Advanced Usage

Automatic Cleanup on Unmount

Ensure requests are cancelled when components unmount:

import { useEffect, useRef } from 'react'; function AutoCleanupComponent() { const abortSignal = useRef({}); useEffect(() => { // Cleanup on component unmount return () => { abortSignal.current?.abort(); }; }, []); const makeRequest = async () => { try { const response = await paywall.makeRequest( "https://onlineapp.pro/api/v1/api-gateway/999?paywall_id=100", { method: 'POST', body: JSON.stringify({ /* data */ }) }, abortSignal ); // Handle response } catch (error) { if (error.name !== 'AbortError') { console.error('Request failed:', error); } } }; return ( <button onClick={makeRequest}> Make Request </button> ); }

Best Practices

1. Always Handle Abort Errors

try { const response = await paywall.makeRequest(url, options, abortSignal); } catch (error) { if (error.name !== 'AbortError') { // Only log non-abort errors console.error('Request failed:', error); } }

2. Clean Up on Component Unmount

useEffect(() => { return () => { abortSignal.current?.abort(); }; }, []);

3. Provide Clear UI Feedback

{loading && ( <div> <span>Processing...</span> <button onClick={cancelRequest}>Cancel</button> </div> )}

4. Handle Token Implications

Note that tokens may still be deducted if the request reaches the API provider before being cancelled. The abort mainly affects the client-side handling of the response.

Next Steps

Last updated on