Overview
When embedding Flatfile in React applications, it’s important to understand the proper authentication flow for making API requests. The publishableKey is designed exclusively for initializing the FlatfileProvider component, while the accessToken should be used for all subsequent API requests.
Security Best Practice
The publishableKey is restricted to its intended use: initializing the Flatfile embedded experience. For security reasons, it cannot be used to make API requests directly. Instead, use the accessToken that becomes available after the Space is created.
Accessing the Access Token
Use the useFlatfileInternal hook to access the accessToken and sessionSpace after initialization:
import { useFlatfileInternal } from "@flatfile/react";
const MyComponent = () => {
const { accessToken, sessionSpace } = useFlatfileInternal();
// accessToken is available after the space is created
console.log("Access Token:", accessToken);
console.log("Session Space:", sessionSpace);
return <div>...</div>;
};
The accessToken will be undefined until the space is actually created (after openPortal() is called and the space is initialized). Always check for its availability before making API calls.
Making API Requests
Basic Pattern
Here’s the recommended pattern for making API requests using the accessToken:
import { useEffect, useState } from "react";
import { useFlatfileInternal } from "@flatfile/react";
import { FlatfileClient } from "@flatfile/api";
const MyComponent = () => {
const { accessToken, sessionSpace } = useFlatfileInternal();
const [data, setData] = useState([]);
useEffect(() => {
// Wait for the space to be created and token to be available
if (!accessToken || !sessionSpace?.id) return;
// Initialize the API client with the accessToken
const api = new FlatfileClient({ token: accessToken });
const fetchData = async () => {
try {
// Make API requests using the authenticated client
const workbooks = await api.workbooks.list({
spaceId: sessionSpace.id
});
setData(workbooks.data);
} catch (error) {
console.error("API Error:", error);
}
};
fetchData();
}, [accessToken, sessionSpace?.id]);
return <div>{data.length} workbooks found</div>;
};
Complete Example
Here’s a complete example showing how to set up authentication and make API requests:
import { useState, useEffect } from "react";
import {
FlatfileProvider,
useFlatfile,
useFlatfileInternal,
Space,
Workbook
} from "@flatfile/react";
import { FlatfileClient } from "@flatfile/api";
// Main App component
export const App = ({ publishableKey }) => (
<FlatfileProvider publishableKey={publishableKey}>
<SpaceWithApiAccess />
</FlatfileProvider>
);
// Component that accesses the API
const SpaceWithApiAccess = () => {
const { openPortal } = useFlatfile();
const { accessToken, sessionSpace } = useFlatfileInternal();
const [records, setRecords] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (!accessToken || !sessionSpace?.id) return;
// Initialize the API client with the accessToken
const api = new FlatfileClient({ token: accessToken });
const fetchData = async () => {
setLoading(true);
try {
// Example: Get all workbooks in the space
const workbooks = await api.workbooks.list({
spaceId: sessionSpace.id
});
console.log("Workbooks:", workbooks.data);
// Example: Get sheets from the first workbook
if (workbooks.data?.[0]?.id) {
const sheets = await api.sheets.list({
workbookId: workbooks.data[0].id
});
// Example: Get records from the first sheet
if (sheets.data?.[0]?.id) {
const sheetRecords = await api.records.get(sheets.data[0].id);
setRecords(sheetRecords.data?.records || []);
}
}
// Example: Get space details
const space = await api.spaces.get(sessionSpace.id);
console.log("Space details:", space.data);
} catch (error) {
console.error("API Error:", error);
} finally {
setLoading(false);
}
};
fetchData();
}, [accessToken, sessionSpace?.id]);
return (
<div>
<button onClick={() => openPortal()}>
Open Flatfile
</button>
{loading && <p>Loading...</p>}
{records.length > 0 && (
<div>
<h2>Records</h2>
<p>Fetched {records.length} records from the space</p>
</div>
)}
<Space config={{ name: "My Space" }}>
<Workbook
config={{
name: "My Workbook",
sheets: [
{
name: "Contacts",
slug: "contacts",
fields: [
{ key: "name", type: "string", label: "Name" },
{ key: "email", type: "string", label: "Email" },
]
}
]
}}
/>
</Space>
</div>
);
};
Common API Operations
Once you have the accessToken, you can perform various operations:
const api = new FlatfileClient({ token: accessToken });
// Spaces
await api.spaces.get(spaceId);
await api.spaces.update(spaceId, { name: "New Name" });
// Workbooks
await api.workbooks.list({ spaceId });
await api.workbooks.get(workbookId);
// Sheets
await api.sheets.list({ workbookId });
await api.sheets.get(sheetId);
// Records
await api.records.get(sheetId);
await api.records.insert(sheetId, [
{ firstName: "John", lastName: "Doe" }
]);
await api.records.update(sheetId, { records: [...] });
await api.records.delete(sheetId, { ids: ["rec_123"] });
// Files
await api.files.list({ spaceId });
await api.files.get(fileId);
// Jobs
await api.jobs.list({ spaceId });
await api.jobs.get(jobId);
// Documents
await api.documents.list(spaceId);
await api.documents.create(spaceId, {
title: "Guide",
body: "# Welcome"
});
Handling User Actions
For API calls triggered by user actions, ensure the token is available:
const MyComponent = () => {
const { accessToken, sessionSpace } = useFlatfileInternal();
const handleFetchRecords = async () => {
if (!accessToken) {
console.error("Space not initialized yet");
return;
}
const api = new FlatfileClient({ token: accessToken });
const records = await api.records.get(sheetId);
console.log(records);
};
return (
<button onClick={handleFetchRecords}>
Fetch Records
</button>
);
};
Creating a Custom Hook
For cleaner code, create a custom hook for API access:
import { useMemo } from "react";
import { useFlatfileInternal } from "@flatfile/react";
import { FlatfileClient } from "@flatfile/api";
const useFlatfileAPI = () => {
const { accessToken, sessionSpace } = useFlatfileInternal();
const api = useMemo(() => {
if (!accessToken) return null;
return new FlatfileClient({ token: accessToken });
}, [accessToken]);
return { api, sessionSpace, isReady: !!api };
};
// Usage
const MyComponent = () => {
const { api, sessionSpace, isReady } = useFlatfileAPI();
useEffect(() => {
if (!isReady) return;
const fetchData = async () => {
const workbooks = await api.workbooks.list({
spaceId: sessionSpace.id
});
console.log(workbooks);
};
fetchData();
}, [isReady, api, sessionSpace]);
return <div>...</div>;
};
Troubleshooting
”accessToken is undefined”
Cause: The space hasn’t been created yet.
Solution: Add null checks and wait for the token:
useEffect(() => {
if (!accessToken) {
console.log("Waiting for space to be created...");
return;
}
// Token is now available
}, [accessToken]);
“Cannot read property ‘id’ of undefined”
Cause: Trying to access sessionSpace.id before the space is created.
Solution: Check both accessToken and sessionSpace:
if (!accessToken || !sessionSpace?.id) return;
Key Differences
| Hook | Exposes |
|---|
useFlatfile | openPortal, closePortal, open, setListener, listener |
useFlatfileInternal | All of the above PLUS accessToken, setAccessToken, sessionSpace, setSessionSpace |
Summary
Three key steps for secure API requests:
- Use
publishableKey only for FlatfileProvider initialization
- Access
accessToken via useFlatfileInternal hook
- Initialize
FlatfileClient with the accessToken for all API requests
This pattern ensures your API calls are properly authenticated and scoped to the correct space.
Next Steps