Sitecore Headless Next.js – Get querystring in Forms Value Provider

With Sitecore Headless, every request is going through the layout service. For example, http://mysite.localhost/forms/subscribe-to-email-updates?email=cielo.chua@email.com, HttpContext request url is something like /sitecore/api/layout/render/default?item=forms/subscribe-to-email-updates&sc_apikey=C1B5BE75-9889-4BDB-946F-FEC88AACAF66&sc_site=mysite&sc_lang=en&tracking=false which did not persist the query string

So if you have a form page that needs to pre-fill a field based on the query string, you would like to forward the query string parameters to the Layout Service.

Create a custom Parameterized Rest Layout Service and overwrite the fetchLayoutData and getFetchParams

import {
  AxiosDataFetcher,
  AxiosResponse,
  LayoutServiceData,
  RestLayoutService,
  RestLayoutServiceConfig,
} from '@sitecore-jss/sitecore-jss-nextjs';
import { IncomingMessage, ServerResponse } from 'http';
import { parse, ParsedUrlQuery } from 'querystring';
interface FetchParams {
  [param: string]: string | number | boolean;
  sc_apikey: string;
  sc_site: string;
  sc_lang: string;
  tracking: boolean;
}
 
export class ParameterizedRestLayoutService extends RestLayoutService {
  constructor(private config: RestLayoutServiceConfig) {
    super(config);
  }
 
  fetchLayoutData = async (
    itemPath: string,
    language: string | undefined,
    req: IncomingMessage,
    res: ServerResponse
  ): Promise<LayoutServiceData> => {
    const reqParams: ParsedUrlQuery = parse(req?.url?.split('?')[1] || '');
    if (!req?.url || !reqParams) return super.fetchLayoutData(itemPath, language, req, res);
 
    const querystringParams = this.getFetchParams(language, reqParams);
    const combinedParams = Object.assign({ item: itemPath }, querystringParams);
    const fetchParams: Record<string, string> = {};
    Object.keys(combinedParams).forEach((key) => {
      if (key == 'path') return;
      fetchParams[key] = combinedParams[key].toString();
    });

    try {
      const fetchUrl = this.resolveLayoutServiceUrl('render');
      const url = this.buildUrl(fetchUrl, fetchParams);
      const response = await this.dataFetcher<LayoutServiceData>(url);
      return response.data;
    } catch (error: any) {
      throw error;
    }
  };
 
  private buildUrl = (urlBase: string, params: Record<string, string>) => {
    const url = new URL(urlBase);
    url.search = new URLSearchParams(params).toString();
    return url.toString();
  };
 
  private dataFetcher<ResponseType>(
    url: string,
    data?: unknown
  ): Promise<AxiosResponse<ResponseType>> {
    return new AxiosDataFetcher().fetch<ResponseType>(url, data);
  }
 
  protected getFetchParams = (language?: string, params?: ParsedUrlQuery): FetchParams => {
    return {
      sc_apikey: this.config.apiKey,
      sc_site: this.config.siteName,
      sc_lang: language || '',
      tracking: this.config.tracking ?? true,
      ...params,
    };
  };
}

Go to the layout-service-factory.ts then use the newly created ParameterizedRestLayoutService

import { LayoutService } from '@sitecore-jss/sitecore-jss-nextjs';
import config from 'temp/config';

import { ParameterizedRestLayoutService } from './parameterized-rest-layout-service';

export class LayoutServiceFactory {
  create(): LayoutService {
    return new ParameterizedRestLayoutService({
      apiHost: config.sitecoreApiHost as string,
      apiKey: config.sitecoreApiKey,
      siteName: config.jssAppName,
      configurationName: 'default',
    });
  }
}

export const layoutServiceFactory = new LayoutServiceFactory();

Then on the Forms Value Provider you can now retrieve the query sting using HttpContext.

public class QueryStringValueProvider : IFieldValueProvider
    {
        public FieldValueProviderContext ValueProviderContext { get; set; }
 
        public object GetValue(string parameters)
        {
            HttpContext httpContext = System.Web.HttpContext.Current;
            string q = HttpUtility.ParseQueryString(httpContext.Request.Url.Query).Get(parameters);
 
            if (string.IsNullOrWhiteSpace(q))
            {
                return string.Empty;
            }
 
            return q;
        }
    }
has context menu

Leave a comment