For the JSVM version please refer to /v023upgrade/jsvm.
Upgrade overview
Please note that you don't have to upgrade to v0.23.0 if you are not planning further developing your existing app or are satisfied with the v0.22.x features set. There are no critical issues with PocketBase v0.22.x and in the case of reported bugs and security vulnerabilities, the fixes will be backported for at least until Q1 of 2025 (if not longer).
If you don't plan upgrading just make sure to pin the SDKs version to their latest PocketBase v0.22.x compatible:
- JS SDK:
<0.22.0
- Dart SDK:
<0.19.0
Before upgrading make sure to backup your current pb_data
directory somewhere safe.
PocketBase v0.23+ comes with many changes and requires applying manual migration steps to your custom Go
code.
Existing data in pb_data/data.db
will be automatically upgraded once the PocketBase v0.23.0
executable is started.
But there is no easy and automated Go code upgrade solution so be prepared because it might take you some time
- from 1h to entire weekend!
Suggested upgrade path:
- Download a backup of your production
pb_data
so that you can adjust your code and test the upgrade locally. - Ensure that you are already using PocketBase v0.22.x (you can find the version number in the
bottom-right corner in the UI). If you are using an older version, please upgrade first to
PocketBase v0.22.x
(there are no major breaking changes in the recent v0.22.x releases so it should be enough to download
the specific executable and run it against your
pb_data
). - If you are using the SDKs, apply the necessary changes to your client-side code by following:
- Temporary comment the
migrations
import statement (if you use migrations). - Apply the necessary Go code changes based on the notes listed below in this document.
- Upgrade the migration files (if any) to the new APIs. If your existing
migrations
files are all autogenerated, you may find it easier to upgrade by deleting all of them and run with the v0.23.0 version of your appgo run main.go migrate collections
which will create a full snapshot of your latest collections configuration.
Optionally, after that you can also rungo run migrate history-sync
to remove the deleted migrations from the applied migrations history. - Uncomment the
migrations
import statement. - Build your Go executable and test run your local changes (including a test against no existing pb_data).
- If everything is working fine, create another more recent production backup for just in case.
After that deploy your app executable to the production server and restart the app service (e.g.systemctl restart pocketbase
).
The below listed changes doesn't cover everything but it should be a good starting point.
If you need help with the "translation" of your old code to the new APIs, feel free to open a Q&A discussion with the specific code sample and I'll try to assist you.
Initialisms case normalization
For consistency with the Go standard library and to silent commonly used linters the following initilisms case normalization was applied for all exported bindings:
Old | New |
---|---|
%Json% | %JSON% |
%Ip% | %IP% |
%Url% | %URL% |
%Smtp% | %SMTP% |
%Tls% | %TLS% |
%Jwt% | %JWT% |
App changes
The Dao
abstraction has been removed and most of the old Dao
methods are now part of the core.App
instance.
The app settings related to the OAuth2 and email templates are moved in the collection options to allow more granular customizations.
For more details about all new app fields and methods, please refer to the
core.App
interface docs.
App.Cache()
App.Store()
App.RefreshSettings()
App.ReloadSettings()
Admin DB methods (admins are now system "_superusers" auth collection records)
App.Dao().AdminQuery()
App.RecordQuery(core.CollectionNameSuperusers)
App.Dao().FindAdminById(id)
App.FindRecordById(core.CollectionNameSuperusers, id)
App.Dao().FindAdminByEmail(email)
App.FindAuthRecordByEmail(core.CollectionNameSuperusers, email)
App.Dao().FindAdminByToken(token, baseTokenKey)
// tokenType could be:
// - "auth" (core.TokenTypeAuth)
// - "file" (core.TokenTypeFile)
// - "verification" (core.TokenTypeVerification)
// - "passwordReset" (core.TokenTypePasswordReset)
// - "emailChange" (core.TokenTypeEmailChange)
App.FindAuthRecordByToken(token, [tokenType])
App.Dao().TotalAdmins()
App.CountRecords(core.CollectionNameSuperusers, [exprs...])
App.Dao().DeleteAdmin(admin)
App.Delete(admin)
App.Dao().SaveAdmin(admin)
App.Save(admin)
Record DB methods
App.Dao().RecordQuery(collectionModelOrIdentifier)
App.RecordQuery(collectionModelOrIdentifier)
App.Dao().FindRecordById(collectionNameOrId, recordId, [optFilters...])
App.FindRecordById(collectionModelOrIdentifier, recordId, [optFilters...])
App.Dao().FindRecordsByIds(collectionNameOrId, recordIds, [optFilters...])
App.FindRecordsByIds(collectionModelOrIdentifier, recordIds, [optFilters...])
App.Dao().FindRecordsByExpr(collectionNameOrId, [exprs...])
App.FindAllRecords(collectionModelOrIdentifier, [exprs...])
App.Dao().FindFirstRecordByData(collectionNameOrId, key, value)
App.FindFirstRecordByData(collectionModelOrIdentifier, key, value)
App.Dao().FindRecordsByFilter(collectionNameOrId, filter, sort, limit, offset, [params])
App.FindRecordsByFilter(collectionModelOrIdentifier, filter, sort, limit, offset, [params])
N/A
App.CountRecords(collectionModelOrIdentifier, [exprs...])
App.Dao().FindAuthRecordByToken(token, baseTokenKey)
// tokenType could be:
// - auth
// - file
// - verification
// - passwordReset
// - emailChange
App.FindAuthRecordByToken(token, [tokenType])
App.Dao().FindAuthRecordByEmail(collectionNameOrId, email)
App.FindAuthRecordByEmail(collectionModelOrIdentifier, email)
App.Dao().FindAuthRecordByUsername(collectionNameOrId, username)
// The "username" field is no longer required and has no special meaning.
App.FindFirstRecordByData(collectionModelOrIdentifier, "username", username)
App.Dao().SuggestUniqueAuthRecordUsername(ollectionNameOrId, baseUsername, [excludeIds...])
// The "username" field is no longer required and has no special meaning. As a workaound you can use something like:
func suggestUniqueAuthRecordUsername(
collectionModelOrIdentifier,
baseUsername,
) string {
username := baseUsername
for i = 0; i < 10; i++ { // max 10 attempts
total, err := App.CountRecords(
collectionModelOrIdentifier,
dbx.NewExp("LOWER([[username]])={:username}", dbx.Params{"username": username.toLowerCase()}),
)
if err == nil && total == 0 {
break // already unique
}
username = baseUsername + security.RandomStringWithAlphabet(3+i, "123456789")
}
return username
}
App.Dao().CanAccessRecord(record, requestInfo, accessRule)
App.CanAccessRecord(record, requestInfo, accessRule)
App.Dao().ExpandRecord(record, expands, optFetchFunc)
App.ExpandRecord(record, expands, optFetchFunc)
App.Dao().ExpandRecords(records, expands, optFetchFunc)
App.ExpandRecords(records, expands, optFetchFunc)
App.Dao().SaveRecord(record)
App.Save(record)
App.Dao().DeleteRecord(record)
App.Delete(record)
ExternalAuth DB methods (converted to "_externalAuths" collection records)
App.Dao().FindAllExternalAuthsByRecord(authRecord)
App.FindAllExternalAuthsByRecord(authRecord)
N/A
App.FindAllExternalAuthsByCollection(collection)
App.Dao().FindFirstExternalAuthByExpr(expr)
App.FindFirstExternalAuthByExpr(expr)
App.Dao().FindExternalAuthByRecordAndProvider(authRecord)
App.FindFirstExternalAuthByExpr(dbx.HashExp{
"collectionRef": authRecord.Collection().Id,
"recordRef": authRecord.Id,
"provider": provider,
})
App.Dao().DeleteExternalAuth(model)
App.Delete(model)
App.Dao().SaveExternalAuth(model)
App.Save(model)
Collection DB methods
App.Dao().CollectionQuery()
App.CollectionQuery()
App.Dao().FindCollectionsByType(collectionType)
App.FindAllCollections(collectionTypes...)
App.Dao().FindCollectionByNameOrId(nameOrId)
App.FindCollectionByNameOrId(nameOrId)
App.Dao().IsCollectionNameUnique(name, excludeIds...)
App.IsCollectionNameUnique(name, excludeIds...)
App.Dao().FindCollectionReferences(collection, excludeIds...)
App.FindCollectionReferences(collection, excludeIds...)
App.Dao().DeleteCollection(collection)
App.Delete(collection)
App.Dao().SaveCollection(collection)
App.Save(collection)
App.Dao().ImportCollections(importedCollections, deleteMissing, afterSync)
App.ImportCollections(importSliceOfMap, deleteMissing)
N/A
App.ImportCollectionsByMarshaledJSON(sliceOfMapsBytes, deleteMissing)
App.Dao().SyncRecordTableSchema(newCollection, oldCollection)
App.SyncRecordTableSchema(newCollection, oldCollection)
Log DB methods
App.Dao().LogQuery()
App.LogQuery()
App.Dao().FindLogById(id)
App.FindLogById(id)
App.Dao().LogsStats(expr)
App.LogsStats(expr)
App.Dao().DeleteOldLogs(createdBefore)
App.DeleteOldLogs(createdBefore)
App.Dao().SaveLog(log)
App.AuxSave(log)
Other DB methods
App.Dao().SaveSettings(newSettings, [optEncryptionKey])
App.Save(newSettings)
titles := []string{"title1", "title2", "title3"}
collection, err := App.Dao().FindCollectionByNameOrId("articles")
if err != nil {
return err
}
err = App.Dao().RunInTransaction((txDao) => {
// create new record for each title
for _, title := range titles {
record := models.NewRecord(collection)
record.Set("title", title)
err := txDao.SaveRecord(record)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return err
}
titles := []string{"title1", "title2", "title3"}
collection, err := App.FindCollectionByNameOrId("articles")
if err != nil {
return err
}
err = App.RunInTransaction((txApp) => {
// create new record for each title
for _, title := range titles {
record := core.NewRecord(collection)
record.Set("title", title)
err := txApp.Save(record)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return err
}
App.Dao().Vacuum()
App.Vacuum() / App.AuxVacuum()
App.Dao().HasTable(tableName)
App.HasTable(tableName)
App.Dao().TableColumns(tableName)
App.TableColumns(tableName)
App.Dao().TableInfo(tableName)
App.TableInfo(tableName)
App.Dao().TableIndexes(tableName)
App.TableIndexes(tableName)
App.Dao().DeleteTable(tableName)
App.DeleteTable(tableName)
App.Dao().DeleteView(name)
App.DeleteView(name)
App.Dao().SaveView(name, selectQuery)
App.SaveView(name, selectQuery)
App.Dao().CreateViewSchema(selectQuery)
App.CreateViewFields(selectQuery)
Record model changes
For all changes please refer to the Record Go struct
The models
and its subpackages are merged in core
.
The Record create/update operations are simplified and the Dao
and
forms.RecordUpsert
abstractions have been removed.
App.Save(record)
both validate and persist the record. Use
App.SaveNoValidate(record)
if you want to skip the validations.
Files are treated as regular field value, so for uploading a new record file is enough to call
record.Set("yourFileField", *filesystem.File)
.
Additionally, the record.Set()
method now supports all field modifiers that are available
with the Web APIs. For example:
record.Set("total+", 10)
adds 10
to the existing total
record field
value. More information about the available modifiers you can find in the main documentation.
forms.RecordUpsert migration
record, err := app.Dao().FindRecordById("articles", "RECORD_ID")
if err != nil {
return err
}
form := forms.NewRecordUpsert(app, record)
form.LoadData(map[string]any{
"title": "Lorem ipsum",
"active": true,
})
// upload file(s)
f1, _ := filesystem.NewFileFromPath("/path/to/file1")
f2, _ := filesystem.NewFileFromPath("/path/to/file2")
form.AddFiles("documents", f1, f2)
// mark file(s) for deletion
form.RemoveFiles("featuredImages", "demo_xzihx0w.png")
err = form.Submit();
if err != nil {
return err
}
record, err := app.FindRecordById("articles", "RECORD_ID")
if err != nil {
return err
}
// there is also record.Load(map[string]any{ ... })
record.Set("title", "Lorem ipsum")
record.Set("active", true)
// upload file(s)
f1, _ := filesystem.NewFileFromPath("/path/to/file1")
f2, _ := filesystem.NewFileFromPath("/path/to/file2")
record.Set("documents", []any{f1, f2})
// mark file(s) for deletion
record.Set("featuredImages-", "demo_xzihx0w.png")
err = App.Save(record)
if err != nil {
return err
}
Model changes
record.SchemaData()
record.FieldsData()
record.UnknownData()
record.CustomData()
record.WithUnknownData(state)
record.WithCustomData(state)
record.OriginalCopy()
record.Original()
record.CleanCopy()
record.Fresh()
record.GetTime(fieldName)
record.GetDateTime(fieldName).Time()
tokens.* package merge
tokens.NewRecordAuthToken(app, record)
record.NewAuthToken()
tokens.NewRecordVerifyToken(app, record)
record.NewVerificationToken()
tokens.NewRecordResetPasswordToken(app, record)
record.NewPasswordResetToken()
tokens.NewRecordChangeEmailToken(app, record, newEmail)
record.NewEmailChangeToken()
tokens.NewRecordFileToken(app, record)
record.NewFileToken()
Collection model changes
For all changes please refer to the Collection Go struct
Themodels
and its subpackages are merged in core
.
There are now new dedicated collection type factories:
core.NewBaseCollection(name)
core.NewAuthCollection(name)
core.NewViewCollection(name)
Collection.Schema
is renamed to Collection.Fields
and now has a type of
core.FieldsList
(which is just a convenient alias for regular slice).
There are now also dedicated typed fields in the core
package:
core.NumberField
core.BoolField
core.TextField
core.URLField
core.EmailField
core.EditorField
core.DateField
core.AutodateField
core.JSONField
core.RelationField
core.SelectField
core.FileField
All previous dynamic Collection.Options.*
fields are flattened for consistency with the JSON Web
APIs:
Collection.Options.Query = "..."
Collection.ViewQuery = "..."
Collection.Options.ManageRule = "..."
Collection.ManageRule = "..."
Collection.Options.OnlyVerified = true
Collection.AuthRule = "verified = true"
Collection.Options.OnlyVerified = true
Collection.AuthRule = "verified = true"
Collection.Options.AllowOAuth2Auth = true
// note: providers can be set via Collection.OAuth2.Providers
Collection.OAuth2.Enabled = true
Collection.Options.AllowUsernameAuth = true
Collection.PasswordAuth.Enabled = true
Collection.PasswordAuth.IdentityFields = []string{"username"}
Collection.Options.AllowEmailAuth = true
Collection.PasswordAuth.Enabled = true
Collection.PasswordAuth.IdentityFields = []string{"email"}
Collection.Options.RequireEmail = true
Collection.Options.ExceptEmailDomains = []string{"example.com"}
Collection.Options.OnlyEmailDomains = []string{"example.com"}
// you can adjust the system "email" field options in the same way as the regular Collection.Fields, e.g.:
Collection.Fields.GetByName("email").Require = true
Collection.Fields.GetByName("email").ExceptDomains = []string{"example.com"}
Collection.Fields.GetByName("email").OnlyDomains = []string{"example.com"}
Collection.Options.MinPasswordLength = 10
// you can adjust the system "password" field options in the same way as the regular Collection.Fields. e.g.:
Collection.Fields.GetByName("password").Min = 10
The Collection create/update operations are also simplified and the Dao
and
forms.CollectionUpsert
abstractions have been removed.
App.Save(collection)
both validate and persist the collection. Use
App.SaveNoValidate(collection)
if you want to skip the validations.
Below is an example forms.CollectionUpsert
migration:
collection := &models.Collection{}
form := forms.NewCollectionUpsert(app, collection)
form.Name = "example"
form.Type = models.CollectionTypeBase
form.ViewRule = types.Pointer("@request.auth.id != ''")
form.CreateRule = types.Pointer("")
form.UpdateRule = types.Pointer("@request.auth.id != ''")
form.Schema.AddField(&schema.SchemaField{
Name: "title",
Type: schema.FieldTypeText,
Required: true,
Options: &schema.TextOptions{
Max: types.Pointer(10),
},
})
form.Schema.AddField(&schema.SchemaField{
Name: "users",
Type: schema.FieldTypeRelation,
Required: true,
Options: &schema.RelationOptions{
MaxSelect: types.Pointer(5),
CollectionId: "ae40239d2bc4477",
CascadeDelete: true,
},
})
err := form.Submit()
if err != nil {
return err
}
collection := core.NewBaseCollection("example")
collection.ViewRule = types.Pointer("@request.auth.id != ''")
collection.CreateRule = types.Pointer("")
collection.UpdateRule = types.Pointer("@request.auth.id != ''")
collection.Fields.Add(&core.TextField{
Name: "title",
Required: true,
Max: 10,
})
collection.Fields.Add(&core.RelationField{
Name: "users",
Required: true,
MaxSelect: 5,
CollectionId: "ae40239d2bc4477",
CascadeDelete: true,
})
err := app.Save(collection);
if err != nil {
return err
}
Migration changes
If your existing migrations are all autogenerated, you may find it easier to upgrade by deleting
all of them and run with your PocketBase v0.23.0 app
go run main.go migrate collections
which will create a full snapshot of your latest collections configuration.
Optionally, after that you can also run go run main.go migrate history-sync
to remove
the deleted migrations from the applied migrations history.
The migrations.Register
method accepts the transactional app instance as argument instead of
dbx.Builder
. This allow access not only to the database but also to the app settings,
filesystem, mailer, etc.
m.Register(func(db dbx.Builder) error {
dao := daos.New(db)
settings, _ := dao.FindSettings()
settings.Meta.AppName = "test"
settings.Logs.MaxDays = 2
return dao.SaveSettings(settings)
}, nil)
m.Register(func(app core.App) error {
settings := app.Settings()
settings.Meta.AppName = "test"
settings.Logs.MaxDays = 2
return app.Save(settings)
}, nil)
Cron changes
PocketBase v0.23.0 comes with app level cron scheduler - app.Cron()
(this will allow us in the future to show the registered jobs in the UI).
You can still initialize a cron scheduler manually if you want to (especially if you use a custom cron
package), but app.Cron()
will be the recommended way shown in the documentation:
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
scheduler := cron.New()
// prints "Hello!" every 2 minutes
scheduler.MustAdd("hello", "*/2 * * * *", func() {
println("Hello!")
})
scheduler.Start()
return nil
})
// prints "Hello!" every 2 minutes
app.Cron().MustAdd("hello", "*/2 * * * *", func() {
println("Hello!")
})
Routing changes
PocketBase v0.23.0 ships with a new router built on top of the standarad
Go 1.22 mux enhancements
.
Some of the more notable differences are:
- Route path parameters must comply with the
Go 1.22 mux syntax
.
Or in other words:param
must be replaced with{param}
. For static path parameter instead of*
you have to use special{path...}
param. - Similar to the new hook handlers, middlewares must call
e.Next()
in order to proceed with the execution chain. - To access the auth state in a route you can call
e.Auth
. Similarly, to set a new logged auth state you can simply assign a new auth record to thee.Auth
field. - Because admins are now
_superusers
auth collection records,e.Auth
will be set to the authenticated superuser.
To check whethere.Auth
is a superuser you can use thee.HasSuperuserAuth()
helper (or alternatively check the record methode.Auth.IsSuperuser()
ore.Auth.Collection().Name == "_superusers"
). apis.RequestInfo(c)
has been replaced withe.RequestInfo()
.
Note also that therequestInfo.Data
field was renamed torequestInfo.Body
for consistency with the@request.body.*
API rules change.
Below you can find a short comparision table with the new routing interface:
Route registration
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
e.Router.GET("/hello/:name", func(c echo.Context) error {
name := c.PathParam("name")
return c.JSON(http.StatusOK, map[string]string{
"message": "Hello " + name,
})
}, apis.RequireRecordAuth())
return nil
})
app.OnServe().BindFunc(func(se *core.ServeEvent) error {
se.Router.GET("/hello/{name}", func(e *core.RequestEvent) error {
name := e.Request.PathValue("name")
return e.JSON(http.StatusOK, map[string]string{
"message": "Hello " + name,
})
}).Bind(apis.RequireAuth())
return se.Next()
})
Static files serving
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
e.Router.GET(
"/*",
apis.StaticDirectoryHandler(os.DirFS("/path/to/public"), false),
)
return nil
})
app.OnServe().BindFunc(func(se *core.ServeEvent) error {
se.Router.GET(
"/{path...}",
apis.Static(os.DirFS("/path/to/public"), false),
)
return se.Next()
})
Middleware registration
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
e.Router.Use((next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// e.g. inspect some header value before processing the request
header := c.Request().Header.Get("Some-Header")
if header == "" {
return apis.NewBadRequestError("Invalid request", nil)
}
return next(c) // proceed with the request chain
}
})
return nil
})
app.OnServe().BindFunc(func(se *core.ServeEvent) error {
se.Router.BindFunc(func(e *core.RequestEvent) error {
// e.g. inspect some header value before processing the request
header := e.Request.Header.Get("Some-Header")
if header == "" {
return e.BadRequestError("Invalid request", nil)
}
return e.Next() // proceed with the request chain
})
return se.Next()
})
Retrieving the current auth state
// "c" is the echo.Context
admin := c.Get("admin") // admin model
record := c.Get("authRecord") // auth record model
// you can also read the auth state from the cached request info
info := apis.RequestInfo(c)
admin := info.Admin
record := info.AuthRecord
isAdminAuth := admin != null
// "e" is core.RequestEvent
auth := e.Auth // auth record model
// you can also read the auth state from the cached request info
info, _ := e.RequestInfo()
auth := info.Auth
// or e.Auth != nil && e.Auth.IsSuperuser()
isSuperuserAuth := e.HasSuperuserAuth()
Reading path parameters
id := c.PathParam("id")
id := e.Request.PathValue("id")
Reading query parameters
search := c.QueryParam("search")
// or via the cached request object
info := apis.RequestInfo(c)
search := info.Query["search"]
search := e.Request.URL.Query().Get("test")
// or via the cached request object
info, _ := e.RequestInfo()
search := info.Query["search"]
Reading request headers
token := c.Request().Header.Get("Some-Header")
// or via the cached request object (the header value is always normalized)
info := apis.RequestInfo(c)
token := info.Headers["some_header"]
token := e.Request.Header.Get("Some-Header")
// or via the cached request object (the header value is always normalized)
info, _ := e.RequestInfo()
token := info.Headers["some_header"]
Writing response headers
c.Response().Header().Set("Some-Header", "123")
e.Response.Header().Set("Some-Header", "123")
Reading request body
// read/scan the request body fields into a typed object
// (note that the echo request body cannot be read twice with "bind")
body := struct {
Title string `json:"title" form:"title"`
}{}
if err := c.Bind(&body); err != nil {
return apis.NewBadRequestError("Failed to read request body", err)
}
// read single multipart/form-data field
title := c.FormValue("title")
// read single multipart/form-data file
doc := c.FormFile("document")
// convert the multipart/form-data file into *filesystem.File
f, err := filesystem.NewFileFromMultipart(doc)
// read/scan the request body fields into a typed object
// (it safe to be read multiple times)
body := struct {
Title string `json:"title" form:"title"`
}{}
if err := e.BindBody(&body); err != nil {
return e.BadRequestError("Failed to read request body", err)
}
// read single multipart/form-data field
title := e.Request.FormValue("title")
// read single multipart/form-data file
doc := e.Request.FormFile("document")
// return the uploaded files as []*filesystem.File
files, err := e.FindUploadedFiles("document")
Writing response body
// send response with json body
c.JSON(200, {"name": "John"})
// send response with string body
c.String(200, "Lorem ipsum...")
// send response with html body
// (check also the "Rendering templates" section)
c.HTML(200, "<h1>Hello!</h1>")
// redirect
c.Redirect(307, "https://example.com")
// send response with no body
c.NoContent(204)
// send response with json body
e.JSON(200, {"name": "John"})
// send response with string body
e.String(200, "Lorem ipsum...")
// send response with html body
// (check also the "Rendering templates" section)
e.HTML(200, "<h1>Hello!</h1>")
// redirect
e.Redirect(307, "https://example.com")
// send response with no body
e.NoContent(204)
Builtin middlewares
apis.RequireRecordAuth([optCollectionNames...])
apis.RequireAuth([optCollectionNames...])
apis.RequireSameContextRecordAuth()
apis.RequireSameCollectionContextAuth("")
apis.RequireAdminAuth()
// the same as apis.RequireAuth("_superusers")
apis.RequireSuperuserAuth()
apis.RequireAdminAuthOnlyIfAny(app)
N/A
apis.RequireAdminOrRecordAuth("users")
apis.RequireAuth("_superusers", "users")
apis.RequireAdminOrOwnerAuth(ownerIdParam)
apis.RequireSuperuserOrOwnerAuth(ownerIdParam)
apis.ActivityLogger(app)
// No longer needed because it is registered by default for all routes.
// If you want to disable the log of successful route execution you can attach the apis.SkipSuccessActivityLog() middleware.
middleware.Gzip()
apis.Gzip()
API helpers
apis.RecordAuthResponse(app, c, record, meta)
apis.RecordAuthResponse(e, record, authMethod, meta)
apis.EnrichRecord(c, app.Dao(), record, "categories")
apis.EnrichRecords(c, app.Dao(), records, "categories")
apis.EnrichRecord(e, record, "categories")
apis.EnrichRecords(e, records, "categories")
Test helpers changes
tests.ApiScenario
- Renamed
ApiScenario.Url
toApiScenario.URL
. - Renamed
ApiScenario.RequestHeaders
toApiScenario.Headers
. Changed the
ApiScenario.TestAppFactory
field function signature:TestAppFactory: func(t *testing.T) *TestApp { ... }
TestAppFactory: func(t testing.TB) *TestApp { ... }
Changed the
ApiScenario.BeforeTestFunc
field function signature:BeforeTestFunc: func(t *testing.T, app *TestApp, e *echo.Echo) { ... }
BeforeTestFunc: func(t testing.TB, app *TestApp, e *core.ServeEvent) { ... }
Changed the
ApiScenario.AfterTestFunc
field function signature:AfterTestFunc: func(t *testing.T, app *TestApp, res *http.Response) { ... }
AfterTestFunc: func(t testing.TB, app *TestApp, res *http.Response) { ... }
ApiScenario.ExpectedEvents
is now optional and performs partial match (see issue#5333 ):In the rare cases where you want to ensure that no other events except those listed have fired, you can use the wildcard
"*": 0
entry:ExpectedEvents: map[string]int{ "*": 0, "EventA": 1, "EventB": 2, }
tests.TestMailer
- Converted
TestMailer.SentMessages
field toTestMailer.Messages()
method. - Converted
TestMailer.TotalSend
field toTestMailer.TotalSend()
method. - Converted
TestMailer.LastMessage
field toTestMailer.LastMessage()
method. - Added
TestMailer.FirstMessage()
method.
Event hooks changes
For more details about all new hooks and their event properties, please refer to the
On*
method docs in the
core.App
interface docs.
OnBeforeBootstrap -> OnBootstrap
app.OnBeforeBootstrap().Add(func(e *core.BootstrapEvent) error {
// ...
return nil
})
app.OnBootstrap().BindFunc(func(e *core.BootstrapEvent) error {
// ...
return e.Next()
})
OnAfterBootstrap -> OnBootstrap
app.OnAfterBootstrap().Add(func(e *core.BootstrapEvent) error {
// ...
return nil
})
app.OnBootstrap().BindFunc(func(e *core.BootstrapEvent) error {
if err := e.Next(); err != nil {
return err
}
// ...
return nil
})
OnBeforeServe -> OnServe
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
// ...
return nil
})
app.OnServe().BindFunc(func(e *core.ServeEvent) error {
// ...
return e.Next()
})
OnBeforeApiError/OnAfterApiError -> any middleware
app.OnBeforeApiError().Add(func(e *core.ApiErrorEvent) error {
// ...
return nil
})
app.OnServe().BindFunc(func(se *core.ServeEvent) error {
se.Router.BindFunc((e *core.RequestEvent) error {
err := e.Next()
// ...
return err
})
return se.Next()
})
OnTerminate
app.OnTerminate().Add(func(e *core.TerminateEvent) error {
// ...
return nil
})
app.OnTerminate().BindFunc(func(e *core.TerminateEvent) error {
// ...
return e.Next()
})
OnModelBeforeCreate -> OnRecordCreate
app.OnModelBeforeCreate().Add(func(e *core.ModelEvent) error {
// e.Model ...
return nil
})
// note: OnModelCreate is available if you are targetting non-Record/Collection models
app.OnRecordCreate().BindFunc(func(e *core.RecordEvent) error {
// e.Record ...
return e.Next()
})
OnModelAfterCreate -> OnRecordAfterCreateSuccess
app.OnModelAfterCreate().Add(func(e *core.ModelEvent) error {
// e.Model ...
return nil
})
// note: OnModelAfterCreateSuccess is available if you are targetting non-Record/Collection models
app.OnRecordAfterCreateSuccess().BindFunc(func(e *core.RecordEvent) error {
// e.Record ...
return e.Next()
})
OnModelBeforeUpdate -> OnRecordUpdate
app.OnModelBeforeUpdate().Add(func(e *core.ModelEvent) error {
// e.Model ...
return nil
})
// note: OnModelUpdate is available if you are targetting non-Record/Collection models
app.OnRecordUpdate().BindFunc(func(e *core.RecordEvent) error {
// e.Record ...
return e.Next()
})
OnModelAfterUpdate -> OnRecordAfterUpdateSuccess
app.OnModelAfterUpdate().Add(func(e *core.ModelEvent) error {
// e.Model ...
return nil
})
// note: OnModelAfterUpdateSuccess is available if you are targetting non-Record/Collection models
app.OnRecordAfterUpdateSuccess().BindFunc(func(e *core.RecordEvent) error {
// e.Record ...
return e.Next()
})
OnModelBeforeDelete -> OnRecordDelete
app.OnModelBeforeDelete().Add(func(e *core.ModelEvent) error {
// e.Model ...
return nil
})
// note: OnModelDelete is available if you are targetting non-Record/Collection models
app.OnRecordBeforeDelete().BindFunc(func(e *core.RecordEvent) error {
// e.Record ...
return e.Next()
})
OnModelAfterDelete -> OnRecordAfterDeleteSuccess
app.OnModelAfterDelete().Add(func(e *core.ModelEvent) error {
// e.Model ...
return nil
})
// note: OnModelAfterDeleteSuccess is available if you are targetting non-Record/Collection models
app.OnRecordAfterDeleteSuccess().BindFunc(func(e *core.RecordEvent) error {
// e.Record ...
return e.Next()
})
OnMailerBeforeAdminResetPasswordSend -> OnMailerRecordPasswordResetSend
app.OnMailerBeforeAdminResetPasswordSend().Add(func(e *core.MailerAdminEvent) error {
// ...
return nil
})
app.OnMailerRecordPasswordResetSend().BindFunc(func(e *core.MailerRecordEvent) error {
// e.Admin -> e.Record ...
return e.Next()
})
OnMailerAfterAdminResetPasswordSend -> OnMailerRecordPasswordResetSend
app.OnMailerAfterAdminResetPasswordSend().Add(func(e *core.MailerAdminEvent) error {
// ...
return nil
})
app.OnMailerRecordPasswordResetSend().BindFunc(func(e *core.MailerRecordEvent) error {
if err := e.Next(); err != nil {
return err
}
// e.Admin -> e.Record ...
return nil
})
OnMailerBeforeRecordResetPasswordSend -> OnMailerRecordPasswordResetSend
app.OnMailerBeforeRecordResetPasswordSend().Add(func(e *core.MailerRecordEvent) error {
// ...
return nil
})
app.OnMailerRecordPasswordResetSend().BindFunc(func(e *core.MailerRecordEvent) error {
// ...
return e.Next()
})
OnMailerAfterRecordResetPasswordSend -> OnMailerRecordPasswordResetSend
app.OnMailerAfterRecordResetPasswordSend().Add(func(e *core.MailerRecordEvent) error {
// ...
return nil
})
app.OnMailerRecordPasswordResetSend().BindFunc(func(e *core.MailerRecordEvent) error {
if err := e.Next(); err != nil {
return err
}
// ...
return nil
})
OnMailerBeforeRecordVerificationSend -> OnMailerRecordVerificationSend
app.OnMailerBeforeRecordVerificationSend().Add(func(e *core.MailerRecordEvent) error {
// ...
return nil
})
app.OnMailerRecordVerificationSend().BindFunc(func(e *core.MailerRecordEvent) error {
// ...
return e.Next()
})
OnMailerAfterRecordVerificationSend -> OnMailerRecordVerificationSend
app.OnMailerAfterRecordVerificationSend().Add(func(e *core.MailerRecordEvent) error {
// ...
return nil
})
app.OnMailerRecordVerificationSend().BindFunc(func(e *core.MailerRecordEvent) error {
if err := e.Next(); err != nil {
return err
}
// ...
return nil
})
OnMailerBeforeRecordChangeEmailSend -> OnMailerRecordEmailChangeSend
app.OnMailerBeforeRecordChangeEmailSend().Add(func(e *core.MailerRecordEvent) error {
// ...
return nil
})
app.OnMailerRecordEmailChangeSend().BindFunc(func(e *core.MailerRecordEvent) error {
// ...
return e.Next()
})
OnMailerAfterRecordChangeEmailSend -> OnMailerRecordEmailChangeSend
app.OnMailerAfterRecordChangeEmailSend().Add(func(e *core.MailerRecordEvent) error {
// ...
return nil
})
app.OnMailerRecordEmailChangeSend().BindFunc(func(e *core.MailerRecordEvent) error {
if err := e.Next(); err != nil {
return err
}
// ...
return nil
})
OnRecordsListRequest
Side-note: if you are using this hook simply for adding/removing record fields you may want to check
onRecordEnrich
instead
app.OnRecordsListRequest().Add(func(e *core.RecordsListEvent) error {
// ...
return nil
})
app.OnRecordsListRequest().BindFunc(func(e *core.RecordsListRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRecordViewRequest
Side-note: if you are using this hook simply for adding/removing record fields you may want to check
onRecordEnrich
instead
app.OnRecordViewRequest().Add(func(e *core.RecordViewEvent) error {
// ...
return nil
})
app.OnRecordViewRequest().BindFunc(func(e *core.RecordRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRecordBeforeCreateRequest -> OnRecordCreateRequest
With v0.23+ the hook fires before the record validations allowing you to adjust "Nonempty" fields before checking the constraint.
app.OnRecordBeforeCreateRequest().Add(func(e *core.RecordCreateEvent) error {
// ...
return nil
})
app.OnRecordCreateRequest().BindFunc(func(e *core.RecordRequestEvent) error {
// uploaded files are simple values as part of the e.Record;
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRecordAfterCreateRequest -> OnRecordCreateRequest
app.OnRecordAfterCreateRequest().Add(func(e *core.RecordCreateEvent) error {
// ...
return nil
})
app.OnRecordCreateRequest().BindFunc(func(e *core.RecordRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnRecordBeforeUpdateRequest -> OnRecordUpdateRequest
With v0.23+ the hook fires before the record validations allowing you to adjust "Nonempty" fields before checking the constraint.
app.OnRecordBeforeUpdateRequest().Add(func(e *core.RecordUpdateEvent) error {
// ...
return nil
})
app.OnRecordUpdateRequest().BindFunc(func(e *core.RecordRequestEvent) error {
// uploaded files are simple values as part of the e.Record;
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRecordAfterUpdateRequest -> OnRecordUpdateRequest
app.OnRecordAfterUpdateRequest().Add(func(e *core.RecordUpdateEvent) error {
// ...
return nil
})
app.OnRecordUpdateRequest().BindFunc(func(e *core.RecordRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnRecordBeforeDeleteRequest -> OnRecordDeleteRequest
app.OnRecordBeforeDeleteRequest().Add(func(e *core.RecordDeleteEvent) error {
// ...
return nil
})
app.OnRecordDeleteRequest().BindFunc(func(e *core.RecordRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRecordAfterDeleteRequest -> OnRecordDeleteRequest
app.OnRecordBeforeDeleteRequest().Add(func(e *core.RecordDeleteEvent) error {
// ...
return nil
})
app.OnRecordDeleteRequest().BindFunc(func(e *core.RecordRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnRecordAuthRequest
app.OnRecordAuthRequest().Add(func(e *core.RecordAuthEvent) error {
// ...
return nil
})
app.OnRecordAuthRequest().BindFunc(func(e *core.RecordAuthRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRecordBeforeAuthWithPasswordRequest -> OnRecordAuthWithPasswordRequest
app.OnRecordBeforeAuthWithPasswordRequest().Add(func(e *core.RecordAuthWithPasswordEvent) error {
// ...
return nil
})
app.OnRecordAuthWithPasswordRequest().BindFunc(func(e *core.RecordAuthWithPasswordRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRecordAfterAuthWithPasswordRequest -> OnRecordAuthWithPasswordRequest
app.OnRecordAfterAuthWithPasswordRequest().Add(func(e *core.RecordAuthWithPasswordEvent) error {
// ...
return nil
})
app.OnRecordAuthWithPasswordRequest().BindFunc(func(e *core.RecordAuthWithPasswordRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnRecordBeforeAuthWithOAuth2Request -> OnRecordAuthWithOAuth2Request
app.OnRecordBeforeAuthWithOAuth2Request().Add(func(e *core.RecordAuthWithOAuth2Event) error {
// ...
return nil
})
app.OnRecordAuthWithOAuth2Request().BindFunc(func(e *core.RecordAuthWithOAuth2RequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRecordAfterAuthWithOAuth2Request -> OnRecordAuthWithOAuth2Request
app.OnRecordAfterAuthWithOAuth2Request().Add(func(e *core.RecordAuthWithOAuth2Event) error {
// ...
return nil
})
app.OnRecordAuthWithOAuth2Request().BindFunc(func(e *core.RecordAuthWithOAuth2RequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnRecordBeforeAuthRefreshRequest -> OnRecordAuthRefreshRequest
app.OnRecordBeforeAuthRefreshRequest().Add(func(e *core.RecordAuthRefreshEvent) error {
// ...
return nil
})
app.OnRecordAuthRefreshRequest().BindFunc(func(e *core.RecordAuthRefreshRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRecordAfterAuthRefreshRequest -> OnRecordAuthRefreshRequest
app.OnRecordAfterAuthRefreshRequest().Add(func(e *core.RecordAuthRefreshEvent) error {
// ...
return nil
})
app.OnRecordAuthRefreshRequest().BindFunc(func(e *core.RecordAuthRefreshRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnRecordListExternalAuthsRequest -> OnRecordsListRequest
External auths are converted to system_externalAuths
collection records. app.OnRecordListExternalAuthsRequest().Add(func(e *core.RecordListExternalAuthsEvent) error {
// ...
return nil
})
app.OnRecordsListRequest(core.CollectionNameExternalAuths).BindFunc(func(e *core.RecordsListRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRecordBeforeUnlinkExternalAuthRequest -> OnRecordDeleteRequest
External auths are converted to system_externalAuths
collection records. app.OnRecordBeforeUnlinkExternalAuthRequest().Add(func(e *core.RecordUnlinkExternalAuthEvent) error {
// ...
return nil
})
app.OnRecordDeleteRequest(core.CollectionNameExternalAuths).BindFunc(func(e *core.RecordRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRecordAfterUnlinkExternalAuthRequest -> OnRecordDeleteRequest
External auths are converted to system_externalAuths
collection records. app.OnRecordAfterUnlinkExternalAuthRequest().Add(func(e *core.RecordUnlinkExternalAuthEvent) error {
// ...
return nil
})
app.OnRecordDeleteRequest(core.CollectionNameExternalAuths).BindFunc(func(e *core.RecordRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnRecordBeforeRequestPasswordResetRequest -> OnRecordRequestPasswordResetRequest
app.OnRecordBeforeRequestPasswordResetRequest().Add(func(e *core.RecordRequestPasswordResetEvent) error {
// ...
return nil
})
app.OnRecordRequestPasswordResetRequest().BindFunc(func(e *core.RecordRequestPasswordResetRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRecordAfterRequestPasswordResetRequest -> OnRecordRequestPasswordResetRequest
app.OnRecordAfterRequestPasswordResetRequest().Add(func(e *core.RecordRequestPasswordResetEvent) error {
// ...
return nil
})
app.OnRecordRequestPasswordResetRequest().BindFunc(func(e *core.RecordRequestPasswordResetRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnRecordBeforeConfirmPasswordResetRequest -> OnRecordConfirmPasswordResetRequest
app.OnRecordBeforeConfirmPasswordResetRequest().Add(func(e *core.RecordConfirmPasswordResetEvent) error {
// ...
return nil
})
app.OnRecordConfirmPasswordResetRequest().BindFunc(func(e *core.RecordConfirmPasswordResetRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRecordAfterConfirmPasswordResetRequest -> OnRecordConfirmPasswordResetRequest
app.OnRecordAfterConfirmPasswordResetRequest().Add(func(e *core.RecordConfirmPasswordResetEvent) error {
// ...
return nil
})
app.OnRecordConfirmPasswordResetRequest().BindFunc(func(e *core.RecordConfirmPasswordResetRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnRecordBeforeRequestVerificationRequest -> OnRecordRequestVerificationRequest
app.OnRecordBeforeRequestVerificationRequest().Add(func(e *core.RecordRequestVerificationEvent) error
// ...
return nil
})
app.OnRecordRequestVerificationRequest().BindFunc(func(e *core.RecordRequestVerificationRequestEvent) error
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRecordAfterRequestVerificationRequest -> OnRecordRequestVerificationRequest
app.OnRecordAfterRequestVerificationRequest().Add(func(e *core.RecordRequestVerificationEvent) error
// ...
return nil
})
app.OnRecordRequestVerificationRequest().BindFunc(func(e *core.RecordRequestVerificationRequestEvent) error
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnRecordBeforeConfirmVerificationRequest -> OnRecordConfirmVerificationRequest
app.OnRecordBeforeConfirmVerificationRequest().Add(func(e *core.RecordConfirmVerificationEvent) error
// ...
return nil
})
app.OnRecordConfirmVerificationRequest().BindFunc(func(e *core.RecordConfirmVerificationRequestEvent) error
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRecordAfterConfirmVerificationRequest -> OnRecordConfirmVerificationRequest
app.OnRecordAfterConfirmVerificationRequest().Add(func(e *core.RecordConfirmVerificationEvent) error
// ...
return nil
})
app.OnRecordConfirmVerificationRequest().BindFunc(func(e *core.RecordConfirmVerificationRequestEvent) error
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnRecordBeforeRequestEmailChangeRequest -> OnRecordRequestEmailChangeRequest
app.OnRecordBeforeRequestEmailChangeRequest().Add(func(e *core.RecordRequestEmailChangeEvent) error {
// ...
return nil
})
app.OnRecordRequestEmailChangeRequest().BindFunc(func(e *core.RecordRequestEmailChangeRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRecordAfterRequestEmailChangeRequest -> OnRecordRequestEmailChangeRequest
app.OnRecordAfterRequestEmailChangeRequest().Add(func(e *core.RecordRequestEmailChangeEvent) error {
// ...
return nil
})
app.OnRecordRequestEmailChangeRequest().BindFunc(func(e *core.RecordRequestEmailChangeRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnRecordBeforeConfirmEmailChangeRequest ->
app.OnRecordBeforeConfirmEmailChangeRequest().Add(func(e *core.RecordConfirmEmailChangeEvent) error {
// ...
return nil
})
app.OnRecordConfirmEmailChangeRequest().BindFunc(func(e *core.RecordConfirmEmailChangeRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRecordAfterConfirmEmailChangeRequest -> OnRecordConfirmEmailChangeRequest
app.OnRecordAfterConfirmEmailChangeRequest().Add(func(e *core.RecordConfirmEmailChangeEvent) error {
// ...
return nil
})
app.OnRecordConfirmEmailChangeRequest().BindFunc(func(e *core.RecordConfirmEmailChangeRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnRealtimeConnectRequest
app.OnRealtimeConnectRequest().Add(func(e *core.RealtimeConnectEvent) error {
// ...
return nil
})
app.OnRealtimeConnectRequest().BindFunc(func(e *core.RealtimeConnectRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRealtimeDisconnectRequest -> OnRealtimeConnectRequest
app.OnRealtimeDisconnectRequest().Add(func(e *core.RealtimeDisconnectEvent) error {
// ...
return nil
})
app.OnRealtimeConnectRequest().BindFunc(func(e *core.RealtimeConnectRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnRealtimeBeforeMessageSend -> OnRealtimeMessageSend
app.OnRealtimeBeforeMessageSend().Add(func(e *core.RealtimeMessageEvent) error {
// ...
return nil
})
app.OnRealtimeMessageSend().BindFunc(func(e *core.RealtimeMessageEvent) error {
// ...
return e.Next()
})
OnRealtimeAfterMessageSend -> OnRealtimeMessageSend
app.OnRealtimeAfterMessageSend().Add(func(e *core.RealtimeMessageEvent) error {
// ...
return nil
})
app.OnRealtimeMessageSend().BindFunc(func(e *core.RealtimeMessageEvent) error {
if err := e.Next(); err != nil {
return err
}
// ...
return nil
})
OnRealtimeBeforeSubscribeRequest -> OnRealtimeSubscribeRequest
app.OnRealtimeBeforeSubscribeRequest().Add(func(e *core.RealtimeSubscribeEvent) error {
// ...
return nil
})
app.OnRealtimeSubscribeRequest().BindFunc(func(e *core.RealtimeSubscribeRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnRealtimeAfterSubscribeRequest -> OnRealtimeSubscribeRequest
app.OnRealtimeAfterSubscribeRequest().Add(func(e *core.RealtimeSubscribeEvent) error {
// ...
return nil
})
app.OnRealtimeSubscribeRequest().BindFunc(func(e *core.RealtimeSubscribeRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnFileDownloadRequest
app.OnFileDownloadRequest().Add(func(e *core.FileDownloadEvent) error {
// ...
return nil
})
app.OnFileDownloadRequest().BindFunc(func(e *core.FileDownloadRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnFileBeforeTokenRequest -> OnFileTokenRequest
app.OnFileBeforeTokenRequest().Add(func(e *core.FileTokenEvent) error {
// ...
return nil
})
app.OnFileTokenRequest().BindFunc(func(e *core.FileTokenRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnFileAfterTokenRequest -> OnFileTokenRequest
app.OnFileAfterTokenRequest().Add(func(e *core.FileTokenEvent) error {
// ...
return nil
})
app.OnFileTokenRequest().BindFunc(func(e *core.FileTokenRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnCollectionsListRequest
app.OnCollectionsListRequest().Add(func(e *core.CollectionsListEvent) error {
// ...
return nil
})
app.OnCollectionsListRequest().BindFunc(func(e *core.CollectionsListRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnCollectionViewRequest
app.OnCollectionViewRequest().Add(func(e *core.CollectionViewEvent) error {
// ...
return nil
})
app.OnCollectionViewRequest().BindFunc(func(e *core.CollectionRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnCollectionBeforeCreateRequest -> OnCollectionCreateRequest
With v0.23+ the hook fires before the collection validations.
app.OnCollectionBeforeCreateRequest().Add(func(e *core.CollectionCreateEvent) error {
// ...
return nil
})
app.OnCollectionCreateRequest().BindFunc(func(e *core.CollectionRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnCollectionAfterCreateRequest -> OnCollectionCreateRequest
app.OnCollectionAfterCreateRequest().Add(func(e *core.CollectionCreateEvent) error {
// ...
return nil
})
app.OnCollectionCreateRequest().BindFunc(func(e *core.CollectionRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnCollectionBeforeUpdateRequest -> OnCollectionUpdateRequest
With v0.23+ the hook fires before the collection validations allowing you to adjust "Nonempty" fields before checking the constraint.
app.OnCollectionBeforeUpdateRequest().Add(func(e *core.CollectionUpdateEvent) error {
// ...
return nil
})
app.OnCollectionUpdateRequest().BindFunc(func(e *core.CollectionRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnCollectionAfterUpdateRequest -> OnCollectionUpdateRequest
app.OnCollectionAfterUpdateRequest().Add(func(e *core.CollectionUpdateEvent) error {
// ...
return nil
})
app.OnCollectionUpdateRequest().BindFunc(func(e *core.CollectionRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnCollectionBeforeDeleteRequest -> OnCollectionDeleteRequest
app.OnCollectionBeforeDeleteRequest().Add(func(e *core.CollectionDeleteEvent) error {
// ...
return nil
})
app.OnCollectionDeleteRequest().BindFunc(func(e *core.CollectionRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnCollectionAfterDeleteRequest -> OnCollectionDeleteRequest
app.OnCollectionAfterDeleteRequest().Add(func(e *core.CollectionDeleteEvent) error {
// ...
return nil
})
app.OnCollectionDeleteRequest().BindFunc(func(e *core.CollectionRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnCollectionsBeforeImportRequest -> OnCollectionsImportRequest
app.OnCollectionsBeforeImportRequest().Add(func(e *core.CollectionsImportEvent) error {
// ...
return nil
})
app.OnCollectionImportRequest().BindFunc(func(e *core.CollectionsImportRequestEvent) error {
// "e.Collections" is replaced with "e.CollectionsData" map...
return e.Next()
})
OnCollectionAfterImportRequest -> OnCollectionImportRequest
app.OnCollectionAfterImportRequest().Add(func(e *core.CollectionsImportEvent) error {
// ...
return nil
})
app.onCollectionImportRequest().BindFunc(func(e *core.CollectionsImportRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// "e.Collections" is replaced with "e.CollectionsData" map...
return nil
})
OnSettingsListRequest
app.OnSettingsListRequest().Add(func(e *core.SettingsListEvent) error {
// ...
return nil
})
app.OnSettingsListRequest().BindFunc(func(e *core.SettingsListRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnSettingsBeforeUpdateRequest -> OnSettingsUpdateRequest
With v0.23+ the hook fires before the settings validations.
app.OnSettingsBeforeUpdateRequest().Add(func(e *core.SettingsUpdateEvent) error {
// ...
return nil
})
app.OnSettingsUpdateRequest().BindFunc(func(e *core.SettingsUpdateRequestEvent) error {
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnSettingsAfterUpdateRequest -> OnSettingsUpdateRequest
onSettingsAfterUpdateRequest((e) => {
// ...
return nil
})
onSettingsUpdateRequest((e) => {
if err := e.Next(); err != nil {
return err
}
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnAdminsListRequest -> OnRecordsListRequest
app.OnAdminsListRequest().Add(func(e *core.AdminsListEvent) error {
// ...
return nil
})
app.OnRecordsListRequest(core.CollectionNameSuperusers).BindFunc(func(e *core.RecordsListRequestEvent) error {
// e.Admins -> e.Records
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
}, )
OnAdminViewRequest -> OnRecordViewRequest
app.OnAdminViewRequest().Add(func(e *core.AdminViewEvent) error {
// ...
return nil
})
app.OnRecordViewRequest(core.CollectionNameSuperusers).BindFunc(func(e *core.RecordRequestEvent) error {
// e.Admin -> e.Record
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnAdminBeforeCreateRequest -> OnRecordCreateRequest
app.OnAdminBeforeCreateRequest().Add(func(e *core.AdminCreateEvent) error {
// ...
return nil
})
app.OnRecordCreateRequest(core.CollectionNameSuperusers).BindFunc(func(e *core.RecordRequestEvent) error {
// e.Admin -> e.Record
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnAdminAfterCreateRequest -> OnRecordCreateRequest
app.OnAdminAfterCreateRequest().Add(func(e *core.AdminCreateEvent) error {
// ...
return nil
})
app.OnRecordCreateRequest(core.CollectionNameSuperusers).BindFunc(func(e *core.RecordRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// e.Admin -> e.Record
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnAdminBeforeUpdateRequest -> OnRecordUpdateRequest
app.OnAdminBeforeUpdateRequest().Add(func(e *core.AdminUpdateEvent) error {
// ...
return nil
})
app.OnRecordUpdateRequest(core.CollectionNameSuperusers).BindFunc(func(e *core.RecordRequestEvent) error {
// e.Admin -> e.Record
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnAdminAfterUpdateRequest -> OnRecordUpdateRequest
app.OnAdminAfterUpdateRequest().Add(func(e *core.AdminUpdateEvent) error {
// ...
return nil
})
app.OnRecordUpdateRequest(core.CollectionNameSuperusers).BindFunc(func(e *core.RecordRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// e.Admin -> e.Record
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnAdminBeforeDeleteRequest -> OnRecordDeleteRequest
app.OnAdminBeforeDeleteRequest().Add(func(e *core.AdminDeleteEvent) error {
// ...
return nil
})
app.OnRecordDeleteRequest(core.CollectionNameSuperusers).BindFunc(func(e *core.RecordRequestEvent) error {
// e.Admin -> e.Record
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnAdminAfterDeleteRequest -> OnRecordDeleteRequest
app.OnAdminAfterDeleteRequest().Add(func(e *core.AdminDeleteEvent) error {
// ...
return nil
})
app.OnRecordDeleteRequest(core.CollectionNameSuperusers).BindFunc(func(e *core.RecordRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// e.Admin -> e.Record
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnAdminAuthRequest -> OnRecordAuthRequest
app.OnAdminAuthRequest().Add(func(e *core.AdminAuthEvent) error {
// ...
return nil
})
app.OnRecordAuthRequest(core.CollectionNameSuperusers).BindFunc(func(e *core.RecordAuthRequestEvent) error {
// e.Admin -> e.Record
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnAdminBeforeAuthWithPasswordRequest -> OnRecordAuthWithPasswordRequest
app.OnAdminBeforeAuthWithPasswordRequest().Add(func(e *core.AdminAuthWithPasswordEvent) error {
// ...
return nil
})
app.OnRecordAuthWithPasswordRequest(core.CollectionNameSuperusers).BindFunc(func(e *core.RecordAuthWithPasswordRequestEvent) error {
// e.Admin -> e.Record
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnAdminAfterAuthWithPasswordRequest -> OnRecordAuthWithPasswordRequest
app.OnAdminAfterAuthWithPasswordRequest().Add(func(e *core.AdminAuthWithPasswordEvent) error {
// ...
return nil
})
app.OnRecordAuthWithPasswordRequest(core.CollectionNameSuperusers).BindFunc(func(e *core.RecordAuthWithPasswordRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// e.Admin -> e.Record
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnAdminBeforeAuthRefreshRequest -> OnRecordAuthRefreshRequest
app.OnAdminBeforeAuthRefreshRequest().Add(func(e *core.AdminAuthRefreshEvent) error {
// ...
return nil
})
app.OnRecordAuthRefreshRequest(core.CollectionNameSuperusers).BindFunc(func(e *core.RecordAuthRefreshRequestEvent) error {
// e.Admin -> e.Record
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnAdminAfterAuthRefreshRequest -> OnRecordAuthRefreshRequest
app.OnAdminAfterAuthRefreshRequest().Add(func(e *core.AdminAuthRefreshEvent) error {
// ...
return nil
})
app.OnRecordAuthRefreshRequest(core.CollectionNameSuperusers).BindFunc(func(e *core.RecordAuthRefreshRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// e.Admin -> e.Record
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnAdminBeforeRequestPasswordResetRequest -> OnRecordRequestPasswordResetRequest
app.OnAdminBeforeRequestPasswordResetRequest().Add(func(e *core.AdminRequestPasswordResetEvent) error {
// ...
return nil
})
app.OnRecordRequestPasswordResetRequest(core.CollectionNameSuperusers)
.BindFunc(func(e *core.RecordRequestPasswordResetRequestEvent) error {
// e.Admin -> e.Record
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnAdminAfterRequestPasswordResetRequest -> OnRecordRequestPasswordResetRequest
app.OnAdminAfterRequestPasswordResetRequest().Add(func(e *core.AdminRequestPasswordResetEvent) error {
// ...
return nil
})
app.OnRecordRequestPasswordResetRequest(core.CollectionNameSuperusers)
.BindFunc(func(e *core.RecordRequestPasswordResetRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// e.Admin -> e.Record
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})
OnAdminBeforeConfirmPasswordResetRequest -> OnRecordConfirmPasswordResetRequest
app.OnAdminBeforeConfirmPasswordResetRequest().Add(func(e *core.AdminConfirmPasswordResetEvent) error {
// ...
return nil
})
app.OnRecordConfirmPasswordResetRequest(core.CollectionNameSuperusers)
.BindFunc(func(e *core.RecordConfirmPasswordResetRequestEvent) error {
// e.Admin -> e.Record
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return e.Next()
})
OnAdminAfterConfirmPasswordResetRequest -> OnRecordConfirmPasswordResetRequest
app.OnAdminAfterConfirmPasswordResetRequest().Add(func(e *core.AdminConfirmPasswordResetEvent) error {
// ...
return nil
})
app.OnRecordConfirmPasswordResetRequest(core.CollectionNameSuperusers)
.BindFunc(func(e *core.RecordConfirmPasswordResetRequestEvent) error {
if err := e.Next(); err != nil {
return err
}
// e.Admin -> e.Record
// "e.HttpContext" is no longer available because "e" is the request event itself ...
return nil
})