Movatterモバイル変換


[0]ホーム

URL:


backend

package
v1.5.1Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 20, 2023 License:MITImports:32Imported by:0

Details

Repository

github.com/staticbackendhq/core

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:

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

Examples

Constants

This section is empty.

Variables

View Source
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

func BuildQueryFilters(p ...any) (q [][]any, errerror)

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]]

funcGetJWT

func GetJWT(tokenstring) ([]byte,error)

GetJWT returns a session token from a token

funcSetup

func Setup(cfgconfig.AppConfig)

Setup initializes the core services based on the configuration received.

Types

typeDatabase

type Database[Tany] struct {// contains filtered or unexported fields}

Database enables all CRUD and querying operations on a specific type

funcCollection

func Collection[Tany](authmodel.Auth, basemodel.DatabaseConfig, colstring)Database[T]

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

func (dDatabase[T]) BulkCreate(entities []T)error

BulkCreate creates multiple records in the collection/repository

func (Database[T])Create

func (dDatabase[T]) Create(data T) (inserted T, errerror)

Create creates a record in the collection/repository

func (Database[T])Delete

func (dDatabase[T]) Delete(idstring) (int64,error)

Delete removes a record from a collection

func (Database[T])GetByID

func (dDatabase[T]) GetByID(idstring) (entity T, errerror)

GetByID returns a specific record from a collection/repository

func (Database[T])IncrementValue

func (dDatabase[T]) IncrementValue(id, fieldstring, nint)error

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.

func (Database[T])Update

func (dDatabase[T]) Update(idstring, vany) (entity T, errerror)

Update updates some fields of a record

func (Database[T])UpdateMany

func (dDatabase[T]) UpdateMany(filters [][]any, vany) (int64,error)

UpdateMany updates multiple records matching filters

typeFileStore

type FileStore struct {// contains filtered or unexported fields}

FileStore exposes file functions

func (FileStore)Delete

func (fFileStore) Delete(fileIDstring)error

Delete removes a file from storage and database

func (FileStore)Save

func (fFileStore) Save(filename, namestring, fileio.ReadSeeker, sizeint64) (sfSavedFile, errerror)

Save saves a file content to the file storage (Storer interface) and to thedatabase

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

type PagedResult[Tany] struct {Pageint64Sizeint64Totalint64Results []T}

PageResult wraps a slice of type T with paging information

typeSavedFile

type SavedFile struct {IDstring `json:"id"`URLstring `json:"url"`}

SavedFile when a file is saved it has an ID and an URL

typeUser

type User struct {// contains filtered or unexported fields}

User handles everything related to accounts and users inside a database

func (User)Authenticate

func (uUser) Authenticate(email, passwordstring) (string,error)

Authenticate tries to authenticate an email/password and return a session token

func (User)CreateAccountAndUser

func (uUser) CreateAccountAndUser(email, passwordstring, roleint) ([]byte,model.User,error)

CreateAccountAndUser creates an account with a user

func (User)CreateUser

func (uUser) CreateUser(accountID, email, passwordstring, roleint) ([]byte,model.User,error)

CreateUser creates a user for an Account

func (User)GetAuthToken

func (uUser) GetAuthToken(tokmodel.User) (jwtBytes []byte, errerror)

GetAuthToken returns a session token for a user

func (User)Register

func (uUser) Register(email, passwordstring) (string,error)

Register creates a new account and user

func (User)ResetPassword

func (uUser) ResetPassword(email, code, passwordstring)error

ResetPassword resets the password of a matching email/code for a user

func (User)SetPasswordResetCode

func (uUser) SetPasswordResetCode(email, codestring)error

SetPasswordResetCode sets the password forget code for a user

func (User)SetUserRole

func (uUser) SetUserRole(emailstring, roleint)error

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

func (uUser) UserSetPassword(email, oldpw, newpwstring)error

UserSetPassword password changes initiated by the user

func (User)ValidateMagicLink

func (uUser) ValidateMagicLink(email, codestring) (string,error)

ValidateMagicLink validates a magic link code and returns a session token onsuccess

Source Files

View all Source files

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f orF : Jump to
y orY : Canonical URL
go.dev uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic.Learn more.

[8]ページ先頭

©2009-2025 Movatter.jp