# Embedding immudb in your application

Using the Go client SDK means you are connecting to a immudb database server. There are cases where you don't want a separate server but embed immudb directly in the same application process, as a library.

# Embedding immudb key-value store

immudb already provides an embeddable key-value store in the embedded (opens new window) package.

The following example shows how to create or open a store, write some data and read it back:

package main

import (
	"fmt"
	"log"

	"github.com/codenotary/immudb/embedded/store"
)

func handleErr(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	// create/open immudb store at specified path
	st, err := store.Open("data", store.DefaultOptions())
	handleErr(err)

	// close the store to free resources
	defer st.Close()

	// create a transaction
	tx, err := st.NewTx()
	handleErr(err)

	// ensure tx is closed (it won't affect committed tx)
	defer tx.Cancel()

	// write key-value pair into the tx context, no change will be applied yet
	err = tx.Set([]byte("hello"), nil, []byte("immutable-world!"))
	handleErr(err)

	// transaction is committed and changes are applied
	hdr, err := tx.Commit()
	handleErr(err)

	fmt.Printf("tx %d successfully committed\n", hdr.ID)

	// fetch the latest entry of a key
	// dsue to performance considerations, only metadata, hash, and size are returned at first
	valRef, err := st.Get([]byte("hello"))
	handleErr(err)

	// read the actual value
	val, err := valRef.Resolve()
	handleErr(err)

	fmt.Printf("key '%s' = '%s' found at tx %d (%d key-updates)\n", []byte("hello"), val, valRef.Tx(), valRef.HC())
}

# Embedding immudb SQL store

immudb provides you a immutable embedded SQL engine which keeps all history, is tamper-proof and can travel in time. The SQL engine is mounted on top of the embedded key value store.

The following illustrative example showcase how to initialize the SQL engine, write and read data in the scope of a SQL transaction:

package main

import (
	"log"

	"github.com/codenotary/immudb/embedded/sql"
	"github.com/codenotary/immudb/embedded/store"
)

func handleErr(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	// create/open immudb store at specified path
	st, err := store.Open("data", store.DefaultOptions())
	handleErr(err)
	defer st.Close()

	// initialize sql engine (specify a key-prefix to isolate generated kv entries)
	engine, err := sql.NewEngine(st, sql.DefaultOptions().WithPrefix([]byte("sql")))
	handleErr(err)

	_, _, err = engine.Exec("CREATE DATABASE db1;", nil, nil)
	handleErr(err)

	// a sql tx is created and carried over next statements
	sqltx, _, err := engine.Exec("BEGIN TRANSACTION;", nil, nil)
	handleErr(err)

	// ensure tx is closed (it won't affect committed tx)
	defer engine.Exec("ROLLBACK;", nil, sqltx)

	// set the database to use in the context of the ongoing sql tx
	_, _, err = engine.Exec("USE DATABASE db1;", nil, sqltx)
	handleErr(err)

	// creates a table
	_, _, err = engine.Exec(`
		CREATE TABLE journal (
			id INTEGER,
			date TIMESTAMP,
			creditaccount INTEGER,
			debitaccount INTEGER,
			amount INTEGER,
			description VARCHAR,
			PRIMARY KEY id
		);`, nil, sqltx)
	handleErr(err)

	// insert some rows
	_, _, err = engine.Exec(`
		INSERT INTO journal (
			id,
			date,
			creditaccount,
			debitaccount,
			amount,
			description
		) VALUES 
			(1, NOW(), 100, 0, 4000, 'CREDIT'),
			(2, NOW(), 0, 50, 4100, 'DEBIT')
		;`, nil, sqltx)
	handleErr(err)

	// query data including ongoing and unconfirmed changes
	rowReader, err := engine.Query(`
			SELECT id, date, creditaccount, debitaccount, amount, description
			FROM journal
			WHERE amount > @value;
	`, map[string]interface{}{"value": 100}, sqltx)
	handleErr(err)

	// ensure row reader is closed
	defer rowReader.Close()

	// selected columns can be read from the rowReader
	cols, err := rowReader.Columns()
	handleErr(err)

	for {
		// iterate over result set
		row, err := rowReader.Read()
		if err == sql.ErrNoMoreRows {
			break
		}
		handleErr(err)

		// each row contains values for the selected columns
		log.Printf("row: %v\n", row.Values[cols[0].Selector()].Value())
	}

	// close row reader
	rowReader.Close()

	// commit ongoing transaction
	_, _, err = engine.Exec("COMMIT;", nil, sqltx)
	handleErr(err)
}

If you need to change options like where things get stored by default, you can do that in the underlying store objects that the SQL engine is using.