This library provides access to FileMaker Server using XML Web publishing.
Initially the library was a port of https://github.com/PerfectlySoft/Perfect-FileMaker, but it evolved a lot since.
The library is used in production to proxy the calls from API server to FileMaker database.
FileMaker differs Postgres or MySQL, so we cannot run docker and test the library in CI/CD. Maybe we could run EC2 with Windows and install FileMaker Server on it to run the integration test, yet it seems a bit overkill and very time-consuming at this moment.
Run the command below in the root directory of your project:
go get github.com/amanbolat/gofmcon
Then import the lib in your code:
import "github.com/amanbolat/gofmcon"Full example
package main
import (
	"encoding/json"
	fm "github.com/amanbolat/gofmcon"
	"log"
	"github.com/kelseyhightower/envconfig"
	"fmt"
	"errors"
)
// config represents all the configuration we need in order to
// create a new FMConnector and establish the connection with 
// FileMaker database 
type config struct {
	FmHost         string `split_words:"true" required:"true"`
	FmUser         string `split_words:"true" required:"true"`
	FmPort         string `split_words:"true" required:"true"`
	FmDatabaseName string `split_words:"true" required:"true"`
	FmPass         string `split_words:"true" required:"true"`
}
type postStore struct {
	fmConn *fm.FMConnector
	dbName string
}
type Post struct {
	Author  string `json:"Author"`
	Title   string `json:"Title"`
	Content string `json:"Content"`
}
func (p *Post) Populate(record *fm.Record) {
	p.Author = record.Field("author")
	p.Title = record.Field("title")
	p.Content = record.Field("content")
}
func main() {
	var conf = &config{}
	err := envconfig.Process("", conf)
	if err != nil {
		log.Fatal(err)
	}
	fmConn := fm.NewFMConnector(conf.FmHost, conf.FmPort, conf.FmUser, conf.FmPass)
	store := postStore{fmConn: fmConn, dbName: conf.FmDatabaseName}
	posts, err := store.GetAllPosts(fmConn)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Print(posts)
}
func (ps *postStore) GetAllPosts() ([]Post, error) {
	var posts []Post
	q := fm.NewFMQuery(ps.dbName, "posts_list_layout", fm.FindAll)
	fmset, err := ps.fmConn.Query(q)
	if err != nil {
		return posts, errors.New("failed to get posts")
	}
	// Populate it with record
	for _, r := range fmset.Resultset.Records {
		p := Post{}
		b, _ := r.JsonFields()
		_ = json.Unmarshal(b, &p)
		posts = append(posts, p)
	}
	return posts, nil
}Get a single record
    q := fm.NewFMQuery(databaseName, layout_name, fm.Find)
    q.WithFields(
        fm.FMQueryField{Name: "field_name", Value: "001", Op: fm.Equal},
    ).Max(1)Check if the error is FileMaker specific one
    fmSet, err := fmConn.Query(q)
    if err != nil {
        if err.Error() == fmt.Sprintf("FileMaker_error: %s", fm.FileMakerErrorCodes[401]) {
            // your code
        }
    
        // else do something
    }Create a record
    q := fm.NewFMQuery(databaseName, layout_name, fm.New)
    q.WithFields(
        fm.FMQueryField{Name: "field_name", Value: "some_value"},
    )
    
    fmSet, err := fmConn.Query(q)Sort the records
    q.WithSortFields(fm.FMSortField{Name: "some_field", Order: fm.Descending})Update a record
Your object should have FileMaker record id to update record in database. Please see more in FileMaker documentation.
    q := fm.NewFMQuery(databaseName, layout_name, fm.Edit)
    q.WithFields(
        fm.FMQueryField{Name: "field_name", Value: "some_new_value"},
    )
    q.WithRecordId(updated_object.FMRecordID)Run a script
    // SCRIPT_DELIMITER can be '|', '_' or any other symbol that will be
    // parsed on FileMaker side to get all the parameters from the string
    q.WithPostFindScripts(SCRIPT_NAME, strings.Join([]string{param_1, param_2, param_3}, SCRIPT_DELIMITER))