JavaScript Examples
Real-world JavaScript and TypeScript examples for integrating with the FundlyHub API using standard Fetch API.
Getting Started
API Configuration
Define your API base URL and helper functions.
javascript
// config.js
export const API_BASE_URL = 'https://api.fundlyhub.org/api/v1';
// Helper for authenticated requests (uses httpOnly cookies)
export const authenticatedFetch = async (endpoint, options = {}) => {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
...options,
credentials: 'include',
headers: {
'Content-Type': 'application/json',
...options.headers,
},
});
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new Error(error.message || `Request failed with status ${response.status}`);
}
return response.json();
};Fundraiser Management
Create a Complete Fundraiser
Full example including image handling and error management.
javascript
import { authenticatedFetch, API_BASE_URL } from './config';
export const createFundraiser = async (fundraiserData) => {
try {
// 1. Create the fundraiser
const fundraiser = await authenticatedFetch('/fundraisers', {
method: 'POST',
body: JSON.stringify({
title: fundraiserData.title,
// Required. Lowercase letters, digits and hyphens; check it first with
// GET /fundraisers/check-slug/:slug.
slug: fundraiserData.slug,
summary: fundraiserData.summary,
story_html: fundraiserData.story_html,
// Integer cents — the form field is dollars, so convert here.
goal_amount_cents: Math.round(parseFloat(fundraiserData.goal_amount) * 100),
// A category id, slug or name. There is no `category_id` field.
category: fundraiserData.category,
location: fundraiserData.location,
tags: fundraiserData.tags || [],
end_date: fundraiserData.end_date,
// Always send a status: 'draft' saves work in progress (and is the
// only create an unverified email may make); 'active' publishes.
status: fundraiserData.publish ? 'active' : 'draft'
})
});
// The campaign is wrapped in `data`. Read its status: asking for
// 'active' can come back 'pending' when it is held for review.
console.log('Fundraiser created:', fundraiser.data.id, fundraiser.data.status);
return { success: true, data: fundraiser.data };
} catch (error) {
console.error('Error creating fundraiser:', error);
return { success: false, error: error.message };
}
};Fundraiser Dashboard Data
Fetch a user's fundraisers and their live statistics.
javascript
import { authenticatedFetch } from './config';
export const getFundraiserDashboard = async (myProfileId) => {
try {
// 1. Get the user's own campaigns. There is no /fundraisers/me: a
// profile's campaigns come from /users/:id/campaigns, and signing in
// as that profile includes its unpublished ones too.
const { data } = await authenticatedFetch(`/users/${myProfileId}/campaigns?limit=100`);
// The list also carries campaigns this person endorsed; keep their own.
const myFundraisers = data.filter(f => f.owner_user_id === myProfileId);
// 2. Calculate dashboard totals — money is integer cents.
const stats = {
total_campaigns: myFundraisers.length,
active_campaigns: myFundraisers.filter(f => f.status === 'active').length,
total_raised_cents: myFundraisers.reduce((sum, f) => sum + (Number(f.total_raised_cents) || 0), 0),
total_donors: myFundraisers.reduce((sum, f) => sum + (Number(f.donor_count) || 0), 0)
};
return {
success: true,
fundraisers: myFundraisers,
stats
};
} catch (error) {
console.error('Error loading dashboard:', error);
return { success: false, error: error.message };
}
};User Authentication
Complete Auth Flow
Sign up, sign in, and session management using AWS Cognito with httpOnly cookies.
javascript
// auth.js
const API_BASE_URL = 'https://api.fundlyhub.org/api/v1';
export const authService = {
// Sign up via Cognito
async register(userData) {
try {
const response = await fetch(`${API_BASE_URL}/cognito/signup`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify(userData)
});
if (!response.ok) throw new Error('Registration failed');
return { success: true, message: 'Confirmation code sent to email' };
} catch (error) {
return { success: false, error: error.message };
}
},
// Confirm registration with email code
async confirm(email, code) {
try {
const response = await fetch(`${API_BASE_URL}/cognito/confirm`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ email, code })
});
if (!response.ok) throw new Error('Confirmation failed');
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
},
// Sign in — cookies are set automatically
async login(email, password) {
try {
const response = await fetch(`${API_BASE_URL}/cognito/signin`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ email, password })
});
if (!response.ok) throw new Error('Invalid credentials');
const { user } = await response.json();
return { success: true, user };
} catch (error) {
return { success: false, error: error.message };
}
},
// Google SSO — redirect-based flow
loginWithGoogle() {
window.location.href = `${API_BASE_URL}/cognito/oauth/google`;
},
// Sign out — clears session cookies
async logout() {
await fetch(`${API_BASE_URL}/cognito/logout`, {
method: 'POST',
credentials: 'include'
});
window.location.href = '/';
},
// Check auth status
async isAuthenticated() {
try {
const response = await fetch(`${API_BASE_URL}/cognito/me`, {
credentials: 'include'
});
return response.ok;
} catch {
return false;
}
}
};Advanced Examples
Infinite Scroll with Search
Implement efficient pagination and search.
javascript
// useInfiniteSearch.js
const API_BASE_URL = 'https://api.fundlyhub.org/api/v1';
export const useInfiniteSearch = () => {
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(false);
const [page, setPage] = useState(0);
const LIMIT = 20;
const loadMore = useCallback(async (query = '', reset = false) => {
if (loading) return;
setLoading(true);
try {
const currentOffset = reset ? 0 : page * LIMIT;
const params = new URLSearchParams({
limit: LIMIT,
offset: currentOffset,
status: 'active'
});
// `q` is the listing's free-text filter (2+ characters).
if (query.trim().length >= 2) params.set('q', query.trim());
const response = await fetch(`${API_BASE_URL}/fundraisers?${params}`);
// The listing answers { data, pagination }, not a bare array.
const { data: newItems } = await response.json();
setItems(prev => reset ? newItems : [...prev, ...newItems]);
setPage(prev => reset ? 1 : prev + 1);
} finally {
setLoading(false);
}
}, [page, loading]);
return { items, loading, loadMore };
};