Skip to main content

2 posts tagged with "react"

View All Tags

Build apps faster with @occtoo/destination-client

Β· 8 min read
Emil Nilsson
Browser whisperer

Terrance Fletcher, the ruthless music instructor in the movie Whiplash, once said, "There are no two words in the English language more harmful than 'good job'." While Fletcher's approach may be a bit extreme, the sentiment behind his words is clear: we should always strive to be better, to push ourselves to new heights, and to never settle for mediocrity. This philosophy is especially relevant in the world of software development, where innovation and continuous improvement are essential for success.

At Occtoo, we're committed to helping developers build better apps faster. Our platform provides a powerful set of tools and services that streamline the development process, enabling teams to deliver high-quality applications quickly and efficiently. One of the key components of our platform is the Occtoo Destination service, which allows developers to connect to various data sources and retrieve data from destination endpoints.

To make it even easier for developers to interact with destination endpoints, we've created the @occtoo/destination-client npm package. This package simplifies the process of fetching data from destination endpoints, providing an intuitive API and automatic TypeScript typings generation. In this blog post, we'll explore the features of the @occtoo/destination-client package and show you how to use it to build apps faster and more efficiently.

What is @occtoo/destination-client?​

The @occtoo/destination-client npm package is designed to simplify interactions with your data. Occtoo's platform enables seamless data integration across various systems, and this package acts as a client to efficiently retrieve data from these destinations. It ensures that developers can easily access and work with their data, enhancing productivity and streamlining development workflows.

Core concept - Studio-first, schema-first​

Core concept

Occtoo Studio provides a powerful UI that allows you to create destination endpoints effortlessly, without writing any code. This user-friendly interface lets you define data structures, including fields, types, and relationships with data from various systems.

After defining your destination endpoint in Occtoo Studio, you can use the @occtoo/destination-client package to generate a client. This package automatically creates TypeScript typings based on the schema defined in Occtoo Studio, ensuring type safety and enhancing code quality.

The advantage of this approach is that all type updates and changes can be made through the Occtoo Studio UI. Whenever you modify the schema, you can simply regenerate the client using the @occtoo/destination-client package. This ensures that your client stays up-to-date with the latest schema changes, eliminating the need for manual typings generation or tedious code updates.

Working schema-first with Occtoo Studio and @occtoo/destination-client offers a streamlined and efficient method for integrating with various systems. It removes the need for manual typing and provides a visual interface for managing data structures and mappings. This approach lets you focus on building your application logic, not having to deal with the code complexities of data integration.

Key Features​

  1. Quick Setup: Set up the client in minutes with minimal configuration πŸ•ΆοΈ
  2. TypeScript Support: Automatically generates necessary TypeScript typings, enhancing code quality and development speed πŸ•ΆοΈ
  3. Easy Regeneration: Easily regenerate the client to adapt to changes in the destination endpoint configurations πŸ•ΆοΈ
  4. Intuitive API: Provides a straightforward API for fetching data πŸ•ΆοΈ
  5. Small package size: The generated client is small in size, providing a minimal footprint in your application source code πŸ•ΆοΈ

Installation​

Prerequisites​

Before you start using the @occtoo/destination-client, you need to have an Occtoo account and a destination endpoint set up in Occtoo Studio. If you don't have an account, you can sign up for a free trial free trial to get started.

In this guide, we'll assume that you already have a React and TypeScript app up and running. The code examples provided below use an empty template created with Next.js, specifically utilizing the latest React Server Components feature. However, please note that the @occtoo/destination-client can be utilized in almost any JavaScript or TypeScript project.

To begin using @occtoo/destination-client, install it via npm:

npm install @occtoo/destination-client

Client setup​

