🚨 Announcement: A New Chapter for Vendure read more

← Back to the blog

Announcing Vendure v1.7

Author avatar
August 26, 2022
Michael Bromley
@michlbrmly

Version 1.7 of the open-source headless commerce framework Vendure is available now! This release contains a wide array of new features, fixes and performance improvements!

v1.7.0 Changelog

Upgrading from v1.x.x

This minor release contains no breaking schema or GraphQL API changes, so updating should be a matter of changing all @vendure/... dependencies in your package.json file to 1.7.0.

{
  "dependencies": {
-    "@vendure/admin-ui-plugin": "1.6.5",
-    "@vendure/asset-server-plugin": "1.6.5",
-    "@vendure/core": "1.6.5",
-    ... etc
+    "@vendure/admin-ui-plugin": "1.7.0",
+    "@vendure/asset-server-plugin": "1.7.0",
+    "@vendure/core": "1.7.0",
+    ... etc
  },
  "devDependencies": {
-    "@vendure/testing": "1.6.5", 
+    "@vendure/testing": "1.7.0",
  }
}

Also see the Updating Vendure guide for more information.

Next-gen image support

The default SharpAssetPreviewStrategy now supports uploads in the webp and avif formats, and you can now configure the default settings for the generated asset preview images:

AssetServerPlugin.init({
  previewStrategy: new SharpAssetPreviewStrategy({
    jpegOptions: { quality: 95, progressive: true },
    webpOptions: { quality: 95, nearLossless: true },
    avifOptions: { quality: 70 },
  }),
}),

But how about your existing assets that you already uploaded as jpegs? No worries - you can now convert your assets into next-generation image formats on-the-fly! Simply append ?format=webp or ?format=avif to your asset urls, and you’ll get your existing images converted to next-gen formats - typically at a fraction of the size of a jpeg!

See for yourself!

jpeg original - 12.2kb

jpeg original - 12.2kb

webp version - 6.2kb

webp version - 6.2kb

avif version - 3.3kb

avif version - 3.3kb

Note that browser support for webp is almost universal, while avif support is currently lacking in Safari and Edge.

Improved setup experience

The @vendure/create package is the recommended way to start a new Vendure project, and as such, it’s an important part of the overall developer experience. With this release we have re-evaluated the installation process and scaffold, resulting in a number of improvements:

  • We removed the option to generate a JavaScript project, so all new Vendure projects are standardized on TypeScript. The inclusion of the JavaScript option was seen as a way to make Vendure more accessible to developers unfamiliar with TypeScript. However, we have never provided documentation in JavaScript, and as soon as a developer starts to build anything non-trivial, they will run into many issues such as lack of support for decorators as well as an overall degraded developer experience. Thus, by standardizing on TypeScript only, we’re being more honest that TypeScript knowledge is indeed required have the intended developer experience with Vendure.
  • We’re bundling dotenv and auto-generating your .env file as well as the required TypeScript typings to use environment variables in a type-safe way. This change makes it much easier to move from development to staging and production, simply by providing the relevant environment variables.
  • If you use Postgres, you can now configure your preferred schema rather than always using the default public schema.
  • We’ve bundled a reference Dockerfile and docker-compose.yml files to help you get started in a Docker environment.
  • The question of how to handle migrations in production is now answered by a new approach to building and bundling migration scripts with your built JavaScript files in the /dist directory. This allows you to test & tweak your migrations locally, and then push them to your production environment and have them automatically applied.

Type-safe custom states

Thanks to a very smart PR from Alexander Shitikov we now have a way to extend the Order process with custom states in a type-safe way.

Let’s say you’ve added a custom state, ValidatingCustomer, to the Order process. Any methods that expect the OrderState type won’t know about this new custom state, so up until now you’d have to use type assertions 'ValidatingCustomer' as OrderState when dealing with any of these APIs.

order.state = 'ValidatingCustomer';
//    ^ TS2322: Type '"ValidatingCustomer"' is not assignable to type 'OrderState'

Now we have an elegant way to tell TypeScript about our custom states, allowing type-safe use of 'ValidatingCustomer' in any of the core APIs!

// types.ts
import { CustomOrderStates } from '@vendure/core';

declare module '@vendure/core' {
  interface CustomOrderStates {
    ValidatingCustomer: never;
  }
}

// No more TS error!
order.state = 'ValidatingCustomer';

Payment cancellation

We’ve added a new cancelPayment method to our PaymentMethodHandler API, which allows us to correctly handle the situation where a payment has been started but not completed, and then needs to be cancelled.

Previously, a payment could be cancelled, but it would not then trigger any required clean-up logic with the payment provider’s API. This new cancelPayment method allows you to gracefully handle cancellation scenarios. Note that this differs from the already-supported refund scenario, which applies if the payment has already been settled or “captured”.

