Skip to main content

Kickstarting a type-safe app using Occtoo

· 9 min read
Emil Nilsson
Browser whisperer

In this article, we'll explore how to quickly set up a frontend application using Occtoo as a data provider and the openapi-typescript-codegen library to enhance development with React, React Query, and TypeScript. By following a schema-first approach and leveraging typed queries, we'll ensure accurate typings and autocompletion for all operations against your Occtoo Destination API endpoint. Let's get started!

Frontend example of a product listing page

Occtoo as a data source

Occtoo is an Experience Data Platform designed to accelerate the way companies create meaningful customer experiences across various touchpoints. Our platform is tailored to assist digital officers, marketers, and developers in transitioning to a new paradigm where they can dedicate less time to data integration and more time to unleashing their creative potential with data.

An Occtoo Destination offers one or more endpoints that allow you to query specific subsets of your data. It also encompasses facets and various mechanisms for fine-tuning queries by leveraging them.

For more in-depth information about how to query a destination, please refer to the destination docs.

In this article, we will be utilizing the following destination endpoint, generated for demonstration purposes:

https://global.occtoo.com/occtoodemo/occtooFrontEndDocumentation/v3

Setting up a project with Vite

Prerequisites: Node.js installed (https://nodejs.org/)

Vite is a build tool designed to simplify the process of setting up a React app with minimal configuration. While there are other alternatives like Next.js that are often preferred for large-scale production applications, Vite shines when you're either learning React or eager to swiftly start building a single-page application.

With Vite, you gain access to a development server for local development, along with a suite of commands for tasks like building, testing, and deploying your project to live environments. Additionally, Vite offers seamless support for TypeScript right out of the box.

You can use the following script to scaffold a new Vite project:

npm create vite@latest

Then follow the prompts to enter a project name, select React as framework, and Typescript as variant (Typescript + SWC = faster compiler).

Looking at package.json we should now have these commands:

"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
}

To start the app, run:

npm run dev

Our newly created app is now up and running and we're all set to begin developing.

Schema-first with Codegen

openapi-typescript-codegen is an open-source code generation tool that generates TypeScript client code from an OpenAPI specification including interfaces and classes for API requests and responses. The tool provides a simple and efficient way to generate client code with type-safety, making it easier for developers to consume REST-APIs as well as reducing the amount of manual work required to build the client code.

Use the command below to install openapi-typescript-codegen.

npm install openapi-typescript-codegen

And add this script to the scripts-section in your package.json file:

"codegen": "openapi 
--input https://global.occtoo.com/occtoodemo/occtooFrontEndDocumentation/v3/openapi
--output ./src/generated"

This script will utilize an Open API specification (JSON) provided as input (https://global.occtoo.com/occtoodemo/occtooFrontEndDocumentation/v3/openapi) to generate Typescript typings, and save them to the designated output folder.

The scripts section in your package.json file should now look like this:

"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"codegen": "openapi --input https://global.occtoo.com/occtoodemo/occtooFrontEndDocumentation/v3/openapi --output ./src/generated"
}

Now use the command below to run the script:

npm run codegen 

After executing this command, all types necessary to call the API will be generated and stored in the src/generated folder for later use when making API calls with React Query (also referred to as Tanstack Query).

note

This command needs to be executed each time a change is made to the OpenAPI spec provided. If not, this might lead to local typings not matching the destination endpoint output anymore.

React Query

Next, we need to add React Query to make HTTP requests against our API. React Query is a library that provides a powerful and flexible way to fetch, cache, and update data in React. It is designed to simplify the process of making asynchronous or remote data requests and provides a number of helpful features such as caching, polling, and refetching.

Install React Query using the command below:

npm install @tanstack/react-query 

Next, import the dependencies and create a new instance of the QueryClient. Then pass it to the QueryClientProvider component wrapping the App component.

/src/main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
},
},
});

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</React.StrictMode>,
)
note

The refetchOnWindowFocus option is optional, but tells React Query to not refetch any query on window focus.

Querying the Destination Endpoint

Finally, it’s time to write our first query. We will be using React Query and generated types to execute a query and render the result in a list. Specifically, we'll import the useQuery hook from @tanstack/react-query and the client and response type generated from the src/generated folder using the codegen script we added earlier.

