Skip to Content
DocsDeveloper GuideNetworking & Data

Networking and Data

WebF provides standard web APIs for networking and data storage, allowing you to connect your application to backend services and persist data locally.

Fetching Data from the Internet

WebF provides the standard fetch API and supports popular networking libraries, making it easy to connect to backend services.

Using the Fetch API

The modern fetch API is fully supported for making network requests. You can use it for GET, POST, and other HTTP methods, just like in a browser.

GET Request

// Simple GET request async function getData() { try { const response = await fetch('https://api.example.com/data'); const data = await response.json(); console.log(data); return data; } catch (error) { console.error('Error fetching data:', error); } }

POST Request

async function postData(url = '', data = {}) { try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } catch (error) { console.error('Error posting data:', error); throw error; } } // Usage postData('https://api.example.com/users', { username: 'webf', email: 'user@example.com' }) .then(data => console.log('Success:', data));

Other HTTP Methods

// PUT request async function updateData(url, data) { const response = await fetch(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return response.json(); } // DELETE request async function deleteData(url) { const response = await fetch(url, { method: 'DELETE' }); return response.json(); } // PATCH request async function patchData(url, data) { const response = await fetch(url, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return response.json(); }

Using in React Components

Here’s how you might use fetch inside a React component:

import { useState, useEffect } from 'react'; function DataFetcher() { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { fetch('https://api.example.com/data') .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }) .then(data => { setData(data); setLoading(false); }) .catch(error => { setError(error.message); setLoading(false); }); }, []); // Empty dependency array means this runs once on mount if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error}</div>; if (!data) return null; return <pre>{JSON.stringify(data, null, 2)}</pre>; }

Using Networking Libraries

Because the underlying fetch API is supported, popular networking libraries work out of the box.

Axios

npm install axios
import axios from 'axios'; // GET request const response = await axios.get('https://api.example.com/users'); console.log(response.data); // POST request const newUser = await axios.post('https://api.example.com/users', { name: 'John Doe', email: 'john@example.com' }); // With interceptors axios.interceptors.request.use(config => { config.headers.Authorization = `Bearer ${token}`; return config; });

React Query / TanStack Query

For advanced data fetching and caching:

npm install @tanstack/react-query
import { useQuery, useMutation, QueryClient, QueryClientProvider } from '@tanstack/react-query'; const queryClient = new QueryClient(); function App() { return ( <QueryClientProvider client={queryClient}> <Users /> </QueryClientProvider> ); } function Users() { const { data, isLoading, error } = useQuery({ queryKey: ['users'], queryFn: () => fetch('https://api.example.com/users').then(res => res.json()) }); const mutation = useMutation({ mutationFn: (newUser) => { return fetch('https://api.example.com/users', { method: 'POST', body: JSON.stringify(newUser), headers: { 'Content-Type': 'application/json' } }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }); } }); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; return ( <div> {data.map(user => <div key={user.id}>{user.name}</div>)} <button onClick={() => mutation.mutate({ name: 'New User' })}> Add User </button> </div> ); }

SWR

npm install swr
import useSWR from 'swr'; const fetcher = url => fetch(url).then(res => res.json()); function Profile() { const { data, error, isLoading } = useSWR('/api/user', fetcher); if (isLoading) return <div>Loading...</div>; if (error) return <div>Failed to load</div>; return <div>Hello {data.name}!</div>; }

Working with WebSockets

WebSockets are fully supported for real-time communication:

const socket = new WebSocket('wss://example.com/socket'); socket.addEventListener('open', (event) => { console.log('Connected to server'); socket.send('Hello Server!'); }); socket.addEventListener('message', (event) => { console.log('Message from server:', event.data); }); socket.addEventListener('error', (event) => { console.error('WebSocket error:', event); }); socket.addEventListener('close', (event) => { console.log('Disconnected from server'); }); // Send data socket.send(JSON.stringify({ type: 'message', content: 'Hello' })); // Close connection socket.close();

Local Data Storage

