Fields
Quick Summary​
Fields (or properties) in the schema are the attributes of the node. For example, a User
with 4 fields: age, name, username and created_at:

Fields are returned from the schema using the Fields method. For example:
package schema
import (
"time"
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// User schema.
type User struct {
ent.Schema
}
// Fields of the user.
func (User) Fields() []ent.Field {
return []ent.Field{
field.Int("age"),
field.String("name"),
field.String("username").
Unique(),
field.Time("created_at").
Default(time.Now),
}
}
All fields are required by default, and can be set to optional using the Optional method.
Types​
The following types are currently supported by the framework:
- All Go numeric types. Like
int,uint8,float64, etc. boolstringtime.TimeUUID[]byte(SQL only).JSON(SQL only).Enum(SQL only).Other(SQL only).
package schema
import (
"time"
"net/url"
"github.com/google/uuid"
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// User schema.
type User struct {
ent.Schema
}
// Fields of the user.
func (User) Fields() []ent.Field {
return []ent.Field{
field.Int("age").
Positive(),
field.Float("rank").
Optional(),
field.Bool("active").
Default(false),
field.String("name").
Unique(),
field.Time("created_at").
Default(time.Now),
field.JSON("url", &url.URL{}).
Optional(),
field.JSON("strings", []string{}).
Optional(),
field.Enum("state").
Values("on", "off").
Optional(),
field.UUID("uuid", uuid.UUID{}).
Default(uuid.New),
}
}
To read more about how each type is mapped to its database-type, go to the Migration section.
ID Field​
The id field is builtin in the schema and does not need declaration. In SQL-based
databases, its type defaults to int (but can be changed with a codegen option)
and auto-incremented in the database.
In order to configure the id field to be unique across all tables, use the
WithGlobalUniqueID option when running schema migration.
If a different configuration for the id field is needed, or the id value should
be provided on entity creation by the application (e.g. UUID), override the builtin
id configuration. For example:
// Fields of the Group.
func (Group) Fields() []ent.Field {
return []ent.Field{
field.Int("id").
StructTag(`json:"oid,omitempty"`),
}
}
// Fields of the Blob.
func (Blob) Fields() []ent.Field {
return []ent.Field{
field.UUID("id", uuid.UUID{}).
Default(uuid.New).
StorageKey("oid"),
}
}
// Fields of the Pet.
func (Pet) Fields() []ent.Field {
return []ent.Field{
field.String("id").
MaxLen(25).
NotEmpty().
Unique().
Immutable(),
}
}
If you need to set a custom function to generate IDs, you can use DefaultFunc
to specify a function which will always be ran when the resource is created.
See the related FAQ for more information.
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.Int64("id").
DefaultFunc(func() int64 {
// An example of a dumb ID generator - use a production-ready alternative instead.
return time.Now().Unix() << 8 | atomic.AddInt64(&counter, 1) % 256
}),
}
}
Database Type​
Each database dialect has its own mapping from Go type to database type. For example,
the MySQL dialect creates float64 fields as double columns in the database. However,
there is an option to override the default behavior using the SchemaType method.
package schema
import (
"entgo.io/ent"
"entgo.io/ent/dialect"
"entgo.io/ent/schema/field"
)
// Card schema.
type Card struct {
ent.Schema
}
// Fields of the Card.
func (Card) Fields() []ent.Field {
return []ent.Field{
field.Float("amount").
SchemaType(map[string]string{
dialect.MySQL: "decimal(6,2)", // Override MySQL.
dialect.Postgres: "numeric", // Override Postgres.
}),
}
}
Go Type​
The default type for fields are the basic Go types. For example, for string fields, the type is string,
and for time fields, the type is time.Time. The GoType method provides an option to override the
default ent type with a custom one.
The custom type must be either a type that is convertible to the Go basic type, a type that implements the ValueScanner interface, or has an External ValueScanner. Also, if the provided type implements the Validator interface and no validators have been set, the type validator will be used.
package schema
import (
"database/sql"
"entgo.io/ent"
"entgo.io/ent/dialect"
"entgo.io/ent/schema/field"
"github.com/shopspring/decimal"
)
// Amount is a custom Go type that's convertible to the basic float64 type.
type Amount float64
// Card schema.
type Card struct {
ent.Schema
}
// Fields of the Card.
func (Card) Fields() []ent.Field {
return []ent.Field{
field.Float("amount").
GoType(Amount(0)),
field.String("name").
Optional().
// A ValueScanner type.
GoType(&sql.NullString{}),
field.Enum("role").
// A convertible type to string.
GoType(role.Role("")),
field.Float("decimal").
// A ValueScanner type mixed with SchemaType.
GoType(decimal.Decimal{}).
SchemaType(map[string]string{
dialect.MySQL: "decimal(6,2)",
dialect.Postgres: "numeric",
}),
}
}
External ValueScanner​
Ent allows attaching custom ValueScanner for basic or custom Go types. This enables the use of standard
schema fields while maintaining control over how they are stored in the database without implementing a ValueScanner
interface. Additionally, this option enables users to use GoType that does not implement the ValueScanner, such
as *url.URL.
At this stage, this option is only available for text and numeric fields, but it will be extended to other types in the future.
- TextMarshaller
- BinaryMarshaller
- Functions based
- Custom
Fields with a custom Go type that implements the encoding.TextMarshaller and encoding.TextUnmarshaller interfaces can
use the field.TextValueScanner as a ValueScanner. This ValueScanner calls MarshalText and UnmarshalText for
writing and reading field values from the database:
field.String("big_int").
GoType(&big.Int{}).
ValueScanner(field.TextValueScanner[*big.Int]{})
Fields with a custom Go type that implements the encoding.BinaryMarshaller and encoding.BinaryUnmarshaller interfaces can
use the field.BinaryValueScanner as a ValueScanner. This ValueScanner calls MarshalBinary and UnmarshalBinary for
writing and reading field values from the database:
field.String("url").
GoType(&url.URL{}).
ValueScanner(field.BinaryValueScanner[*url.URL]{})
The field.ValueScannerFunc allows setting two functions to be used for writing and reading database values: V
for driver.Value and S for sql.Scanner:
field.String("encoded").
ValueScanner(field.ValueScannerFunc[string, *sql.NullString]{
V: func(s string) (driver.Value, error) {
return base64.StdEncoding.EncodeToString([]byte(s)), nil
},
S: func(ns *sql.NullString) (string, error) {
if !ns.Valid {
return "", nil
}
b, err := base64.StdEncoding.DecodeString(ns.String)
if err != nil {
return "", err
}
return string(b), nil
},
})
field.String("prefixed").
ValueScanner(PrefixedHex{
prefix: "0x",
})
// PrefixedHex is a custom type that implements the TypeValueScanner interface.
type PrefixedHex struct {
prefix string
}
// Value implements the TypeValueScanner.Value method.
func (p PrefixedHex) Value(s string) (driver.Value, error) {
return p.prefix + ":" + hex.EncodeToString([]byte(s)), nil
}
// ScanValue implements the TypeValueScanner.ScanValue method.
func (PrefixedHex) ScanValue() field.ValueScanner {
return &sql.NullString{}
}
// FromValue implements the TypeValueScanner.FromValue method.
func (p PrefixedHex) FromValue(v driver.Value) (string, error) {
s, ok := v.(*sql.NullString)
if !ok {
return "", fmt.Errorf("unexpected input for FromValue: %T", v)
}
if !s.Valid {
return "", nil
}
d, err := hex.DecodeString(strings.TrimPrefix(s.String, p.prefix+":"))
if err != nil {
return "", err
}
return string(d), nil
}
Other Field​
Other represents a field that is not a good fit for any of the standard field types. Examples are a Postgres Range type or Geospatial type
package schema