CardQL SDK for React web applications with hooks, context providers, and pre-built components.
npm install @cardql/react
# or
yarn add @cardql/react
# or
pnpm add @cardql/react
Wrap your app with the CardQL provider:
import React from "react";
import { CardQLProvider } from "@cardql/react";
function App() {
return (
<CardQLProvider
config={{
apiKey: "your-api-key",
endpoint: "https://api.cardql.com/graphql",
}}>
<YourApp />
</CardQLProvider>
);
}
Use CardQL hooks in your components:
import React from "react";
import { usePayments, useCreatePayment } from "@cardql/react";
function PaymentList() {
const { data: paymentsData, loading, error } = usePayments();
const createPayment = useCreatePayment({
onSuccess: (data) => {
console.log("Payment created:", data.createPayment);
},
});
const handleCreatePayment = async () => {
await createPayment.mutateAsync({
amount: "10.00",
currency: "USD",
merchantID: "merchant_123",
userID: "user_456",
});
};
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h2>Payments</h2>
<button onClick={handleCreatePayment} disabled={createPayment.loading}>
{createPayment.loading ? "Creating..." : "Create Payment"}
</button>
{paymentsData?.payments.map((payment) => (
<div key={payment.id}>
{payment.amount} {payment.currency} - {payment.status}
</div>
))}
</div>
);
}
Use ready-made components:
import React from "react";
import { PaymentForm } from "@cardql/react";
function CheckoutPage() {
return (
<div>
<h1>Checkout</h1>
<PaymentForm
merchantID="merchant_123"
userID="user_456"
onSuccess={(payment) => {
console.log("Payment successful:", payment);
// Redirect to success page
}}
onError={(error) => {
console.error("Payment failed:", error);
// Show error message
}}
/>
</div>
);
}
interface CardQLProviderProps {
config: CardQLConfig;
children: ReactNode;
}
<CardQLProvider config={{ apiKey: "...", endpoint: "..." }}>
<App />
</CardQLProvider>;
Access the CardQL context:
const { cardql, config } = useCardQL();
Get the CardQL client directly:
const cardql = useCardQLClient();
Get the CardQL API directly:
const api = useCardQLApi();
Generic hook for GraphQL queries:
const { data, loading, error, refetch } = useQuery(
"query GetPayments { payments { id amount status } }",
variables,
{
enabled: true,
refetchOnMount: true,
refetchInterval: 30000,
onSuccess: (data) => console.log(data),
onError: (error) => console.error(error),
}
);
Generic hook for GraphQL mutations:
const { data, loading, error, mutate, mutateAsync } = useMutation(
"mutation CreatePayment($input: CreatePaymentInput!) { createPayment(input: $input) { id } }",
{
onSuccess: (data, variables) => console.log("Success:", data),
onError: (error, variables) => console.error("Error:", error),
onSettled: (data, error, variables) => console.log("Settled"),
}
);
// Query hooks
const { data, loading, error } = useAccounts();
const { data, loading, error } = useAccount(accountID);
// Mutation hooks
const createAccount = useCreateAccount();
const updateAccount = useUpdateAccount();
const deleteAccount = useDeleteAccount();
// Query hooks
const { data, loading, error } = useCustomers();
const { data, loading, error } = useCustomer(customerID);
// Mutation hooks
const createCustomer = useCreateCustomer({
onSuccess: (data) => {
console.log("Customer created:", data.createCustomer);
},
});
// Usage
await createCustomer.mutateAsync({
firstName: "John",
lastName: "Doe",
email: "john@example.com",
});
// Query hooks
const { data, loading, error } = usePayments();
const { data, loading, error } = usePayment(paymentID);
// Mutation hooks
const createPayment = useCreatePayment();
const updatePayment = useUpdatePayment();
const deletePayment = useDeletePayment();
// Usage
const handlePayment = async () => {
try {
const result = await createPayment.mutateAsync({
amount: "25.99",
currency: "USD",
merchantID: "merchant_123",
userID: "user_456",
description: "Product purchase",
});
console.log("Payment created:", result.createPayment);
} catch (error) {
console.error("Payment failed:", error);
}
};
const { data, loading, error } = useMerchants();
const { data, loading, error } = useMerchant(merchantID);
const createMerchant = useCreateMerchant();
const updateMerchant = useUpdateMerchant();
const deleteMerchant = useDeleteMerchant();
const { data, loading, error } = useLedgers();
const { data, loading, error } = useLedger(ledgerID);
const createLedger = useCreateLedger();
const updateLedger = useUpdateLedger();
const deleteLedger = useDeleteLedger();
Pre-built payment form component:
<PaymentForm
merchantID="merchant_123"
userID="user_456"
onSuccess={(payment) => {
// Handle successful payment
router.push("/success");
}}
onError={(error) => {
// Handle payment error
setErrorMessage(error.message);
}}
className="custom-payment-form"
disabled={false}
/>
Execute custom GraphQL queries:
import { useQuery } from "@cardql/react";
function CustomPaymentList() {
const { data, loading, error } = useQuery(
`
query GetPaymentsByMerchant($merchantID: String!) {
payments(where: { merchantID: $merchantID }) {
id
amount
currency
status
customer {
firstName
lastName
}
}
}
`,
{
merchantID: "merchant_123",
}
);
// ... render logic
}
// Poll every 30 seconds
const { data } = usePayments({
refetchInterval: 30000,
});
// Manual refetch
const { data, refetch } = usePayments();
const handleRefresh = () => {
refetch();
};
import { usePayments, useCreatePayment } from "@cardql/react";
function PaymentComponent() {
const { data, loading, error } = usePayments({
onError: (error) => {
console.error("Failed to load payments:", error);
// Send to error tracking service
},
});
const createPayment = useCreatePayment({
onError: (error, variables) => {
console.error("Payment creation failed:", error);
// Show user-friendly error message
if (error.code === "INSUFFICIENT_FUNDS") {
alert("Insufficient funds");
} else {
alert("Payment failed. Please try again.");
}
},
});
// ... component logic
}
function PaymentList() {
const { data, loading, error } = usePayments();
const createPayment = useCreatePayment();
if (loading) {
return <PaymentSkeleton />;
}
if (error) {
return <ErrorMessage error={error} />;
}
return (
<div>
<button
onClick={() => createPayment.mutate(paymentData)}
disabled={createPayment.loading}>
{createPayment.loading ? <Spinner /> : "Create Payment"}
</button>
{/* Payment list */}
</div>
);
}
import { useForm } from "react-hook-form";
import { useCreateCustomer } from "@cardql/react";
function CustomerForm() {
const { register, handleSubmit, reset } = useForm();
const createCustomer = useCreateCustomer({
onSuccess: () => {
reset(); // Clear form on success
},
});
const onSubmit = async (data) => {
await createCustomer.mutateAsync(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("firstName")} placeholder="First Name" />
<input {...register("lastName")} placeholder="Last Name" />
<input {...register("email")} placeholder="Email" />
<button type="submit" disabled={createCustomer.loading}>
{createCustomer.loading ? "Creating..." : "Create Customer"}
</button>
</form>
);
}
The React SDK is fully typed:
import type { Payment, CreatePaymentInput } from "@cardql/react";
const createPayment = useCreatePayment();
// TypeScript knows the exact shape of the data
const handleSubmit = async (input: CreatePaymentInput) => {
const result = await createPayment.mutateAsync(input);
// result.createPayment is typed as Payment
console.log(result.createPayment.id);
};
The pre-built components use CSS classes that you can style:
.cardql-payment-form {
max-width: 400px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
}
.cardql-form-field {
margin-bottom: 16px;
}
.cardql-form-field label {
display: block;
margin-bottom: 4px;
font-weight: 500;
}
.cardql-form-field input,
.cardql-form-field select {
width: 100%;
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 4px;
}
.cardql-error {
color: #dc3545;
margin-bottom: 16px;
padding: 8px;
background-color: #f8d7da;
border-radius: 4px;
}
.cardql-submit-button {
width: 100%;
padding: 12px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.cardql-submit-button:disabled {
background-color: #6c757d;
cursor: not-allowed;
}
Place the CardQL provider at the highest level where you need CardQL functionality:
// ✅ Good - wrap entire app
function App() {
return (
<CardQLProvider config={config}>
<Router>
<Routes>
<Route path="/payments" component={PaymentPage} />
<Route path="/customers" component={CustomerPage} />
</Routes>
</Router>
</CardQLProvider>
);
}
Use error boundaries to catch and handle errors:
import { ErrorBoundary } from "react-error-boundary";
function ErrorFallback({ error, resetErrorBoundary }) {
return (
<div role="alert">
<h2>Something went wrong:</h2>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
}
function App() {
return (
<CardQLProvider config={config}>
<ErrorBoundary FallbackComponent={ErrorFallback}>
<YourApp />
</ErrorBoundary>
</CardQLProvider>
);
}
Always handle loading states for better UX:
function PaymentList() {
const { data, loading, error } = usePayments();
if (loading) return <PaymentSkeleton />;
if (error) return <ErrorMessage error={error} />;
if (!data?.payments.length) return <EmptyState />;
return <PaymentGrid payments={data.payments} />;
}
MIT
For support, please contact the CardQL team or visit our documentation.