Go
Primate runs Go with WebAssembly compilation, strongly-typed validation, sessions, and server-side routing.
Setup
Install Go
First, install Go 1.27 or later from go.dev.
Install Module
npm install @primate/go
Configure
import config from "primate/config";
import go from "@primate/go";
export default config({
modules: [go()],
});
Initialize Go
Create a go.mod file in your project root:
go mod init your-project-nameAdd the Primate Go dependency:
go get primate.run/go@latestThe primate.run/go vanity metadata directs the Go toolchain to immutable
Repopack release artifacts without changing your global Go environment. The Go
proxy and checksum database continue to cache and verify the module normally.
This command creates go.mod and go.sum files that manage your dependencies.
Routes
Create Go route handlers in routes using .go files. Routes are compiled to
WebAssembly and run in the JavaScript runtime.
// routes/hello.go
package main
import "primate.run/go/route"
var _ = route.Get(func(request route.Request) any {
return "Hello, world!"
})
HTTP Methods
All standard HTTP methods are supported:
package main
import "primate.run/go/route"
var _ = route.Get(func(request route.Request) any {
return "GET request"
})
var _ = route.Post(func(request route.Request) any {
return "POST request"
})
var _ = route.Put(func(request route.Request) any {
return "PUT request"
})
var _ = route.Delete(func(request route.Request) any {
return "DELETE request"
})
Request Handling
Query Parameters
Access query parameters through the Query request bag:
// routes/query.go
package main
import "primate.run/go/route"
var _ = route.Get(func(request route.Request) any {
if request.Query.Has("foo") {
foo, _ := request.Query.Get("foo")
return foo
}
return "foo missing"
})
Request Body
Handle different body types based on content:
JSON Body
// routes/json.go
package main
import (
"primate.run/go/route"
"primate.run/go/types"
)
var _ = route.Post(func(request route.Request) any {
json, err := request.Body.JSON()
if err != nil {
return types.Dict{"error": err.Error()}
}
return json
})
Form Fields
// routes/form.go
package main
import (
"primate.run/go/route"
"primate.run/go/types"
)
var _ = route.Post(func(request route.Request) any {
form, err := request.Body.Form()
if err != nil {
return types.Dict{"error": err.Error()}
}
return form
})
Text Body
// routes/text.go
package main
import "primate.run/go/route"
var _ = route.Post(func(request route.Request) any {
text, _ := request.Body.Text()
return text
})
Binary Data
// routes/blob.go
package main
import (
"primate.run/go/route"
"primate.run/go/types"
)
var _ = route.Post(func(request route.Request) any {
blob, err := request.Body.Blob()
if err != nil {
return types.Dict{"error": err.Error()}
}
head := []int{}
for i := 0; i < len(blob.Data) && i < 4; i++ {
head = append(head, int(blob.Data[i]))
}
return map[string]any{
"type": blob.Type,
"size": len(blob.Data),
"head": head,
}
})
File Uploads
Handle multipart file uploads:
// routes/upload.go
package main
import (
"primate.run/go/route"
"primate.run/go/types"
)
var _ = route.Post(func(request route.Request) any {
// get form fields
form, err := request.Body.Form()
if err != nil {
return types.Dict{"error": err.Error()}
}
// get uploaded files
files, err := request.Body.Files()
if err != nil {
return types.Dict{"error": err.Error()}
}
// process files
for _, file := range files {
// file.Field - form field name
// file.Name - original filename
// file.Type - MIME type
// file.Size - file size in bytes
// file.Bytes - file content
}
return types.Dict{
"form": form,
"files": len(files),
}
})
Responses
Plain Data
Return any Go type that can be JSON serialized:
var _ = route.Get(func(request route.Request) any {
return types.Dict{"name": "Donald"}
})
var _ = route.Get(func(request route.Request) any {
return "Hello, world!"
})
var _ = route.Get(func(request route.Request) any {
return types.Array[types.Dict]{
{"name": "Donald"},
{"name": "Ryan"},
}
})
Views
Render views with props:
// routes/view.go
package main
import (
"primate.run/go/response"
"primate.run/go/route"
"primate.run/go/types"
)
var _ = route.Get(func(request route.Request) any {
return response.View("index.html", types.Dict{"hello": "world"})
})With options:
var _ = route.Get(func(request route.Request) any {
return response.View("index.html",
types.Dict{"hello": "world"},
types.Dict{"partial": true},
)
})
Redirects
Redirect to another route:
// routes/redirect.go
package main
import (
"primate.run/go/response"
"primate.run/go/route"
)
var _ = route.Get(func(request route.Request) any {
return response.Redirect("/redirected")
})With custom status code:
var _ = route.Get(func(request route.Request) any {
return response.Redirect("/redirected", 301) // moved permanently
})
Error Responses
Return error responses:
// routes/error.go
package main
import (
"primate.run/go/response"
"primate.run/go/route"
)
var _ = route.Get(func(request route.Request) any {
return response.Error()
})With custom error options:
var _ = route.Get(func(request route.Request) any {
return response.Error(types.Dict{"body": "Custom error message"})
})
Sessions
Manage user sessions with the session package:
// routes/session.go
package main
import (
"primate.run/go/route"
"primate.run/go/session"
"primate.run/go/types"
)
var _ = route.Get(func(request route.Request) any {
// create a session
s := session.From(request)
if err := s.Create(types.Dict{"foo": "bar"}); err != nil {
panic(err)
}
// get session data
data, err := s.Get()
if err != nil {
panic(err)
}
return data
})
Session Methods
s := session.From(request)- bind the session to this request, including after Go goroutine suspension; do not use a process-global sessions.Create(data)- creates a new session, returning an error on failures.Get()- returns data and an error if the session is absents.Try()- returns data (or nil) and an errors.Set(data)- updates session data, returning an error on failures.Destroy()- destroys the session, returning an error on failures.Exists()- checks if a request-local session ID existss.Id()- reads the current request-local session ID
Types
Primate Go provides convenient type aliases in the core package for common
data structures:
import "primate.run/go/core"
// dictionary (map[string]any)
data := core.Dict{"key": "value", "count": 42}
// generic object with typed values
user := core.Object[string]{"name": "John", "role": "admin"}
// generic array with typed elements
names := core.Array[string]{"John", "Bob", "Charlie"}
// array of dictionaries
users := core.Array[core.Dict]{
{"name": "Donald"},
{"name": "Ryan"},
}
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
| extension | string |
".go" |
Associated file extension |
Example
import go from "@primate/go";
import config from "primate/config";
export default config({
modules: [
go({
// use `.golang` as associated file extension
extension: ".golang",
}),
],
});