The following overview for the advanced to-do application will explain the architecture, components, features, and pre-requisites necessary to build the application with the Ontology SDK (OSDK):
To access and install the advanced to-do application example, open the Code Workspaces application and choose to create a + New Workspace. Then, select VS Code > Applications and find the application in the curated examples list.
You can also search for the application in the platform Examples (Build with AIP) or by searching the Ontology SDK reference examples in Developer Console.
The sections below describe the architecture of the advanced to-do application you can create, including routing, client configuration, and usage in components.
The application follows a modern React architecture that includes the following features:
useProjects
, useTasks
, and so on).The advanced to-do application uses React Router ↗ for client-side routing. The routing configuration is set up in the main application entry point.
The application uses createBrowserRouter
from React Router with a simple configuration:
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
const router = createBrowserRouter( [ { path: "/", element: <Home />, }, { path: "/auth/callback", element: <AuthCallback />, }, ], { basename: import.meta.env.BASE_URL }, ); ReactDOM.createRoot(document.getElementById("root")!).render( <StrictMode> <OsdkProvider client={client}> <RouterProvider router={router} />, </OsdkProvider> </StrictMode> );
The following routes are used:
/
: The main application home page./auth/callback
: The route that handles OAuth authentication callbacks.Authentication is managed through the OSDK OAuth client, which automatically redirects unauthenticated users to the login page.
OsdkProvider
componentThe advanced to-do application uses the OsdkProvider
component from the @osdk/react
package to provide authentication context and client access throughout the component tree. This strategy follows the React context pattern, making OAuth authentication and OSDK client operations available without prop drilling.
Copied!1 2 3 4 5 6 7 8 9 10 11
import { OsdkProvider } from "@osdk/react"; import client from "./client"; // In the application's root ReactDOM.createRoot(document.getElementById("root")!).render( <StrictMode> <OsdkProvider client={client}> <RouterProvider router={router} /> </OsdkProvider> </StrictMode> );
The client is configured in a separate client.ts
file that handles the following:
The OAuth client handles the following:
Components can access the authenticated OSDK client using the useOsdk
hook:
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
import { useOsdk } from '@osdk/react'; function MyComponent() { const { client } = useOsdk(); const fetchData = useCallback(async () => { const tasksPage = await client(OsdkITask).where({ projectId: { $eq: project.$primaryKey }, }).fetchPage({ $orderBy: { "dueDate": "desc", "status": "asc" }, }); // Process and return the data }, [client, project.$primaryKey]); // Rest of the component }
This usage pattern ensures the following:
The advanced to-do application uses environment variables for configuration, allowing for different settings between development and production environments. Configuration values are primarily applied through meta tags in the HTML, which are then read by the client.ts
file.
The application uses the following environment files:
.env
: Base environment variables used in all environments..env.development
: Development-specific variables (used with npm run dev
)..env.production
: Production-specific variables (used with npm run build
).index.html
The application uses meta tags in index.html
to dynamically configure the OSDK client. This approach avoids hardcoding sensitive information directly into JavaScript code:
Copied!1 2 3 4
<meta name="osdk-clientId" content="%VITE_FOUNDRY_CLIENT_ID%" /> <meta name="osdk-redirectUrl" content="%VITE_FOUNDRY_REDIRECT_URL%" /> <meta name="osdk-foundryUrl" content="%VITE_FOUNDRY_API_URL%" /> <meta name="osdk-ontologyRid" content="%VITE_FOUNDRY_ONTOLOGY_RID%" />
osdk-clientId
: The OAuth client ID for authentication.osdk-redirectUrl
: The URL for redirect after successful authentication.osdk-foundryUrl
: The base URL of the Foundry API.osdk-ontologyRid
: The resource identifier for the application's ontology.# Authentication settings
VITE_FOUNDRY_CLIENT_ID=your-client-id
VITE_FOUNDRY_REDIRECT_URL=http://localhost:3000/
VITE_FOUNDRY_API_URL=https://<my-foundry-instance>.palantirfoundry.com
VITE_FOUNDRY_ONTOLOGY_RID=ri.ontology.main.ontology.12345678-abcd-1234-efgh-1234567890ab
The development environment typically uses the following configuration:
Example .env.development
:
VITE_FOUNDRY_CLIENT_ID=your-dev-client-id
VITE_FOUNDRY_REDIRECT_URL=http://localhost:3000/
VITE_FOUNDRY_API_URL=https://dev-foundry.palantirfoundry.com
VITE_FOUNDRY_ONTOLOGY_RID=ri.ontology.main.ontology.dev-12345
The production environment uses the following configuration:
Example .env.production
:
VITE_FOUNDRY_CLIENT_ID=your-prod-client-id
VITE_FOUNDRY_REDIRECT_URL=https://your-production-domain.com/
VITE_FOUNDRY_API_URL=https://prod-foundry.palantirfoundry.com
VITE_FOUNDRY_ONTOLOGY_RID=ri.ontology.main.ontology.prod-67890
The advanced to-do application uses OSDK interfaces to implement the following polymorphic task types:
ITask
: The base interface for all tasks.osdkCodingTask
: The implementation for coding-related tasks.osdkLearningTask
: The implementation for learning tasks with media content.Copied!1 2 3 4 5 6 7
// The task details component decides which concrete implementation to render if (task.osdkTask.$objectType === "osdkCodingTask") { return <TaskDetailsCoding task={task} />; } if (task.osdkTask.$objectType === "osdkLearningTask") { return <TaskDetailsLearning task={task} />; }
The application demonstrates working with binary content through media sets:
Copied!1 2 3 4 5 6 7
// Fetching and displaying media content from media sets const response = await learningTask.mediaReference.fetchContents(); const blob: Blob | undefined = await response.blob(); const mediaUrl = blob ? URL.createObjectURL(blob) : ""; // Getting media metadata const mediaTypeResp = await learningTask.mediaReference.fetchMetadata();
Media rendering is handled dynamically based on the media type:
The application uses runtime-derived properties to efficiently calculate aggregate statistics at the server level:
Copied!1 2 3 4 5 6 7 8 9
.withProperties({ "numberOfTasks": (baseObjectSet) => baseObjectSet.pivotTo("osdkTodoTask").aggregate("$count"), "numberOfCompletedTasks": (baseObjectSet) => baseObjectSet.pivotTo("osdkTodoTask").where({ "status": { $eq: "COMPLETED" }, }).aggregate("$count"), // Additional statistics... })
The example above demonstrates the following:
pivotTo
.where
conditions.aggregate
.The application accesses and uses ontology metadata to create dynamic interfaces:
Copied!1 2 3 4
const getObjectTypeMetadata = useCallback(async () => { const objectTypeMetadata = await client.fetchMetadata(OsdkITask); setMetadata(objectTypeMetadata); }, []);
This metadata is used for the following:
The application implements real-time updates using OSDK subscriptions:
Copied!1 2 3 4 5 6 7 8 9 10
const subscription = client(OsdkITask) .where({ projectId: { $eq: project.$primaryKey } }) .subscribe({ onChange(update) { // Handle real-time updates }, onSuccessfulSubscription() { /* ... */ }, onError(err) { /* ... */ }, onOutOfDate() { /* ... */ }, });
This provides the following benefits:
The application also demonstrates batch fetching and caching of user data:
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
const getBatchUserDetails = useCallback(async (userIds: string[]) => { const cachedUsers: UserDetails = {}; const usersToFetch: string[] = []; userIds.forEach((userId) => { const cachedUser: State<unknown, unknown> | undefined = cache.get(`user-${userId}`); if (cachedUser && cachedUser.data) { cachedUsers[userId] = cachedUser.data as User; } else { usersToFetch.push(userId); } }); if (usersToFetch.length > 0) { const usersRequest = await getBatch(client, usersToFetch.map((userId) => ({ userId }))); Object.entries(usersRequest.data).forEach(([userId, user]) => { cachedUsers[userId] = user; mutate(`user-${userId}`, user, { revalidate: false }); }); } return cachedUsers; }, [cache]);
For more information about OSDK and the advanced features used in this application, review the documentation below: