Skip to main content

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!

Release 17th June

ยท 3 min read
Andreas Wellรฉn
Andreas Wellรฉn
Swiss army knife of the product team

New features ๐ŸŽ‰โ€‹

Segments UX & UI overhaulโ€‹

The segment block has undergone a UX and UI overhaul to enhance usability and further improve the user experience. The segment overview now includes various sorting options, allowing users to sort not only by name but also by creation date and last update date. Additionally, segments now clearly display the type of selection being made (query or fixed) and the number of matching cards for that segment at the time of viewing the page.

New UX and UI on segment overview

New creation flowโ€‹

Segment creation is now a guided experience, assisting the user throughout the entire setup process. Initially, users set the name, card, category, and select one of the newly available, stylish icons.

Create new segment as a guided experience

The selection type is displayed clearly, explaining the differences between the various types. Creating fixed segments from a CSV file is highlighted as a separate option for ease of use (more on that feature further down ๐Ÿ™ƒโ†“).

Clearly stated segment selections

Segment types are out, categories are in!โ€‹

Previously, before creating a segment, users needed to create a segment type, which was a bit cumbersome. This step has now been removed and replaced with segment categories. Categories provide a way for users to structure segments based on the same card into a subgroup (a categorization, if you will ๐Ÿ˜‰). Categories are either created or selected during the segment creation process. If no specific category is actively selected, the segment will be automatically categorized with the card name.

New filtering panelโ€‹

The filter panel has been moved to pop out from the right-hand side when opened and now displays all properties of the card by default. The interaction of all property types (e.g., text, boolean, timestamp, etc.) has been improved to cater to a more user-centric experience.

New segment filter panel displaying all available properties

Better visualization of excluded itemsโ€‹

Finding which cards were manually excluded from a segment was previously practically impossible, stirring waves of uncertainty. The presentation of excluded items in the UI has been enhanced, and functionality has been added to easily display the entire list of excluded items for a better overview.

Better visualization of excluded items for fixed segments

Create fixed segments directly from a CSV fileโ€‹

It is now possible to create a fixed segment based on a pre-existing CSV file containing card IDs. During the creation process, the list is validated against the current dataset in Occtoo. Any missing IDs are flagged, making them available for download and further analysis.

Create a fixed segment based on a CSV list of IDs

Release 22th May

ยท 2 min read
Andreas Wellรฉn
Andreas Wellรฉn
Swiss army knife of the product team

New features ๐ŸŽ‰โ€‹

User managementโ€‹

We are excited to announce the release of our new user management feature, designed to streamline and enhance the way you manage user roles within Occtoo Studio. This feature introduces a structured approach to user permissions, ensuring that your team members have access to the tools they need without compromising security or operational efficiency. With the addition of three distinct user rolesโ€”Administrator, Contributor, and Readerโ€”you can now tailor access levels to align with individual responsibilities and organizational needs.

User management with user roles

The Administrator role is the most comprehensive, offering full access to all Occtoo blocks and operations, including user management. This role is ideal for team leaders or IT personnel who require the ability to oversee and modify user permissions, manage content, and perform any administrative tasks. On the other hand, the Contributor role provides similar extensive access but excludes the capability to manage users. Contributors can interact with and modify all Occtoo blocks and operations, making this role perfect for content creators and developers who need robust tools to carry out their tasks effectively without the additional responsibility of user administration.

Change user role

Lastly, the Reader role is designed for users who need to access and view data without the ability to make any changes. This role ensures that important information can be disseminated across the organization without risking unintended modifications or deletions. Readers can access all content within Occtoo, making it suitable for analysts, stakeholders, or any team members who require insight into the systemโ€™s data without engaging in operational actions. This new user management feature not only enhances security but also improves workflow by clearly delineating user responsibilities, empowering your team to work more efficiently and securely.

Define role when adding user

Release 7th March

ยท 2 min read
Andreas Wellรฉn
Andreas Wellรฉn
Swiss army knife of the product team

New features ๐ŸŽ‰โ€‹

Update existing destinationโ€‹

With this latest update to the Occtoo Studio, users now have the possibility to make non-breaking changes to their destination endpoints. This means that for any additions to existing endpoints, one doesn't need to create a new destination version, making life much easier for any dependent experiences

Update an existing destination

Users now have the possibility to add new or previously excluded properties to an endpoint. It is also possible to enable facets on existing properties.

Add or update properties

Additionally, users can add relations, including related information directly.

Add or update properties

Last but not least, users now have the possibility to alter the segments used in endpoints. The new update gives them the ability to include or remove segments, altering the presented result to their liking.

Modify segments of published destination

More information can be found in the docs.

Connected blocksโ€‹

Each individual object in Occtoo blocks now has "connected blocks" section, visualizing and linking which other individuals they either contribute to or are dependent on. This makes the life of users easier as they now can follow the chain from any block in any direction, understanding how all things in between are connected.

Object from block card linking to other induviduals

In the example above we can see how the card "Customer" is based on the source named "Customer", and that it also is used by 3 segments and 8 destinations.

More information can be found in the docs per block: source, card, segment and destination.

Opt-in gzip response from destinationsโ€‹

With the latest release, we also enable the possibility to request API content to be compressed using gzip. Gzip compression drastically reduces JSON response size, boosting network efficiency and accelerating data transfer. This optimization cuts down on redundant information, resulting in faster loading times and smoother user experiences, especially on slower connections.

By setting the content header property Accept-Encoding to value gzip (NB! lower case) the response will be returned using gzip compression. Side note: This will only apply on new destinations and destination versions.

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.