A team of First Line Software Optimizely experts recently showed their talent in the final round the Optimizely Hackathon in London. Now, we're getting a closer look at the result of this team's hard work, courtesy of Optimizely Developer Damian Smutek, the Tech Lead of this project.
Read on to see how Damian and the team created an internationally-recognized solution, using a new product, and under a time crunch!
Recently, I had the pleasure of participating in the Optimizely Hackathon 2024 and leading a team in building a project that explored Optimizely's newest product — SaaS CMS. It aims to cut out heavy backend dependencies, letting editors build websites with just frontend components.
As this was a new product for us, we decided to build a fully functional site with the core of SaaS CMS and see how it differs from traditional PaaS. One of the must-have features was a listing page that would provide an interactive way to find entities saved in our data store. This seemed like a big challenge from the start. We knew from experience that building a search function is a huge, time-consuming task. And time was crucial here. Since it was a hackathon, it was limited — at the beginning, we didn't even know how much we had it.
One key consideration was how to index content without the Search and Navigation module available in SaaS. GraphQL seemed like a potential solution, but we were searching for a more time-efficient option. Fortunately, apart from access to SaaS, we also got a license to Coveo, a platform that allows content indexing and retrieval via GraphQL, along with a powerful search API. While this resolves the indexing challenge, the task of building the search UI still remains.
At this point, we discovered Coveo's Search Page feature — a powerful tool for building listing pages. It offers all the essentials: free-text search, filters, pagination, and sorting. With just a few clicks, we had a fully functional search interface. The final step was simply injecting the JavaScript snippet into our code, which immediately loaded the search functionality onto our website.
Let me take you on a journey to show what we built and how we did it.
The first step is configuring the data source, which can be multiple depending on the needs. We used two sources: one for Job Offers and another for Candidate Profiles, as we wanted to provide two separate listings.
Creating a source happens in the Content → Sources section. Where after clicking “Add source” there are many of the existing and ready-to-use connectors. In our case, we were interested in the Optimizely GraphQL connector.
Once the source type is chosen, the setup modal appears, presenting three main tabs:
2. Content to Include- This section contains a JSON-based configuration of the data source. For basic use, only a few essential fields are needed:
Several key points are worth noting here:
3. GraphQL Queries - Here, the query is defined to retrieve all necessary data. Its name is referenced in the previous section. In our case, we used it to extract details about Job Offers.
It’s worth noting that special variables, such as offset and pageSize, are injected into the query to control batch pagination.
From this point, an index can be built. Another thing worth doing is to set up automatic rebuild in recurrence time frames, so data is always fresh. This can be achieved by selecting the source in the Sources list and clicking “…More”.
Once the data is fetched, the next step is mapping the necessary fields. For example, in our Job Offers listing, we needed fields like required skills, a company name that published the offer, salary, and location. These fields would later enable us to build filter options, allowing users to quickly find relevant offers.
To set up mappings, the desired source must be selected in the Sources section. This reveals a Mappings button in the top bar. Clicking this opens a modal where default mappings are viewed and new ones can be added by clicking the “Add Mapping” button. This will bring up a separate modal where new mappings can be configured.
In the mapping modal, existing fields can be chosen or a new one can be created. For instance, we created a new field named “required_skills”. Many configuration options are available in the field modal, such as setting the field as a multi-value facet or enabling it for free-text searches.
After selecting a field, it is required to create a mapping rule, which points to a specific data property in the JSON response from the GraphQL query. For required skills, which are stored as an array of strings, the mapping rule looks like this: %[raw.RequiredSkills].
In most cases, the mapping rule is simply a property name wrapped in %[]. However, it can vary for more complex fields—such as URLs, where we used %[raw._metadata.url.base], or rich text fields, where we used %[raw.Description.html]. Note that data from GraphQL is stored within the raw object.
With the data in place, it's time to build the search page. The page creator can be found under Search → Search Pages, where the type of builder needs to be selected. We opted for the recommended Simple Builder, which allows for easy configuration of layout, styles, and, most importantly - filters. These settings are organized into separate sections:
With everything set up, the final step is to render the listing on the CMS page. Before copying the embed script tag from the Settings section in the Search Page Builder, it is required to create an API key.
This can be done in the Coveo Dashboard under Organization → API Keys. In this section, a new key can be added, specifying a name and, more importantly, configuring the necessary access rights in the Privileges tab.
For a search page, the following permissions must be set:
These permissions can be set manually in the Analytics and Search subtabs, or the Anonymous Search preset can be used, which configures them automatically.
Additionally, it’s recommended to follow the warning prompt to limit the key’s scope by selecting the appropriate Search Hub name - an identifier automatically generated and visible in the Search Builder's settings.
After saving the key settings, its value will only be visible once, so it’s crucial to save and paste it into the script snippet from the Search Builder.
The raw HTML embed code should look something like this:
If the app is built with Next.js, as ours was, this reusable client component can be used.
After copying and pasting the code, the fully functional listing page will be ready for use.
After adding a new source, existing search pages may appear broken —suddenly displaying data from all sources rather than only the intended one.
To resolve this issue, configure Coveo to direct the data flow by modifying the default Query Pipeline. This can be done by navigating to Dashboard → Search → Query Pipelines, selecting the ‘default’ pipeline, and clicking “Edit components."
In the newly opened window, it is needed to go to the Advanced tab to add a new Filter rule. In the Filter creation modal two things must be configured for each combination of the source and the search page:
These custom filters ensure that data flows to the correct locations.
As shown above, even advanced features like a search page can now be implemented easily and quickly, even in several minutes. All it takes are well-suited cloud products like Optimizely SaaS CMS and Coveo, along with minimal configuration. The future truly is now.
This article was created with the support of Wim Nijmeijer from Coveo. His insights and extensive knowledge were essential in ensuring the accuracy and quality of the content.
More details about the Coveo connector for Optimizely SaaS CMS can be found in the documentation.
Damian Smutek has been a Senior Software Developer at FLS since February 2023, specializing in Optimizely projects. With 7 years of experience, the majority of Damian's career has been dedicated to working on Optimizely-specific solutions. You can find the original publication of this blog as well as Damian's other work on his blog.