gokv / postgres

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

gokv/postgres

GoDoc Build Status

A reflectionless gokv/store PostgreSQL client.

This package is not ready for production use.


The aim of this package is to provide an idiomatic Go API to the SQL persistence. The data is accepted in a key/value format, where the value implements JSON marshaler/unmarshaler, and is persisted as JSONb.

A typical approach to database abstraction is the ORM (Object-Relational Mapper): a piece of software that maps your domain models to some SQL code. In this case, the actual mapping functions are defined by the consumer: the persisted types need to provide an explicit implementation of MarhalJSON and UnmarshalJSON.

Here is how the usage experience was designed:

// Instantiate a new PostgreSQL store
s, _ = postgres.New(db, "table_name")

// store defines the methods required in this function, or in this package.
type store interface {
  Set(ctx context.Context, id string, v json.Marshaler) error
}

// SaveNewBook accepts the postgres.Store as a locally-defined interface.
// Later on, another Store implementation could be passed instead, for example
// using Redis.
//
// The `book` type must explicitly implement `json.Marshaler`
func SaveNewBook(s store, newBook book) error {
  return s.Set(req.Context(), uuid.New(), newBook)
}

Requisites

  • Go v1.8 (for context.Context integration in the stdlib)
  • Postgres v9.5 (first version to provide upsert via the ON CONFLICT clause)

Use

The New initialisator prepares SQL statements against the given *sql.DB, and exposes methods that act upon those prepared statements.

Here's how to use it:

package main

import (
	"context"
	"database/sql"
	"encoding/json"
	"log"
	"time"

	"github.com/gokv/postgres"
	"github.com/google/uuid"
	_ "github.com/lib/pq"
)

// User is the type to persist
type User struct {
	Firstname string
	Lastname  string
	CreatedAt time.Time
}

// The type has to explicitly implement json.Marshaler
func (u User) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Firstname string    `json:"firstname"`
		Lastname  string    `json:"lastname"`
		CreatedAt time.Time `json:"created_at"`
	}{
		Firstname: u.Firstname,
		Lastname:  u.Lastname,
		CreatedAt: u.CreatedAt,
	})
}

func main() {
	// Create the connection abstraction
	db, err := sql.Open("postgres", "host=postgres user=username password=secret dbname=store")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// Pass the *sql.DB and the name of the table the Store will be bound to.
	// WithCreateTable is the option for creating the table if it doesn't exist
	// already.
	s, err := postgres.New(db, "users", WithCreateTable)
	if err != nil {
		log.Fatal(err)
	}
	defer s.Close()

	// Add a new entry
	id, err := s.Add(context.Context(), User{
		Firstname: "Giacomo",
		Lastname:  "Leopardi",
		CreatedAt: time.Now(),
	})
	if err != nil {
		log.Fatal(err)
	}

  fmt.Println("The new user's unique ID is %q", id)
}

Roadmap

  • Basic CRUD
  • Search

About

License:MIT License


Languages

Language:Go 98.5%Language:Makefile 1.5%