Go filesystem and email
Read, write, and delete PocketBase files, then send custom email from Go extensions.
This guide shows how to use PocketBase's filesystem abstraction and mail client from Go extension code. You will read a stored file, persist a file through a record, remove a file, and send a custom email after a record event.
Before you begin
You need a running Go application, a collection with a file field such as avatar, and storage configured in Dashboard > Settings > Files storage. For email, configure Dashboard > Settings > Mail settings with either sendmail or SMTP before testing delivery. Use a test recipient such as dev@example.com.
Read a stored file
- Find the record and construct its file key. Use
BaseFilesPath()and the file field value.
record, err := app.FindAuthRecordByEmail("users", "dev@example.com")
if err != nil { return err }
key := record.BaseFilesPath() + "/" + record.GetString("avatar")The resulting key has the form collectionId/recordId/filename.
- Open the filesystem and reader. Close both resources when the operation ends.
fsys, err := app.NewFilesystem()
if err != nil { return err }
defer fsys.Close()
reader, err := fsys.GetReader(key)
if err != nil { return err }
defer reader.Close()GetReader returns the stored content. Use List(prefix) when you need keys below a storage prefix.
- Copy and inspect the content.
content := new(bytes.Buffer)
if _, err := io.Copy(content, reader); err != nil { return err }
log.Printf("read %d bytes", content.Len())The buffer contains the file content, and the reader is closed before the function returns.
Save and delete record files
- Attach a file and save the record. Create a
filesystem.Filefrom bytes, a path, a URL, or multipart input.
f, err := filesystem.NewFileFromBytes([]byte("sample"), "notes.txt")
if err != nil { return err }
record.Set("documents", f)
if err := app.Save(record); err != nil { return err }Record saves perform the collection file field's size and MIME validation. Replacing an existing file removes the old file after a successful save.
- Remove one or all files from a record field. Set a single field to
nil, or use the-modifier for selected filenames.
record.Set("documents-", "notes.txt")
if err := app.Save(record); err != nil { return err }The record reference and associated storage file are updated together through the record save. For direct storage operations, create a filesystem with app.NewFilesystem() and use Delete(key).
Send a custom email
- Create a message after the triggering operation succeeds. Bind to a request hook, call
e.Next(), then build amailer.Messagefrom the configured sender.
app.OnRecordCreateRequest("users").BindFunc(func(e *core.RecordRequestEvent) error {
if err := e.Next(); err != nil { return err }
message := &mailer.Message{
From: mail.Address{
Address: e.App.Settings().Meta.SenderAddress,
Name: e.App.Settings().Meta.SenderName,
},
To: []mail.Address{{Address: e.Record.Email()}},
Subject: "Welcome to the example app",
HTML: "<p>Your account was created.</p>",
}
return e.App.NewMailClient().Send(message)
})mailer.Message also supports CC, BCC, attachments, and custom headers. The observable result is a message accepted by the configured sendmail or SMTP client.
Recover from common failures
- If
GetReadercannot find a file, recompute the full key withBaseFilesPath()and the stored filename. - If
app.Saverejects a file, check the collection file field's size and MIME settings before retrying. - If email sending fails, verify the Dashboard mail settings and inspect the returned error; do not treat a successful record save as proof that delivery succeeded.
- If a reader or filesystem remains open, add
defer reader.Close()anddefer fsys.Close()on every successful allocation path.
Next steps
For system email customization, see Go hooks and event lifecycle. For record persistence and transactions, continue with Go records, queries, and database operations.