backend
packageThis package is not in the latest version of its module.
Details
Validgo.mod file
The Go module system was introduced in Go 1.11 and is the official dependency management solution for Go.
Redistributable license
Redistributable licenses place minimal restrictions on how software can be used, modified, and redistributed.
Tagged version
Modules with tagged versions give importers more predictable builds.
Stable version
When a project reaches major version v1 it is considered stable.
- Learn more about best practices
Repository
Links
Documentation¶
Overview¶
Package backend allows a Go program to import a standard Go packageinstead of self-hosting the backend API in a separate web server.
You need to call theSetup function to initialize all services passingagithub.com/staticbackendhq/core/config.AppConfig. You may createenvironment variables and load the config directly by confing.Load function.
// You may load the configuration from environment variables// config.Current = config.LoadConfig()// this sample uses the in-memory database provider built-in// you can use PostgreSQL or MongoDBconfig.Current = config.AppConfig{ AppEnv: "dev", DataStore: "mem", DatabaseURL: "mem", LocalStorageURL: "http://localhost:8099",}backend.Setup(config.Current)The building blocks ofStaticBackend are exported as variables and can beused directly accessing their interface's functions. For instanceto use thegithub.com/staticbackendhq/core/cache.Volatilizer (cache andpub/sub) you'd use theCache variable:
if err := backend.Cache.Set("key", "value"); err != nil { return err}val, err := backend.Cache.Get("key")The available services are as follow:
- Cache: caching and pub/sub
- DB: a rawgithub.com/staticbackendhq/core/database.Persister instance (see below for when to use it)
- Filestore: raw blob storage
- Emailer: to send emails
- Config: the config that was passed toSetup
- Log: logger
You may see those services as raw building blocks that give you the mostflexibility to build on top.
For easy of use, this package wraps important / commonly usedfunctionalities into more developer friendly implementations.
For instance, theMembership function wants agithub.com/staticbackendhq/core/model.DatabaseConfig and allows the callerto create account and user as well as reseting password etc.
usr := backend.Membership(base)sessionToken, user, err := usr.CreateAccountAndUser("me@test.com", "passwd", 100)To contrast, all of those can be done from your program by using theDB(github.com/staticbackendhq/core/database.Persister) data store, but forconvenience this package offers easier / ready-made functions for commonuse-cases. Example for database CRUD and querying:
tasks := backend.Collection[Task](auth, base, "tasks")newTask, err := tasks.Create(Task{Name: "testing"})TheCollection returns a strongly-typed structure where functionsinput/output are properly typed, it's a generic type.
StaticBackend makes your Go web application multi-tenant by default.For this reason you must supply agithub.com/staticbackendhq/core/model.DatabaseConfig and (database) andsometimes agithub.com/staticbackendhq/core/model.Auth (user performingthe actions) to the different parts of the system so the data and securityare applied to the right tenant, account and user.
You'd design your application around one or more tenants. Each tenant hastheir own database. It's fine to have one tenant/database. In that caseyou might create the tenant and its database and use the database ID inan environment variable. From a middleware you might load the database fromthis ID.
// if you'd want to use SB's middleware (it's not required)// you use whatever you like for your web handlers and middleware.// SB is a library not a framework.func DetectTenant() middleware.Middleware { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // check for presence of a public DB ID // this can come from cookie, URL query param key := r.Header.Get("DB-ID") // for multi-tenant, DB ID can be from an env var if len(key) == 0 { key = os.Getenv("SINGLE_TENANT_DBID") } var curDB model.DatabaseConfig if err := backend.Cache.GetTyped(key, &curDB); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } curDB, err := backend.DB.FindDatabase(key) // err != nil return HTTP 400 Bad request err = backend.Cache.SetTyped(key, curDB) // add the tenant's DB in context for the rest of // your pipeline to have the proper DB. ctx := r.Context() ctx = context.WithValue(ctx, ContextBase, curDB) next.ServeHTTP(w, r.WithContext(ctx))) }) }}You'd create a similar middleware for adding the current user into therequest context.
If you ever decide to switch to a multi-tenant design, you'd already be allset with this middleware, instead of getting the ID from the env variable,you'd define how the user should provide their database ID.
Example¶
package mainimport ("fmt""log""time""github.com/staticbackendhq/core/backend""github.com/staticbackendhq/core/config""github.com/staticbackendhq/core/model")type EntityDemo struct {ID string `json:"id"`AccountID string `json:"accountId"`Name string `json:"name"`Status string `json:"status"`}func (x EntityDemo) String() string {return fmt.Sprintf("%s | %s", x.Name, x.Status)}func main() {// we initiate config.Current as type config.AppConfig// using the in-memory database engine.// You'd use PostgreSQL or Mongo in your real configuration// Also note that the config package has a LoadConfig() function that loads// config from environment variables i.e.:// config.Current = LoadConfig()config.Current = config.AppConfig{AppEnv: "dev",Port: "8099",DatabaseURL: "mem",DataStore: "mem",LocalStorageURL: "http://localhost:8099",NoFullTextSearch: true,}// the Setup function will initialize all services based on configbackend.Setup(config.Current)// StaticBackend is multi-tenant by default, so you'll minimaly need// at least one Tenant with their Database for your app//// In a real application you need to decide if your customers will// have their own Database (multi-tenant) or not.cus := model.Tenant{Email: "new@tenant.com",IsActive: true,Created: time.Now(),}cus, err := backend.DB.CreateTenant(cus)if err != nil {fmt.Println(err)return}base := model.DatabaseConfig{TenantID: cus.ID,Name: "random-name-here",IsActive: true,Created: time.Now(),}base, err = backend.DB.CreateDatabase(base)if err != nil {fmt.Println(err)return}// let's create a user in this new Database// You'll need to create an model.Account and model.User for each of// your users. They'll need a session token to authenticate.usr := backend.Membership(base)// Role 100 is for root user, root user is your app's super user.// As the builder of your application you have a special user which can// execute things on behalf of other users. This is very useful on// background tasks were your app does not have the user's session token.sessionToken, user, err := usr.CreateAccountAndUser("user1@mail.com", "passwd123456", 100)if err != nil {log.Fatal(err)}fmt.Println(len(sessionToken) > 10)// In a real application, you'd store the session token for this user// inside local storage and/or a cookie etc. On each request you'd// request this session token and authenticate this user via a middleware.// we simulate having authenticated this user (from middleware normally)auth := model.Auth{AccountID: user.AccountID,UserID: user.ID,Email: user.Email,Role: user.Role,Token: user.Token,}// this is what you'd normally do in your web handlers to execute a request// we create a ready to use CRUD and Query collection that's typed with// our EntityDemo. In your application you'd get a Collection for your own// type, for instance: Product, Order, Customer, Blog, etc.//// Notice how we're passing the auth: current user and base: current database// so the operations are made from the proper user and in the proper DB/Tenant.entities := backend.Collection[EntityDemo](auth, base, "entities")// once we have this collection, we can perform database operationsnewEntity := EntityDemo{Name: "Go example code", Status: "new"}newEntity, err = entities.Create(newEntity)if err != nil {fmt.Println(err)return}fmt.Println(newEntity)// the Create function returned our EntityDemo with the ID and AccountID// filled, we can now update this record.newEntity.Status = "updated"newEntity, err = entities.Update(newEntity.ID, newEntity)if err != nil {fmt.Println(err)return}// let's fetch this entity via its ID to make sure our changes have// been persisted.check, err := entities.GetByID(newEntity.ID)if err != nil {fmt.Print(err)return}fmt.Println(check)}Output:trueGo example code | newGo example code | updated
Index¶
- Variables
- func BuildQueryFilters(p ...any) (q [][]any, err error)
- func GetJWT(token string) ([]byte, error)
- func Setup(cfg config.AppConfig)
- type Database
- func (d Database[T]) BulkCreate(entities []T) error
- func (d Database[T]) Create(data T) (inserted T, err error)
- func (d Database[T]) Delete(id string) (int64, error)
- func (d Database[T]) GetByID(id string) (entity T, err error)
- func (d Database[T]) IncrementValue(id, field string, n int) error
- func (d Database[T]) List(lp model.ListParams) (res PagedResult[T], err error)
- func (d Database[T]) Query(filters [][]any, lp model.ListParams) (res PagedResult[T], err error)
- func (d Database[T]) Update(id string, v any) (entity T, err error)
- func (d Database[T]) UpdateMany(filters [][]any, v any) (int64, error)
- type FileStore
- type MagicLinkData
- type PagedResult
- type SavedFile
- type User
- func (u User) Authenticate(email, password string) (string, error)
- func (u User) CreateAccountAndUser(email, password string, role int) ([]byte, model.User, error)
- func (u User) CreateUser(accountID, email, password string, role int) ([]byte, model.User, error)
- func (u User) GetAuthToken(tok model.User) (jwtBytes []byte, err error)
- func (u User) Register(email, password string) (string, error)
- func (u User) ResetPassword(email, code, password string) error
- func (u User) SetPasswordResetCode(email, code string) error
- func (u User) SetUserRole(email string, role int) error
- func (u User) SetupMagicLink(data MagicLinkData) error
- func (u User) UserSetPassword(email, oldpw, newpw string) error
- func (u User) ValidateMagicLink(email, code string) (string, error)
Examples¶
Constants¶
This section is empty.
Variables¶
var (// Config reflect the configuration received on SetupConfigconfig.AppConfig// DB initialized Persister data storeDBdatabase.Persister// Emailer initialized Mailer for sending emailsEmaileremail.Mailer// Filestore initialized Storer for raw save/delete blob fileFilestorestorage.Storer// Cache initialized Volatilizer for cache and pub/subCachecache.VolatilizerSearch *search.Search// Log initialized Logger for all loggingLog *logger.Logger// Membership exposes Account and User functionalities like register, login, etc// account and user functionalities.Membership func(model.DatabaseConfig)User// Storage exposes file storage functionalities. It wraps the blob// storage as well as the database storage.Storage func(model.Auth,model.DatabaseConfig)FileStore// Scheduler to execute schedule jobs (only on PrimaryInstance)Scheduler *function.TaskScheduler)
All StaticBackend services (need to call Setup before using them).
Functions¶
funcBuildQueryFilters¶
BuildQueryFilters helps building the proper slice of filters.
The arguments must be divided by 3 and has the following order:
field name | operator | value
backend.BuildQueryFilters("done", "=", false)This would filter for the false value in the "done" field.
Supported operators: =, !=, >, <, >=, <=, in, !in
Example¶
package mainimport ("fmt""github.com/staticbackendhq/core/backend")func main() {filters, err := backend.BuildQueryFilters("done", "=", true,"effort", ">=", 15,)if err != nil {fmt.Println(err)return}fmt.Println(filters)}Output:[[done = true] [effort >= 15]]
Types¶
typeDatabase¶
type Database[Tany] struct {// contains filtered or unexported fields}
Database enables all CRUD and querying operations on a specific type
funcCollection¶
Collection returns a ready to use Database to perform DB operations on aspecific type. You must pass auth which is the user performing the action andthe tenant's database in which this action will be executed. The col is thename of the collection.
Collection name only accept alpha-numberic values and cannot start with a digit.
func (Database[T])BulkCreate¶
BulkCreate creates multiple records in the collection/repository
func (Database[T])IncrementValue¶
IncrementValue increments or decrements a specifc field from a collection/repository
func (Database[T])List¶
func (dDatabase[T]) List(lpmodel.ListParams) (resPagedResult[T], errerror)
List returns records from a collection/repository using paging/sorting params
func (Database[T])Query¶
func (dDatabase[T]) Query(filters [][]any, lpmodel.ListParams) (resPagedResult[T], errerror)
Query returns records that match with the provided filters.
typeFileStore¶
type FileStore struct {// contains filtered or unexported fields}FileStore exposes file functions
typeMagicLinkData¶
type MagicLinkData struct {FromEmailstring `json:"fromEmail"`FromNamestring `json:"fromName"`Emailstring `json:"email"`Subjectstring `json:"subject"`Bodystring `json:"body"`MagicLinkstring `json:"link"`}MagicLinkData magic links for no-password sign-in
typePagedResult¶
PageResult wraps a slice of type T with paging information
typeUser¶
type User struct {// contains filtered or unexported fields}User handles everything related to accounts and users inside a database
func (User)Authenticate¶
Authenticate tries to authenticate an email/password and return a session token
func (User)CreateAccountAndUser¶
CreateAccountAndUser creates an account with a user
func (User)CreateUser¶
CreateUser creates a user for an Account
func (User)GetAuthToken¶
GetAuthToken returns a session token for a user
func (User)ResetPassword¶
ResetPassword resets the password of a matching email/code for a user
func (User)SetPasswordResetCode¶
SetPasswordResetCode sets the password forget code for a user
func (User)SetUserRole¶
SetUserRole changes the role of a user
func (User)SetupMagicLink¶
func (uUser) SetupMagicLink(dataMagicLinkData)error
SetupMagicLink initialize a magic link and send the email to the user
func (User)UserSetPassword¶
UserSetPassword password changes initiated by the user