Setting up the @occtoo/destination-client in your project is incredibly fast and easy. Here’s how you can get started:

  1. πŸ”Œ Add your destination config

    First, put your Occtoo Destination values in your .env file. If it doesn't exist, create one in the root of your project. Add the following values:

    OCCTOO_DESTINATION_ID=...
    OCCTOO_DESTINATION_URL=...

    The destination config can be found in the Occtoo Studio UI. For demo purposes if you don't have a destination, you can use the following values:

    OCCTOO_DESTINATION_ID=8903b5d2-9b7f-4297-a04e-a3d339187389
    OCCTOO_DESTINATION_URL=https://global.occtoo.com/occtoodemo/occtooFrontEndDocumentation/v3
  2. β˜• Generate the client

    Run the following command to generate the client:

    npx @occtoo/destination-client generate

    This command will generate the client based on the schema defined in Occtoo Studio.

  3. πŸš€ Start fetching

    Create an instance of the DestinationClient class and use it to interact with the destination endpoint:

    import { DestinationClient } from '@occtoo/destination-client';

    const getProducts = async () => {
    // initialize the client
    const destinationClient = new DestinationClient();

    // fetch data from destination endpoint "products"
    const destinationEndpointResponse = await destinationClient.post("/products", {
    filter: [
    {
    must: {
    category: ["Pants"],
    },
    },
    ],
    sortAsc: ["id"],
    skip: 0,
    top: 14,
    });

    return destinationEndpointResponse;
    };

    πŸŽ‰ Done!

  4. πŸ—οΈ Optional: Authenticate the Client

    If you're using a protected destination, you'll need to provide the client ID and client secret during client initialization. These credentials can be generated directly by registering an application through the destination view in the Occtoo Studio UI.

    First, add the client ID and client secret to your .env file:

    OCCTOO_DESTINATION_APP_ID=...
    OCCTOO_DESTINATION_APP_SECRET=...

    Then, authenticate the client:

    // initialize the client
    const destinationClient = new DestinationClient({
    credentials: {
    clientId: process.env.OCCTOO_DESTINATION_APP_ID,
    clientSecret: process.env.OCCTOO_DESTINATION_APP_SECRET,
    },
    });

    // authenticate the client
    const accessToken = await destinationClient.authenticate();

    // ... fetch data using the client

    The authenticate method will authenticate the current client instance, and also return an access token that you can store and use to make requests to the destination endpoint.

Fetching Data​

Once the client is initialized, you can utilize the auto-generated TypeScript typings to fetch data from the destination endpoint. The client provides methods for making HTTP requests to the destination endpoint, including filtering, sorting and pagination.

Filtering, sorting, and limiting data​

Here's an example of how you can fetch products from a destination endpoint, filter them by category, sort them by ID, and limit the results to 14 items:

const getProducts = async () => {
// initialize the client
const destinationClient = new DestinationClient();

// fetch data from destination endpoint "products"
const destinationEndpointResponse = await destinationClient.post("/products", {
filter: [
{
must: {
category: ["Pants"],
},
},
],
sortAsc: ["id"],
skip: 0,
top: 14,
});

return destinationEndpointResponse;
};

Using the Response​

The response from the destination endpoint will contain the data fetched based on the provided filters, sorting, and pagination. It will also include the auto-generated TypeScript typings, allowing you to access the data with type safety.

import { DestinationClient } from '@occtoo/destination-client';

const getProducts = async () => {
const destinationClient = new DestinationClient();

const destinationEndpointResponse = await destinationClient.post("/products", {
filter: [
{
must: {
category: ["Pants"],
},
},
],
sortAsc: ["id"],
skip: 0,
top: 14,
});

return destinationEndpointResponse;
};

export default async function ProductsComponent() {
const products = await getProducts();

return (
<div>
{products.results?.map((product) => (
<div key={product.id}>
<div>
{product.urls?.length && (
<img
src={product.urls.split('|')[0] + "?format=small"}
alt={product.id || "product"}
/>
)}
</div>
<div>{product.name}</div>
</div>
))}
</div>
);
}

Regenerating the Client​

One of the standout features of the @occtoo/destination-client is how easy it is to regenerate the client when there are changes to the destination endpoint schema or version. This ensures that your client remains up-to-date with minimal effort. By simply regenerating the client, any modifications made in the Occtoo Studio UI are seamlessly reflected in your application code.

  1. Optional: Update Configurations

    If there's a new version of the destination you're using, update your configuration settings as needed in your .env file:

    OCCTOO_DESTINATION_ID=...
    OCCTOO_DESTINATION_URL=...

    Note: A new version of a destination is typically created when breaking changes are introduced to the schema. If you're using the same version, you can skip this step.

  2. Regenerate the Client

    Run the following command to regenerate the client:

    npx @occtoo/destination-client generate

    Done! Your client is now updated with the latest schema changes and any breaking changes or new fields will now be reflected in your application code. 😲

No more shady type definition files, no more outdated clients​

No more struggling in the dark and being unaware of the types provided by REST APIs πŸ•΅οΈβ€β™‚οΈ No more outdated clients that break your app when the schema changes 🀯

The @occtoo/destination-client npm package simplifies the process of interacting with your data, offering an intuitive API, automatic TypeScript typings generation, and effortless client regeneration. Through Occtoo and this package, developers can seamlessly connect to various systems and effortlessly fetch data without the headache of manual configurations and tedious updates.

Give it a shot today or book a demo and witness the smoothness of data integration with Occtoo's destination services!

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.