-
Changes to the
mailer.Mailer
interface (minor breaking if you are sending custom emails):// Old: app.NewMailClient().Send(from, to, subject, html, attachments?) // New: app.NewMailClient().Send(&mailer.Message{ From: from, To: to, Subject: subject, HTML: html, Attachments: attachments, // new configurable fields Bcc: []string{"[email protected]", "[email protected]"}, Cc: []string{"[email protected]", "[email protected]"}, Headers: map[string]string{"Custom-Header": "test"}, Text: "custom plain text version", })
-
Added the new
*mailer.Message
to theMailerRecordEvent
,MailerAdminEvent
event structs.
The biggest change is the merge of the User
models and the profiles
collection per #376.
There is no longer user
type field and the users are just an "auth" collection (we now support collection types, currently only "base" and "auth").
This should simplify the users management and at the same time allow us to have unlimited multiple "auth" collections each with their own custom fields and authentication options (eg. staff, client, etc.).
In addition to the Users
and profiles
merge, this release comes with several other improvements:
-
Added indirect expand support #312.
-
The
json
field type now supports filtering and sorting #423. -
The
relation
field now allows unlimittedmaxSelect
(aka. without upper limit). -
Added support for combined email/username + password authentication (see below
authWithPassword()
). -
Added support for full "manager-subordinate" users management, including a special API rule to allow directly changing system fields like email, password, etc. without requiring
oldPassword
or other user verification. -
Enabled OAuth2 account linking on authorized request from the same auth collection (this is useful for example if the OAuth2 provider doesn't return an email and you want to associate it with the current logged in user).
-
Added option to toggle the record columns visibility from the table listing.
-
Added support for collection schema fields reordering.
-
Added several new OAuth2 providers (Microsoft Azure AD, Spotify, Twitch, Kakao).
-
Improved memory usage on large file uploads #835.
-
More detailed API preview docs and site documentation (the repo is located at https://github.com/pocketbase/site).
-
Other minor performance improvements (mostly related to the search apis).
The merge of users and profiles comes with several required db changes.
The easiest way to apply them is to use the new temporary upgrade
command:
# make sure to have a copy of your pb_data in case something fails
cp -r ./pb_data ./pb_data_backup
# run the upgrade command
./pocketbase08 upgrade
# start the application as usual
./pocketbase08 serve
The upgrade command:
- Creates a new
users
collection with merged fields from the_users
table and theprofiles
collection. The new user records will have the ids from theprofiles
collection. - Changes all
user
type fields torelation
and update the references to point to the new user ids. - Renames all
@collection.profiles.*
,@request.user.*
and@request.user.profile.*
filters to@collection.users.*
and@request.auth.*
. - Appends
2
to all schema field names and api filter rules that conflicts with the new system reserved ones:collectionId => collectionId2 collectionName => collectionName2 expand => expand2 // only for the "profiles" collection fields: username => username2 email => email2 emailVisibility => emailVisibility2 verified => verified2 tokenKey => tokenKey2 passwordHash => passwordHash2 lastResetSentAt => lastResetSentAt2 lastVerificationSentAt => lastVerificationSentAt2
Please check the individual SDK package changelog and apply the necessary changes in your code:
-
npm install pocketbase@latest --save
-
dart pub add pocketbase:^0.5.0 # or with Flutter: flutter pub add pocketbase:^0.5.0
You don't have to read this if you are using an official SDK.
-
The authorization schema is no longer necessary. Now it is auto detected from the JWT token payload:
Old New Authorization: Admin TOKEN Authorization: TOKEN Authorization: User TOKEN Authorization: TOKEN -
All datetime stings are now returned in ISO8601 format - with Z suffix and space as separator between the date and time part:
Old New 2022-01-02 03:04:05.678 2022-01-02 03:04:05.678Z -
Removed the
@
prefix from the system record fields for easier json parsing:Old New @collectionId collectionId @collectionName collectionName @expand expand -
All users api handlers are moved under
/api/collections/:collection/
:Old New GET /api/users/auth-methods GET /api/collections/:collection/auth-methods POST /api/users/refresh POST /api/collections/:collection/auth-refresh POST /api/users/auth-via-oauth2 POST /api/collections/:collection/auth-with-oauth2
You can now also pass optionalcreateData
object on OAuth2 sign-up.
Also please note that now required user/profile fields are properly validated when creating new auth model on OAuth2 sign-up.POST /api/users/auth-via-email POST /api/collections/:collection/auth-with-password
Handles username/email + password authentication.
{"identity": "usernameOrEmail", "password": "123456"}
POST /api/users/request-password-reset POST /api/collections/:collection/request-password-reset POST /api/users/confirm-password-reset POST /api/collections/:collection/confirm-password-reset POST /api/users/request-verification POST /api/collections/:collection/request-verification POST /api/users/confirm-verification POST /api/collections/:collection/confirm-verification POST /api/users/request-email-change POST /api/collections/:collection/request-email-change POST /api/users/confirm-email-change POST /api/collections/:collection/confirm-email-change GET /api/users GET /api/collections/:collection/records GET /api/users/:id GET /api/collections/:collection/records/:id POST /api/users POST /api/collections/:collection/records PATCH /api/users/:id PATCH /api/collections/:collection/records/:id DELETE /api/users/:id DELETE /api/collections/:collection/records/:id GET /api/users/:id/external-auths GET /api/collections/:collection/records/:id/external-auths DELETE /api/users/:id/external-auths/:provider DELETE /api/collections/:collection/records/:id/external-auths/:provider In relation to the above changes, the
user
property in the auth response is renamed torecord
. -
The admins api was also updated for consistency with the users api changes:
Old New POST /api/admins/refresh POST /api/admins/auth-refresh POST /api/admins/auth-via-email POST /api/admins/auth-with-password
{"identity": "[email protected]", "password": "123456"}
(notice that theemail
body field was renamed toidentity
) -
To prevent confusion with the auth method responses, the following endpoints now returns 204 with empty body (previously 200 with token and auth model):
POST /api/admins/confirm-password-reset POST /api/collections/:collection/confirm-password-reset POST /api/collections/:collection/confirm-verification POST /api/collections/:collection/confirm-email-change
-
Renamed the "user" related settings fields returned by
GET /api/settings
:Old New userAuthToken recordAuthToken userPasswordResetToken recordPasswordResetToken userEmailChangeToken recordEmailChangeToken userVerificationToken recordVerificationToken
You don't have to read this if you are not using PocketBase as framework.
-
Removed
forms.New*WithConfig()
factories to minimize ambiguities. If you need to pass a transaction Dao you can use the newSetDao(dao)
method available to the form instances. -
forms.RecordUpsert.LoadData(data map[string]any)
now can bulk load external data from a map. To load data from a request instance, you could useforms.RecordUpsert.LoadRequest(r, optKeysPrefix = "")
. -
schema.RelationOptions.MaxSelect
has new type*int
(you can use the newtypes.Pointer(123)
helper to assign pointer values). -
Renamed the constant
apis.ContextUserKey
("user") toapis.ContextAuthRecordKey
("authRecord"). -
Replaced user related middlewares with their auth record alternative:
Old New apis.RequireUserAuth() apis.RequireRecordAuth(optCollectionNames ...string) apis.RequireAdminOrUserAuth() apis.RequireAdminOrRecordAuth(optCollectionNames ...string) N/A RequireSameContextRecordAuth()
(requires the auth record to be from the same context collection) -
The following record Dao helpers now uses the collection id or name instead of
*models.Collection
instance to reduce the verbosity when fetching records:Old New dao.FindRecordById(collection, ...) dao.FindRecordById(collectionNameOrId, ...) dao.FindRecordsByIds(collection, ...) dao.FindRecordsByIds(collectionNameOrId, ...) dao.FindRecordsByExpr(collection, ...) dao.FindRecordsByExpr(collectionNameOrId, ...) dao.FindFirstRecordByData(collection, ...) dao.FindFirstRecordByData(collectionNameOrId, ...) dao.IsRecordValueUnique(collection, ...) dao.IsRecordValueUnique(collectionNameOrId, ...) -
Replaced all User related Dao helpers with Record equivalents:
Old New dao.UserQuery() dao.RecordQuery(collection) dao.FindUserById(id) dao.FindRecordById(collectionNameOrId, id) dao.FindUserByToken(token, baseKey) dao.FindAuthRecordByToken(token, baseKey) dao.FindUserByEmail(email) dao.FindAuthRecordByEmail(collectionNameOrId, email) N/A dao.FindAuthRecordByUsername(collectionNameOrId, username) -
Moved the formatted
ApiError
struct and factories to thegithub.com/pocketbase/pocketbase/apis
subpackage:Old New Import path github.com/pocketbase/pocketbase/tools/rest github.com/pocketbase/pocketbase/apis Fields rest.ApiError{} apis.ApiError{} rest.NewNotFoundError() apis.NewNotFoundError() rest.NewBadRequestError() apis.NewBadRequestError() rest.NewForbiddenError() apis.NewForbiddenError() rest.NewUnauthorizedError() apis.NewUnauthorizedError() rest.NewApiError() apis.NewApiError() -
Renamed
models.Record
helper getters:Old New SetDataValue Set GetDataValue Get GetBoolDataValue GetBool GetStringDataValue GetString GetIntDataValue GetInt GetFloatDataValue GetFloat GetTimeDataValue GetTime GetDateTimeDataValue GetDateTime GetStringSliceDataValue GetStringSlice -
Added new auth collection
models.Record
helpers:func (m *Record) Username() string func (m *Record) SetUsername(username string) error func (m *Record) Email() string func (m *Record) SetEmail(email string) error func (m *Record) EmailVisibility() bool func (m *Record) SetEmailVisibility(visible bool) error func (m *Record) IgnoreEmailVisibility(state bool) func (m *Record) Verified() bool func (m *Record) SetVerified(verified bool) error func (m *Record) TokenKey() string func (m *Record) SetTokenKey(key string) error func (m *Record) RefreshTokenKey() error func (m *Record) LastResetSentAt() types.DateTime func (m *Record) SetLastResetSentAt(dateTime types.DateTime) error func (m *Record) LastVerificationSentAt() types.DateTime func (m *Record) SetLastVerificationSentAt(dateTime types.DateTime) error func (m *Record) ValidatePassword(password string) bool func (m *Record) SetPassword(password string) error
-
Added option to return serialized custom
models.Record
fields data:func (m *Record) UnknownData() map[string]any func (m *Record) WithUnkownData(state bool)
-
Deleted
model.User
. Now the user data is stored as an authmodels.Record
.Old New User.Email Record.Email() User.TokenKey Record.TokenKey() User.Verified Record.Verified() User.SetPassword() Record.SetPassword() User.RefreshTokenKey() Record.RefreshTokenKey() etc. -
Replaced
User
related event hooks with theirRecord
alternative:Old New OnMailerBeforeUserResetPasswordSend() *hook.Hook[*MailerUserEvent] OnMailerBeforeRecordResetPasswordSend() *hook.Hook[*MailerRecordEvent] OnMailerAfterUserResetPasswordSend() *hook.Hook[*MailerUserEvent] OnMailerAfterRecordResetPasswordSend() *hook.Hook[*MailerRecordEvent] OnMailerBeforeUserVerificationSend() *hook.Hook[*MailerUserEvent] OnMailerBeforeRecordVerificationSend() *hook.Hook[*MailerRecordEvent] OnMailerAfterUserVerificationSend() *hook.Hook[*MailerUserEvent] OnMailerAfterRecordVerificationSend() *hook.Hook[*MailerRecordEvent] OnMailerBeforeUserChangeEmailSend() *hook.Hook[*MailerUserEvent] OnMailerBeforeRecordChangeEmailSend() *hook.Hook[*MailerRecordEvent] OnMailerAfterUserChangeEmailSend() *hook.Hook[*MailerUserEvent] OnMailerAfterRecordChangeEmailSend() *hook.Hook[*MailerRecordEvent] OnUsersListRequest() *hook.Hook[*UserListEvent] OnRecordsListRequest() *hook.Hook[*RecordsListEvent] OnUserViewRequest() *hook.Hook[*UserViewEvent] OnRecordViewRequest() *hook.Hook[*RecordViewEvent] OnUserBeforeCreateRequest() *hook.Hook[*UserCreateEvent] OnRecordBeforeCreateRequest() *hook.Hook[*RecordCreateEvent] OnUserAfterCreateRequest() *hook.Hook[*UserCreateEvent] OnRecordAfterCreateRequest() *hook.Hook[*RecordCreateEvent] OnUserBeforeUpdateRequest() *hook.Hook[*UserUpdateEvent] OnRecordBeforeUpdateRequest() *hook.Hook[*RecordUpdateEvent] OnUserAfterUpdateRequest() *hook.Hook[*UserUpdateEvent] OnRecordAfterUpdateRequest() *hook.Hook[*RecordUpdateEvent] OnUserBeforeDeleteRequest() *hook.Hook[*UserDeleteEvent] OnRecordBeforeDeleteRequest() *hook.Hook[*RecordDeleteEvent] OnUserAfterDeleteRequest() *hook.Hook[*UserDeleteEvent] OnRecordAfterDeleteRequest() *hook.Hook[*RecordDeleteEvent] OnUserAuthRequest() *hook.Hook[*UserAuthEvent] OnRecordAuthRequest() *hook.Hook[*RecordAuthEvent] OnUserListExternalAuths() *hook.Hook[*UserListExternalAuthsEvent] OnRecordListExternalAuths() *hook.Hook[*RecordListExternalAuthsEvent] OnUserBeforeUnlinkExternalAuthRequest() *hook.Hook[*UserUnlinkExternalAuthEvent] OnRecordBeforeUnlinkExternalAuthRequest() *hook.Hook[*RecordUnlinkExternalAuthEvent] OnUserAfterUnlinkExternalAuthRequest() *hook.Hook[*UserUnlinkExternalAuthEvent] OnRecordAfterUnlinkExternalAuthRequest() *hook.Hook[*RecordUnlinkExternalAuthEvent] -
Replaced
forms.UserEmailLogin{}
withforms.RecordPasswordLogin{}
(for both username and email depending on which is enabled for the collection). -
Renamed user related
core.Settings
fields:Old New core.Settings.UserAuthToken{} core.Settings.RecordAuthToken{} core.Settings.UserPasswordResetToken{} core.Settings.RecordPasswordResetToken{} core.Settings.UserEmailChangeToken{} core.Settings.RecordEmailChangeToken{} core.Settings.UserVerificationToken{} core.Settings.RecordVerificationToken{} -
Marked as "Deprecated" and will be removed in v0.9:
core.Settings.EmailAuth{} core.EmailAuthConfig{} schema.FieldTypeUser schema.UserOptions{}
-
The second argument of
apis.StaticDirectoryHandler(fileSystem, enableIndexFallback)
now is used to enable/disable index.html forwarding on missing file (eg. in case of SPA).