Note that the Stripe, Braintree and Mollie integrations have not yet been updated to support this new feature, but support is being worked on.

Support for custom GraphQL scalars

GraphQL scalars are the basic building-block types of GraphQL, from which you can construct more complex object types. By default, GraphQL provides only a limited set of scalars - Int, Float, Boolean, String, ID. In addition to those, Vendure also added DateTime and JSON. But up until now there has been no way to provide your own scalars.

With this release it is now possible to provide your own custom scalars as part of your plugins.

Let’s say you want to use one of the many useful scalars provided by the popular graphql-scalars library:

import { GraphQLEmailAddress } from 'graphql-scalars';

@VendurePlugin({
  imports: [PluginCommonModule],
  shopApiExtensions: {
    schema: gql`
      scalar EmailAddress
    `,
    scalars: { 
      EmailAddress: GraphQLEmailAddress,
    },
  },
})
export class MyPlugin {}

With support for custom scalars, you have greater control over data validation and can create richer, more expressive GraphQL schema extensions.

Improved product option handling

We’ve made a number of improvements to the APIs and UI experience of managing product options. It is now possible to delete unused options and option groups, providing that no existing product variants are making use of them.

When adding options to a new product, it’s also possible to rename options by clicking the name, whereas before renaming involved deleting the option and re-adding it, potentially causing the loss of already-entered form data.

Import assets from urls

When importing products using the csv import format, it is now possible to provide urls in addition to local file paths, and the asset will be imported from the specified url.

More broadly, there is now a new assetImportStrategy config option which allows you to define your own custom AssetImportStrategy, enabling you to import assets from any source.

Other notable changes

  • It’s now possible to filter the order list by transactionId, allowing you to easily look up an order based on the payment reference (#1520)
  • Data tables in the Admin UI now display the total number of items (#1580)
  • Tabbed custom fields are now supported in the Order detail view of the Admin UI (#1562)
  • If you’re using our BraintreePlugin, it is now possible to configure what metadata gets saved to the resulting Payment using the new extractMetadata option.
  • The TransactionalConnection.getRepository() method, when used without a RequestContext argument, is now deprecated and will be removed in v2. This is because it has been found to be unsafe to use it without providing a RequestContext, since transactions can no longer be tracked, leading to very difficult-to-debug data consistency errors. If you need to interact with the database and do not have a RequestContext object available, use the rawConnection instead:
    // before
    const order = await this.connection
      .getRepository(Order).findOne(id);
      
    // use this now
    const order = await this.connection.rawConnection
      .getRepository(Order).findOne(id);
    
  • The CustomerGroup list view in the Admin UI can now be filtered and paginated - useful when you have a large number of CustomerGroups.
  • The DefaultSearchPlugin now correctly handles language fallbacks, so that if a Product is not yet translated into the active language, other existing language versions will still be displayed.
  • There has been a performance overhaul of the Order detail view in the Admin UI as well as the underlying API queries, leading to huge performance improvements in scenarios where there are a large amount of OrderItems (many hundreds or thousands).

Thank you to all contributors

I’d like to thank the wonderful Vendure community who contribute their ideas, bug reports, and code to the project daily. This release includes code contributions by:

  • Alexander Shitikov:
    • feat(core): Deprecation of getRepository without context argument (#1603)
    • fix(core): Introduced errorOnFail flag for job.updates() method (#1627)
    • feat(core): Support save points (nested transactions) (#1579)
    • fix(core): Use RequestContext where available in all DB operations (#1639)
    • feat(core): Enable defining custom states in a type-safe manner (#1678)
    • feat(core): Use language fallback on DefaultSearchPlugin search (#1696)
    • fix(core): Add check to fix transition from AddingItems with an empty order (#1736)
  • Alexis Vigoureux:
    • fix(docs): Fix introspection link (#1576)
    • fix(elasticsearch-plugin): Missing CustomMappingsResolver in Admin API (#1599)
    • fix(asset-server-plugin): Detect protocol for assetUrlPrefix when behind a proxy (#1641)
  • Artem Danilov:
    • fix(core): Use correct sequence of language fallbacks (#1730) (#1737)
  • Arth:
    • docs: Fix typo in deployment.md (#1633)
  • Kevin Mattutat:
    • feat(event): Add event for a completed initialization (#1619)
  • Martijn van de Brug:
    • feat(admin-ui): Show total items in datatables (#1580)
    • fix(core): Prevent negative order total with fixed discounts (#1721)
    • docs: Add notes on how to test your changes locally as contributor (#1722)
    • fix(admin-ui): Lib es2019 so ts understands array.flat (#1728)
    • feat(core): Make slug unique per channel instead of globally unique (#1729)
Author avatar
Written by
Michael Bromley
Michael is the creator of Vendure. He lives in Vienna, Austria.
Twitter logo GitHub logo