Upgrading @clerk/backend
to Core 2
Core 2 is included in the Backend SDK starting with version 1. This new version ships with a variety of smaller DX improvements and housekeeping items. Each of the potentially breaking changes are detailed in this guide, below.
By the end of this guide, you’ll have successfully upgraded your Backend project to use @clerk/backend
v1. You’ll learn how to update your dependencies, resolve breaking changes, and find deprecations. Step-by-step instructions will lead you through the process.
Preparing to upgrade
Before upgrading, it's highly recommended that you update your Clerk SDKs to the latest Core 1 version (npm i @clerk/backend@0
). Some changes required for Core 2 SDKs can be applied incrementally to the v1 release, which should contribute to a smoother upgrading experience. After updating, look out for deprecation messages in your terminal and browser console. By resolving these deprecations you'll be able to skip many breaking changes from Core 2.
Additionally, some of the minimum version requirements for some base dependencies have been updated such that versions that are no longer supported or are at end-of-life are no longer guaranteed to work correctly with Clerk.
Updating Node.js
You need to have Node.js 18.17.0
or later installed. Last year, Node.js 16 entered EOL (End of life) status, so support for this version has been removed across Clerk SDKs. You can check your Node.js version by running node -v
in your terminal. Learn more about how to update and install Node.js.
Updating to Core 2
Whenever you feel ready, go ahead and install the latest version of any Clerk SDKs you are using. Make sure that you are prepared to patch some breaking changes before your app will work properly, however. The commands below demonstrate how to install the latest version.
CLI upgrade helper
Clerk now provides a @clerk/upgrade
CLI tool that you can use to ease the upgrade process. The tool will scan your codebase and produce a list of changes you'll need to apply to your project. It should catch the vast majority of the changes needed for a successful upgrade to any SDK including Core 2. This can save you a lot of time reading through changes that don't apply to your project.
To run the CLI tool, navigate to your project and run it in the terminal:
If you are having trouble with npx
, it's also possible to install directly with npm i @clerk/upgrade -g
, and can then be run with the clerk-upgrade
command.
Breaking Changes
request
separated from options
as params to authenticateRequest
There has been a change to the way the params of the authenticateRequest
function are structured. The request
param, formerly included in an options
object, has been moved to stand on its own as the first param to the function, while the options
object remains as the second param. Example below:
clockSkewInSeconds
-> clockSkewInMs
The clockSkewInSeconds
option has been renamed to clockSkewInMs
in order to accurately reflect that its value is expected to be in milliseconds rather than seconds. The value does not need to change here, only the name. This change affects the following imports:
verifyJwt
verifyToken
Clerk.authenticateRequest
Import paths changes
Some top level import paths have been changed in order to improve tree-shaking and more clearly categorize sets of functionality. Some methods have been moved under an /internal
path, indicating that they are only intended for internal use, are exempt from semver, and should be used with great caution.
verifyJwt
import moved to @clerk/backend/jwt
verifyJwt
import moved to @clerk/backend/jwt
The verifyJwt
import path has changed from @clerk/backend
to @clerk/backend/jwt
. You must update your import path in order for it to work correctly. Example below of the fix that needs to be made
decodeJwt
import moved to @clerk/backend/jwt
decodeJwt
import moved to @clerk/backend/jwt
The decodeJwt
import path has changed from @clerk/backend
to @clerk/backend/jwt
. You must update your import path in order for it to work correctly. Example below of the fix that needs to be made
signJwt
import moved to @clerk/backend/jwt
signJwt
import moved to @clerk/backend/jwt
The signJwt
import path has changed from @clerk/backend
to @clerk/backend/jwt
. You must update your import path in order for it to work correctly. Example below of the fix that needs to be made
constants
import moved to @clerk/backend/internal
constants
import moved to @clerk/backend/internal
The constants
import path has changed from @clerk/backend
to @clerk/backend/internal
. You must update your import path in order for it to work correctly. Note that internal imports are not intended for usage and are outside the scope of semver. Example below of the fix that needs to be made:
redirect
import moved to @clerk/backend/internal
redirect
import moved to @clerk/backend/internal
The redirect
import path has changed from @clerk/backend
to @clerk/backend/internal
. You must update your import path in order for it to work correctly. Note that internal imports are not intended for usage and are outside the scope of semver. Example below of the fix that needs to be made:
createAuthenticateRequest
import moved to @clerk/backend/internal
createAuthenticateRequest
import moved to @clerk/backend/internal
The createAuthenticateRequest
import path has changed from @clerk/backend
to @clerk/backend/internal
. You must update your import path in order for it to work correctly. Note that internal imports are not intended for usage and are outside the scope of semver. Example below of the fix that needs to be made:
createIsomorphicRequest
import moved to @clerk/backend/internal
createIsomorphicRequest
import moved to @clerk/backend/internal
The createIsomorphicRequest
import path has changed from @clerk/backend
to @clerk/backend/internal
. You must update your import path in order for it to work correctly. Note that internal imports are not intended for usage and are outside the scope of semver. Example below of the fix that needs to be made:
createIsomorphicRequest
import moved to /internal
createIsomorphicRequest
import moved to /internal
The createIsomorphicRequest
import was intended for those building custom Clerk integrations for frameworks and has been moved to @clerk/backend/internal
to reflect this. Please use caution when using internal imports as they are outside the bounds of semver.
SignJWTError
import moved to @clerk/backend/errors
SignJWTError
import moved to @clerk/backend/errors
The SignJWTError
import path has changed from @clerk/backend
to @clerk/backend/errors
. You must update your import path in order for it to work correctly. Example below of the fix that needs to be made:
TokenVerificationError
import moved to @clerk/backend/errors
TokenVerificationError
import moved to @clerk/backend/errors
The TokenVerificationError
import path has changed from @clerk/backend
to @clerk/backend/errors
. You must update your import path in order for it to work correctly. Example below of the fix that needs to be made:
TokenVerificationErrorAction
import moved to @clerk/backend/errors
TokenVerificationErrorAction
import moved to @clerk/backend/errors
The TokenVerificationErrorAction
import path has changed from @clerk/backend
to @clerk/backend/errors
. You must update your import path in order for it to work correctly. Example below of the fix that needs to be made:
TokenVerificationErrorReason
import moved to @clerk/backend/errors
TokenVerificationErrorReason
import moved to @clerk/backend/errors
The TokenVerificationErrorReason
import path has changed from @clerk/backend
to @clerk/backend/errors
. You must update your import path in order for it to work correctly. Example below of the fix that needs to be made:
httpOptions
parameter removed
The httpOptions
parameter was removed from the internal buildRequest
function but it is used by most public facing APIs. Hence you were able to pass httpOptions
to some functions which is not possible anymore. If you're currently relying on this functionality and wish to update, please reach out to Clerk's support.
The internal change looks like this:
Removed: orgs
claim on JWT
In the previous version of Clerk's SDKs, if you decode the session token that Clerk returns from the server, you'll currently find an orgs
claim on it. It lists all the orgs associated with the given user. Now, Clerk returns the org_id
, org_slug
, and org_role
of the active organization.
The orgs
claim was part of the JwtPayload
. Here are a few examples of where the JwtPayload
could be found.
Next.js
Fastify
@clerk/backend
@clerk/clerk-sdk-node
If you would like to have your JWT return all of the user's organizations, you can create a custom JWT template in your dashboard. Add { "orgs": "user.organizations" }
to it.
Changes to pagination arguments for some functions
There were some changes made to pagination-related arguments passed into functions, in order to make it more clear how to control paginated results. See each function impacted by these changes below:
Organization.getRoles
arguments changed
Organization.getRoles
arguments changedThere have been a couple changes to the pagination arguments that can be passed into this function - limit
has been renamed to pageSize
, and offset
has been renamed to initialPage
. This will help to make it more clear and simple to reason about pagination control. Example of how changes might look below:
Organization.getMemberships
arguments changed
Organization.getMemberships
arguments changedThere have been a couple changes to the pagination arguments that can be passed into this function - limit
has been renamed to pageSize
, and offset
has been renamed to initialPage
. This will help to make it more clear and simple to reason about pagination control. Example of how changes might look below:
Organization.getDomains
arguments changed
Organization.getDomains
arguments changedThere have been a couple changes to the pagination arguments that can be passed into this function - limit
has been renamed to pageSize
, and offset
has been renamed to initialPage
. This will help to make it more clear and simple to reason about pagination control. Example of how changes might look below:
Organization.getInvitations
arguments changed
Organization.getInvitations
arguments changedThere have been a couple changes to the pagination arguments that can be passed into this function - limit
has been renamed to pageSize
, and offset
has been renamed to initialPage
. This will help to make it more clear and simple to reason about pagination control. Example of how changes might look below:
Organization.getMembershipRequests
arguments changed
Organization.getMembershipRequests
arguments changedThere have been a couple changes to the pagination arguments that can be passed into this function - limit
has been renamed to pageSize
, and offset
has been renamed to initialPage
. This will help to make it more clear and simple to reason about pagination control. Example of how changes might look below:
User.getOrganizationInvitations
arguments changed
User.getOrganizationInvitations
arguments changedThere have been a couple changes to the pagination arguments that can be passed into this function - limit
has been renamed to pageSize
, and offset
has been renamed to initialPage
. This will help to make it more clear and simple to reason about pagination control. Example of how changes might look below:
User.getOrganizationSuggestions
arguments changed
User.getOrganizationSuggestions
arguments changedThere have been a couple changes to the pagination arguments that can be passed into this function - limit
has been renamed to pageSize
, and offset
has been renamed to initialPage
. This will help to make it more clear and simple to reason about pagination control. Example of how changes might look below:
User.getOrganizationMemberships
arguments changed
User.getOrganizationMemberships
arguments changedThere have been a couple changes to the pagination arguments that can be passed into this function - limit
has been renamed to pageSize
, and offset
has been renamed to initialPage
. This will help to make it more clear and simple to reason about pagination control. Example of how changes might look below:
Clients.getClientList
arguments changed
Clients.getClientList
arguments changedThere have been a couple changes to the pagination arguments that can be passed into this function - limit
has been renamed to pageSize
, and offset
has been renamed to initialPage
. This will help to make it more clear and simple to reason about pagination control. Example of how changes might look below:
Sessions.getSessionList
arguments changed
Sessions.getSessionList
arguments changedThere have been a couple changes to the pagination arguments that can be passed into this function - limit
has been renamed to pageSize
, and offset
has been renamed to initialPage
. This will help to make it more clear and simple to reason about pagination control. Example of how changes might look below:
Changes to some function return signatures
There have been changes to return signatures for some functions. Since Clerk's API responses are paginated, the totalCount
property is helpful in determining the total number of items in the response easily. This change also aligns the response shape with what is returned from the Clerk backend API. Each impacted function is listed below, along with code examples:
Users.getOrganizationMembershipList
return signature changed
Users.getOrganizationMembershipList
return signature changedThe response payload of Users.getOrganizationMembershipList
was changed as part of the core 2 release. Rather than directly returning data
, the return signature is now { data, totalCount }
. Since backend API responses are paginated, the totalCount
property is helpful in determining the total number of items in the response easily, and this change in the backend SDK aligns the response shape with what the backend API returns directly.
Here's an example of how the response shape would change with this modification:
Users.getOrganizationInvitationList
return signature changed
Users.getOrganizationInvitationList
return signature changedThe response payload of Users.getOrganizationInvitationList
was changed as part of the core 2 release. Rather than directly returning data
, the return signature is now { data, totalCount }
. Since backend API responses are paginated, the totalCount
property is helpful in determining the total number of items in the response easily, and this change in the backend SDK aligns the response shape with what the backend API returns directly.
Here's an example of how the response shape would change with this modification:
Organizations.getOrganizationInvitationList
return type changed
Organizations.getOrganizationInvitationList
return type changedThe return type for this function was previously [Items]
but has now been updated to { data: [Items], totalCount: number }
. Since Clerk's API responses are paginated, the totalCount
property is helpful in determining the total number of items in the response easily. A before/after code example can be seen below:
User.getOrganizationMembershipList
return type changed
User.getOrganizationMembershipList
return type changedThe return type for this function was previously [Items]
but has now been updated to { data: [Items], totalCount: number }
. Since Clerk's API responses are paginated, the totalCount
property is helpful in determining the total number of items in the response easily. A before/after code example can be seen below:
Users.getOrganizationList
return signature changed
Users.getOrganizationList
return signature changedThe response payload of Users.getOrganizationList
was changed as part of the core 2 release. Rather than directly returning data
, the return signature is now { data, totalCount }
. Since backend API responses are paginated, the totalCount
property is helpful in determining the total number of items in the response easily, and this change in the backend SDK aligns the response shape with what the backend API returns directly.
Here's an example of how the response shape would change with this modification:
Organization.getOrganizationList
return type changed
Organization.getOrganizationList
return type changedThe return type for this function was previously [Items]
but has now been updated to { data: [Items], totalCount: number }
. Since Clerk's API responses are paginated, the totalCount
property is helpful in determining the total number of items in the response easily. A before/after code example can be seen below:
Invitations.getInvitationList
return signature changed
Invitations.getInvitationList
return signature changedThe response payload of Invitations.getInvitationList
was changed as part of the core 2 release. Rather than directly returning data
, the return signature is now { data, totalCount }
. Since backend API responses are paginated, the totalCount
property is helpful in determining the total number of items in the response easily, and this change in the backend SDK aligns the response shape with what the backend API returns directly.
Here's an example of how the response shape would change with this modification:
Sessions.getSessionList
return signature changed
Sessions.getSessionList
return signature changedThe response payload of Sessions.getSessionList
was changed as part of the core 2 release. Rather than directly returning data
, the return signature is now { data, totalCount }
. Since backend API responses are paginated, the totalCount
property is helpful in determining the total number of items in the response easily, and this change in the backend SDK aligns the response shape with what the backend API returns directly.
Here's an example of how the response shape would change with this modification:
Users.getUserList
return signature changed
Users.getUserList
return signature changedThe response payload of Users.getUserList
was changed as part of the core 2 release. Rather than directly returning data
, the return signature is now { data, totalCount }
. Since backend API responses are paginated, the totalCount
property is helpful in determining the total number of items in the response easily, and this change in the backend SDK aligns the response shape with what the backend API returns directly.
Here's an example of how the response shape would change with this modification:
AllowlistIdentifiers.getAllowlistIdentifierList
return signature changed
AllowlistIdentifiers.getAllowlistIdentifierList
return signature changedThe response payload of AllowlistIdentifiers.getAllowlistIdentifierList
was changed as part of the core 2 release. Rather than directly returning data
, the return signature is now { data, totalCount }
. Since backend API responses are paginated, the totalCount
property is helpful in determining the total number of items in the response easily, and this change in the backend SDK aligns the response shape with what the backend API returns directly.
Here's an example of how the response shape would change with this modification:
Clients.getClientList
return signature changed
Clients.getClientList
return signature changedThe response payload of Clients.getClientList
was changed as part of the core 2 release. Rather than directly returning data
, the return signature is now { data, totalCount }
. Since backend API responses are paginated, the totalCount
property is helpful in determining the total number of items in the response easily, and this change in the backend SDK aligns the response shape with what the backend API returns directly.
Here's an example of how the response shape would change with this modification:
RedirectUrls.getRedirectUrlList
return signature changed
RedirectUrls.getRedirectUrlList
return signature changedThe response payload of RedirectUrls.getRedirectUrlList
was changed as part of the core 2 release. Rather than directly returning data
, the return signature is now { data, totalCount }
. Since backend API responses are paginated, the totalCount
property is helpful in determining the total number of items in the response easily, and this change in the backend SDK aligns the response shape with what the backend API returns directly.
Here's an example of how the response shape would change with this modification:
Users.getUserOauthAccessToken
return signature changed
Users.getUserOauthAccessToken
return signature changedThe response payload of Users.getUserOauthAccessToken
was changed as part of the core 2 release. Rather than directly returning data
, the return signature is now { data, totalCount }
. Since backend API responses are paginated, the totalCount
property is helpful in determining the total number of items in the response easily, and this change in the backend SDK aligns the response shape with what the backend API returns directly.
Here's an example of how the response shape would change with this modification:
Users.getOrganizationMembershipList
return signature changed
Users.getOrganizationMembershipList
return signature changedThe response payload of Users.getOrganizationMembershipList
was changed as part of the core 2 release. Rather than directly returning data
, the return signature is now { data, totalCount }
. Since backend API responses are paginated, the totalCount
property is helpful in determining the total number of items in the response easily, and this change in the backend SDK aligns the response shape with what the backend API returns directly.
Here's an example of how the response shape would change with this modification:
Image URL Name Consolidation
There are a number of Clerk primitives that contain images, and previously they each had different property names, like avatarUrl
, logoUrl
, profileImageUrl
, etc. In order to promote consistency and make it simpler for developers to know where to find associated images, all image properties are now named imageUrl
. See the list below for all affected classes:
Organization.logoUrl
-> Organization.imageUrl
Organization.logoUrl
-> Organization.imageUrl
The logoUrl
property of any Organization
object has been changed to imageUrl
.
User.profileImageUrl
-> .imageUrl
User.profileImageUrl
-> .imageUrl
The profileImageUrl
property of any User
object has been changed to imageUrl
.
ExternalAccount.picture
-> .imageUrl
ExternalAccount.picture
-> .imageUrl
The picture
property of any ExternalAccount
object has been changed to imageUrl
.
ExternalAccountJSON.avatar_url
-> .imageUrl
ExternalAccountJSON.avatar_url
-> .imageUrl
The avatarUrl
property of any ExternalAccountJSON
object has been changed to imageUrl
.
OrganizationJSON.logo_url
-> .imageUrl
OrganizationJSON.logo_url
-> .imageUrl
The logo_url
property of any OrganizationJSON
object has been changed to imageUrl
.
UserJSON.profile_image_url
-> .imageUrl
UserJSON.profile_image_url
-> .imageUrl
The profile_image_url
property of any UserJSON
object has been changed to imageUrl
.
OrganizationMembershipPublicUserData.profileImageUrl
-> .imageUrl
OrganizationMembershipPublicUserData.profileImageUrl
-> .imageUrl
The profileImageUrl
property of any OrganizationMembershipPublicUserData
object has been changed to imageUrl
.
OrganizationMembershipPublicUserDataJSON.profile_image_url
-> .imageUrl
OrganizationMembershipPublicUserDataJSON.profile_image_url
-> .imageUrl
The profile_image_url
property of any OrganizationMembershipPublicUserDataJSON
object has been changed to imageUrl
.
Deprecation removals & housekeeping
As part of this major version, a number of previously deprecated props, arguments, methods, etc. have been removed. Additionally there have been some changes to things that are only used internally, or only used very rarely. It's highly unlikely that any given app will encounter any of these items, but they are all breaking changes, so they have all been documented below.
User.update({ password: 'x' })
-> User.updatePassword('x')
User.update({ password: 'x' })
-> User.updatePassword('x')
If you are updating a user's password via the User.update
method, it must be changed to User.updatePassword
instead. This method will require the current password as well as the desired new password. We made this update to improve the security of password changes. Example below:
frontendApi
-> publishableKey
as param to createClerkClient
frontendApi
-> publishableKey
as param to createClerkClientThe frontendApi
argument passed to createClerkClient
must be changed to publishableKey
. Note that the values of the two keys are different, so both keys and values need to be changed. You can find your application's publishable key in the Clerk dashboard. Also note that the import value has changed for creating a new Clerk client, which will be addressed by a separate line item if relevant to your codebase.
apiKey
-> secretKey
as param to createClerkClient
apiKey
-> secretKey
as param to createClerkClientThe apiKey
argument passed to createClerkClient
must be changed to secretKey
. Also note that the import value has changed for creating a new Clerk client, which will be addressed by a separate line item if relevant to your codebase.
API_URL
value has changed
API_URL
value has changedThe value of this export has changed from https://api.clerk.dev
to https://api.clerk.com
. If you were relying on the text content of this value not changing, you may need to make adjustments.
Clerk
-> createClerkClient
Clerk
-> createClerkClient
The top level Clerk
import was renamed to createClerkClient
. This is just a name change and can be treated as a text replacement, no changes to the params or return types.
pkgVersion
-> clerkJSVersion
pkgVersion
-> clerkJSVersion
The pkgVersion
parameter was removed from the loadInterstitialFromLocal
, loadInterstitialFromBAPI
, and buildPublicInterstitialUrl
functions. Use clerkJSVersion
instead. Example:
clerkClient.__unstable_options
removed
clerkClient.__unstable_options
removedThe clerkClient.__unstable_options
property was removed. Previously, you could use it to update the internal options. Instead, create a new clerkClient
instance using createClerkClient
and pass the options in this way. For example:
createEmail
import removed
createEmail
import removedThe createEmail
import has been removed. There is no replacement at this time because we need to rethink how createEmail
behaves and align it with the newer sendSms
method. If this is an issue for your implementation, please contact Clerk support.
MembershipRole
type replaced by OrganizationCustomRoleKey
type
MembershipRole
type replaced by OrganizationCustomRoleKey
typeThe MembershipRole
type was replaced with OrganizationCustomRoleKey
(related to roles and permissions). An example of where this type might be found:
To support the existing roles admin
, basic_member
, and guest_member
apply interface merging using the following snippet:
buildRequestUrl
import removed
buildRequestUrl
import removedThe buildRequestUrl
import was intended for those building custom Clerk integrations for frameworks and has been removed in favor of other methods internally. If you were relying on this function and this is an issue, please reach out to Clerk support.
Organization.members_count
-> Organization.membersCount
Organization.members_count
-> Organization.membersCount
The members_count
attribute of the Organization
resource has been renamed to membersCount
to match the naming convention of other attributes.
Feedback
Last updated on