To get started, let's import the required dependencies to App.tsx:

/src/App.tsx
import { useQuery } from '@tanstack/react-query';
import { DefaultService, productsApiResponse } from './generated';

Next, we can use the useQuery hook:

/src/App.tsx
const { data, isLoading, isError } = useQuery<productsApiResponse>(['products'], () =>
DefaultService.products({
top: 50,
skip: 0,
includeTotals: true,
}),
);

Here's what's happening in the code above:

  • The useQuery hook returns an object with a number of helpful properties. We’re using three of them: data, isLoading, and isError. These properties help us render different results depending on the state of the query.
  • We specify the type of the data that will be returned from the query using the productsApiResponse type from the generated types.
  • The first parameter of the useQuery function takes a QueryKey value (['products']). This key is used for client-side cache handling and should be unique for each query in the app.
  • The second parameter is a function, in this case a query to the API using the client generated by the codegen library.
  • Inside this function, we set a number of parameters to filter our result. In this case we request the first 50 products and the count of the total available products.

By using this pattern, we can ensure that the query result is fully typed, enabling autocompletion. Furthermore, the variables top, skip, and includeTotals are all fully typed, thanks to the typings generated from the OpenAPI specification which provides peace of mind and allows us to set up our query confidently, without worrying about unexpected client-side errors.

Now, let's render the result by using the different state variables provided by useQuery:

/src/App.tsx
if (isLoading) return <div>Loading...</div>;

if (isError) return <div> Error fetching data</div>;

return (
<ul>
{data?.results?.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);

In the code above, we render different components depending on the state of the query. If the data is loading, we show a "Loading..." message. If there's an error fetching the data, we show an error message. Otherwise, we render a list of entries returned by the query.

As you can see, the response of the query is fully typed and provides autocompletion when typing out the result.

An image from the static

Finally, App.tsx should look something like this:

/src/App.tsx
import { useQuery } from '@tanstack/react-query';
import { DefaultService, productsApiResponse } from './generated';

function App() {
const { data, isLoading, isError } = useQuery<productsApiResponse>(['products'], () =>
DefaultService.products({
top: 50,
skip: 0,
includeTotals: true,
}),
);

if (isLoading) return <div>Loading...</div>;

if (isError) return <div> Error fetching data</div>;

return (
<ul>
{data?.results?.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
}

export default App;

Next steps: Pagination and Filtering

After querying and generating a product result, you may also want to add pagination or execute additional queries to refine and narrow down the results using specific product attributes such as product type, category, or color. Occtoo simplifies this process by including facets in the response for each query made against a Destination. These facets can be utilized in subsequent queries to streamline the refinement of your search results.

Facets serve as a valuable means to incorporate relevant options for refining search results. When presented as filters, they become a dynamic subset of filtering criteria, adapting in real-time based on the results obtained from each query. Facets empower users to fine-tune their search and pinpoint precisely what they are seeking.

Much like the previous query result array, facets are also included in each query data object and come with comprehensive type specifications.

An image from the static

This makes it straightforward to render and construct a filter component. For more comprehensive insights and examples demonstrating how this is accomplished, please feel free to explore our example project repository.

An image from the static

Example project

We've created an example project on GitHub that demonstrates how to use the libraries discussed in this article to set up a product listing app using Occtoo efficiently. You're welcome to use this project as a starting point or boilerplate for your next application that uses Occtoo as a data service.

https://github.com/Occtoo/occtoo-boilerplate-react

Summary

In this article, we explored how to quickly set up a frontend application using Occtoo and the openapi-typescript-codegen library to enhance development with React, React Query, and TypeScript. By following a schema-first approach and leveraging typed queries, we ensured accurate typings and autocompletion for all operations against our Occtoo Destination API endpoint.

We learned how to set up a Vite project with TypeScript, install necessary npm packages, use the OpenAPI specification to generate TypeScript typings, and how to use React Query (also referred to as Tanstack Query) with the generated types to execute a query and render the result.

Working with Occtoo destinations following a schema-first approach provides us with the assurance that we are accessing the correct data, helping us write more reliable code with fewer bugs.