go-migrationGo Migration
Schema Builder
Documentation

Column Modifiers

Customize columns with chainable modifiers like Nullable, Default, Primary, Unique, Unsigned, and AutoIncrement.

Column Modifiers

Column modifiers let you customize column behavior. They're chainable — call them directly on the column definition returned by any column type method.

go
bp.String("email", 255).Unique()
bp.Integer("age").Nullable().Default(0)
bp.BigInteger("count").Unsigned().Default(0)

Available Modifiers

ModifierDescriptionExample
.Nullable()Allow NULL valuesbp.String("phone", 20).Nullable()
.Default(value)Set a default valuebp.Boolean("active").Default(true)
.DefaultRaw(expression)Set a raw SQL expression as the defaultbp.Timestamp("created_at").DefaultRaw("CURRENT_TIMESTAMP")
.Primary()Set as primary keybp.UUID("id").Primary()
.Unique()Add a unique constraintbp.String("email", 255).Unique()
.Unsigned()Make numeric column unsignedbp.Integer("age").Unsigned()
.AutoIncrement()Enable auto-incrementbp.BigInteger("id").AutoIncrement().Primary()

Nullable

By default, columns are NOT NULL. Use .Nullable() to allow NULL values:

go
bp.String("phone", 20).Nullable()
bp.Text("bio").Nullable()
bp.Timestamp("deleted_at").Nullable()

This is common for optional fields and soft-delete timestamps.

Default

Set a default value that the database uses when no value is provided during insert:

go
bp.Boolean("active").Default(true)
bp.Integer("view_count").Default(0)
bp.String("role", 50).Default("user")
bp.JSON("settings").Default("{}")

The default value is set at the database level, not in Go code. The database engine applies it when a row is inserted without specifying that column.

DefaultRaw

Default quotes its value as a literal. When you need the default to be a SQL expression evaluated by the database — such as a function call — use .DefaultRaw() instead. The expression is inserted verbatim, without quoting:

go
bp.Timestamp("created_at").DefaultRaw("CURRENT_TIMESTAMP")
bp.UUID("id").DefaultRaw("gen_random_uuid()")

The expression you pass to DefaultRaw is database-specific. For example, gen_random_uuid() is PostgreSQL-specific. If you target multiple databases, make sure the expression is supported by each one.

.DefaultRaw("…") is shorthand for passing a schema.Raw(…) value to .Default(). The two are equivalent — use whichever reads better:

go
import "github.com/gopackx/go-migration/pkg/schema"

bp.Timestamp("created_at").DefaultRaw("CURRENT_TIMESTAMP")
// is the same as:
bp.Timestamp("created_at").Default(schema.Raw("CURRENT_TIMESTAMP"))

schema.Raw() returns a schema.RawExpression, which the grammar inserts verbatim (unquoted) wherever a default value is expected.

Primary

Designate a column as the primary key:

go
bp.UUID("id").Primary()

For auto-incrementing integer primary keys, use bp.ID() instead — it combines BigInteger, Unsigned, AutoIncrement, and Primary in one call and creates a column named id:

go
bp.ID()

Unique

Add a unique constraint to ensure no duplicate values:

go
bp.String("email", 255).Unique()
bp.String("slug", 255).Unique()
bp.UUID("external_id").Unique()

For composite unique constraints, use a unique index instead.

Unsigned

Restrict numeric columns to non-negative values:

go
bp.Integer("age").Unsigned()
bp.BigInteger("file_size").Unsigned()

Unsigned() is only emitted by the MySQL grammar (e.g. INT UNSIGNED). PostgreSQL and SQLite have no unsigned integer types, so the modifier is silently ignored on those grammars — no CHECK constraint is generated. If you need a non-negative guarantee on PostgreSQL or SQLite, add it yourself with a raw check, for example a separate migration that runs ALTER TABLE ... ADD CONSTRAINT ... CHECK (col >= 0). See Database Grammars for the full type mapping.

AutoIncrement

Enable auto-incrementing for integer columns:

go
bp.BigInteger("id").AutoIncrement().Primary()

In most cases, you should use bp.ID() which sets up auto-increment automatically. Use .AutoIncrement() directly only when you need custom control over the primary key definition.

Chaining Multiple Modifiers

Modifiers can be chained in any order:

go
bp.Integer("sort_order").Unsigned().Default(0)
bp.String("status", 20).Default("draft")
bp.Decimal("balance", 12, 2).Unsigned().Default(0)
bp.BigInteger("parent_id").Unsigned().Nullable()

Complete Example

migrations/20240301_create_orders_table.go
package migrations

import (
    "github.com/gopackx/go-migration/pkg/schema"
)

type CreateOrdersTable struct{}

func (m *CreateOrdersTable) Up(s *schema.Builder) error {
    return s.Create("orders", func(bp *schema.Blueprint) {
        bp.ID()
        bp.BigInteger("user_id").Unsigned()
        bp.String("order_number", 50).Unique()
        bp.String("status", 20).Default("pending")
        bp.Decimal("total", 12, 2).Unsigned().Default(0)
        bp.Decimal("tax", 10, 2).Unsigned().Default(0)
        bp.Text("notes").Nullable()
        bp.Boolean("paid").Default(false)
        bp.Timestamp("shipped_at").Nullable()
        bp.Timestamps()
    })
}

func (m *CreateOrdersTable) Down(s *schema.Builder) error {
    return s.DropIfExists("orders")
}

What's Next?

  • Indexes — add indexes for query performance
  • Foreign Keys — define relationships between tables