Custom Headless APIs

Custom Headless APIs have two primary use cases:

  • For a fully headless solution, you will need custom APIs to serve catalogs, categories, and products to your client application.

  • For any special requirements that are not covered by the out-of-the-box APIs.

The goal is to create endpoints that give you freedom of implementation while respecting our authentication.

Prerequisites

  • Out-of-the-box Headless API available (for example, by using our Standalone Template as a starting point).

  • Secrets are set up for your stores, and the URI whitelist is correctly configured in the API Access part of the administration interface. You can learn about it here.

Creating the API Controller

  • In your project, create a new Class and Inherit from Ucommerce.Web.WebSite.Controllers.HeadlessControllerBase

This will inherit our authentication, restricting its usage to only authenticated clients.

  • Add a route to your controller. It can match the out-of-the-box route as follows:

[Route("api/v1.0/products")]
  • In your controller's constructor, you can inject any components you may need in your implementation. For example,

private readonly IIndex<ProductSearchModel> _productIndex;

public ControllerName(IIndex<ProductSearchModel> productIndex)
{
    _productIndex = productIndex;
}

Creating the method

  • Create and annotate a new method with the HTTP method type and route.

[HttpGet]
[Route("")]
  • For help with return types and choosing the right implementation details, visit Microsoft's documentation.

  • Headless API authentication happens on a per-store basis. You can resolve the StoreId from the claim as follows:

var storeGuid = Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)
  • Complete your implementation and return the results to your client.

Complete example

For reference, here is a full controller example that will return products for a given Category:

using System.Globalization;
using System.Security.Authentication;
using System.Security.Claims;
using Microsoft.AspNetCore.Mvc;
using Ucommerce.Extensions.Search.Abstractions;
using Ucommerce.Extensions.Search.Abstractions.Models.IndexModels;
using Ucommerce.Extensions.Search.Abstractions.Models.SearchModels;
using Ucommerce.Web.WebSite.Controllers;

namespace project.CustomHeadlessControllers;

[Route("api/v1.0/products")]
public class HeadlessProductController : HeadlessControllerBase
{
    private readonly IIndex<ProductSearchModel> _productIndex;

    public HeadlessProductController(IIndex<ProductSearchModel> productIndex)
    {
        _productIndex = productIndex;
    }

    [HttpGet("")]
    public async Task<ActionResult<YourResponseModel>> GetProducts(
        [FromQuery] Guid categoryId,
        [FromQuery] string cultureCode,
        CancellationToken token)
    {
        var storeGuid =
            Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier) ?? throw new AuthenticationException());
        var culture = new CultureInfo(cultureCode);
        var products = _productIndex.AsSearchable(culture).Where(x => x.CategoryIds.Contains(categoryId));

        // 
        var productsResponse = new YourResponseModel()
        {
            Products = YourMapProducts(products)
        };

        return productsResponse;
    }

    // YourMapProducts implementation
}
pageHeadless

Last updated