Customization and extensibility
The Evolve Framework is an ongoing, backwards-compatible refactoring. Not all services have been migrated yet, and APIs may still evolve. Everything described here is functional but should be considered pre-release.
The framework provides sensible defaults for every commerce domain. When your project needs different behavior, you can override individual resolvers, extend schemas, or add entirely new modules.
Use modules as-is
The simplest approach: compose framework modules without modification. This is the right choice when the default resolvers and schemas match your requirements.
const module = new GraphQLCompositeModule([
new CustomerGraphQLModule(),
new CartGraphQLModule(),
new CheckoutGraphQLModule(),
]);
Override individual resolvers
When you need to change how a specific operation works, subclass the framework module and override the resolver:
import { CartGraphQLModule } from "@evolve-framework/commercetools";
import { customCartResolver } from "./resolvers/cart.ts";
export class ProjectCartModule extends CartGraphQLModule {
getResolvers() {
const resolvers = super.getResolvers();
resolvers.Query.cart = customCartResolver;
return resolvers;
}
}
// Compose with your override instead of the default:
const module = new GraphQLCompositeModule([
new CheckoutGraphQLModule(),
new ProjectCartModule(),
]);
This replaces only the cart query resolver. All other cart resolvers
(mutations, field resolvers) keep their default behavior.
Module configuration
Modules accept a config object through their constructor. This is used to
pass service-level settings (such as API keys or endpoints) into resolvers
via the request context:
new CartGraphQLModule({
config: {
paymentServiceEndpoints: config.PAYMENT_SERVICE_ENDPOINTS,
},
});
GraphQLCompositeModule aggregates all module configs. Call
module.getConfig() and pass it into the context factory so resolvers can
access it as context.config.
Add project-specific modules
For functionality that does not exist in the framework, create your own
module by extending AbstractGraphQLModule:
import { AbstractGraphQLModule } from "@evolve-framework/core";
import { gql } from "graphql-tag";
export class LoyaltyModule extends AbstractGraphQLModule {
typedefs = gql`
type LoyaltyAccount {
points: Int!
tier: String!
}
extend type Customer {
loyalty: LoyaltyAccount
}
`;
resolvers = {
Customer: {
loyalty: loyaltyResolver,
},
};
}
Compose it alongside framework modules:
const module = new GraphQLCompositeModule([
new CustomerGraphQLModule(),
new LoyaltyModule(),
]);
GraphQLCompositeModule merges all type definitions and resolvers, so your
extend type Customer works seamlessly with the framework's customer
schema.
Client context patterns
Your context.ts determines how the service authenticates with its
SAAS backend. Each implementation package provides client context loaders
for its integration. The @evolve-framework/commercetools package provides
two patterns:
Direct authentication (account service only): the service manages tokens itself.
import {
CommercetoolsClientLoader,
clientFactory,
} from "@evolve-framework/commercetools";
const loader = new CommercetoolsClientLoader(clientFactory);
Federated authentication (all other services): the service delegates authentication to the account service.
import {
RemoteClientContextLoader,
clientFactory,
} from "@evolve-framework/commercetools";
const loader = new RemoteClientContextLoader(
config.ACCOUNT_SERVICE_ENDPOINT,
clientFactory,
);
Other implementation packages will provide their own client context loaders following the same patterns.
Testing approach
The framework ships with its own test suite covering default behavior. Your project tests should focus on what you own:
- Custom resolvers: test any resolver you override or add
- Module composition: verify that your module combination produces the expected schema
- REST endpoints: test service-specific API routes
- Event handlers and jobs: test project-specific business logic
Test utilities
The @evolve-framework/commercetools/testing export provides:
createTestServer(): creates a full GraphQL server with all modules for integration testscreateTestExecutor(): creates an HTTP executor with proper store context headers- Mock clients:
ctMockandctMockClientfor mocking commercetools API calls
Test factories
The @evolve-framework/commercetools/factories export provides 20+
factories (built on fishery) for generating realistic test data: carts,
customers, products, categories, orders, payments, and more. Use these
instead of writing mock data by hand.
If you override a framework resolver, you take ownership of testing that resolver. The framework tests only cover default behavior.