Use StatelyDB with Go

Go is built for simplicity, performance, and concurrency. Your database should be too. Too often, Go developers are forced to choose between the complexity of a heavyweight Object Relational Mapper (ORM), the tedious boilerplate of manual SQL mapping, or the awkward, untyped nature of raw NoSQL drivers. This adds friction to development and creates a drag on your team's velocity.

StatelyDB offers a new approach that feels native to the Go ecosystem. By combining a schema-first workflow with powerful code generation, we eliminate the boilerplate and give you back what you value most: the ability to work with clean, type-safe Go structs. Stop fighting your database and start building.

The Problem with Data in Go

If you are a Go developer, this probably sounds familiar. You want to store a simple struct in a database. You end up:

Wrestling with ORMs

You pull in a large, complex ORM library with its own domain specific language, performance overhead, and limitations, adding a heavy layer of abstraction between your code and your data.

Writing Manual Boilerplate

You spend hours writing repetitive sql. Scan code or meticulously managing db struct tags for every field, a process that is both tedious and highly error-prone.

Losing Type Safety

You work with NoSQL databases and are forced to handle map[string]interface{}, losing all the benefits of Go's strong type system and opening the door to runtime errors from unexpected data shapes.

Fearing Migrations

You need to change a data model, and now you face the operational risk of writing and executing a complex schema migration, hoping you do not break production.

These challenges create a tax on development. They slow you down, introduce bugs, and make your application more complex than it needs to be.

The StatelyDB Developer Experience

StatelyDB was designed to solve these problems by putting type safety and developer productivity first. Our workflow is simple, powerful, and removes the friction between your application and your data.

1
Define Your Schema Once

Your data model is the single source of truth. You define it once using our simple, TypeScript-based Elastic Schema™. This schema is not just for validation; it is the blueprint for your entire data layer.

// schema/schema.ts
import { itemType, string, uuid } from "@stately-cloud/schema";

/** A user of our new application. */
itemType("User", {
  keyPath: "/user-:id",
  fields: {
    id: { type: uuid, initialValue: "uuid" },
    name: { type: string },
    email: { type: string },
  },
});
2
Generate Native Go Types

Run a single command to generate a Go package with native structs for every item in your schema. There is no ORM, no magic, just clean, idiomatic Go code.

stately schema generate \
  --language go \
  --schema-id <your-schema-id> \
  ./pkg/schema

This command generates a schema package you can import directly into your application.

// pkg/schema/schema.go (Generated)
package schema

import (
	"github.com/google/uuid"
)

type User struct {
	Id    uuid.UUID
	Name  string
	Email string
}

// ... and other generated helpers
3
Interact with a Simple, Typed API

With the generated package, interacting with StatelyDB is as simple as working with any other Go struct. Our Go SDK provides a clean, minimal API that feels natural to use.
Compare the simplicity of StatelyDB to a typical DynamoDB interaction.

StatelyDB: Simple, Typed, and Clean

import "your-repo/pkg/schema"

func CreateUser(ctx context.Context, client stately.Client) (*schema.User, error) {
	// Just create and put a native Go struct.
	user, err := client.Put(ctx, &schema.User{
		Name:  "Alex Doe",
		Email: "alex@example.com",
	})
	if err != nil {
		return nil, err
	}
	return user.(*schema.User), nil
}

Typical DynamoDB SDK: Verbose and Untyped

import "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
import "github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"

func CreateUser(ctx context.Context, client *dynamodb.Client) (*User, error) {
    user := &User{
        ID:    uuid.New(),
        Name:  "Alex Doe",
        Email: "alex@example.com",
    }

    // Manually marshal the struct into a map of attribute values.
    av, err := attributevalue.MarshalMap(user)
    if err != nil {
        return nil, err
    }

    // Add partition and sort keys manually.
    av["PK"] = &types.AttributeValueMemberS{Value: fmt.Sprintf("USER#%s", user.ID.String())}
    av["SK"] = &types.AttributeValueMemberS{Value: "METADATA"}

    // Construct and send the verbose request.
    _, err = client.PutItem(ctx, &dynamodb.PutItemInput{
        TableName: aws.String("my-table"),
        Item:      av,
    })

    // ... error handling ...
    return user, err
}

