
Golang Samber Mo
Model nullable database columns with mo.Option[T] so one Go struct scans SQL rows and serializes JSON without separate DTOs or awkward *string pointers.
Overview
golang-samber-mo is an agent skill for the Build phase that models nullable Go API and database fields with mo.Option[T] instead of pointers or duplicate NullString DTOs.
Install
npx skills add https://github.com/samber/cc-skills-golang --skill golang-samber-moWhat is this skill?
- Recommends mo.Option[string] over *string when the same struct serves DB and JSON
- Documents sql.Scanner and driver.Valuer support for direct row.Scan
- Documents json.Marshaler/Unmarshaler for null vs absent JSON semantics
- Explains why *string forces custom JSON handling to distinguish null from missing
- Avoids parallel sql.NullString DTOs when a single domain type can carry both concerns
- Eval scenario contrasts *string vs mo.Option[string] for nullable phone and bio columns
Adoption & trust: 3.4k installs on skills.sh; 2k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your User struct uses *string for nullable columns and you need one type for SQL scanning and JSON without custom serializers or a second response model.
Who is it for?
Indie Go API authors adding nullable VARCHAR/TEXT columns who already depend on samber/mo and want a single struct for persistence and HTTP.
Skip if: Projects with no JSON exposure, or teams standardized on code-generated protobuf-only models with no hand-written structs.
When should I use this skill?
You are modeling nullable SQL columns in Go structs that must both Scan rows and marshal JSON API responses using samber/mo.
What do I get? / Deliverables
You get struct fields and explanations using mo.Option with correct Scan/Value and JSON null semantics ready for repositories and handlers.
- Updated struct definitions with mo.Option fields
- Explanation of JSON and SQL behavior for nullable fields
Recommended Skills
Journey fit
How it compares
Focused mo.Option patterns for Go persistence and JSON—not a general nullable-types essay or an ORM migration skill.
Common Questions / FAQ
Who is golang-samber-mo for?
Solo builders shipping Go REST services who want nullable DB fields represented cleanly in both sql.Scan and json.Encode paths.
When should I use golang-samber-mo?
During Build backend work when defining or refactoring structs for nullable phone, bio, or similar columns shared between repositories and API responses.
Is golang-samber-mo safe to install?
It is code-guidance only; review Security Audits on this Prism page and validate generated structs against your schema and API contracts.
SKILL.md
READMESKILL.md - Golang Samber Mo
[ { "id": 1, "name": "option-vs-pointer-for-nullable-db-field", "description": "Tests whether the model uses Option[T] instead of *T for nullable database columns when both SQL scanning and JSON marshaling are needed", "prompt": "I'm adding a REST API to an existing Go service. The User table has a nullable 'phone' column (VARCHAR) and a nullable 'bio' column (TEXT). The same User struct is used both for database row scanning and for JSON API responses. Right now I'm using *string for these fields. I have samber/mo available. Is there a better option and what does the struct look like?", "trap": "Without the skill, the model suggests keeping *string (it works for both DB and JSON), or proposes sql.NullString for DB + a separate DTO struct for JSON. The skill teaches that mo.Option[T] natively implements BOTH sql.Scanner/driver.Valuer AND json.Marshaler/Unmarshaler, eliminating the need for two types or custom serialization code.", "assertions": [ {"id": "1.1", "text": "Recommends switching from *string to mo.Option[string] for the nullable fields"}, {"id": "1.2", "text": "Explains that Option implements sql.Scanner and driver.Valuer so row.Scan works directly"}, {"id": "1.3", "text": "Explains that Option implements json.Marshaler/Unmarshaler so the same struct works for JSON responses"}, {"id": "1.4", "text": "Explicitly states that *string requires custom JSON handling to distinguish null vs absent, which Option avoids"}, {"id": "1.5", "text": "Does NOT recommend maintaining two separate structs (one for DB, one for JSON) as the solution"} ] }, { "id": 2, "name": "result-vs-tuple-error-boundary", "description": "Tests whether the model knows when to use Result[T] vs (T, error)", "prompt": "I'm writing a Go function that reads a config file, parses YAML, validates the config, and returns the result. The function is part of a public API package. Should I use samber/mo Result[T] as the return type?", "trap": "Without the skill, the model either always uses Result or always uses (T, error). The correct answer is: use (T, error) at public API boundaries for Go idiom compliance, but use Result internally for chaining.", "assertions": [ {"id": "2.1", "text": "Recommends returning (Config, error) at the public API boundary, not Result[Config]"}, {"id": "2.2", "text": "Suggests using Result[T] internally for chaining the read-parse-validate pipeline"}, {"id": "2.3", "text": "Shows TupleToResult to convert from Go-style to Result at the start of the chain"}, {"id": "2.4", "text": "Shows .Get() or extraction at the end to convert back to (T, error) for the public return"}, {"id": "2.5", "text": "Explains that Result is for internal composition, not public API signatures"} ] }, { "id": 3, "name": "either-vs-result-two-valid-types", "description": "Tests whether the model uses Either[L,R] when both outcomes are valid (not errors)", "prompt": "I have a Go function that looks up a user. It can return either a cached user (from Redis, includes cache metadata) or a fresh user (from the database, no cache metadata). Both outcomes are perfectly valid. What type should the return be?", "trap": "Without the skill, the model uses an interface, two separate return values, or Result. Either[CachedUser, FreshUser] is correct because neither outcome is an error.", "assertions": [ {"id": "3.1", "text": "Uses mo.Either[CachedUser, FreshUser] or equivalent Either type"}, {"id": "3.2", "text": "Does NOT use Result[T] (neither outcome is an error)"}, {"id": "3.3", "text": "Explains that Either is for two valid alternatives, Result is for success/failure"}, {"id": "3.4", "text": "Shows Left/Right constructors for the two outcomes"}, {"id": "3.5", "text": "Shows Match or IsLeft/IsRight to handle both cases"} ] }, { "id": 4, "name": "sub-package-for-type-changing-ma