Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/cli/src/helpers/core/post-installation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,7 @@ function displayGoInstructions(config: ProjectConfig & { depsInstalled: boolean
const ormNames: Record<string, string> = {
gorm: "GORM",
sqlc: "sqlc",
ent: "Ent",
};
output += `${pc.cyan("•")} Database: ${ormNames[goOrm] || goOrm}\n`;
}
Expand Down
5 changes: 5 additions & 0 deletions apps/cli/src/prompts/go-ecosystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ export async function getGoOrmChoice(goOrm?: GoOrm) {
label: "sqlc",
hint: "Generate type-safe Go code from SQL",
},
{
value: "ent" as const,
label: "Ent",
hint: "Code-first ORM by Meta with graph traversal API, 15k+ stars",
},
{
value: "none" as const,
label: "None",
Expand Down
8 changes: 8 additions & 0 deletions apps/web/src/lib/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3047,6 +3047,14 @@ export const TECH_OPTIONS: Record<
color: "from-orange-500 to-orange-700",
default: false,
},
{
id: "ent",
name: "Ent",
description: "Code-first ORM by Meta with compile-time safety and graph traversal API",
icon: "",
color: "from-blue-500 to-indigo-600",
default: false,
},
{
id: "none",
name: "No ORM",
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/lib/tech-resource-links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,7 @@ const BASE_LINKS: LinkMap = {
chi: { docsUrl: "https://go-chi.io/", githubUrl: "https://github.com/go-chi/chi" },
gorm: { docsUrl: "https://gorm.io/docs/", githubUrl: "https://github.com/go-gorm/gorm" },
sqlc: { docsUrl: "https://docs.sqlc.dev/", githubUrl: "https://github.com/sqlc-dev/sqlc" },
ent: { docsUrl: "https://entgo.io/docs/getting-started/", githubUrl: "https://github.com/ent/ent" },
"grpc-go": {
docsUrl: "https://grpc.io/docs/languages/go/quickstart/",
githubUrl: "https://github.com/grpc/grpc-go",
Expand Down
29 changes: 29 additions & 0 deletions packages/template-generator/src/processors/readme-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,8 @@ function generateGoReadmeContent(config: ProjectConfig): string {
features.push("- **GORM** - Full-featured ORM for Go");
} else if (goOrm === "sqlc") {
features.push("- **SQLc** - Generate type-safe code from SQL");
} else if (goOrm === "ent") {
features.push("- **Ent** - Code-first ORM by Meta with graph traversal API");
}

// API
Expand Down Expand Up @@ -1299,6 +1301,11 @@ function generateGoReadmeContent(config: ProjectConfig): string {
structure.push("│ │ └── models.go");
structure.push("│ └── handlers/ # HTTP handlers");
structure.push("│ └── handlers.go");
} else if (goOrm === "ent") {
structure.push("│ ├── models/ # Request/response types");
structure.push("│ │ └── models.go");
structure.push("│ └── handlers/ # HTTP handlers");
structure.push("│ └── handlers.go");
}
} else if (auth === "go-better-auth") {
structure.push("├── internal/");
Expand Down Expand Up @@ -1396,6 +1403,28 @@ cp .env.example .env
\`\`\`bash
sqlc generate
\`\`\`
`;
} else if (goOrm === "ent") {
databaseSetup = `
## Database Setup

This project uses Ent (by Meta) as the ORM. To set up:

1. Copy the environment file:
\`\`\`bash
cp .env.example .env
\`\`\`

2. Update \`DATABASE_URL\` in \`.env\` with your database connection string.

3. Generate Ent client code from schemas:
\`\`\`bash
go generate ./ent
\`\`\`

Supported databases:
- SQLite (default): leave \`DATABASE_URL\` empty
- PostgreSQL: \`DATABASE_URL=postgres://user:pass@localhost:5432/dbname?sslmode=disable\`
`;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
{{/if}}
{{#if (or (eq goOrm "gorm") (eq goOrm "sqlc"))}}
{{#if (or (or (eq goOrm "gorm") (eq goOrm "sqlc")) (eq goOrm "ent"))}}

"{{projectName}}/internal/database"
{{/if}}
Expand Down Expand Up @@ -191,6 +191,33 @@ func main() {
{{/if}}
_ = pool // Use pool in your handlers
{{/if}}
{{#if (eq goOrm "ent")}}
// Initialize database (run `go generate ./ent` to use Ent client)
db, err := database.InitDB()
if err != nil {
{{#if (eq goLogging "zap")}}
logger.Fatal("Failed to connect to database", zap.Error(err))
{{else if (eq goLogging "zerolog")}}
logger.Fatal().Err(err).Msg("Failed to connect to database")
{{else if (eq goLogging "slog")}}
logger.Error("Failed to connect to database", "error", err)
os.Exit(1)
{{else}}
panic("Failed to connect to database: " + err.Error())
{{/if}}
}
defer database.Close()
{{#if (eq goLogging "zap")}}
logger.Info("Database connected successfully")
{{/if}}
{{#if (eq goLogging "zerolog")}}
logger.Info().Msg("Database connected successfully")
{{/if}}
{{#if (eq goLogging "slog")}}
logger.Info("Database connected successfully")
{{/if}}
_ = db // Use db in your handlers
{{/if}}

{{#if (or (or (or (or (or (eq goWebFramework "gin") (eq goWebFramework "echo")) (eq goWebFramework "fiber")) (eq goWebFramework "chi")) (eq goApi "grpc-go")) (eq auth "go-better-auth"))}}
// Get host from environment
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{{#if (eq goOrm "ent")}}
package ent

//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./schema
{{/if}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{{#if (eq goOrm "ent")}}
package schema

import (
"time"

"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
)

// Post holds the schema definition for the Post entity.
type Post struct {
ent.Schema
}

// Fields of the Post.
func (Post) Fields() []ent.Field {
return []ent.Field{
field.String("title").
NotEmpty(),
field.Text("content").
Default(""),
field.Bool("published").
Default(false),
field.Int("author_id"),
field.Time("created_at").
Default(time.Now).
Immutable(),
field.Time("updated_at").
Default(time.Now).
UpdateDefault(time.Now),
}
}

// Edges of the Post.
func (Post) Edges() []ent.Edge {
return []ent.Edge{
edge.From("author", User.Type).
Ref("posts").
Field("author_id").
Unique().
Required(),
}
}
{{/if}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{{#if (eq goOrm "ent")}}
package schema

import (
"time"

"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
)

// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}

// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name").
NotEmpty(),
field.String("email").
NotEmpty().
Unique(),
field.Time("created_at").
Default(time.Now).
Immutable(),
field.Time("updated_at").
Default(time.Now).
UpdateDefault(time.Now),
}
}

// Edges of the User.
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("posts", Post.Type),
}
}
{{/if}}
5 changes: 5 additions & 0 deletions packages/template-generator/templates/go-base/go.mod.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ require (
{{#if (eq goOrm "sqlc")}}
github.com/jackc/pgx/v5 v5.9.1
{{/if}}
{{#if (eq goOrm "ent")}}
entgo.io/ent v0.14.0
github.com/lib/pq v1.10.9
github.com/mattn/go-sqlite3 v1.14.24
{{/if}}
{{#if (eq goApi "grpc-go")}}
google.golang.org/grpc v1.80.0
google.golang.org/protobuf v1.36.11
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,89 @@ func Close() {
}
}
{{/if}}
{{#if (eq goOrm "ent")}}
package database

import (
"database/sql"
"os"
"strings"

_ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"
)

var DB *sql.DB

// InitDB initializes the database connection.
// After running `go generate ./ent`, you can switch to using the Ent client directly.
func InitDB() (*sql.DB, error) {
dsn := os.Getenv("DATABASE_URL")

var db *sql.DB
var err error

if dsn == "" {
// Default to SQLite for development
db, err = sql.Open("sqlite3", "file:app.db?cache=shared&_fk=1")
} else if strings.HasPrefix(dsn, "postgres") {
db, err = sql.Open("postgres", dsn)
} else {
// Assume SQLite file path
db, err = sql.Open("sqlite3", "file:"+dsn+"?cache=shared&_fk=1")
}

if err != nil {
return nil, err
}

// Verify connection
if err := db.Ping(); err != nil {
db.Close()
return nil, err
}

// Create tables if they don't exist (SQLite dev mode)
if dsn == "" || !strings.HasPrefix(dsn, "postgres") {
if _, err := db.Exec(sqliteSchema); err != nil {
db.Close()
return nil, err
}
}

DB = db
return DB, nil
}

// GetDB returns the database instance
func GetDB() *sql.DB {
return DB
}

// Close closes the database connection
func Close() {
if DB != nil {
DB.Close()
}
}

const sqliteSchema = `
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT NOT NULL DEFAULT '',
published BOOLEAN NOT NULL DEFAULT 0,
author_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
`
{{/if}}
Loading
Loading