With StatelyDB, you write less code, eliminate entire classes of errors, and can focus on your application's logic, not on the plumbing of data access.

4
Evolve Fearlessly

The best part? When your application's needs change, you can evolve your data model without fear. Simply update your schema.ts file, add a migration command, and regenerate your Go types. StatelyDB's Elastic Schema ensures that your changes are always backwards and forwards compatible, so you can deploy your new code without risky database migrations or downtime.

The StatelyDB Developer Experience

StatelyDB was designed to solve these problems by putting type safety and developer productivity first. Our workflow is simple, powerful, and removes the friction between your application and your data.

Define Your Schema Once

Your data model is the single source of truth. You define it once using our simple, TypeScript-based Elastic Schema™. This schema is not just for validation; it is the blueprint for your entire data layer.

// schema/schema.ts
import { itemType, string, uuid } from "@stately-cloud/schema";

/** A user of our new application. */
itemType("User", {
  keyPath: "/user-:id",
  fields: {
    id: { type: uuid, initialValue: "uuid" },
    name: { type: string },
    email: { type: string },
  },
});
Generate Native Go Types

Run a single command to generate a Go package with native structs for every item in your schema. There is no ORM, no magic, just clean, idiomatic Go code.

stately schema generate \
  --language go \
  --schema-id <your-schema-id> \
  ./pkg/schema

This command generates a schema package you can import directly into your application.

// pkg/schema/schema.go (Generated)
package schema

import (
	"github.com/google/uuid"
)

type User struct {
	Id    uuid.UUID
	Name  string
	Email string
}

// ... and other generated helpers
Interact with a Simple, Typed API

With the generated package, interacting with StatelyDB is as simple as working with any other Go struct. Our Go SDK provides a clean, minimal API that feels natural to use.
Compare the simplicity of StatelyDB to a typical DynamoDB interaction.

StatelyDB: Simple, Typed, and Clean

import "your-repo/pkg/schema"

func CreateUser(ctx context.Context, client stately.Client) (*schema.User, error) {
	// Just create and put a native Go struct.
	user, err := client.Put(ctx, &schema.User{
		Name:  "Alex Doe",
		Email: "alex@example.com",
	})
	if err != nil {
		return nil, err
	}
	return user.(*schema.User), nil
}

Typical DynamoDB SDK: Verbose and Untyped

import "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
import "github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"

func CreateUser(ctx context.Context, client *dynamodb.Client) (*User, error) {
    user := &User{
        ID:    uuid.New(),
        Name:  "Alex Doe",
        Email: "alex@example.com",
    }

    // Manually marshal the struct into a map of attribute values.
    av, err := attributevalue.MarshalMap(user)
    if err != nil {
        return nil, err
    }

    // Add partition and sort keys manually.
    av["PK"] = &types.AttributeValueMemberS{Value: fmt.Sprintf("USER#%s", user.ID.String())}
    av["SK"] = &types.AttributeValueMemberS{Value: "METADATA"}

    // Construct and send the verbose request.
    _, err = client.PutItem(ctx, &dynamodb.PutItemInput{
        TableName: aws.String("my-table"),
        Item:      av,
    })

    // ... error handling ...
    return user, err
}

With StatelyDB, you write less code, eliminate entire classes of errors, and can focus on your application's logic, not on the plumbing of data access.

Evolve Fearlessly

The best part? When your application's needs change, you can evolve your data model without fear. Simply update your schema.ts file, add a migration command, and regenerate your Go types. StatelyDB's Elastic Schema ensures that your changes are always backwards and forwards compatible, so you can deploy your new code without risky database migrations or downtime.

Get Started with Go

Ready to try a database that feels like it was designed for Go?

1

Follow our Getting Started Guide to create your account, store, and schema.

2

Install the Go SDK: go get github.com/StatelyCloud/go-sdk

3

Generate your Go types as shown above.

4

Initialize the client and start building.

No items found.

No items found.

It is that simple

Start Building for Free