WebF provides standard Web Storage APIs for persisting data locally on the device.

localStorage

Use localStorage to store simple key-value pairs that persist even after the application is closed and reopened.

// Save data localStorage.setItem('username', 'JohnDoe'); localStorage.setItem('settings', JSON.stringify({ theme: 'dark', fontSize: 14 })); // Retrieve data const username = localStorage.getItem('username'); // "JohnDoe" const settings = JSON.parse(localStorage.getItem('settings')); // Remove item localStorage.removeItem('username'); // Clear all data localStorage.clear(); // Check if key exists if (localStorage.getItem('username') !== null) { console.log('Username exists'); } // Get number of items console.log(localStorage.length); // Iterate through items for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); const value = localStorage.getItem(key); console.log(`${key}: ${value}`); }

sessionStorage

Use sessionStorage for data that should only be available for the current session. This data is cleared when the application is completely closed.

// Save session data sessionStorage.setItem('sessionId', 'abc123'); sessionStorage.setItem('tempData', JSON.stringify({ items: [1, 2, 3] })); // Retrieve session data const sessionId = sessionStorage.getItem('sessionId'); const tempData = JSON.parse(sessionStorage.getItem('tempData')); // Clear session data sessionStorage.clear();

Storage Best Practices

// Helper functions for type-safe storage const storage = { set(key, value) { try { localStorage.setItem(key, JSON.stringify(value)); return true; } catch (error) { console.error('Storage error:', error); return false; } }, get(key, defaultValue = null) { try { const item = localStorage.getItem(key); return item ? JSON.parse(item) : defaultValue; } catch (error) { console.error('Storage error:', error); return defaultValue; } }, remove(key) { localStorage.removeItem(key); }, clear() { localStorage.clear(); } }; // Usage storage.set('user', { id: 1, name: 'John' }); const user = storage.get('user');

React Hook for localStorage

import { useState, useEffect } from 'react'; function useLocalStorage(key, initialValue) { const [storedValue, setStoredValue] = useState(() => { try { const item = window.localStorage.getItem(key); return item ? JSON.parse(item) : initialValue; } catch (error) { console.error(error); return initialValue; } }); const setValue = (value) => { try { const valueToStore = value instanceof Function ? value(storedValue) : value; setStoredValue(valueToStore); window.localStorage.setItem(key, JSON.stringify(valueToStore)); } catch (error) { console.error(error); } }; return [storedValue, setValue]; } // Usage function App() { const [name, setName] = useLocalStorage('name', 'Guest'); return ( <div> <input value={name} onChange={(e) => setName(e.target.value)} /> <p>Hello, {name}!</p> </div> ); }

Complex Data Storage

Important: IndexedDB is not supported in WebF.

For more complex data storage needs, such as managing a local database with queries, relationships, or large datasets, the recommended approach is to use a dedicated native plugin that can interface with a native database solution:

  • SQLite: Via a custom native plugin
  • Hive: Flutter’s native key-value database
  • Isar: High-performance Flutter database

See the Build Native Plugins guide to learn how to expose native database functionality to your WebF app.

Error Handling

Always implement proper error handling for network requests:

async function fetchWithErrorHandling(url) { try { const response = await fetch(url); // Check if response is ok (status 200-299) if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } // Check content type const contentType = response.headers.get('content-type'); if (!contentType || !contentType.includes('application/json')) { throw new TypeError('Response was not JSON'); } const data = await response.json(); return { success: true, data }; } catch (error) { if (error instanceof TypeError) { // Network error or CORS issue return { success: false, error: 'Network error. Please check your connection.' }; } else { // HTTP error return { success: false, error: error.message }; } } }

Request Timeouts

Implement timeouts to prevent hanging requests:

async function fetchWithTimeout(url, timeout = 5000) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); try { const response = await fetch(url, { signal: controller.signal }); clearTimeout(timeoutId); return await response.json(); } catch (error) { if (error.name === 'AbortError') { throw new Error('Request timeout'); } throw error; } }

Next Steps