A golang package for using Google spreadsheets as a database instead of the actual database management system.
!!! Caution !!!
- Currently we are not measuring performance. It is intended for use in small applications where performance is not an issue.
- The Google Sheets API has a usage limit. Do not use this package for applications that require access beyond this usage limit.
- Load sheet data into cache
- Apply cache update information to sheet asynchronously
- Exclusive control when updating cache and inserting a row to a sheet
- Automatic generation of CRUD functions based on model (structure definition)
- Automatic numbering and initial value setting of ID
- Unique and non-null constraints
- Cascade delete child data when deleting parent data
- Notification mechanism on asynchronous update error
The following features are not included.
- SQL query
- Transaction control (commit and rollback)
- Read Lock for Update
go get github.com/takuoki/sheetdb
This package uses Google OAuth2.0. So before executing tool, you have to prepare credentials.json. See Go Quickstart, or blog post (Japanese) for the details.
Write the structure of the model as follows. Please refer to the sheetdb-modeler tool documentation for details.
//go:generate sheetdb-modeler -type=User -children=Foo,FooChild,Bar -initial=10001
// User is a struct of user.
type User struct {
UserID int `json:"user_id" db:"primarykey"`
Name string `json:"name"`
Email string `json:"email" db:"unique"`
Sex Sex `json:"sex"`
Birthday *sheetdb.Date `json:"birthday"`
}
//go:generate sheetdb-modeler -type=Foo -parent=User -children=FooChild
// Foo is a struct of foo which is a child of user.
type Foo struct {
UserID int `json:"user_id" db:"primarykey"`
FooID int `json:"foo_id" db:"primarykey"`
Value float32 `json:"value"`
Note string `json:"note" db:"allowempty"`
}
//go:generate sheetdb-modeler -type=FooChild -parent=Foo
// FooChild is a struct of foo child.
type FooChild struct {
UserID int `json:"user_id" db:"primarykey"`
FooID int `json:"foo_id" db:"primarykey"`
ChildID int `json:"child_id" db:"primarykey"`
Value string `json:"value" db:"unique"`
}
//go:generate sheetdb-modeler -type=Bar -parent=User
// Bar is a struct of bar which is a child of user.
type Bar struct {
UserID int `json:"user_id" db:"primarykey"`
Datetime sheetdb.Datetime `json:"datetime" db:"primarykey"`
Value float32 `json:"value"`
Note string `json:"note" db:"allowempty"`
}
You can generate in bulk with the go generate
command by putting //go:generate sheetdb-modeler
comments in the code of the target package.
Please refer to the sheetdb-modeler tool documentation for details.
go generate ./sample
Prepare a spreadsheet according to the header comments of each generated file.
// Code generated by "sheetdb-modeler"; DO NOT EDIT.
// Create a Spreadsheet (sheet name: "users") as data storage.
// The spreadsheet header is as follows:
// user_id | name | email | sex | birthday | updated_at | deleted_at
// Please copy and paste this header on the first line of the sheet.
By setting Logger, you can customize log output freely.
If you want to send an alert to Slack, you can use SlackLogger
in this package.
Configuration of Logger is package scope. Please note that it is not client scope.
sheetdb.SetLogger(sheetdb.NewSlackLogger(
"project name",
"service name",
"icon_emoji",
"https://hooks.slack.com/services/zzz/zzz/zzzzz",
sheetdb.LevelError,
))
Create a new client using the New
function.
Set the created client in the package global variable. The name of the variable is the name specified with the -client
option (the default is dbClient
).
var dbClient *sheetdb.Client
// Initialize initializes this package.
func Initialize(ctx context.Context) error {
client, err := sheetdb.New(
ctx,
`{"installed":{"client_id":"..."}`, // Google API credentials
`{"access_token":"..."`, // Google API token
"xxxxx", // Google spreadsheet ID
)
// ...
dbClient = client
return nil
}
err := client.LoadData(ctx)
The functions in this section are generated automatically by sheetdb-modeler.
GetModelName
function returns an instance of that model by the primary key(s).
If it can not be found, this function returns *sheetdb.NotFoundError
.
user, err := GetUser(userID)
foo, err := GetFoo(userID, fooID)
fooChild, err := GetFooChild(userID, fooID, childID)
bar, err := GetBar(userID, datetime)
If the model is a child model of another model, GetModelName
method is also added to the parent model.
foo, err := user.GetFoo(fooID)
fooChild, err := foo.GetFooChild(childID)
bar, err := user.GetBar(datetime)
For fields defined as unique, GetModelNameByFieldName
function is also generated.
For child models, this method is also added to the parent model.
user, err := GetUserByEmail(email)
fooChild, err := GetFooChildByValue(userID, fooID, value)
fooChild, err := foo.GetFooChildByValue(value)
GetModelNames
function returns all instances of that model.
If the model is a child model of another model, this function returns all instances that parent model has.
For child models, this method is also added to the parent model.
users, err := GetUsers()
foos, err := GetFoos(userID)
fooChildren, err := GetFooChildren(userID, fooID)
bars, err := GetBars(userID)
foos, err := user.GetFoos()
fooChildren, err := foo.GetFooChildren()
bars, err := user.GetBars()
You can get filtered results using ModelFilter
option.
users, err := GetUsers(sample.UserFilter(func(user *sample.User) bool {
return user.Sex == sample.Male
}))
You can also get sorted results using ModelSort
option.
If the sort option is not specified, the order of results is random.
users, err := GetUsers(sample.UserSort(func(users []*sample.User) {
sort.Slice(users, func(i, j int) bool {
return users[i].UserID < users[j].UserID
})
}))
AddModel
adds a new instance.
If the primary key matches the automatic numbering rule, it will be automatically numbered.
For child models, AddModel
method is added to the parent model.
user, err := AddUser(name, email, sex, birthday)
foo, err := AddFoo(userID, value, note)
fooChild, err := AddFooChild(userID, fooID, value)
bar, err := AddBar(userID, datetime, value, note)
foo, err := user.AddFoo(value, note)
fooChild, err := foo.AddFooChild(value)
bar, err := user.AddBar(datetime, value, note)
UpdateModel
updates an existing instance.
You can not change the primary key(s).
For child models, UpdateModel
method is added to the parent model.
user, err := UpdateUser(userID, name, email, sex, birthday)
foo, err := UpdateFoo(userID, fooID, value, note)
fooChild, err := UpdateFooChild(userID, fooID, childID, value)
bar, err := UpdateBar(userID, datetime, value, note)
foo, err := user.UpdateFoo(fooID, value, note)
fooChild, err := foo.UpdateFooChild(childID, value, note)
bar, err := user.UpdateBar(datetime, value, note)
DeleteModel
deletes an existing instance.
If the model has child models, this function deletes the instances of the child model in cascade.
For child models, DeleteModel
method is added to the parent model.
err = DeleteUser(userID)
err = DeleteFoo(userID, fooID)
err = DeleteFooChild(userID, fooID, childID)
err = DeleteBar(userID, datetime)
err = user.DeleteFoo(fooID)
err = foo.DeleteFooChild(childID)
err = user.DeleteBar(datetime)