Skip to content

New: AI agent integration via Model Context Protocol (MCP).Learn more

Adding a field to the checkout

Projects commonly need extra fields in the checkout, such as a purchase order number, delivery instructions, or a VAT ID. This guide walks through adding a custom field end to end: schema, resolver, form component, validation, and persistence.

  1. Define the custom type in Terraform

    Register the custom field on the order resource in commercetools through Terraform:

    resource "commercetools_type" "order_custom_fields" {
    key = "order-custom-fields"
    name = { en = "Order custom fields" }
    resource_type_ids = ["order"]
    field {
    name = "purchaseOrderNumber"
    label = { en = "Purchase order number" }
    type { name = "String" }
    }
    }
  2. Extend the GraphQL schema

    Create a project-specific module that adds the field to the schema. Extend AbstractModule and compose it alongside the framework modules:

    import { AbstractModule } from "@evolve-framework/core";
    import { gql } from "graphql-tag";
    export class OrderCustomFieldsModule extends AbstractModule {
    typedefs = gql`
    extend type Order {
    purchaseOrderNumber: String
    }
    `;
    resolvers = {
    Order: {
    purchaseOrderNumber: (order) =>
    order.custom?.fields?.purchaseOrderNumber ?? null,
    },
    };
    }

    Add it to the checkout service’s module composition:

    backend/services/checkout-commercetools/src/module.ts
    export const createModule = (): CompositeModule => {
    return new CompositeModule([
    new CheckoutModule({ clientFactory }),
    new PaymentModule({ clientFactory }),
    new CartModule({ clientFactory }),
    new OrderCustomFieldsModule(),
    ]);
    };
  3. Run codegen

    After updating the schema, run GraphQL Code Generator to produce updated TypeScript types for the frontend:

    Terminal window
    pnpm codegen
  4. Add the form component

    The checkout is structured as separate step components, each with their own form and Zod validation. Create a new field component using React Hook Form:

    frontend/site/src/components/checkout/purchase-order-field.tsx
    import { useFormContext } from "react-hook-form";
    export const PurchaseOrderField = () => {
    const { register, formState: { errors } } = useFormContext();
    return (
    <div>
    <label htmlFor="purchaseOrderNumber">PO Number</label>
    <input
    id="purchaseOrderNumber"
    {...register("purchaseOrderNumber")}
    />
    {errors.purchaseOrderNumber && (
    <p>{errors.purchaseOrderNumber.message}</p>
    )}
    </div>
    );
    };
  5. Add Zod validation

    Add the field to the relevant checkout step’s validation schema:

    import { z } from "zod";
    export const orderDetailsSchema = z.object({
    // ... existing fields for this step
    purchaseOrderNumber: z
    .string()
    .max(50, "PO number must be 50 characters or fewer")
    .optional(),
    });

    React Hook Form uses this schema via zodResolver to validate the field before submission.

  6. Persist the value

    Save the field through a commercetools setCustomField action. In the checkout service, add a resolver or API extension handler that writes the value when the order is created:

    const updateAction = {
    action: "setCustomField",
    name: "purchaseOrderNumber",
    value: input.purchaseOrderNumber,
    };