-
Bumped the minimum required Go version to 1.21.0 in order to integrate with the builtin
slog
package. -
removed requests table in favor of logs (note: existing logs will be deleted!)
-
Renamed:
Dao.RequestQuery(...) -> Dao.LogQuery(...) Dao.FindRequestById(...) -> Dao.FindLogById(...) Dao.RequestsStats(...) -> Dao.LogsStats(...) Dao.RequestsStats(...) -> Dao.LogsStats(...) Dao.DeleteOldRequests(...) -> Dao.DeleteOldLogs(...) Dao.SaveRequest(...) -> Dao.SaveLog(...)
-
removed
app.IsDebug()
and the--debug
flag -
(@todo docs) Implemented
slog.Logger
viaapp.Logger()
. Logs db writes are debounced and batched. DB write happens on- 3sec after the last debounced log write
- when the batch threshold, currently 200, is reached (this is primarily to prevent the memory to grow unrestricted)
- right before app termination to attempt saving the current logs queue Several minor log improvements:
- Log the requests execution times.
- Added option to toggle IP request logging.
- Added option to specify a minimum log level.
- Added option to export individual or bulk selected logs as json.
-
Soft-deprecated and renamed
app.Cache()
withapp.Store()
. -
Updated links formatting in the autogenerated html->text mail body.
-
Added
expiry
field to the OAuth2 user response containing the optional expiration time of the OAuth2 access token (#3617). -
Added new
filesystem.Copy(src, dest)
method to copy existing files from one location to another. This is usually useful when duplicating records with file fields programmatically. -
Added new
displayName
field for eachlistAuthMethods()
OAuth2 provider item. The value of thedisplayName
property is currently configurable from the UI only for the OIDC providers. -
Added new
PKCE()
andSetPKCE(enable)
OAuth2 methods to indicate whether the PKCE flow is supported or not. The PKCE value is currently configurable from the UI only for the OIDC providers. This was added to accommodate OIDC providers that may throw an error if unsupported PKCE params are submitted with the auth request (eg. LinkedIn; see #3799). -
Allow a single OAuth2 user to be used for authentication in multiple auth collection.
⚠️ Because now you can have more than one external provider withcollectionId-provider-providerId
pair,Dao.FindExternalAuthByProvider(provider, providerId)
method was removed in favour of the more genericDao.FindFirstExternalAuthByExpr(expr)
.
-
Added support for comments in the API rules and filter expressions.
-
Added support for specifying a collection alias in
@collection.someCollection:alias.*
. -
Added a default red colored Stderr output for the commands errors. You can now also silence individually custom commands errors using the
cobra.Command.SilenceErrors
field. -
Slightly speed up (~10%) the thumbs generation by changing from cubic (
CatmullRom
) to bilinear (Linear
) resampling filter (the quality difference is very little). -
Limit the concurrent thumbs generation to avoid high CPU and memory usage in spiky scenarios (#3794; thanks @t-muehlberger). Currently the max concurrent thumbs generation processes are limited to "total of logical process CPUs + 1". This is arbitrary chosen and may change in the future depending on the users feedback and usage patterns. If you are experiencing OOM errors during large image thumb generations, especially in container environment, you can try defining the
GOMEMLIMIT=500MiB
env variable before starting the executable. -
Minor JSVM updates and fixes:
- updated
$security.parseUnverifiedJWT(token)
and$security.parseJWT(token, key)
to return the payload result as plain object
- updated
-
Removed incorrectly imported empty
local('')
font-face declarations. -
Added
onlyVerified
auth collection option to globally disallow authentication requests for unverified users. -
Added
filesystem.NewFileFromUrl(ctx, url)
helper method to construct a*filesystem.BytesReader
file from the specified url. -
Added
$apis.requireGuestOnly()
middleware JSVM binding (#3896). -
Use
IS NOT
instead of!=
as not-equal SQL query operator to handle the cases when comparing with nullable columns or expressions (eg.json_extract
overjson
field). Based on my local dataset I wasn't able to find a significant difference in the performance between the 2 operators, but if you stumble on a query that you think may be affected negatively by this, please report it and I'll test it further. -
Trigger the
app.OnTerminate()
hook onapp.Restart()
call. A new boolIsRestart
field was also added to thecore.TerminateEvent
event.
- Synced with the recent fixes in v0.19.4.
- Synced with the recent fixes in v0.19.3.
-
Added experimental
expand
,filter
,fields
, custom query and headers parameters support for the realtime subscriptions. Requires JS SDK v0.20.0-rc or Dart SDK v0.17.0-rc.// JS SDK v0.20.0-rc pb.collection("example").subscribe("*", (e) => { ... }, { expand: "someRelField", filter: "status = 'active'", fields: "id,expand.someRelField.*:excerpt(100)", })
// Dart SDK v0.17.0-rc pb.collection("example").subscribe("*", (e) { ... }, expand: "someRelField", filter: "status = 'active'", fields: "id,expand.someRelField.*:excerpt(100)", )
-
Other minor fixes and impovements (eg. added negative string number normalization support for the
json
field type)
-
Fixed TinyMCE source code viewer textarea styles (#3715).
-
Fixed
text
field min/max validators to properly count multi-byte characters (#3735). -
Allowed hyphens in
username
(#3697). More control over the system fields settings will be available in the future. -
Updated the JSVM generated types to use directly the value type instead of
* | undefined
union in functions/methods return declarations.
-
Added the release notes to the console output of
./pocketbase update
(#3685). -
Added missing documention for the JSVM
$mails.*
bindings. -
Relaxed the OAuth2 redirect url validation to allow any string value (#3689; thanks @sergeypdev). Note that the redirect url format is still bound to the accepted values by the specific OAuth2 provider.
-
Fixed
tokenizer.Scan()/ScanAll()
to ignore the separators from the default trim cutset. An option to return also the empty found tokens was also added viaTokenizer.KeepEmptyTokens(true)
. This should fix the parsing of whitespace characters around view query column names when no quotes are used (#3616). -
Fixed the
:excerpt(max, withEllipsis?)
fields
query param modifier to properly add space to the generated text fragment after block tags.
-
Added Patreon OAuth2 provider (#3323; thanks @ghostdevv).
-
Added mailcow OAuth2 provider (#3364; thanks @thisni1s).
-
Added support for
:excerpt(max, withEllipsis?)
fields
modifier that will return a short plain text version of any string value (html tags are stripped). This could be used to minimize the downloaded json data when listing records with largeeditor
html values.await pb.collection("example").getList(1, 20, { "fields": "*,description:excerpt(100)" })
-
Several Admin UI improvements:
- Count the total records separately to speed up the query execution for large datasets (#3344).
- Enclosed the listing scrolling area within the table so that the horizontal scrollbar and table header are always reachable (#2505).
- Allowed opening the record preview/update form via direct URL (#2682).
- Reintroduced the local
date
field tooltip on hover. - Speed up the listing loading times for records with large
editor
field values by initially fetching only a partial of the records data (the complete record data is loaded on record preview/update). - Added "Media library" (collection images picker) support for the TinyMCE
editor
field. - Added support to "pin" collections in the sidebar.
- Added support to manually resize the collections sidebar.
- More clear "Nonempty" field label style.
- Removed the legacy
.woff
and.ttf
fonts and keep only.woff2
.
-
Removed the explicit
Content-Type
charset from the realtime response due to compatibility issues with IIS (#3461). TheConnection:keep-alive
realtime response header was also removed as it is not really used with HTTP2 anyway. -
Added new JSVM bindings:
new Cookie({ ... })
constructor for creating*http.Cookie
equivalent value.new SubscriptionMessage({ ... })
constructor for creating a custom realtime subscription payload.- Soft-deprecated
$os.exec()
in favour of$os.cmd()
to make it more clear that the call only prepares the command and doesn't execute it.
-
⚠️ Bumped the min required Go version to 1.19.
-
Added global
raw
template function to allow outputting raw/verbatim HTML content in the JSVM templates (#3476).{{.description|raw}}
-
Trimmed view query semicolon and allowed single quotes for column aliases (#3450). Single quotes are usually not a valid identifier quote characters, but for resilience and compatibility reasons SQLite allows them in some contexts where only an identifier is expected.
-
Bumped the GitHub action to use min Go 1.21.2 (the fixed issues are not critical as they are mostly related to the compiler/build tools).
-
Fixed empty thumbs directories not getting deleted on Windows after deleting a record img file (#3382).
-
Updated the generated JSVM typings to silent the TS warnings when trying to access a field/method in a Go->TS interface.
- Minor fix for the View collections API Preview and Admin UI listings incorrectly showing the
created
andupdated
fields asN/A
when the view query doesn't have them.
-
Fixed JS error in the Admin UI when listing records with invalid
relation
field value (#3372). This could happen usually only during custom SQL import scripts or when directly modifying the record field value without data validations. -
Updated Go deps and the generated JSVM types.
-
Return the response headers and cookies in the
$http.send()
result (#3310). -
Added more descriptive internal error message for missing user/admin email on password reset requests.
-
Updated Go deps.
- Fixed minor Admin UI JS error in the auth collection options panel introduced with the change from v0.18.4.
- Added escape character (
\
) support in the Admin UI to allow usingselect
field values with comma (#2197).
-
Exposed a global JSVM
readerToString(reader)
helper function to allow reading Goio.Reader
values (#3273). -
Bumped the GitHub action to use min Go 1.21.1 for the prebuilt executable since it contains some minor
html/template
andnet/http
security fixes.
-
Prevent breaking the record form in the Admin UI in case the browser's localStorage quota has been exceeded when uploading or storing large
editor
values (#3265). -
Updated docs and missing JSVM typings.
-
Exposed additional crypto primitives under the
$security.*
JSVM namespace (#3273):// HMAC with SHA256 $security.hs256("hello", "secret") // HMAC with SHA512 $security.hs512("hello", "secret") // compare 2 strings with a constant time $security.equal(hash1, hash2)
- Excluded the local temp dir from the backups (#3261).
-
Simplified the
serve
command to accept domain name(s) as argument to reduce any additional manual hosts setup that sometimes previously was needed when deploying on production (#3190)../pocketbase serve yourdomain.com
-
Added
fields
wildcard (*
) support. -
Added option to upload a backup file from the Admin UI (#2599).
-
Registered a custom Deflate compressor to speedup (nearly 2-3x) the backups generation for the sake of a small zip size increase. Based on several local tests,
pb_data
of ~500MB (from which ~350MB+ are several hundred small files) results in a ~280MB zip generated for ~11s (previously it resulted in ~250MB zip but for ~35s). -
Added the application name as part of the autogenerated backup name for easier identification (#3066).
-
Added new
SmtpConfig.LocalName
option to specify a custom domain name (or IP address) for the initial EHLO/HELO exchange (#3097). This is usually required for verification purposes only by some SMTP providers, such as on-premise Gmail SMTP-relay. -
Added
NoDecimal
number
field option. -
editor
field improvements:- Added new "Strip urls domain" option to allow controlling the default TinyMCE urls behavior (default to
false
for new content). - Normalized pasted text while still preserving links, lists, tables, etc. formatting (#3257).
- Added new "Strip urls domain" option to allow controlling the default TinyMCE urls behavior (default to
-
Added option to auto generate admin and auth record passwords from the Admin UI.
-
Added JSON validation and syntax highlight for the
json
field in the Admin UI (#3191). -
Added datetime filter macros:
// all macros are UTC based @second - @now second number (0-59) @minute - @now minute number (0-59) @hour - @now hour number (0-23) @weekday - @now weekday number (0-6) @day - @now day number @month - @now month number @year - @now year number @todayStart - beginning of the current day as datetime string @todayEnd - end of the current day as datetime string @monthStart - beginning of the current month as datetime string @monthEnd - end of the current month as datetime string @yearStart - beginning of the current year as datetime string @yearEnd - end of the current year as datetime string
-
Added cron expression macros (#3132):
@yearly - "0 0 1 1 *" @annually - "0 0 1 1 *" @monthly - "0 0 1 * *" @weekly - "0 0 * * 0" @daily - "0 0 * * *" @midnight - "0 0 * * *" @hourly - "0 * * * *"
-
⚠️ Added offset argumentDao.FindRecordsByFilter(collection, filter, sort, limit, offset, [params...])
. If you don't need an offset, you can set it to0
. -
To minimize the footguns with
Dao.FindFirstRecordByFilter()
andDao.FindRecordsByFilter()
, the functions now supports an optional placeholder params argument that is safe to be populated with untrusted user input. The placeholders are in the same format as when binding regular SQL parameters.// unsanitized and untrusted filter variables status := "..." author := "..." app.Dao().FindFirstRecordByFilter("articles", "status={:status} && author={:author}", dbx.Params{ "status": status, "author": author, }) app.Dao().FindRecordsByFilter("articles", "status={:status} && author={:author}", "-created", 10, 0, dbx.Params{ "status": status, "author": author, })
-
Added JSVM
$mails.*
binds for the corresponding Go mails package functions. -
Added JSVM helper crypto primitives under the
$security.*
namespace:$security.md5(text) $security.sha256(text) $security.sha512(text)
-
⚠️ DeprecatedRelationOptions.DisplayFields
in favor of the newSchemaField.Presentable
option to avoid the duplication when a single collection is referenced more than once and/or by multiple other collections. -
⚠️ Fill theLastVerificationSentAt
andLastResetSentAt
fields only after a successfull email send (#3121). -
⚠️ Skip APIfields
json transformations for non 20x responses (#3176). -
⚠️ Changes totests.ApiScenario
struct:-
The
ApiScenario.AfterTestFunc
now receive as 3rd argument*http.Response
pointer instead of*echo.Echo
as the latter is not really useful in this context.// old AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) // new AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response)
-
The
ApiScenario.TestAppFactory
now accept the test instance as argument and no longer expect an error as return result (#3025).// old TestAppFactory: func() (*tests.TestApp, error) // new TestAppFactory: func(t *testing.T) *tests.TestApp
Returning a
nil
app instance from the factory results in test failure. You can enforce a custom test failure by callingt.Fatal(err)
inside the factory.
-
-
Bumped the min required TLS version to 1.2 in order to improve the cert reputation score.
-
Reduced the default JSVM prewarmed pool size to 25 to reduce the initial memory consumptions (you can manually adjust the pool size with
--hooksPool=50
if you need to, but the default should suffice for most cases). -
Update
gocloud.dev
dependency to v0.34 and explicitly set the newNoTempDir
fileblob option to prevent the cross-device link error introduced with v0.33. -
Other minor Admin UI and docs improvements.
-
Fixed the autogenerated
down
migrations to properly revert the old collection rules in case a change was made inup
(#3192; thanks @impact-merlinmarek). Existingdown
migrations can't be fixed but that should be ok as usually thedown
migrations are rarely used against prod environments since they can cause data loss and, while not ideal, the previous old behavior of always setting the rules tonull/nil
is safer than not updating the rules at all. -
Updated some Go deps.
- Fixed JSVM
require()
file path error when using Windows-style path delimiters (#3163).
- Added quotes around the wrapped view query columns introduced with v0.17.4.
-
Fixed Views record retrieval when numeric id is used (#3110). With this fix we also now properly recognize
CAST(... as TEXT)
andCAST(... as BOOLEAN)
astext
andbool
fields. -
Fixed
relation
"Cascade delete" tooltip message (#3098). -
Fixed jsvm error message prefix on failed migrations (#3103; thanks @nzhenev).
-
Disabled the initial Admin UI admins counter cache when there are no initial admins to allow detecting externally created accounts (eg. with the
admin
command) (#3106). -
Downgraded
google/go-cloud
dependency to v0.32.0 until v0.34.0 is released to prevent theos.TempDir
cross-device link
errors as too many users complained about it.
-
Fixed Docker
cross-device link
error when creatingpb_data
backups on a local mounted volume (#3089). -
Fixed the error messages for relation to views (#3090).
-
Always reserve space for the scrollbar to reduce the layout shifts in the Admin UI records listing due to the deprecated
overflow: overlay
. -
Enabled lazy loading for the Admin UI thumb images.
-
Soft-deprecated
$http.send({ data: object, ... })
in favour of$http.send({ body: rawString, ... })
to allow sending non-JSON body with the request (#3058). The existingdata
prop will still work, but it is recommended to usebody
instead (to send JSON you can useJSON.stringify(...)
as body value). -
Added
core.RealtimeConnectEvent.IdleTimeout
field to allow specifying a different realtime idle timeout duration per client basis (#3054). -
Fixed
apis.RequestData
deprecation log note (#3068; thanks @gungjodi).
-
Use relative path when redirecting to the OAuth2 providers page in the Admin UI to support subpath deployments (#3026; thanks @sonyarianto).
-
Manually trigger the
OnBeforeServe
hook fortests.ApiScenario
(#3025). -
Trigger the JSVM
cronAdd()
handler only on appserve
to prevent unexpected (and eventually duplicated) cron handler calls when custom console commands are used (#3024). -
The
console.log()
messages are now written to thestdout
instead ofstderr
.
-
New more detailed guides for using PocketBase as framework (both Go and JS). If you find any typos or issues with the docs please report them in https://github.com/pocketbase/site.
-
Added new experimental JavaScript app hooks binding via goja. They are available by default with the prebuilt executable if you create
*.pb.js
file(s) in thepb_hooks
directory. Lower your expectations because the integration comes with some limitations. For more details please check the Extend with JavaScript guide. Optionally, you can also enable the JS app hooks as part of a custom Go build for dynamic scripting but you need to register thejsvm
plugin manually:jsvm.MustRegister(app core.App, config jsvm.Config{})
-
Added Instagram OAuth2 provider (#2534; thanks @pnmcosta).
-
Added VK OAuth2 provider (#2533; thanks @imperatrona).
-
Added Yandex OAuth2 provider (#2762; thanks @imperatrona).
-
Added new fields to
core.ServeEvent
:type ServeEvent struct { App App Router *echo.Echo // new fields Server *http.Server // allows adjusting the HTTP server config (global timeouts, TLS options, etc.) CertManager *autocert.Manager // allows adjusting the autocert options (cache dir, host policy, etc.) }
-
Added
record.ExpandedOne(rel)
andrecord.ExpandedAll(rel)
helpers to retrieve casted single or multiple expand relations from the already loaded "expand" Record data. -
Added rule and filter record
Dao
helpers:app.Dao().FindRecordsByFilter("posts", "title ~ 'lorem ipsum' && visible = true", "-created", 10) app.Dao().FindFirstRecordByFilter("posts", "slug='test' && active=true") app.Dao().CanAccessRecord(record, requestInfo, rule)
-
Added
Dao.WithoutHooks()
helper to create a newDao
from the current one but without the create/update/delete hooks. -
Use a default fetch function that will return all relations in case the
fetchFunc
argument ofDao.ExpandRecord(record, expands, fetchFunc)
andDao.ExpandRecords(records, expands, fetchFunc)
isnil
. -
For convenience it is now possible to call
Dao.RecordQuery(collectionModelOrIdentifier)
with just the collection id or name. In case an invalid collection id/name string is passed the query will be resolved with cancelled context error. -
Refactored
apis.ApiError
validation errors serialization to allowmap[string]error
andmap[string]any
when generating the public safe formattedApiError.Data
. -
Added support for wrapped API errors (in case Go 1.20+ is used with multiple wrapped errors, the first
apis.ApiError
takes precedence). -
Added
?download=1
file query parameter to the file serving endpoint to force the browser to always download the file and not show its preview. -
Added new utility
github.com/pocketbase/pocketbase/tools/template
subpackage to assist with rendering HTML templates using the standard Gohtml/template
andtext/template
syntax. -
Added
types.JsonMap.Get(k)
andtypes.JsonMap.Set(k, v)
helpers for the cases where the type aliased direct map access is not allowed (eg. in goja). -
Soft-deprecated
security.NewToken()
in favor ofsecurity.NewJWT()
. -
Hook.Add()
andHook.PreAdd
now returns a unique string identifier that could be used to remove the registered hook handler viaHook.Remove(handlerId)
. -
Changed the after* hooks to be called right before writing the user response, allowing users to return response errors from the after hooks. There is also no longer need for returning explicitly
hook.StopPropagtion
when writing custom response body in a hook because we will skip the finalizer response body write if a response was already "committed". -
⚠️ Renamed*Options{}
toConfig{}
for consistency and replaced the unnecessary pointers with their value equivalent to keep the applied configuration defaults isolated within their function calls:old: pocketbase.NewWithConfig(config *pocketbase.Config) *pocketbase.PocketBase new: pocketbase.NewWithConfig(config pocketbase.Config) *pocketbase.PocketBase old: core.NewBaseApp(config *core.BaseAppConfig) *core.BaseApp new: core.NewBaseApp(config core.BaseAppConfig) *core.BaseApp old: apis.Serve(app core.App, options *apis.ServeOptions) error new: apis.Serve(app core.App, config apis.ServeConfig) (*http.Server, error) old: jsvm.MustRegisterMigrations(app core.App, options *jsvm.MigrationsOptions) new: jsvm.MustRegister(app core.App, config jsvm.Config) old: ghupdate.MustRegister(app core.App, rootCmd *cobra.Command, options *ghupdate.Options) new: ghupdate.MustRegister(app core.App, rootCmd *cobra.Command, config ghupdate.Config) old: migratecmd.MustRegister(app core.App, rootCmd *cobra.Command, options *migratecmd.Options) new: migratecmd.MustRegister(app core.App, rootCmd *cobra.Command, config migratecmd.Config)
-
⚠️ Changed the type ofsubscriptions.Message.Data
fromstring
to[]byte
becauseData
usually is a json bytes slice anyway. -
⚠️ Renamedmodels.RequestData
tomodels.RequestInfo
and soft-deprecatedapis.RequestData(c)
in favor ofapis.RequestInfo(c)
to avoid the stuttering with theData
field. The oldapis.RequestData()
method still works to minimize the breaking changes but it is recommended to replace it withapis.RequestInfo(c)
. -
⚠️ Changes to the List/Search APIs-
Added new query parameter
?skipTotal=1
to skip theCOUNT
query performed with the list/search actions (#2965). If?skipTotal=1
is set, the response fieldstotalItems
andtotalPages
will have-1
value (this is to avoid having different JSON responses and to differentiate from the zero default). With the latest JS SDK 0.16+ and Dart SDK v0.11+ versionsskipTotal=1
is set by default for thegetFirstListItem()
andgetFullList()
requests. -
The count and regular select statements also now executes concurrently, meaning that we no longer perform normalization over the
page
parameter and in case the user request a page that doesn't exist (eg.?page=99999999
) we'll return emptyitems
array. -
Reverted the default
COUNT
column toid
as there are some common situations where it can negatively impact the query performance. Additionally, from this version we also setPRAGMA temp_store = MEMORY
so that also helps with the temp B-TREE creation whenid
is used. There are still scenarios whereCOUNT
queries withrowid
executes faster, but the majority of the time when nested relations lookups are used it seems to have the opposite effect (at least based on the benchmarks dataset).
-
-
⚠️ Disallowed relations to views from non-view collections (#3000). The change was necessary because I wasn't able to find an efficient way to track view changes and the previous behavior could have too many unexpected side-effects (eg. view with computed ids). There is a system migration that will convert the existing viewrelation
fields tojson
(multiple) andtext
(single) fields. This could be a breaking change if you haverelation
to view and useexpand
or some of therelation
view fields as part of a collection rule. -
⚠️ Added an extraaction
argument to theDao
hooks to allow skipping the default persist behavior. In preparation for the logs generalization, theDao.After*Func
methods now also allow returning an error. -
Allowed
0
asRelationOptions.MinSelect
value to avoid the ambiguity between 0 and non-filled input value (#2817). -
Fixed zero-default value not being used if the field is not explicitly set when manually creating records (#2992). Additionally,
record.Get(field)
will now always return normalized value (the same as in the json serialization) for consistency and to avoid ambiguities with what is stored in the related DB table. The schema fields columnsDEFAULT
definition was also updated for new collections to ensure thatNULL
values can't be accidentally inserted. -
Fixed
migrate down
not returning the correctlastAppliedMigrations()
when the stored migration applied time is in seconds. -
Fixed realtime delete event to be called after the record was deleted from the DB (including transactions and cascade delete operations).
-
Other minor fixes and improvements (typos and grammar fixes, updated dependencies, removed unnecessary 404 error check in the Admin UI, etc.).
- Added multiple valued fields (
relation
,select
,file
) normalizations to ensure that the zero-default value of a newly created multiple field is applied for already existing data (#2930).
- Register the
eagerRequestInfoCache
middleware only for the internalapi
group routes to avoid conflicts with custom route handlers (#2914).
-
Fixed unique validator detailed error message not being returned when camelCase field name is used (#2868).
-
Updated the index parser to allow no space between the table name and the columns list (#2864).
-
Updated go deps.
- Minor optimization for the list/search queries to use
rowid
with theCOUNT
statement when available. This eliminates the temp B-TREE step when executing the query and for large datasets (eg. 150k) it could have 10x improvement (from ~580ms to ~60ms).
-
Fixed collection index column sort normalization in the Admin UI (#2681; thanks @SimonLoir).
-
Removed unnecessary admins count in
apis.RequireAdminAuthOnlyIfAny()
middleware (#2726; thanks @svekko). -
Fixed
multipart/form-data
request bind not populating map array values (#2763). -
Upgraded npm and Go dependencies.
-
Fixed the Admin UI serialization of implicit relation display fields (#2675).
-
Reset the Admin UI sort in case the active sort collection field is renamed or deleted.
-
Fixed the selfupdate command not working on Windows due to missing
.exe
in the extracted binary path (#2589). Note that the command on Windows will work from v0.16.4+ onwards, meaning that you still will have to update manually one more time to v0.16.4. -
Added
int64
,int32
,uint
,uint64
anduint32
support when scanningtypes.DateTime
(#2602) -
Updated dependencies.
-
Fixed schema fields sort not working on Safari/Gnome Web (#2567).
-
Fixed default
PRAGMA
s not being applied for new connections (#2570).
-
Fixed backups archive not excluding the local
backups
directory on Windows (#2548). -
Changed file field to not use
dataTransfer.effectAllowed
when dropping files since it is not reliable and consistent across different OS and browsers (#2541). -
Auto register the initial generated snapshot migration to prevent incorrectly reapplying the snapshot on Docker restart (#2551).
-
Fixed missing view id field error message typo.
-
Fixed backup restore not working in a container environment when
pb_data
is mounted as volume (#2519). -
Fixed Dart SDK realtime API preview example (#2523; thanks @xFrann).
-
Fixed typo in the backups create panel (#2526; thanks @dschissler).
-
Removed unnecessary slice length check in
list.ExistInSlice
(#2527; thanks @KunalSin9h). -
Avoid mutating the cached request data on OAuth2 user create (#2535).
-
Fixed Export Collections "Download as JSON" (#2540).
-
Fixed file field drag and drop not working in Firefox and Safari (#2541).
-
Added automated backups (+ cron rotation) APIs and UI for the
pb_data
directory. The backups can be also initialized programmatically usingapp.CreateBackup("backup.zip")
. There is also experimental restore method -app.RestoreBackup("backup.zip")
(currently works only on UNIX systems as it relies on execve). The backups can be stored locally or in external S3 storage (it has its own configuration, separate from the file uploads storage filesystem). -
Added option to limit the returned API fields using the
?fields
query parameter. The "fields picker" is applied forSearchResult.Items
and every other JSON response. For example:// original: {"id": "RECORD_ID", "name": "abc", "description": "...something very big...", "items": ["id1", "id2"], "expand": {"items": [{"id": "id1", "name": "test1"}, {"id": "id2", "name": "test2"}]}} // output: {"name": "abc", "expand": {"items": [{"name": "test1"}, {"name": "test2"}]}} const result = await pb.collection("example").getOne("RECORD_ID", { expand: "items", fields: "name,expand.items.name", })
-
Added new
./pocketbase update
command to selfupdate the prebuilt executable (with option to generate a backup of yourpb_data
). -
Added new
./pocketbase admin
console command:// creates new admin account ./pocketbase admin create [email protected] 123456890 // changes the password of an existing admin account ./pocketbase admin update [email protected] 0987654321 // deletes single admin account (if exists) ./pocketbase admin delete [email protected]
-
Added
apis.Serve(app, options)
helper to allow starting the API server programmatically. -
Updated the schema fields Admin UI for "tidier" fields visualization.
-
Updated the logs "real" user IP to check for
Fly-Client-IP
header and changed theX-Forward-For
header to use the first non-empty leftmost-ish IP as it the closest to the "real IP". -
Added new
tools/archive
helper subpackage for managing archives (currently works only with zip). -
Added new
tools/cron
helper subpackage for scheduling task using cron-like syntax (this eventually may get exported in the future in a separate repo). -
Added new
Filesystem.List(prefix)
helper to retrieve a flat list with all files under the provided prefix. -
Added new
App.NewBackupsFilesystem()
helper to create a dedicated filesystem abstraction for managing app data backups. -
Added new
App.OnTerminate()
hook (executed right before app termination, eg. onSIGTERM
signal). -
Added
accept
file field attribute with the field MIME types (#2466; thanks @Nikhil1920). -
Added support for multiple files sort in the Admin UI (#2445).
-
Added support for multiple relations sort in the Admin UI.
-
Added
meta.isNew
to the OAuth2 auth JSON response to indicate a newly OAuth2 created PocketBase user.
-
Updated the Admin UI to use the latest JS SDK to resolve the
isNew
record field conflict (#2385). -
Fixed
editor
field fullscreenz-index
(#2410). -
Inserts the default app settings as part of the system init migration so that they are always available when accessed from within a user defined migration (#2423).
-
Fixed View query
SELECT DISTINCT
identifiers parsing (#2349-5706019). -
Fixed View collection schema incorrectly resolving multiple aliased fields originating from the same field source (#2349-5707675).
-
Added OAuth2 redirect fallback message to notify the user to go back to the app in case the browser window is not auto closed.
-
Trigger the related
Record
model realtime subscription events on custom model struct save (#2325). -
Fixed
Ctrl + S
in theeditor
field not propagating the quick save shortcut to the parent form. -
Added
⌘ + S
alias for the record quick save shortcut (I have no Mac device to test it but it should work based one.metaKey
docs). -
Enabled RTL for the TinyMCE editor (#2327).
-
Reduced the record form vertical layout shifts and slightly improved the rendering speed when loading multiple
relation
fields. -
Enabled Admin UI assets cache.
-
Simplified the OAuth2 authentication flow in a single "all in one" call (#55). Requires JS SDK v0.14.0+ or Dart SDK v0.9.0+. The manual code-token exchange flow is still supported but the SDK method is renamed to
authWithOAuth2Code()
(to minimize the breaking changes the JS SDK has a function overload that will proxy the existingauthWithOauth2
calls toauthWithOAuth2Code
). For more details and example, you could check https://pocketbase.io/docs/authentication/#oauth2-integration. -
Added support for protected files (#215). Requires JS SDK v0.14.0+ or Dart SDK v0.9.0+. It works with a short lived (~5min) file token passed as query param with the file url. For more details and example, you could check https://pocketbase.io/docs/files-handling/#protected-files.
-
⚠️ Fixed typo inRecord.WithUnkownData()
->Record.WithUnknownData()
. -
Added simple loose wildcard search term support in the Admin UI.
-
Added auto "draft" to allow restoring previous record state in case of accidental reload or power outage.
-
Added
Ctrl + S
shortcut to save the record changes without closing the panel. -
Added "drop files" support for the file upload field.
-
Refreshed the OAuth2 Admin UI.
-
Added checks for
nil
hooks informs.RecordUpsert
when used with customDao
(#2277). -
Fixed unique detailed field error not returned on record create failure (#2287).
- Fixed concurrent map write pannic on
list.ExistInSliceWithRegex()
cache (#2272).
-
Fixed Admin UI Logs
meta
visualization in Firefox (#2221). -
Downgraded to v1 of the
aws/aws-sdk-go
package since v2 has compatibility issues with GCS (#2231). -
Upgraded the GitHub action to use min Go 1.20.3 for the prebuilt executable since it contains some minor
net/http
security fixes.
- Reverted part of the old
COALESCE
handling as a fallback to support empty string comparison with missing joined relation fields.
-
Fixed realtime events firing before the files upload completion.
-
Updated the underlying S3 lib to use
aws-sdk-go-v2
(#1346; thanks @yuxiang-gao). -
Updated TinyMCE to v6.4.1.
-
Updated the godoc of
Dao.Save*
methods.
-
Added experimental Apple OAuth2 integration.
-
Added
@request.headers.*
filter rule support. -
Added support for advanced unique constraints and indexes management (#345, #544)
-
Simplified the collections fields UI to allow easier and quicker scaffolding of the data schema.
-
Deprecated
SchemaField.Unique
. Unique constraints are now managed via indexes. TheUnique
field is a no-op and will be removed in future version. -
Removed the
COALESCE
wrapping from some of the generated filter conditions to make better use of the indexes (#1939). -
Detect
id
aliased view columns as singlerelation
fields (#2029). -
Optimized single relation lookups.
-
Normalized record values on
maxSelect
field option change (select
,file
,relation
). When changing from single to multiple all already inserted single values are converted to an array. When changing from multiple to single only the last item of the already inserted array items is kept. -
Changed the cost/round factor of bcrypt hash generation from 13 to 12 since several users complained about the slow authWithPassword responses on lower spec hardware. The change will affect only new users. Depending on the demand, we might make it configurable from the auth options.
-
Simplified the default mail template styles to allow more control over the template layout (#1904).
-
Added option to explicitly set the record id from the Admin UI (#2118).
-
Added
migrate history-sync
command to clean_migrations
history table from deleted migration files references. -
Added new fields to the
core.RecordAuthWithOAuth2Event
struct:IsNewRecord bool, // boolean field indicating whether the OAuth2 action created a new auth record ProviderName string, // the name of the OAuth2 provider (eg. "google") ProviderClient auth.Provider, // the loaded Provider client instance
-
Added CGO linux target for the prebuilt executable.
-
⚠️ Renameddaos.GetTableColumns()
todaos.TableColumns()
for consistency with the other Dao table related helpers. -
⚠️ Renameddaos.GetTableInfo()
todaos.TableInfo()
for consistency with the other Dao table related helpers. -
⚠️ Changedtypes.JsonArray
to support specifying a generic type, aka.types.JsonArray[T]
. If you have previously usedtypes.JsonArray
, you'll have to update it totypes.JsonArray[any]
. -
⚠️ Registered theRemoveTrailingSlash
middleware only for the/api/*
routes since it is causing issues with subpath file serving endpoints (#2072). -
⚠️ Changed the request logsmethod
value to UPPERCASE, eg. "get" => "GET" (#1956). -
Other minor UI improvements.
- Removed eager unique collection name check to support lazy validation during bulk import.
-
Fixed view collections import (#2044).
-
Updated the records picker Admin UI to show properly view collection relations.
- Fixed Admin UI js error when selecting multiple
file
field asrelation
"Display fields" (#1989).
- Added
HEAD
request method support for the/api/files/:collection/:recordId/:filename
route (#1976).
-
Added new "View" collection type allowing you to create a read-only collection from a custom SQL
SELECT
statement. It supports:- aggregations (
COUNT()
,MIN()
,MAX()
,GROUP BY
, etc.) - column and table aliases
- CTEs and subquery expressions
- auto
relation
fields association file
fields proxying (up to 5 linked relations, eg. view1->view2->...->base)filter
,sort
andexpand
- List and View API rules
- aggregations (
-
Added auto fail/retry (default to 8 attempts) for the
SELECT
queries to gracefully handle thedatabase is locked
errors (#1795). The default max attempts can be accessed or changed viaDao.MaxLockRetries
. -
Added default max query execution timeout (30s). The default timeout can be accessed or changed via
Dao.ModelQueryTimeout
. For the prebuilt executables it can be also changed via the--queryTimeout=10
flag. -
Added support for
dao.RecordQuery(collection)
to scan directly theOne()
andAll()
results in*models.Record
or[]*models.Record
without the need of explicitNullStringMap
. -
Added support to overwrite the default file serve headers if an explicit response header is set.
-
Added file thumbs when visualizing
relation
display file fields. -
Added "Min select"
relation
field option. -
Enabled
process.env
in JS migrations to allow accessingos.Environ()
. -
Added
UploadedFiles
field to theRecordCreateEvent
andRecordUpdateEvent
event structs. -
⚠️ Moved file upload after the record persistent to allow setting custom record id safely from theOnModelBeforeCreate
hook. -
⚠️ ChangedSystem.GetFile()
to return directly*blob.Reader
instead of theio.ReadCloser
interface. -
⚠️ ChangedTo
,Cc
andBcc
ofmailer.Message
to[]mail.Address
for consistency and to allow multiple recipients and optional name.If you are sending custom emails, you'll have to replace:
message := &mailer.Message{ ... // (old) To: mail.Address{Address: "[email protected]"} To: []mail.Address{{Address: "[email protected]", Name: "Some optional name"}}, // (old) Cc: []string{"[email protected]"} Cc: []mail.Address{{Address: "[email protected]", Name: "Some optional name"}}, // (old) Bcc: []string{"[email protected]"} Bcc: []mail.Address{{Address: "[email protected]", Name: "Some optional name"}}, ... }
-
⚠️ Refactored the Authentik integration as a more generic "OpenID Connect" provider (oidc
) to support any OIDC provider (Okta, Keycloak, etc.). If you've previously used Authentik, make sure to rename the provider key in your code tooidc
. To enable more than one OIDC provider you can use the additionaloidc2
andoidc3
provider keys. -
⚠️ Removed the previously deprecatedDao.Block()
andDao.Continue()
helpers in favor ofDao.NonconcurrentDB()
. -
Updated the internal redirects to allow easier subpath deployment when behind a reverse proxy.
-
Other minor Admin UI improvements.
-
Fixed "Toggle column" reactivity when navigating between collections (#1836).
-
Logged the current datetime on server start (#1822).
-
Fixed the "Clear" button of the datepicker component not clearing the value (#1730).
-
Increased slightly the fields contrast (#1742).
-
Auto close the multi-select dropdown if "Max select" is reached.
-
Fixed js error on empty relation save.
-
Fixed
overlay-active
css class not being removed on nested overlay panel close (#1718). -
Added the collection name in the page title (#1711).
-
Refactored the relation picker UI to allow server-side search, sort, create, update and delete of relation records (#976).
-
Added new
RelationOptions.DisplayFields
option to specify custom relation field(s) visualization in the Admin UI. -
Added Authentik OAuth2 provider (#1377; thanks @pr0ton11).
-
Added LiveChat OAuth2 provider (#1573; thanks @mariosant).
-
Added Gitea OAuth2 provider (#1643; thanks @hlanderdev).
-
Added PDF file previews (#1548; thanks @mjadobson).
-
Added video and audio file previews.
-
Added rich text editor (
editor
) field for HTML content based on TinyMCE (#370). Currently the new field doesn't have any configuration options or validations but this may change in the future depending on how devs ended up using it. -
Added "Duplicate" Collection and Record options in the Admin UI (#1656).
-
Added
filesystem.GetFile()
helper to read files through the FileSystem abstraction (#1578; thanks @avarabyeu). -
Added new auth event hooks for finer control and more advanced auth scenarios handling:
// auth record OnRecordBeforeAuthWithPasswordRequest() OnRecordAfterAuthWithPasswordRequest() OnRecordBeforeAuthWithOAuth2Request() OnRecordAfterAuthWithOAuth2Request() OnRecordBeforeAuthRefreshRequest() OnRecordAfterAuthRefreshRequest() // admin OnAdminBeforeAuthWithPasswordRequest() OnAdminAfterAuthWithPasswordRequest() OnAdminBeforeAuthRefreshRequest() OnAdminAfterAuthRefreshRequest() OnAdminBeforeRequestPasswordResetRequest() OnAdminAfterRequestPasswordResetRequest() OnAdminBeforeConfirmPasswordResetRequest() OnAdminAfterConfirmPasswordResetRequest()
-
Added
models.Record.CleanCopy()
helper that creates a new record copy with only the latest data state of the existing one and all other options reset to their defaults. -
Added new helper
apis.RecordAuthResponse(app, httpContext, record, meta)
to return a standard Record auth API response (#1623). -
Refactored
models.Record
expand and data change operations to be concurrent safe. -
Refactored all
forms
Submit interceptors to use a generic data type as their payload. -
Added several
store.Store
helpers:store.Reset(newData map[string]T) store.Length() int store.GetAll() map[string]T
-
Added "tags" support for all Record and Model related event hooks.
The "tags" allow registering event handlers that will be called only on matching table name(s) or colleciton id(s)/name(s). For example:
app.OnRecordBeforeCreateRequest("articles").Add(func(e *core.RecordCreateEvent) error { // called only on "articles" record creation log.Println(e.Record) return nil })
For all those event hooks
*hook.Hook
was replaced with*hooks.TaggedHook
, but the hook methods signatures are the same so it should behave as it was previously if no tags were specified. -
⚠️ Fixed thejson
field string value normalization (#1703).In order to support seamlessly both
application/json
andmultipart/form-data
requests, the following normalization rules are applied if thejson
field is a plain string value:- "true" is converted to the json
true
- "false" is converted to the json
false
- "null" is converted to the json
null
- "[1,2,3]" is converted to the json
[1,2,3]
- "{"a":1,"b":2}" is converted to the json
{"a":1,"b":2}
- numeric strings are converted to json number
- double quoted strings are left as they are (aka. without normalizations)
- any other string (empty string too) is double quoted
Additionally, the "Nonempty"
json
field constraint now checks fornull
,[]
,{}
and""
(empty string). - "true" is converted to the json
-
Added
aria-label
to some of the buttons in the Admin UI for better accessibility (#1702; thanks @ndarilek). -
Updated the filename extension checks in the Admin UI to be case-insensitive (#1707; thanks @hungcrush).
-
Other minor improvements (more detailed API file upload errors, UI optimizations, docs improvements, etc.)
- Fixed cascade delete for rel records with the same id as the main record (#1689).
-
Fix realtime API panic on concurrent clients iteration (#1628)
-
app.SubscriptionsBroker().Clients()
now returns a shallow copy of the underlying map. -
Added
Discard()
andIsDiscarded()
helper methods to thesubscriptions.Client
interface. -
Slow clients should no longer "block" the main action completion.
-
-
Fixed
fs.DeleteByPrefix()
hang on invalid S3 settings (#1575). -
Updated file(s) delete to run in the background on record/collection delete to avoid blocking the delete model transaction. Currently the cascade files delete operation is treated as "non-critical" and in case of an error it is just logged during debug. This will be improved in the near future with the planned async job queue implementation.
- Unescaped path parameter values (#1552).
-
Added
+
and-
body field modifiers fornumber
,files
,select
andrelation
fields.{ // oldValue + 2 "someNumber+": 2, // oldValue + ["id1", "id2"] - ["id3"] "someRelation+": ["id1", "id2"], "someRelation-": ["id3"], // delete single file by its name (file fields supports only the "-" modifier!) "someFile-": "filename.png", }
Note1:
@request.data.someField
will contain the final resolved value.Note2: The old index (
"field.0":null
) and filename ("field.filename.png":null
) based suffixed syntax for deleting files is still supported. -
⚠️ Added support for multi-match/match-all request data and collection multi-valued fields (select
,relation
) conditions. If you want a "at least one of" type of condition, you can prefix the operator with?
.// for each someRelA.someRelB record require the "status" field to be "active" someRelA.someRelB.status = "active" // OR for "at least one of" condition someRelA.someRelB.status ?= "active"
Note: Previously the behavior for multi-valued fields was as the "at least one of" type. The release comes with system db migration that will update your existing API rules (if needed) to preserve the compatibility. If you have multi-select or multi-relation filter checks in your client-side code and want to preserve the old behavior, you'll have to prefix with
?
your operators. -
Added support for querying
@request.data.someRelField.*
relation fields.// example submitted data: {"someRel": "REL_RECORD_ID"} @request.data.someRel.status = "active"
-
Added
:isset
modifier for the static request data fields.// prevent changing the "role" field @request.data.role:isset = false
-
Added
:length
modifier for the arrayable request data and collection fields (select
,file
,relation
).// example submitted data: {"someSelectField": ["val1", "val2"]} @request.data.someSelectField:length = 2 // check existing record field length someSelectField:length = 2
-
Added
:each
modifier support for the multi-select
request data and collection field.// check if all selected rows has "pb_" prefix roles:each ~ 'pb_%'
-
Improved the Admin UI filters autocomplete.
-
Added
@random
sort key forRANDOM()
sorted list results. -
Added Strava OAuth2 provider (#1443; thanks @szsascha).
-
Added Gitee OAuth2 provider (#1448; thanks @yuxiang-gao).
-
Added IME status check to the textarea keydown handler (#1370; thanks @tenthree).
-
Added
filesystem.NewFileFromBytes()
helper (#1420; thanks @dschissler). -
Added support for reordering uploaded multiple files.
-
Added
webp
to the default images mime type presets list (#1469; thanks @khairulhaaziq). -
Added the OAuth2 refresh token to the auth meta response (#1487).
-
Fixed the text wrapping in the Admin UI listing searchbar (#1416).
-
Fixed number field value output in the records listing (#1447).
-
Fixed duplicated settings view pages caused by uncompleted transitions (#1498).
-
Allowed sending
Authorization
header with the/auth-with-password
record and admin login requests (#1494). -
migrate down
now reverts migrations in the applied order. -
Added additional list-bucket check in the S3 config test API.
-
Other minor improvements.
- Fixed
Record.MergeExpand
panic when the main model expand map is not initialized (#1365).
-
⚠️ Renamed the metadata keyoriginal_filename
tooriginal-filename
due to an S3 file upload error caused by the underscore character (#1343; thanks @yuxiang-gao). -
Fixed request verification docs api url (#1332; thanks @JoyMajumdar2001)
-
Excluded
collectionId
andcollectionName
from the displayable relation props list (1322; thanks @dhall2).
- Fixed nested multiple expands with shared path (#586).
A new helper method
models.Record.MergeExpand(map[string]any)
was also added to simplify the expand handling and unit testing.
- Fixed nested transactions deadlock when authenticating with OAuth2 (#1291).
-
Added
/api/health
endpoint (thanks @MarvinJWendt). -
Added support for SMTP
LOGIN
auth for Microsoft/Outlook and other providers that don't support thePLAIN
auth method (#1217). -
Reduced memory consumption (you can expect ~20% less allocated memory).
-
Added support for split (concurrent and nonconcurrent) DB connections pool increasing even further the concurrent throughput without blocking reads on heavy write load.
-
Improved record references delete performance.
-
Removed the unnecessary parenthesis in the generated filter SQL query, reducing the "parse stack overflow" errors.
-
Fixed
~
expressions backslash literal escaping (#1231). -
Refactored the
core.app.Bootstrap()
to be called before starting the cobra commands (#1267). -
⚠️ Changedpocketbase.NewWithConfig(config Config)
topocketbase.NewWithConfig(config *Config)
and added 4 new config settings:DataMaxOpenConns int // default to core.DefaultDataMaxOpenConns DataMaxIdleConns int // default to core.DefaultDataMaxIdleConns LogsMaxOpenConns int // default to core.DefaultLogsMaxOpenConns LogsMaxIdleConns int // default to core.DefaultLogsMaxIdleConns
-
Added new helper method
core.App.IsBootstrapped()
to check the current app bootstrap state. -
⚠️ Changedcore.NewBaseApp(dir, encryptionEnv, isDebug)
toNewBaseApp(config *BaseAppConfig)
. -
⚠️ Removedrest.UploadedFile
struct (see belowfilesystem.File
). -
Added generic file resource struct that allows loading and uploading file content from different sources (at the moment multipart/form-data requests and from the local filesystem).
filesystem.File{} filesystem.NewFileFromPath(path) filesystem.NewFileFromMultipart(multipartHeader) filesystem/System.UploadFile(file)
-
Refactored
forms.RecordUpsert
to allow more easily loading and removing files programmatically.forms.RecordUpsert.AddFiles(key, filesystem.File...) // add new filesystem.File to the form for upload forms.RecordUpsert.RemoveFiles(key, filenames...) // marks the filenames for deletion
-
Trigger the
password
validators if any of the others password change fields is set.
- Fixed field column name conflict on record deletion (#1220).
-
Moved the record file upload and delete out of the db transaction to minimize the locking times.
-
Added
Dao
query semaphore and base fail/retry handling to improve the concurrent writes throughput (#1187). -
Fixed records cascade deletion when there are "A<->B" relation references.
-
Replaced
c.QueryString()
withc.QueryParams().Encode()
to allow loading middleware modified query parameters in the default crud actions (#1210). -
Fixed the datetime field not triggering the
onChange
event on manual field edit and added a "Clear" button (#1219). -
Updated the GitHub goreleaser action to use go 1.19.4 since it comes with some security fixes.
-
Fixed concurrent multi-relation cascade update/delete (#1138).
-
Added the raw OAuth2 user data (
meta.rawUser
) and OAuth2 access token (meta.accessToken
) to the auth response (#654). -
BaseModel.UnmarkAsNew()
method was renamed toBaseModel.MarkAsNotNew()
. Additionally, to simplify the insert model queries with custom IDs, it is no longer required to callMarkAsNew()
for manually initialized models with set ID since now this is the default state. When the model is populated with values from the database (eg. after rowScan
) it will be marked automatically as "not new". -
Added
Record.OriginalCopy()
method that returns a newRecord
copy populated with the initially loaded record data (useful if you want to compare old and new field values). -
Added new event hooks:
app.OnBeforeBootstrap() app.OnAfterBootstrap() app.OnBeforeApiError() app.OnAfterApiError() app.OnRealtimeDisconnectRequest() app.OnRealtimeBeforeMessageSend() app.OnRealtimeAfterMessageSend() app.OnRecordBeforeRequestPasswordResetRequest() app.OnRecordAfterRequestPasswordResetRequest() app.OnRecordBeforeConfirmPasswordResetRequest() app.OnRecordAfterConfirmPasswordResetRequest() app.OnRecordBeforeRequestVerificationRequest() app.OnRecordAfterRequestVerificationRequest() app.OnRecordBeforeConfirmVerificationRequest() app.OnRecordAfterConfirmVerificationRequest() app.OnRecordBeforeRequestEmailChangeRequest() app.OnRecordAfterRequestEmailChangeRequest() app.OnRecordBeforeConfirmEmailChangeRequest() app.OnRecordAfterConfirmEmailChangeRequest()
-
The original uploaded file name is now stored as metadata under the
original_filename
key. It could be accessed via:fs, _ := app.NewFilesystem() defer fs.Close() attrs, _ := fs.Attributes(fikeKey) attrs.Metadata["original_name"]
-
Added support for
Partial/Range
file requests (#1125). This is a minor breaking change if you are usingfilesystem.Serve
(eg. as part of a customOnFileDownloadRequest
hook):// old filesystem.Serve(res, e.ServedPath, e.ServedName) // new filesystem.Serve(res, req, e.ServedPath, e.ServedName)
-
Refactored the
migrate
command to support external JavaScript migration files using an embedded JS interpreter (goja). This allow writing custom migration scripts such as programmatically creating collections, initializing default settings, running data imports, etc., with a JavaScript API very similar to the Go one (more documentation will be available soon).The
migrate
command is available by default for the prebuilt executable, but if you use PocketBase as framework you need register it manually:migrationsDir := "" // default to "pb_migrations" (for js) and "migrations" (for go) // load js files if you want to allow loading external JavaScript migrations jsvm.MustRegisterMigrations(app, &jsvm.MigrationsOptions{ Dir: migrationsDir, }) // register the `migrate` command migratecmd.MustRegister(app, app.RootCmd, &migratecmd.Options{ TemplateLang: migratecmd.TemplateLangJS, // or migratecmd.TemplateLangGo (default) Dir: migrationsDir, Automigrate: true, })
The refactoring also comes with automigrations support.
If
Automigrate
is enabled (true
by default for the prebuilt executable; can be disabled with--automigrate=0
), PocketBase will generate seamlessly in the background JS (or Go) migration file with your collection changes. The directory with the JS migrations can be committed to your git repo. All migrations (Go and JS) are automatically executed on server start. Also note that the auto generated migrations are granural (in contrast to themigrate collections
snapshot command) and allow multiple developers to do changes on the collections independently (even editing the same collection) miniziming the eventual merge conflicts. Here is a sample JS migration file that will be generated if you for example edit a single collection name:// pb_migrations/1669663597_updated_posts_old.js migrate((db) => { // up const dao = new Dao(db) const collection = dao.findCollectionByNameOrId("lngf8rb3dqu86r3") collection.name = "posts_new" return dao.saveCollection(collection) }, (db) => { // down const dao = new Dao(db) const collection = dao.findCollectionByNameOrId("lngf8rb3dqu86r3") collection.name = "posts_old" return dao.saveCollection(collection) })
-
Added new
Dao
helpers to make it easier fetching and updating the app settings from a migration:dao.FindSettings([optEncryptionKey]) dao.SaveSettings(newSettings, [optEncryptionKey])
-
Moved
core.Settings
tomodels/settings.Settings
:core.Settings{} -> settings.Settings{} core.NewSettings() -> settings.New() core.MetaConfig{} -> settings.MetaConfig{} core.LogsConfig{} -> settings.LogsConfig{} core.SmtpConfig{} -> settings.SmtpConfig{} core.S3Config{} -> settings.S3Config{} core.TokenConfig{} -> settings.TokenConfig{} core.AuthProviderConfig{} -> settings.AuthProviderConfig{}
-
Changed 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", })
The new
*mailer.Message
struct is also now a member of theMailerRecordEvent
andMailerAdminEvent
events. -
Other minor UI fixes and improvements
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 unlimitedmaxSelect
(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) WithUnknownData(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).