Home / Docs / Getting Started

Getting Started

Guides

Forge is a Go web framework that treats content as a first-class concept. Define a struct, wire a module, and Forge handles routing, templates, lifecycle enforcement, SEO metadata, and AI agent access — with zero third-party dependencies in core.

Installation

go get github.com/forge-cms/forge@latest

Your first content type

Every Forge content type embeds forge.Node:

type Post struct {
    forge.Node
    Title string `forge:"required,min=3"`
    Body  string `forge:"required"`
}

forge.Node provides UUID identity, a URL slug, and the full content lifecycle (Draft → Scheduled → Published → Archived). The lifecycle is always enforced — non-published content never appears on public endpoints.

Wiring the app

package main

import (
    "database/sql"
    "github.com/forge-cms/forge"
    _ "modernc.org/sqlite"
)

func main() {
    db, _ := sql.Open("sqlite", "./data/forge.db")

    app := forge.New(forge.MustConfig(forge.Config{
        BaseURL: "https://example.com",
        Secret:  []byte("...32 random bytes..."),
        DB:      db,
    }))

    app.Partials("templates/partials")

    app.SEO(&forge.HeadAssets{
        Preconnect:  []string{"https://fonts.bunny.net"},
        Stylesheets: []string{"/static/css/app.css"},
        Favicons: []forge.FaviconLink{
            {Rel: "icon", Type: "image/png", Sizes: "32x32", Href: "/static/favicon-32x32.png"},
        },
    })

    app.Content(forge.NewModule((*Post)(nil),
        forge.At("/posts"),
        forge.Repo(forge.NewSQLRepo[*Post](db)),
        forge.Templates("templates/posts"),
    ))

    app.Run(":8080")
}

Forge registers five routes automatically: list, show, create, update, delete. Templates are parsed at startup — missing files cause a fast-fail error, not a runtime 404.

MustConfig validates your configuration at startup. Missing BaseURL or a Secret shorter than 16 bytes panics immediately — never at first request.

Shared partials

Put shared template fragments (nav, footer) in a directory and register them once:

app.Partials("templates/partials")

Every module template and custom handler template parsed via app.MustParseTemplate gets them automatically.

Head and SEO

Declare favicons, fonts, and stylesheets once in main.go via HeadAssets. Forge injects them into every page via {{template "forge:head" .}} — no per-template duplication:

app.SEO(
    &forge.OGDefaults{
        Image:       forge.Image{URL: "https://example.com/og.png"},
        TwitterSite: "@yourhandle",
    },
    &forge.HeadAssets{
        Stylesheets: []string{"/static/app.css"},
        Favicons:    []forge.FaviconLink{{Rel: "icon", Href: "/favicon.ico"}},
    },
)

Next steps

  • [Content Types](/docs/content-types) — struct fields, validation, interfaces
  • [Content Lifecycle](/docs/content-lifecycle) — draft, scheduled, published, archived
  • [Templates and Head](/docs/templates-and-head) — forge:head, HeadAssets, PageHead, ContextFunc
  • [AI Agents via MCP](/docs/mcp) — expose your content to AI agents