React
React Use @enfyra/sdk-react for a client-rendered React application that uses Enfyra as its backend. The package provides a client provider, auth state, queries, mutations, storage, and realtime hooks. This package is CSR-only. It is for a separately deployed React application, n
React
Use @enfyra/sdk-react for a client-rendered React application that uses Enfyra as its backend. The package provides a client provider, auth state, queries, mutations, storage, and realtime hooks.
This package is CSR-only. It is for a separately deployed React application, not an extension running inside Enfyra Admin.
Install
yarn add @enfyra/sdk-react @enfyra/sdk-core zustand
Add the Provider
import { EnfyraProvider } from '@enfyra/sdk-react'
export function App() {
return (
<EnfyraProvider
config={{
baseUrl: 'https://admin.example.com',
}}
>
<ApplicationRoutes />
</EnfyraProvider>
)
}
Configure the Enfyra App URL once. The provider creates the client and keeps the signed-in session available to the SDK hooks.
Access the Original Client
import { useEnfyra } from '@enfyra/sdk-react'
function PublishButton({ articleId }: { articleId: number }) {
const client = useEnfyra()
return (
<button onClick={() => client.post('/articles/publish', { articleId })}>
Publish
</button>
)
}
Authentication
import { useAuth } from '@enfyra/sdk-react'
function Session() {
const { user, isAuthenticated, pending, error, login, logout, refresh } = useAuth()
if (pending) return <p>Checking session…</p>
if (error) return <button onClick={() => refresh()}>Retry session check</button>
if (isAuthenticated) {
return (
<p>
Signed in as {user?.email}
<button onClick={() => logout()}>Sign out</button>
</p>
)
}
return (
<button onClick={() => login({ email, password, remember: true })}>
Sign in
</button>
)
}
Auth state is shared by the provider. A login, logout, or refresh updates all consumers.
For cookie OAuth:
const client = useEnfyra()
window.location.href = client.auth.getOAuthRedirectUrl(
'google',
`${window.location.origin}/dashboard`,
)
Query Data
interface Article {
id: number
title: string
status: string
}
function ArticleList() {
const { data, pending, error, meta, refresh } = useQuery<Article[]>('articles', {
select: ['id', 'title', 'status'],
filter: { status: { _eq: 'published' } },
sort: '-createdAt',
limit: 20,
meta: ['totalCount'],
})
if (pending) return <p>Loading…</p>
if (error) return <button onClick={refresh}>Try again</button>
return (
<ul>
{data?.map((article) => <li key={article.id}>{article.title}</li>)}
</ul>
)
}
Queries execute when the component mounts and whenever their serialized query inputs change. Use immediate: false for a manual query.
Mutations
function CreateArticle() {
const createArticle = useMutation<Article>('articles', {
operation: 'insert',
onSuccess: () => console.log('Created'),
})
async function submit() {
await createArticle.execute({
data: { title: 'New article', status: 'draft' },
})
}
return (
<button disabled={createArticle.pending} onClick={submit}>
Create
</button>
)
}
Update and delete:
const updateArticle = useMutation<Article>('articles', { operation: 'update' })
await updateArticle.execute({ id: articleId, data: { status: 'published' } })
const deleteArticle = useMutation('articles', { operation: 'delete' })
await deleteArticle.execute({ id: articleId })
The hook returns null after a handled failure and exposes the error in hook state. Use the original client when code must throw to an error boundary or inspect the complete error details.
Storage
function UploadField() {
const { upload, uploading, getDownloadUrl } = useStorage()
async function handleFile(file: File) {
const record = await upload(file, { folder: 'customer-documents' })
if (record) console.log(getDownloadUrl(record.id))
}
return (
<input
type="file"
disabled={uploading}
onChange={(event) => {
const file = event.target.files?.[0]
if (file) void handleFile(file)
}}
/>
)
}
Realtime
function Notifications() {
const realtime = useWebSocket('notifications', { immediate: true })
useEffect(() => {
if (!realtime.connected) return
return realtime.on('notification:created', (payload) => {
console.log(payload)
})
}, [realtime.connected, realtime.on])
return realtime.error ? <p>{realtime.error.message}</p> : null
}
CSR Boundary
EnfyraProvider throws when evaluated on the server. Do not use @enfyra/sdk-react in React Server Components or an SSR render process. Use the future Next.js adapter for App Router integration; until that package is ready, use @enfyra/sdk-core only in request-scoped server code and build the proxy explicitly.