Breaking API Changes
Breaks from updated dependencies
TypeScript
- v2 is built on TypeScript v4.9.5. You should update your TypeScript version to match this. Doing so is quite likely to reveal new compiler errors (as is usual with TypeScript minor release updates).
- If you are using
ts-node
, update it to the latest version - If you are targeting
ES2022
orESNEXT
in yourtsconfig.json
, you'll need to set"useDefineForClassFields": false
. See this issue for more context.
Apollo Server & GraphQL
If you have any custom ApolloServerPlugins, the plugin methods must now return a Promise. Example:
export class TranslateErrorsPlugin implements ApolloServerPlugin {
constructor(private i18nService: I18nService) {}
- requestDidStart(): GraphQLRequestListener {
+ async requestDidStart(): Promise<GraphQLRequestListener> {
return {
- willSendResponse: requestContext => {
+ willSendResponse: async requestContext => {
const { errors, context } = requestContext;
if (errors) {
(requestContext.response as any).errors = errors.map(err => {
return this.i18nService.translateError(context.req, err as GraphQLError) as any;
});
}
},
};
}
}
With the update to GraphQL v16, you might run into issues with other packages in the GraphQL ecosystem that also depend on the graphql
package, such as graphql-code-generator
. In this case these packages will also need to be updated.
For instance, if you are using the "typescript-compatibility" plugin to generate namespaced types, you'll need to drop this, as it is no longer maintained.
TypeORM
TypeORM 0.3.x introduced a large number of breaking changes. For a complete guide, see the TypeORM v0.3.0 release notes.
Here are the main API changes you'll likely need to make:
- You can no longer compare to
null
, you need to use the newIsNull()
helper:+ import { IsNull } from 'typeorm';
- .find({ where: { deletedAt: null } })
+ .find({ where: { deletedAt: IsNull() } }) - The
findOne()
method returnsnull
rather thanundefined
if a record is not found. - The
findOne()
method no longer accepts an id argument. Lookup based on id must be done with awhere
clause:- .findOne(variantId)
+ .findOne({ where: { id: variantId } }) - Where clauses must use an entity id rather than passing an entity itself:
- .find({ where: { user } })
+ .find({ where: { user: { id: user.id } } }) - The
findByIds()
method has been deprecated. Use the newIn
helper instead:+ import { In } from 'typeorm';
- .findByIds(ids)
+ .find({ where: { id: In(ids) } })
Vendure TypeScript API Changes
Custom Order / Fulfillment / Payment processes
In v2, the hard-coded states & transition logic for the Order, Fulfillment and Payment state machines has been extracted from the core services and instead reside in a default OrderProcess
, FulfillmentProcess
and PaymentProcess
object. This allows you to fully customize these flows without having to work around the assumptions & logic implemented by the default processes.
What this means is that if you are defining a custom process, you'll now need to explicitly add the default process to the array.
+ import { defaultOrderProcess } from '@vendure/core';
orderOptions: {
- process: [myCustomOrderProcess],
+ process: [defaultOrderProcess, myCustomOrderProcess],
}
Also note that shippingOptions.customFulfillmentProcess
and paymentOptions.customPaymentProcess
are both now renamed to process
. The old names are still usable but are deprecated.
OrderItem no longer exists
As a result of #1981, the OrderItem
entity no longer exists. The function and data of OrderItem
is now transferred to OrderLine
. As a result, the following APIs which previously used OrderItem arguments have now changed:
FulfillmentHandler
ChangedPriceHandlingStrategy
PromotionItemAction
TaxLineCalculationStrategy
If you have implemented any of these APIs, you'll need to check each one, remove the OrderItem
argument from any methods that are using it,
and update any logic as necessary.
You may also be joining the OrderItem relation in your own TypeORM queries, so you'll need to check for code like this:
const order = await this.connection
.getRepository(Order)
.createQueryBuilder('order')
.leftJoinAndSelect('order.lines', 'line')
- .leftJoinAndSelect('line.items', 'items')
or
const order = await this.connection
.getRepository(Order)
.findOne(ctx, orderId, {
- relations: ['lines', 'lines.items'],
+ relations: ['lines'],
});
ProductVariant stock changes
With #1545 we have changed the way we model stock levels in order to support multiple stock locations. This means that the ProductVariant.stockOnHand
and ProductVariant.stockAllocated
properties no longer exist on the ProductVariant
entity in TypeScript.
Instead, this information is now located at ProductVariant.stockLevels
, which is an array of StockLevel entities.
New return type for Channel, TaxCategory & Zone lists
- The
ChannelService.findAll()
method now returns aPaginatedList<Channel>
instead ofChannel[]
. - The
channels
GraphQL query now returns aPaginatedList
rather than a simple array of Channels. - The
TaxCategoryService.findAll()
method now returns aPaginatedList<TaxCategory>
instead ofTaxCategory[]
. - The
taxCategories
GraphQL query now returns aPaginatedList
rather than a simple array of TaxCategories. - The
ZoneService.findAll()
method now returns aPaginatedList<Zone>
instead ofZone[]
. The old behaviour ofZoneService.findAll()
(all Zones, cached for rapid access) can now be found under the newZoneService.getAllWithMembers()
method. - The
zones
GraphQL query now returns aPaginatedList
rather than a simple array of Zones.
Admin UI changes
If you are using the @vendure/ui-devkit
package to generate custom ui extensions, here are the breaking changes to be aware of:
- As part of the major refresh to the Admin UI app, certain layout elements had be changed which can cause your custom routes to look bad. Wrapping all your custom pages in
<vdr-page-block>
(or<div class="page-block">
if not built with Angular components) will improve things. There will soon be a comprehensive guide published on how to create seamless ui extensions that look just like the built-in screens. - If you use any of the scoped method of the Admin UI
DataService
, you might find that some no longer exist. They are now deprecated and will eventually be removed. Use thedataService.query()
anddataService.mutation()
methods only, passing your own GraphQL documents:// Old way
this.dataService.product.getProducts().single$.subscribe(...);// New way
const GET_PRODUCTS = gql`
query GetProducts {
products {
items {
id
name
# ... etc
}
}
}
`;
this.dataService.query(GET_PRODUCTS).single$.subscribe(...); - The Admin UI component
vdr-product-selector
has been renamed tovdr-product-variant-selector
to more accurately represent what it does. If you are usingvdr-product-selector
if any ui extensions code, update it to use the new selector.
Other breaking API changes
- End-to-end tests using Jest will likely run into issues due to our move towards using some dependencies that make use of ES modules. We have found the best solution to be to migrate tests over to Vitest, which can handle this and is also significantly faster than Jest. See the updated Testing guide for instructions on getting started with Vitest.
- Internal
ErrorResult
classes now take a single object argument rather than multiple args. - All monetary values are now represented in the GraphQL APIs with a new
Money
scalar type. If you use graphql-code-generator, you'll want to tell it to treat this scalar as a number:import { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
schema: 'http://localhost:3000/shop-api',
documents: ['src/**/*graphql.ts'],
config: {
scalars: {
Money: 'number',
},
},
generates: {
// ..
}
}; - A new
Region
entity has been introduced, which is a base class forCountry
and the newProvince
entity. TheZone.members
property is now an array ofRegion
rather thanCountry
, since Zones may now be composed of both countries and provinces. If you have defined any custom fields onCountry
, you'll need to change it toRegion
in your custom fields config. - If you are using the s3 storage strategy of the AssetServerPlugin, it has been updated to use v3 of the AWS SDKs. This update introduces an improved modular architecture to the AWS sdk, resulting in smaller bundle sizes. You need to install the
@aws-sdk/client-s3
&@aws-sdk/lib-storage
packages, and can remove theaws-sdk
package. If you are using it in combination with MinIO, you'll also need to rename a config property and provide a region:nativeS3Configuration: {
endpoint: 'http://localhost:9000',
- s3ForcePathStyle: true,
+ forcePathStyle: true,
signatureVersion: 'v4',
+ region: 'eu-west-1',
} - The Stripe plugin has been made channel aware. This means your api key and webhook secret are now stored in the database, per channel, instead of environment variables.
To migrate to v2 of the Stripe plugin from @vendure/payments you need to:
- Remove the apiKey and webhookSigningSecret from the plugin initialization in vendure-config.ts:
StripePlugin.init({
- apiKey: process.env.YOUR_STRIPE_SECRET_KEY,
- webhookSigningSecret: process.env.YOUR_STRIPE_WEBHOOK_SIGNING_SECRET,
storeCustomersInStripe: true,
}), - Start the server and login as administrator. For each channel that you'd like to use Stripe payments, you need to create a payment method with payment handler Stripe payment and the apiKey and webhookSigningSecret belonging to that channel's Stripe account.
- Remove the apiKey and webhookSigningSecret from the plugin initialization in vendure-config.ts:
- If you are using the BullMQJobQueuePlugin, the minimum Redis recommended version is 6.2.0.
- The
WorkerHealthIndicator
which was deprecated in v1.3.0 has been removed, as well as thejobQueueOptions.enableWorkerHealthCheck
config option. - The
CustomerGroupEntityEvent
(fired on creation, update or deletion of a CustomerGroup) has been renamed toCustomerGroupEvent
, and the formerCustomerGroupEvent
(fired when Customers are added to or removed from a group) has been renamed toCustomerGroupChangeEvent
. - We introduced the plugin compatibility API to allow plugins to indicate what version of Vendure they are compatible with. To avoid bootstrap messages you should add this property to your plugins.