🚨 Announcing Vendure v2 Beta

ActiveOrderStrategy

ActiveOrderStrategy

Package: @vendure/core File: active-order-strategy.ts
v1.9.0

This strategy is used to determine the active Order for all order-related operations in the Shop API. By default, all the Shop API operations that relate to the active Order (e.g. activeOrder, addItemToOrder, applyCouponCode etc.) will implicitly create a new Order and set it on the current Session, and then read the session to obtain the active Order. This behaviour is defined by the DefaultActiveOrderStrategy.

The InputType generic argument should correspond to the input type defined by the defineInputType() method.

When defineInputType() is used, then the following Shop API operations will receive an additional activeOrderInput argument allowing the active order input to be specified:

  • activeOrder
  • eligibleShippingMethods
  • eligiblePaymentMethods
  • nextOrderStates
  • addItemToOrder
  • adjustOrderLine
  • removeOrderLine
  • removeAllOrderLines
  • applyCouponCode
  • removeCouponCode
  • addPaymentToOrder
  • setCustomerForOrder
  • setOrderShippingAddress
  • setOrderBillingAddress
  • setOrderShippingMethod
  • setOrderCustomFields
  • transitionOrderToState

Example

mutation AddItemToOrder {
  addItemToOrder(
    productVariantId: 42,
    quantity: 1,
    activeOrderInput: { token: "123456" }
  ) {
    ...on Order {
      id
      # ...etc
    }
  }
}

Example

import { ID } from '@vendure/common/lib/shared-types';
import {
  ActiveOrderStrategy,
  CustomerService,
  idsAreEqual,
  Injector,
  Order,
  OrderService,
  RequestContext,
  TransactionalConnection,
} from '@vendure/core';
import gql from 'graphql-tag';

// This strategy assumes a "orderToken" custom field is defined on the Order
// entity, and uses that token to perform a lookup to determine the active Order.
//
// Additionally, it does _not_ define a `createActiveOrder()` method, which
// means that a custom mutation would be required to create the initial Order in
// the first place and set the "orderToken" custom field.
class TokenActiveOrderStrategy implements ActiveOrderStrategy<{ token: string }> {
  readonly name = 'orderToken';

  private connection: TransactionalConnection;
  private orderService: OrderService;

  init(injector: Injector) {
    this.connection = injector.get(TransactionalConnection);
    this.orderService = injector.get(OrderService);
  }

  defineInputType = () => gql`
    input OrderTokenActiveOrderInput {
      token: String
    }
  `;

  async determineActiveOrder(ctx: RequestContext, input: { token: string }) {
    const qb = this.connection
      .getRepository(ctx, Order)
      .createQueryBuilder('order')
      .leftJoinAndSelect('order.customer', 'customer')
      .leftJoinAndSelect('customer.user', 'user')
      .where('order.customFields.orderToken = :orderToken', { orderToken: input.token });

    const order = await qb.getOne();
    if (!order) {
      return;
    }
    // Ensure the active user is the owner of this Order
    const orderUserId = order.customer && order.customer.user && order.customer.user.id;
    if (order.customer && idsAreEqual(orderUserId, ctx.activeUserId)) {
      return order;
    }
  }
}

// in vendure-config.ts
export const config = {
  // ...
  orderOptions: {
    activeOrderStrategy: new TokenActiveOrderStrategy(),
  },
}

Signature

interface ActiveOrderStrategy<InputType extends Record<string, any> | void = void> extends InjectableStrategy {
  readonly name: string;
  defineInputType?: () => DocumentNode;
  createActiveOrder?: (ctx: RequestContext, input: InputType) => Promise<Order>;
  determineActiveOrder(ctx: RequestContext, input: InputType): Promise<Order | undefined>;
}

Extends

Members

name

readonly property
type:
string
The name of the strategy, e.g. “orderByToken”, which will also be used as the field name in the ActiveOrderInput type.

defineInputType

property
type:
() => DocumentNode

Defines the type of the GraphQL Input object expected by the activeOrderInput input argument.

Example

For example, given the following:

defineInputType() {
  return gql`
     input OrderTokenInput {
       token: String!
     }
  `;
}

assuming the strategy name is “orderByToken”, then the resulting call to activeOrder (or any of the other affected Shop API operations) would look like:

activeOrder(activeOrderInput: {
  orderByToken: {
    token: "foo"
  }
}) {
  # ...
}

Note: if more than one graphql input type is being defined (as in a nested input type), then the first input will be assumed to be the top-level input.

createActiveOrder

property
type:
(ctx: RequestContext, input: InputType) => Promise<Order>

Certain mutations such as addItemToOrder can automatically create a new Order if one does not exist. In these cases, this method will be called to create the new Order.

If automatic creation of an Order does not make sense in your strategy, then leave this method undefined. You’ll then need to take care of creating an order manually by defining a custom mutation.

determineActiveOrder

method
type:
(ctx: RequestContext, input: InputType) => Promise<Order | undefined>

This method is used to determine the active Order based on the current RequestContext in addition to any input values provided, as defined by the defineInputType method of this strategy.

Note that this method is invoked frequently, so you should aim to keep it efficient. The returned Order, for example, does not need to have its various relations joined.