- Added optional
pb.realtime.onDisconnect
hook function. Note that the realtime client autoreconnect on its own and this hook is useful only for the cases where you want to apply a special behavior on server error or after closing the realtime connection.
-
Added annotation to exclude the deprecated
RecordModel.expand
key from the parent JSON serialization (pocketbase#5946). -
Added
RecordModel.set
for consistency withRecordModel.get
.
-
Added support for sending batch/transactional create/updated/delete/upsert requests with the new batch Web APIs.
final batch = pb.createBatch(); batch.collection('example1').create(body: { ... }); batch.collection('example2').update('RECORD_ID', body: { ... }); batch.collection('example3').delete('RECORD_ID'); batch.collection('example4').upsert(body: { ... }); final result = await batch.send();
-
Added support for authenticating with OTP (email code):
final result = await pb.collection('users').requestOTP('test@example.com'); // ... show a modal for users to check their email and to enter the received code ... await pb.collection('users').authWithOTP(result.otpId, 'EMAIL_CODE');
Note that PocketBase v0.23.0 comes also with Multi-factor authentication (MFA) support. When enabled from the dashboard, the first auth attempt will result in 401 response and a
mfaId
response, that will have to be submitted with the second auth request. For example:try { await pb.collection('users').authWithPassword('test@example.com', '1234567890'); } on ClientException catch (e) { final mfaId = e.response['mfaId']; if (mfaId == null) { throw e; // not mfa -> rethrow } // the user needs to authenticate again with another auth method, for example OTP final result = await pb.collection('users').requestOTP('test@example.com'); // ... show a modal for users to check their email and to enter the received code ... await pb.collection('users').authWithOTP(result.otpId, 'EMAIL_CODE', query: { 'mfaId': mfaId }); }
-
Added "impersonate" support for superusers to create a non-refreshable auth token for any other auth record:
// authenticate as superuser await pb.collection('_superusers').authWithPassword('test@example.com', '1234567890'); // create a new auth token for the specified user loaded in a new PocketBase client final userClient = pb.collection('users').impersonate('RECORD_ID', 0); // send the request as the impersonated user final result = await userClient.collection('example').getFullList();
-
Added optional
collection
argument toSettingsService.testEmail()
to allow target the email templates of a specific auth collection. -
Added
pb.collections.getScaffolds()
method to return a collection type indexed map with blank collection models loaded with their type specific defaults. -
Added
pb.collections.truncate(idOrName)
method to delete all records associated with the specified collection. -
Added
body
,query
,headers
optional arguments toauthWithOAuth2()
(#62). -
Instead of replacing the entire
pb.authStore.record
, on auth record update we now only replace the available returned response record data (pocketbase#5638). -
⚠️ Soft-deprecated and aliasedpb.admins
because with PockeBase v0.23+ admins are now stored as regular_superusers
collection records.// before -> after pb.admins.* -> pb.collection('_superusers').*
-
Since there is no longer
AdminModel
,pb.authStore.model
is superseeded bypb.authStore.record
. -
⚠️ Changes to theRecordModel
:- Simplified constructor -
RecordModel([Map<String, dynamic>? data])
. - Simplified new accessor method
get<T>(key, fallback)
. It works with all record data, including nestedexpand
properties, not just the regular record fields!final price = record.get<double>('price'); final user = record.get<RecordModel>('expand.user', null); final userEmail = record.get<String>('expand.user.email', 'N/A');
- Simplified constructor -
-
⚠️ Flatten theCollectionModel
model fields, aka. there is no longer the dynamicCollectionModel.options
map and every Collection type field is added as member to the class. -
⚠️ ChangedAuthMethodsList
fields to accomodate the new auth methods andlistAuthMethods()
response.{ "mfa": { "duration": 100, "enabled": true }, "otp": { "duration": 0, "enabled": false }, "password": { "enabled": true, "identityFields": ["email", "username"] }, "oauth2": { "enabled": true, "providers": [{"name": "gitlab", ...}, {"name": "google", ...}] } }
-
⚠️ Soft-deprecated the OAuth2 success authmeta["avatarUrl"]
response field in favour ofmeta["avatarURL"]
for consistency with the JS SDK and the accepted Go API conventions. -
⚠️ Soft-deprecated and aliased*Url()
->*URL()
fields and methods for consistency with the JS SDK and the accepted Go API conventions. The old methods still works but you may get a analyzer warnings to replace them because they will be removed in the future.pb.baseUrl -> pb.baseURL pb.buildUrl() -> pb.buildURL() pb.files.getUrl() -> pb.files.getURL() pb.backups.getDownloadUrl() -> pb.backups.getDownloadURL()
-
⚠️ RemovedRecordService.listExternalAuths()
andRecordService.unlinkExternalAuth()
methods because_externalAuths
is now a regular collection:// old: pb.collection('users').listExternalAuths("RECORD_ID") pb.collection("_externalAuths").getFullList() // old: pb.collection('users').unlinkExternalAuth("RECORD_ID", "provider") pb.collection("_externalAuths").delete("EXTERNAL_AUTH_RECORD_ID")
-
⚠️ RenamedCollectionModel.schema
toCollectionModel.fields
. -
⚠️ Renamed classSchemaField
toCollectionField
.
-
Manually update the verified state of the current matching
AuthStore
model on successful "confirm-verification" call. -
Manually clear the current matching
AuthStore
on "confirm-email-change" call because previous tokens are always invalidated.
multipart/form-data
body is handled.
-
Properly sent json body with
multipart/form-data
requests. This fixes a similar issue described in js-sdk#274. -
Gracefully handle OAuth2 redirect error with the
authWithOAuth2()
call.
- Throw 404
ClientException
ongetOne("")
with empty id.
-
Added experimental
expand
,filter
,fields
, custom query and headers parameters support for the realtime subscriptions.pb.collection("example").subscribe("*", (e) { ... }, filter: "someField > 10");
This works only with PocketBase v0.20.0+.
-
Changes to the logs service methods in relation to the logs generalization in PocketBase v0.20.0+:
pb.logs.getRequestsList(...) -> pb.logs.getList(...) pb.logs.getRequest(...) -> pb.logs.getOne(...) pb.logs.getRequestsStats(...) -> pb.logs.getStats(...)
-
Added missing
SchemaField.presentable
bool field. -
Added new
AuthMethodProvider.displayName
string field. -
Added new
AuthMethodsList.onlyVerified
bool field.
-
Added
pb.filter(rawExpr, params?)
helper to construct a filter string with placeholder parameters populated from aMap
.final records = await pb.collection("example").getList(filter: pb.filter( // the same as: "title ~ 'exa\\'mple' && created = '2023-10-18 18:20:00.123Z'" "title ~ {:title} && created >= {:created}", { "title": "exa'mple", "created": DateTime.now() }, ));
The supported placeholder parameter values are:
String
(single quotes are autoescaped)DateTime
bool
num
null
- everything else is converted to a string using
jsonEncode()
- Fixed
multipart/form-data
body serialization whennull
values are submitted (#48).
- Added
pb.backups.upload(file)
action (available with PocketBase v0.18.0).
pb.files.getUrl()
now returns empty URI in case an empty filename is passed.
- Run
dart formatter
.
- Updated the examples in README.
- Added new generic
RecordModel.getDataValue<T>(key, [default])
data value getter to support retrieval of any value type. We now also allow accessing nested json object values via dot-notation. For example:The oldfinal record = RecordModel(data: {"a": {"b": [{"b1": 1}, {"b2": 2}, {"b3": 3}]}}); record.getDataValue<int>("a.b.1.b2", 2); // 2 record.getDataValue<int>("a.b.c"); // 0 (no explicit default) record.getDataValue<int>("a.b.c", -1); // -1 (explicit default)
RecordModel
getters are aliased to usegetDataValue
.getStringValue(field) is the same as getDataValue<String>(field) getBoolValue(field) is the same as getDataValue<bool>(field) getIntValue(field) is the same as getDataValue<int>(field) getDoubleValue(field) is the same as getDataValue<double>(field) getListValue<T>(field) is the same as getDataValue<List<T>>(field)
- Added new
AsyncAuthStore
helper class that could be used with any external async persistent layer (shared_preferences, hive, local file, etc.). For example:final prefs = await SharedPreferences.getInstance(); final store = AsyncAuthStore( save: (String data) async => prefs.setString('pb_auth', data), initial: prefs.getString('pb_auth'), ); final pb = PocketBase('http://example.com', authStore: store);
- Require Dart 3.0 or later.
-
Added
skipTotal=1
query parameter by default for thegetFirstListItem()
andgetFullList()
requests. Note that this have performance boost only with PocketBase v0.17+. -
Added optional
download=1
query parameter to force file urls withContent-Disposition: attachment
(supported with PocketBase v0.17+).
- Explicitly disable
BaseRequest.persistentConnection
since it is ignored anyway when using the defaultdart-lang/http.Cient
on Web and it is causing issues with the alternativefetch_client
package.
- Modified the original
http.MultipartRequest
to support List field values (pocketbase#2763).
- Exposed the internal
PocketBase.httpClientFactory
constructor option to allow users to provide their ownhttp.Client
implementation as workaround for the realtime events on Flutter Web (#11).
-
Added
fields
optional parameter to the crud services to limit the returned API fields (available with PocketBase v0.16.0). -
Added
pb.backups
service for the new PocketBase backup and restore APIs (available with PocketBase v0.16.0). -
Added
filesystem
optional parameter topb.settings.testS3()
to allow specifying a filesystem for test -storage
orbackups
(available with PocketBase v0.16.0).
- Fixed
RealtimeService._connect()
completer not properly resolving.
-
Added simplified
authWithOAuth2()
version without having to implement custom redirect, deeplink or even page reload:await pb.collection('users').authWithOAuth2('google', (url) async { await launchUrl(url); })
Works with PocketBase v0.15.0+.
This method initializes a one-off realtime subscription and will call
urlCallback
with the OAuth2 vendor url to authenticate. Once the external OAuth2 sign-in/sign-up flow is completed, the browser window will be automatically closed and the OAuth2 data sent back to the user through the previously established realtime connection.Site-note: when creating the OAuth2 app in the provider dashboard you have to configure
https://yourdomain.com/api/oauth2-redirect
as redirect URL.! The "manual" code exchange flow is still supported as
authWithOAuth2Code(provider, code, codeVerifier, redirectUrl)
. -
Adde new
pb.files
service:// Builds and returns an absolute record file url for the provided filename. 🔓 pb.files.getUrl(record, filename, queryParams = {}); // Requests a new private file access token for the current auth model (admin or record). 🔐 pb.files.getToken(queryParams = {});
pb.getFileUrl()
is soft deprecated and acts as alias callingpb.files.getUrl()
under the hood. Works with PocketBase v0.15.0+. -
Removed deprecated
SchemaField.unique
field.
-
Added
CollectionModel.indexes
field for the new collection indexes support in the upcoming PocketBase v0.14.0. -
Added
pb.settings.generateAppleClientSecret()
for sending a request to generate Apple OAuth2 client secret in the upcoming PocketBase v0.14.0.
- Improved the realtime autoretry handling (added
infinity
and stepped retries timeout duration).
- Added support for handling non-json response when calling
pb.send()
.
- Removed version install constraint from the README.
- Added check for the collection name before auto updating the
pb.authStore
state on auth record update/delete.
-
Allowed sending the existing valid auth token with the
authWithPassword()
calls. -
Minor docs improvements.
- Added
pb.health.check()
that checks the health status of the PocketBase server (available in PocketBase v0.10.0)
- Updated the realtime examples in the README.
⚠️ Please note that this release works only with the new PocketBase v0.8+ API!See the breaking changes below for more information on what has changed.
-
Added new crud method
getFirstListItem(filter)
to fetch a single item by a list filter. -
Added optional named
expand
argument to all crud functions that returns aRecordModel
(with v0.8 we now also support indirect expansion). -
You can now pass additional account
createData
when authenticating with OAuth2. -
Added
AuthMethodsList.usernamePassword
return field (we now support combined username/email authentication; see belowauthWithPassword
).
-
For easier and more conventional parsing, all DateTime strings now have
Z
as suffix, eg.2022-01-01 01:02:03.456Z
. -
Moved
pb.records.getFileUrl()
topb.getFileUrl()
. -
Moved all
pb.records.*
handlers underpb.collection().*
:pb.records.getFullList('example'); => pb.collection('example').getFullList(); pb.records.getList('example'); => pb.collection('example').getList(); pb.records.getOne('example', 'RECORD_ID'); => pb.collection('example').getOne('RECORD_ID'); (no old equivalent) => pb.collection('example').getFirstListItem(filter); pb.records.create('example', ...); => pb.collection('example').create(...); pb.records.update('example', 'RECORD_ID', ...); => pb.collection('example').update('RECORD_ID', ...); pb.records.delete('example', 'RECORD_ID'); => pb.collection('example').delete('RECORD_ID');
-
The
pb.realtime
service has now a more general callback form so that it can be used with custom realtime handlers. Dedicated records specific subscribtions could be found underpb.collection().*
:pb.realtime.subscribe('example', callback) => pb.collection('example').subscribe('*', callback); pb.realtime.subscribe('example/RECORD_ID', callback) => pb.collection('example').subscribe('RECORD_ID', callback); pb.realtime.unsubscribe('example') => pb.collection('example').unsubscribe('*'); pb.realtime.unsubscribe('example/RECORD_ID') => pb.collection('example').unsubscribe('RECORD_ID'); (no old equivalent) => pb.collection('example').unsubscribe();
Additionally,
subscribe()
now returnUnsubscribeFunc
that could be used to unsubscribe only from a single subscription listener. -
Moved all
pb.users.*
handlers underpb.collection().*
:pb.users.listAuthMethods(); => pb.collection('users').listAuthMethods(); pb.users.authViaEmail(email, password); => pb.collection('users').authWithPassword(usernameOrEmail, password); pb.users.authViaOAuth2(provider, code, codeVerifier, redirectUrl, createData: ...); => pb.collection('users').authWithOAuth2(provider, code, codeVerifier, redirectUrl, createData: ...); pb.users.refresh(); => pb.collection('users').authRefresh(); pb.users.requestPasswordReset(email); => pb.collection('users').requestPasswordReset(email); pb.users.confirmPasswordReset(resetToken, newPassword, newPasswordConfirm); => pb.collection('users').confirmPasswordReset(resetToken, newPassword, newPasswordConfirm); pb.users.requestVerification(email); => pb.collection('users').requestVerification(email); pb.users.confirmVerification(verificationToken); => pb.collection('users').confirmVerification(verificationToken); pb.users.requestEmailChange(newEmail); => pb.collection('users').requestEmailChange(newEmail); pb.users.confirmEmailChange(emailChangeToken, password); => pb.collection('users').confirmEmailChange(emailChangeToken, password); pb.users.listExternalAuths(recordId); => pb.collection('users').listExternalAuths(recordId); pb.users.unlinkExternalAuth(recordId, provider); => pb.collection('users').unlinkExternalAuth(recordId, provider);
-
Changes in
pb.admins
for consistency with the new auth handlers inpb.collection().*
:pb.admins.authViaEmail(email, password); => pb.admins.authWithPassword(email, password); pb.admins.refresh(); => pb.admins.authRefresh();
-
To prevent confusion with the auth method responses, the following methods now returns 204 with empty body (previously 200 with token and auth model):
Future<void> pb.admins.confirmPasswordReset(...) Future<void> pb.collection("users").confirmPasswordReset(...) Future<void> pb.collection("users").confirmVerification(...) Future<void> pb.collection("users").confirmEmailChange(...)
-
Removed
UserModel
because users are now regular records (aka.RecordModel
). This means that if you want to access for example the email of an auth record, you can do something like:record.getStringValue('email')
. The old user fieldslastResetSentAt
,lastVerificationSentAt
andprofile
are no longer available (theprofile
fields are available under theRecordModel.data
property like any other fields). -
Since there is no longer
UserModel
,pb.authStore.model
can now be of typeRecordModel
,AdminModel
ornull
. -
RecordModel.expand
is now alwaysMap<String, List<RecordModel>>
and it is resolved recursively (previously it was plainMap<String, dynamic>
). Please note that for easier and unified record(s) access, the map value is alwaysList
, even for single relations. When callingRecordModel.toJson()
it will output theexpand
in its original format. -
Removed
lastResetSentAt
fromAdminModel
. -
Renamed the getter
CrudService.basePath
toCrudService.baseCrudPath
. -
Replaced
ExternalAuthModel.userId
with 2 newrecordId
andcollectionId
props. -
Renamed
SubscriptionEvent
toRecordSubscriptionEvent
.
- Stop sending empty JSON map as body (thanks @rodydavis) [#7].
- Changed the default
ClientException.statusCode
from500
to0
.
- Added
UserService.listExternalAuths()
to list all linked external auth providers for a single user. - Added
UserService.unlinkExternalAuth()
to delete a single user external auth provider relation.
- Renamed
LogRequestModel.ip
toLogRequestModel.remoteIp
. - Added
LogRequestModel.userIp
(the "real" user ip when behind a reverse proxy). - Added
SettingsService.testS3()
to test the S3 storage connection. - Added
SettingsService.testEmail()
to send a test email.
- Added
CollectionService.import()
. - Added
totalPages
to theResultList<M>
dto.
- Fixed base64.decode exception when
AuthStore.isValid
is used (related to dart-lang/sdk #39510; thanks @irmhonde).
- This release just removes the
homepage
directive from thepubspec.yaml
.
- No changes were made again. This release is just to trigger the pub.dev analyzer tool in order to test dart-lang/pub-dev #5927 but this time with ICMP enabled for the homepage domain.
- No changes were made. This release is just to trigger the pub.dev analyzer tool in order to test dart-lang/pub-dev #5927.
- Added RecordService to the library exports for dartdoc.
- First public release.