[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-encore-encore-go-database":3,"mdc-hp66cz-key":40,"related-repo-encore-encore-go-database":1676,"related-org-encore-encore-go-database":1779},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":27,"repoUrl":28,"updatedAt":29,"license":30,"forks":31,"topics":32,"repo":35,"sourceUrl":38,"mdContent":39},"encore-go-database","run database queries and migrations with Encore Go","Work with PostgreSQL in Encore Go using `sqldb.NewDatabase` from `encore.dev\u002Fstorage\u002Fsqldb` — schema migrations and SQL queries.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"encore","Encore","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fencore.png","encoredev",[13,15,18,21,24],{"name":9,"slug":8,"type":14},"tag",{"name":16,"slug":17,"type":14},"Database","database",{"name":19,"slug":20,"type":14},"Migration","migration",{"name":22,"slug":23,"type":14},"SQL","sql",{"name":25,"slug":26,"type":14},"Go","go",26,"https:\u002F\u002Fgithub.com\u002Fencoredev\u002Fskills","2026-04-06T18:09:59.852892",null,5,[33,34],"claude-code","skills",{"repoUrl":28,"stars":27,"forks":31,"topics":36,"description":37},[33,34],"Agent Skills for development with Encore.","https:\u002F\u002Fgithub.com\u002Fencoredev\u002Fskills\u002Ftree\u002FHEAD\u002Fencore\u002Fgo-database","---\nname: encore-go-database\ndescription: Work with PostgreSQL in Encore Go using `sqldb.NewDatabase` from `encore.dev\u002Fstorage\u002Fsqldb` — schema migrations and SQL queries.\nwhen_to_use: >-\n  User wants to add a database table, write a migration, run a SQL query, insert\u002Fupdate\u002Fdelete rows, or design a relational schema in an Encore Go service. Covers `sqldb.NewDatabase`, `db.QueryRow(ctx, ...)`, `db.Query(ctx, ...)`, `db.Exec(ctx, ...)`, `db.Stdlib()` for ORM integration, the `migrations\u002F` directory, `*.up.sql` files, and sequential migration numbering. Trigger phrases: \"Postgres table\", \"user_sessions table\", \"SQL\", \"migration\", \"QueryRow\", \"INSERT\", \"SELECT\", \"schema\", \"sqldb\".\n---\n\n# Encore Go Database Operations\n\n## Instructions\n\n### Database Setup\n\n```go\npackage user\n\nimport \"encore.dev\u002Fstorage\u002Fsqldb\"\n\nvar db = sqldb.NewDatabase(\"userdb\", sqldb.DatabaseConfig{\n    Migrations: \".\u002Fmigrations\",\n})\n```\n\n## Query Methods\n\nEncore's database API mirrors Go's standard `database\u002Fsql` package. Use `.Scan()` to read query results into variables.\n\n### `Query` - Multiple Rows\n\n```go\ntype User struct {\n    ID    string\n    Email string\n    Name  string\n}\n\nfunc listActiveUsers(ctx context.Context) ([]*User, error) {\n    rows, err := db.Query(ctx, `\n        SELECT id, email, name FROM users WHERE active = true\n    `)\n    if err != nil {\n        return nil, err\n    }\n    defer rows.Close()\n\n    var users []*User\n    for rows.Next() {\n        var u User\n        if err := rows.Scan(&u.ID, &u.Email, &u.Name); err != nil {\n            return nil, err\n        }\n        users = append(users, &u)\n    }\n    return users, rows.Err()\n}\n```\n\n### `QueryRow` - Single Row\n\n```go\nfunc getUser(ctx context.Context, id string) (*User, error) {\n    var u User\n    err := db.QueryRow(ctx, `\n        SELECT id, email, name FROM users WHERE id = $1\n    `, id).Scan(&u.ID, &u.Email, &u.Name)\n\n    if errors.Is(err, sqldb.ErrNoRows) {\n        return nil, &errs.Error{\n            Code:    errs.NotFound,\n            Message: \"user not found\",\n        }\n    }\n    if err != nil {\n        return nil, err\n    }\n    return &u, nil\n}\n```\n\n### `Exec` - No Return Value\n\nFor INSERT, UPDATE, DELETE operations:\n\n```go\nfunc createUser(ctx context.Context, email, name string) error {\n    _, err := db.Exec(ctx, `\n        INSERT INTO users (id, email, name)\n        VALUES ($1, $2, $3)\n    `, generateID(), email, name)\n    return err\n}\n\nfunc updateUser(ctx context.Context, id, name string) error {\n    _, err := db.Exec(ctx, `\n        UPDATE users SET name = $1 WHERE id = $2\n    `, name, id)\n    return err\n}\n\nfunc deleteUser(ctx context.Context, id string) error {\n    _, err := db.Exec(ctx, `\n        DELETE FROM users WHERE id = $1\n    `, id)\n    return err\n}\n```\n\n## Migrations\n\n### File Structure\n\n```\nuser\u002F\n└── migrations\u002F\n    ├── 1_create_users.up.sql\n    ├── 2_add_posts.up.sql\n    └── 3_add_indexes.up.sql\n```\n\n### Naming Convention\n\n- Start with a number (1, 2, etc.)\n- Followed by underscore and description\n- End with `.up.sql`\n- Numbers must be sequential\n\n### Example Migration\n\n```sql\n-- migrations\u002F1_create_users.up.sql\nCREATE TABLE users (\n    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n    email TEXT UNIQUE NOT NULL,\n    name TEXT NOT NULL,\n    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()\n);\n\nCREATE INDEX idx_users_email ON users(email);\n```\n\n## Transactions\n\n```go\nfunc transferFunds(ctx context.Context, fromID, toID string, amount int) error {\n    tx, err := db.Begin(ctx)\n    if err != nil {\n        return err\n    }\n    defer tx.Rollback()  \u002F\u002F No-op if committed\n    \n    _, err = tx.Exec(ctx, `\n        UPDATE accounts SET balance = balance - $1 WHERE id = $2\n    `, amount, fromID)\n    if err != nil {\n        return err\n    }\n    \n    _, err = tx.Exec(ctx, `\n        UPDATE accounts SET balance = balance + $1 WHERE id = $2\n    `, amount, toID)\n    if err != nil {\n        return err\n    }\n    \n    return tx.Commit()\n}\n```\n\n## Using Scan\n\nThe `Scan` method reads columns from query results into variables. Columns are mapped by position, not by name - the order of arguments to `Scan` must match the order of columns in your SELECT statement.\n\n```go\ntype User struct {\n    ID        string\n    Email     string\n    Name      string\n    CreatedAt time.Time\n}\n\n\u002F\u002F Single row with QueryRow\nfunc getUser(ctx context.Context, id string) (*User, error) {\n    var u User\n    err := db.QueryRow(ctx, `\n        SELECT id, email, name, created_at FROM users WHERE id = $1\n    `, id).Scan(&u.ID, &u.Email, &u.Name, &u.CreatedAt)\n    if err != nil {\n        return nil, err\n    }\n    return &u, nil\n}\n\n\u002F\u002F You can also scan into an inline struct\nfunc getItem(ctx context.Context, id int64) error {\n    var item struct {\n        ID    int64\n        Title string\n        Done  bool\n    }\n    err := db.QueryRow(ctx, `\n        SELECT id, title, done FROM items WHERE id = $1\n    `, id).Scan(&item.ID, &item.Title, &item.Done)\n    return err\n}\n```\n\n## SQL Injection Protection\n\nAlways use parameterized queries:\n\n```go\n\u002F\u002F SAFE - values are parameterized\nvar u User\nerr := db.QueryRow(ctx, `\n    SELECT id, email, name FROM users WHERE email = $1\n`, email).Scan(&u.ID, &u.Email, &u.Name)\n\n\u002F\u002F WRONG - SQL injection risk\nquery := fmt.Sprintf(\"SELECT * FROM users WHERE email = '%s'\", email)\n```\n\n## Error Handling\n\n```go\nimport (\n    \"errors\"\n    \"encore.dev\u002Fstorage\u002Fsqldb\"\n    \"encore.dev\u002Fbeta\u002Ferrs\"\n)\n\nfunc getUser(ctx context.Context, id string) (*User, error) {\n    var u User\n    err := db.QueryRow(ctx, `\n        SELECT id, email, name FROM users WHERE id = $1\n    `, id).Scan(&u.ID, &u.Email, &u.Name)\n\n    if errors.Is(err, sqldb.ErrNoRows) {\n        return nil, &errs.Error{\n            Code:    errs.NotFound,\n            Message: \"user not found\",\n        }\n    }\n    if err != nil {\n        return nil, err\n    }\n    return &u, nil\n}\n```\n\n## Guidelines\n\n- Always use parameterized queries (`$1`, `$2`, etc.)\n- Use `Scan` to read query results - columns are mapped by position\n- Check for `sqldb.ErrNoRows` when expecting a single row\n- Migrations are applied automatically on startup\n- Database names should be lowercase, descriptive\n- Each service typically has its own database\n- Use transactions for operations that must be atomic\n",{"data":41,"body":43},{"name":4,"description":6,"when_to_use":42},"User wants to add a database table, write a migration, run a SQL query, insert\u002Fupdate\u002Fdelete rows, or design a relational schema in an Encore Go service. Covers `sqldb.NewDatabase`, `db.QueryRow(ctx, ...)`, `db.Query(ctx, ...)`, `db.Exec(ctx, ...)`, `db.Stdlib()` for ORM integration, the `migrations\u002F` directory, `*.up.sql` files, and sequential migration numbering. Trigger phrases: \"Postgres table\", \"user_sessions table\", \"SQL\", \"migration\", \"QueryRow\", \"INSERT\", \"SELECT\", \"schema\", \"sqldb\".",{"type":44,"children":45},"root",[46,55,62,69,144,150,172,184,405,417,553,565,570,736,742,748,758,764,795,801,880,886,1065,1071,1091,1335,1341,1346,1416,1422,1595,1601,1670],{"type":47,"tag":48,"props":49,"children":51},"element","h1",{"id":50},"encore-go-database-operations",[52],{"type":53,"value":54},"text","Encore Go Database Operations",{"type":47,"tag":56,"props":57,"children":59},"h2",{"id":58},"instructions",[60],{"type":53,"value":61},"Instructions",{"type":47,"tag":63,"props":64,"children":66},"h3",{"id":65},"database-setup",[67],{"type":53,"value":68},"Database Setup",{"type":47,"tag":70,"props":71,"children":75},"pre",{"className":72,"code":73,"language":26,"meta":74,"style":74},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","package user\n\nimport \"encore.dev\u002Fstorage\u002Fsqldb\"\n\nvar db = sqldb.NewDatabase(\"userdb\", sqldb.DatabaseConfig{\n    Migrations: \".\u002Fmigrations\",\n})\n","",[76],{"type":47,"tag":77,"props":78,"children":79},"code",{"__ignoreMap":74},[80,91,101,110,118,126,135],{"type":47,"tag":81,"props":82,"children":85},"span",{"class":83,"line":84},"line",1,[86],{"type":47,"tag":81,"props":87,"children":88},{},[89],{"type":53,"value":90},"package user\n",{"type":47,"tag":81,"props":92,"children":94},{"class":83,"line":93},2,[95],{"type":47,"tag":81,"props":96,"children":98},{"emptyLinePlaceholder":97},true,[99],{"type":53,"value":100},"\n",{"type":47,"tag":81,"props":102,"children":104},{"class":83,"line":103},3,[105],{"type":47,"tag":81,"props":106,"children":107},{},[108],{"type":53,"value":109},"import \"encore.dev\u002Fstorage\u002Fsqldb\"\n",{"type":47,"tag":81,"props":111,"children":113},{"class":83,"line":112},4,[114],{"type":47,"tag":81,"props":115,"children":116},{"emptyLinePlaceholder":97},[117],{"type":53,"value":100},{"type":47,"tag":81,"props":119,"children":120},{"class":83,"line":31},[121],{"type":47,"tag":81,"props":122,"children":123},{},[124],{"type":53,"value":125},"var db = sqldb.NewDatabase(\"userdb\", sqldb.DatabaseConfig{\n",{"type":47,"tag":81,"props":127,"children":129},{"class":83,"line":128},6,[130],{"type":47,"tag":81,"props":131,"children":132},{},[133],{"type":53,"value":134},"    Migrations: \".\u002Fmigrations\",\n",{"type":47,"tag":81,"props":136,"children":138},{"class":83,"line":137},7,[139],{"type":47,"tag":81,"props":140,"children":141},{},[142],{"type":53,"value":143},"})\n",{"type":47,"tag":56,"props":145,"children":147},{"id":146},"query-methods",[148],{"type":53,"value":149},"Query Methods",{"type":47,"tag":151,"props":152,"children":153},"p",{},[154,156,162,164,170],{"type":53,"value":155},"Encore's database API mirrors Go's standard ",{"type":47,"tag":77,"props":157,"children":159},{"className":158},[],[160],{"type":53,"value":161},"database\u002Fsql",{"type":53,"value":163}," package. Use ",{"type":47,"tag":77,"props":165,"children":167},{"className":166},[],[168],{"type":53,"value":169},".Scan()",{"type":53,"value":171}," to read query results into variables.",{"type":47,"tag":63,"props":173,"children":175},{"id":174},"query-multiple-rows",[176,182],{"type":47,"tag":77,"props":177,"children":179},{"className":178},[],[180],{"type":53,"value":181},"Query",{"type":53,"value":183}," - Multiple Rows",{"type":47,"tag":70,"props":185,"children":187},{"className":72,"code":186,"language":26,"meta":74,"style":74},"type User struct {\n    ID    string\n    Email string\n    Name  string\n}\n\nfunc listActiveUsers(ctx context.Context) ([]*User, error) {\n    rows, err := db.Query(ctx, `\n        SELECT id, email, name FROM users WHERE active = true\n    `)\n    if err != nil {\n        return nil, err\n    }\n    defer rows.Close()\n\n    var users []*User\n    for rows.Next() {\n        var u User\n        if err := rows.Scan(&u.ID, &u.Email, &u.Name); err != nil {\n            return nil, err\n        }\n        users = append(users, &u)\n    }\n    return users, rows.Err()\n}\n",[188],{"type":47,"tag":77,"props":189,"children":190},{"__ignoreMap":74},[191,199,207,215,223,231,238,246,255,264,273,282,291,300,309,317,326,335,344,353,362,371,380,388,397],{"type":47,"tag":81,"props":192,"children":193},{"class":83,"line":84},[194],{"type":47,"tag":81,"props":195,"children":196},{},[197],{"type":53,"value":198},"type User struct {\n",{"type":47,"tag":81,"props":200,"children":201},{"class":83,"line":93},[202],{"type":47,"tag":81,"props":203,"children":204},{},[205],{"type":53,"value":206},"    ID    string\n",{"type":47,"tag":81,"props":208,"children":209},{"class":83,"line":103},[210],{"type":47,"tag":81,"props":211,"children":212},{},[213],{"type":53,"value":214},"    Email string\n",{"type":47,"tag":81,"props":216,"children":217},{"class":83,"line":112},[218],{"type":47,"tag":81,"props":219,"children":220},{},[221],{"type":53,"value":222},"    Name  string\n",{"type":47,"tag":81,"props":224,"children":225},{"class":83,"line":31},[226],{"type":47,"tag":81,"props":227,"children":228},{},[229],{"type":53,"value":230},"}\n",{"type":47,"tag":81,"props":232,"children":233},{"class":83,"line":128},[234],{"type":47,"tag":81,"props":235,"children":236},{"emptyLinePlaceholder":97},[237],{"type":53,"value":100},{"type":47,"tag":81,"props":239,"children":240},{"class":83,"line":137},[241],{"type":47,"tag":81,"props":242,"children":243},{},[244],{"type":53,"value":245},"func listActiveUsers(ctx context.Context) ([]*User, error) {\n",{"type":47,"tag":81,"props":247,"children":249},{"class":83,"line":248},8,[250],{"type":47,"tag":81,"props":251,"children":252},{},[253],{"type":53,"value":254},"    rows, err := db.Query(ctx, `\n",{"type":47,"tag":81,"props":256,"children":258},{"class":83,"line":257},9,[259],{"type":47,"tag":81,"props":260,"children":261},{},[262],{"type":53,"value":263},"        SELECT id, email, name FROM users WHERE active = true\n",{"type":47,"tag":81,"props":265,"children":267},{"class":83,"line":266},10,[268],{"type":47,"tag":81,"props":269,"children":270},{},[271],{"type":53,"value":272},"    `)\n",{"type":47,"tag":81,"props":274,"children":276},{"class":83,"line":275},11,[277],{"type":47,"tag":81,"props":278,"children":279},{},[280],{"type":53,"value":281},"    if err != nil {\n",{"type":47,"tag":81,"props":283,"children":285},{"class":83,"line":284},12,[286],{"type":47,"tag":81,"props":287,"children":288},{},[289],{"type":53,"value":290},"        return nil, err\n",{"type":47,"tag":81,"props":292,"children":294},{"class":83,"line":293},13,[295],{"type":47,"tag":81,"props":296,"children":297},{},[298],{"type":53,"value":299},"    }\n",{"type":47,"tag":81,"props":301,"children":303},{"class":83,"line":302},14,[304],{"type":47,"tag":81,"props":305,"children":306},{},[307],{"type":53,"value":308},"    defer rows.Close()\n",{"type":47,"tag":81,"props":310,"children":312},{"class":83,"line":311},15,[313],{"type":47,"tag":81,"props":314,"children":315},{"emptyLinePlaceholder":97},[316],{"type":53,"value":100},{"type":47,"tag":81,"props":318,"children":320},{"class":83,"line":319},16,[321],{"type":47,"tag":81,"props":322,"children":323},{},[324],{"type":53,"value":325},"    var users []*User\n",{"type":47,"tag":81,"props":327,"children":329},{"class":83,"line":328},17,[330],{"type":47,"tag":81,"props":331,"children":332},{},[333],{"type":53,"value":334},"    for rows.Next() {\n",{"type":47,"tag":81,"props":336,"children":338},{"class":83,"line":337},18,[339],{"type":47,"tag":81,"props":340,"children":341},{},[342],{"type":53,"value":343},"        var u User\n",{"type":47,"tag":81,"props":345,"children":347},{"class":83,"line":346},19,[348],{"type":47,"tag":81,"props":349,"children":350},{},[351],{"type":53,"value":352},"        if err := rows.Scan(&u.ID, &u.Email, &u.Name); err != nil {\n",{"type":47,"tag":81,"props":354,"children":356},{"class":83,"line":355},20,[357],{"type":47,"tag":81,"props":358,"children":359},{},[360],{"type":53,"value":361},"            return nil, err\n",{"type":47,"tag":81,"props":363,"children":365},{"class":83,"line":364},21,[366],{"type":47,"tag":81,"props":367,"children":368},{},[369],{"type":53,"value":370},"        }\n",{"type":47,"tag":81,"props":372,"children":374},{"class":83,"line":373},22,[375],{"type":47,"tag":81,"props":376,"children":377},{},[378],{"type":53,"value":379},"        users = append(users, &u)\n",{"type":47,"tag":81,"props":381,"children":383},{"class":83,"line":382},23,[384],{"type":47,"tag":81,"props":385,"children":386},{},[387],{"type":53,"value":299},{"type":47,"tag":81,"props":389,"children":391},{"class":83,"line":390},24,[392],{"type":47,"tag":81,"props":393,"children":394},{},[395],{"type":53,"value":396},"    return users, rows.Err()\n",{"type":47,"tag":81,"props":398,"children":400},{"class":83,"line":399},25,[401],{"type":47,"tag":81,"props":402,"children":403},{},[404],{"type":53,"value":230},{"type":47,"tag":63,"props":406,"children":408},{"id":407},"queryrow-single-row",[409,415],{"type":47,"tag":77,"props":410,"children":412},{"className":411},[],[413],{"type":53,"value":414},"QueryRow",{"type":53,"value":416}," - Single Row",{"type":47,"tag":70,"props":418,"children":420},{"className":72,"code":419,"language":26,"meta":74,"style":74},"func getUser(ctx context.Context, id string) (*User, error) {\n    var u User\n    err := db.QueryRow(ctx, `\n        SELECT id, email, name FROM users WHERE id = $1\n    `, id).Scan(&u.ID, &u.Email, &u.Name)\n\n    if errors.Is(err, sqldb.ErrNoRows) {\n        return nil, &errs.Error{\n            Code:    errs.NotFound,\n            Message: \"user not found\",\n        }\n    }\n    if err != nil {\n        return nil, err\n    }\n    return &u, nil\n}\n",[421],{"type":47,"tag":77,"props":422,"children":423},{"__ignoreMap":74},[424,432,440,448,456,464,471,479,487,495,503,510,517,524,531,538,546],{"type":47,"tag":81,"props":425,"children":426},{"class":83,"line":84},[427],{"type":47,"tag":81,"props":428,"children":429},{},[430],{"type":53,"value":431},"func getUser(ctx context.Context, id string) (*User, error) {\n",{"type":47,"tag":81,"props":433,"children":434},{"class":83,"line":93},[435],{"type":47,"tag":81,"props":436,"children":437},{},[438],{"type":53,"value":439},"    var u User\n",{"type":47,"tag":81,"props":441,"children":442},{"class":83,"line":103},[443],{"type":47,"tag":81,"props":444,"children":445},{},[446],{"type":53,"value":447},"    err := db.QueryRow(ctx, `\n",{"type":47,"tag":81,"props":449,"children":450},{"class":83,"line":112},[451],{"type":47,"tag":81,"props":452,"children":453},{},[454],{"type":53,"value":455},"        SELECT id, email, name FROM users WHERE id = $1\n",{"type":47,"tag":81,"props":457,"children":458},{"class":83,"line":31},[459],{"type":47,"tag":81,"props":460,"children":461},{},[462],{"type":53,"value":463},"    `, id).Scan(&u.ID, &u.Email, &u.Name)\n",{"type":47,"tag":81,"props":465,"children":466},{"class":83,"line":128},[467],{"type":47,"tag":81,"props":468,"children":469},{"emptyLinePlaceholder":97},[470],{"type":53,"value":100},{"type":47,"tag":81,"props":472,"children":473},{"class":83,"line":137},[474],{"type":47,"tag":81,"props":475,"children":476},{},[477],{"type":53,"value":478},"    if errors.Is(err, sqldb.ErrNoRows) {\n",{"type":47,"tag":81,"props":480,"children":481},{"class":83,"line":248},[482],{"type":47,"tag":81,"props":483,"children":484},{},[485],{"type":53,"value":486},"        return nil, &errs.Error{\n",{"type":47,"tag":81,"props":488,"children":489},{"class":83,"line":257},[490],{"type":47,"tag":81,"props":491,"children":492},{},[493],{"type":53,"value":494},"            Code:    errs.NotFound,\n",{"type":47,"tag":81,"props":496,"children":497},{"class":83,"line":266},[498],{"type":47,"tag":81,"props":499,"children":500},{},[501],{"type":53,"value":502},"            Message: \"user not found\",\n",{"type":47,"tag":81,"props":504,"children":505},{"class":83,"line":275},[506],{"type":47,"tag":81,"props":507,"children":508},{},[509],{"type":53,"value":370},{"type":47,"tag":81,"props":511,"children":512},{"class":83,"line":284},[513],{"type":47,"tag":81,"props":514,"children":515},{},[516],{"type":53,"value":299},{"type":47,"tag":81,"props":518,"children":519},{"class":83,"line":293},[520],{"type":47,"tag":81,"props":521,"children":522},{},[523],{"type":53,"value":281},{"type":47,"tag":81,"props":525,"children":526},{"class":83,"line":302},[527],{"type":47,"tag":81,"props":528,"children":529},{},[530],{"type":53,"value":290},{"type":47,"tag":81,"props":532,"children":533},{"class":83,"line":311},[534],{"type":47,"tag":81,"props":535,"children":536},{},[537],{"type":53,"value":299},{"type":47,"tag":81,"props":539,"children":540},{"class":83,"line":319},[541],{"type":47,"tag":81,"props":542,"children":543},{},[544],{"type":53,"value":545},"    return &u, nil\n",{"type":47,"tag":81,"props":547,"children":548},{"class":83,"line":328},[549],{"type":47,"tag":81,"props":550,"children":551},{},[552],{"type":53,"value":230},{"type":47,"tag":63,"props":554,"children":556},{"id":555},"exec-no-return-value",[557,563],{"type":47,"tag":77,"props":558,"children":560},{"className":559},[],[561],{"type":53,"value":562},"Exec",{"type":53,"value":564}," - No Return Value",{"type":47,"tag":151,"props":566,"children":567},{},[568],{"type":53,"value":569},"For INSERT, UPDATE, DELETE operations:",{"type":47,"tag":70,"props":571,"children":573},{"className":72,"code":572,"language":26,"meta":74,"style":74},"func createUser(ctx context.Context, email, name string) error {\n    _, err := db.Exec(ctx, `\n        INSERT INTO users (id, email, name)\n        VALUES ($1, $2, $3)\n    `, generateID(), email, name)\n    return err\n}\n\nfunc updateUser(ctx context.Context, id, name string) error {\n    _, err := db.Exec(ctx, `\n        UPDATE users SET name = $1 WHERE id = $2\n    `, name, id)\n    return err\n}\n\nfunc deleteUser(ctx context.Context, id string) error {\n    _, err := db.Exec(ctx, `\n        DELETE FROM users WHERE id = $1\n    `, id)\n    return err\n}\n",[574],{"type":47,"tag":77,"props":575,"children":576},{"__ignoreMap":74},[577,585,593,601,609,617,625,632,639,647,654,662,670,677,684,691,699,706,714,722,729],{"type":47,"tag":81,"props":578,"children":579},{"class":83,"line":84},[580],{"type":47,"tag":81,"props":581,"children":582},{},[583],{"type":53,"value":584},"func createUser(ctx context.Context, email, name string) error {\n",{"type":47,"tag":81,"props":586,"children":587},{"class":83,"line":93},[588],{"type":47,"tag":81,"props":589,"children":590},{},[591],{"type":53,"value":592},"    _, err := db.Exec(ctx, `\n",{"type":47,"tag":81,"props":594,"children":595},{"class":83,"line":103},[596],{"type":47,"tag":81,"props":597,"children":598},{},[599],{"type":53,"value":600},"        INSERT INTO users (id, email, name)\n",{"type":47,"tag":81,"props":602,"children":603},{"class":83,"line":112},[604],{"type":47,"tag":81,"props":605,"children":606},{},[607],{"type":53,"value":608},"        VALUES ($1, $2, $3)\n",{"type":47,"tag":81,"props":610,"children":611},{"class":83,"line":31},[612],{"type":47,"tag":81,"props":613,"children":614},{},[615],{"type":53,"value":616},"    `, generateID(), email, name)\n",{"type":47,"tag":81,"props":618,"children":619},{"class":83,"line":128},[620],{"type":47,"tag":81,"props":621,"children":622},{},[623],{"type":53,"value":624},"    return err\n",{"type":47,"tag":81,"props":626,"children":627},{"class":83,"line":137},[628],{"type":47,"tag":81,"props":629,"children":630},{},[631],{"type":53,"value":230},{"type":47,"tag":81,"props":633,"children":634},{"class":83,"line":248},[635],{"type":47,"tag":81,"props":636,"children":637},{"emptyLinePlaceholder":97},[638],{"type":53,"value":100},{"type":47,"tag":81,"props":640,"children":641},{"class":83,"line":257},[642],{"type":47,"tag":81,"props":643,"children":644},{},[645],{"type":53,"value":646},"func updateUser(ctx context.Context, id, name string) error {\n",{"type":47,"tag":81,"props":648,"children":649},{"class":83,"line":266},[650],{"type":47,"tag":81,"props":651,"children":652},{},[653],{"type":53,"value":592},{"type":47,"tag":81,"props":655,"children":656},{"class":83,"line":275},[657],{"type":47,"tag":81,"props":658,"children":659},{},[660],{"type":53,"value":661},"        UPDATE users SET name = $1 WHERE id = $2\n",{"type":47,"tag":81,"props":663,"children":664},{"class":83,"line":284},[665],{"type":47,"tag":81,"props":666,"children":667},{},[668],{"type":53,"value":669},"    `, name, id)\n",{"type":47,"tag":81,"props":671,"children":672},{"class":83,"line":293},[673],{"type":47,"tag":81,"props":674,"children":675},{},[676],{"type":53,"value":624},{"type":47,"tag":81,"props":678,"children":679},{"class":83,"line":302},[680],{"type":47,"tag":81,"props":681,"children":682},{},[683],{"type":53,"value":230},{"type":47,"tag":81,"props":685,"children":686},{"class":83,"line":311},[687],{"type":47,"tag":81,"props":688,"children":689},{"emptyLinePlaceholder":97},[690],{"type":53,"value":100},{"type":47,"tag":81,"props":692,"children":693},{"class":83,"line":319},[694],{"type":47,"tag":81,"props":695,"children":696},{},[697],{"type":53,"value":698},"func deleteUser(ctx context.Context, id string) error {\n",{"type":47,"tag":81,"props":700,"children":701},{"class":83,"line":328},[702],{"type":47,"tag":81,"props":703,"children":704},{},[705],{"type":53,"value":592},{"type":47,"tag":81,"props":707,"children":708},{"class":83,"line":337},[709],{"type":47,"tag":81,"props":710,"children":711},{},[712],{"type":53,"value":713},"        DELETE FROM users WHERE id = $1\n",{"type":47,"tag":81,"props":715,"children":716},{"class":83,"line":346},[717],{"type":47,"tag":81,"props":718,"children":719},{},[720],{"type":53,"value":721},"    `, id)\n",{"type":47,"tag":81,"props":723,"children":724},{"class":83,"line":355},[725],{"type":47,"tag":81,"props":726,"children":727},{},[728],{"type":53,"value":624},{"type":47,"tag":81,"props":730,"children":731},{"class":83,"line":364},[732],{"type":47,"tag":81,"props":733,"children":734},{},[735],{"type":53,"value":230},{"type":47,"tag":56,"props":737,"children":739},{"id":738},"migrations",[740],{"type":53,"value":741},"Migrations",{"type":47,"tag":63,"props":743,"children":745},{"id":744},"file-structure",[746],{"type":53,"value":747},"File Structure",{"type":47,"tag":70,"props":749,"children":753},{"className":750,"code":752,"language":53},[751],"language-text","user\u002F\n└── migrations\u002F\n    ├── 1_create_users.up.sql\n    ├── 2_add_posts.up.sql\n    └── 3_add_indexes.up.sql\n",[754],{"type":47,"tag":77,"props":755,"children":756},{"__ignoreMap":74},[757],{"type":53,"value":752},{"type":47,"tag":63,"props":759,"children":761},{"id":760},"naming-convention",[762],{"type":53,"value":763},"Naming Convention",{"type":47,"tag":765,"props":766,"children":767},"ul",{},[768,774,779,790],{"type":47,"tag":769,"props":770,"children":771},"li",{},[772],{"type":53,"value":773},"Start with a number (1, 2, etc.)",{"type":47,"tag":769,"props":775,"children":776},{},[777],{"type":53,"value":778},"Followed by underscore and description",{"type":47,"tag":769,"props":780,"children":781},{},[782,784],{"type":53,"value":783},"End with ",{"type":47,"tag":77,"props":785,"children":787},{"className":786},[],[788],{"type":53,"value":789},".up.sql",{"type":47,"tag":769,"props":791,"children":792},{},[793],{"type":53,"value":794},"Numbers must be sequential",{"type":47,"tag":63,"props":796,"children":798},{"id":797},"example-migration",[799],{"type":53,"value":800},"Example Migration",{"type":47,"tag":70,"props":802,"children":805},{"className":803,"code":804,"language":23,"meta":74,"style":74},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","-- migrations\u002F1_create_users.up.sql\nCREATE TABLE users (\n    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n    email TEXT UNIQUE NOT NULL,\n    name TEXT NOT NULL,\n    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()\n);\n\nCREATE INDEX idx_users_email ON users(email);\n",[806],{"type":47,"tag":77,"props":807,"children":808},{"__ignoreMap":74},[809,817,825,833,841,849,857,865,872],{"type":47,"tag":81,"props":810,"children":811},{"class":83,"line":84},[812],{"type":47,"tag":81,"props":813,"children":814},{},[815],{"type":53,"value":816},"-- migrations\u002F1_create_users.up.sql\n",{"type":47,"tag":81,"props":818,"children":819},{"class":83,"line":93},[820],{"type":47,"tag":81,"props":821,"children":822},{},[823],{"type":53,"value":824},"CREATE TABLE users (\n",{"type":47,"tag":81,"props":826,"children":827},{"class":83,"line":103},[828],{"type":47,"tag":81,"props":829,"children":830},{},[831],{"type":53,"value":832},"    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n",{"type":47,"tag":81,"props":834,"children":835},{"class":83,"line":112},[836],{"type":47,"tag":81,"props":837,"children":838},{},[839],{"type":53,"value":840},"    email TEXT UNIQUE NOT NULL,\n",{"type":47,"tag":81,"props":842,"children":843},{"class":83,"line":31},[844],{"type":47,"tag":81,"props":845,"children":846},{},[847],{"type":53,"value":848},"    name TEXT NOT NULL,\n",{"type":47,"tag":81,"props":850,"children":851},{"class":83,"line":128},[852],{"type":47,"tag":81,"props":853,"children":854},{},[855],{"type":53,"value":856},"    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()\n",{"type":47,"tag":81,"props":858,"children":859},{"class":83,"line":137},[860],{"type":47,"tag":81,"props":861,"children":862},{},[863],{"type":53,"value":864},");\n",{"type":47,"tag":81,"props":866,"children":867},{"class":83,"line":248},[868],{"type":47,"tag":81,"props":869,"children":870},{"emptyLinePlaceholder":97},[871],{"type":53,"value":100},{"type":47,"tag":81,"props":873,"children":874},{"class":83,"line":257},[875],{"type":47,"tag":81,"props":876,"children":877},{},[878],{"type":53,"value":879},"CREATE INDEX idx_users_email ON users(email);\n",{"type":47,"tag":56,"props":881,"children":883},{"id":882},"transactions",[884],{"type":53,"value":885},"Transactions",{"type":47,"tag":70,"props":887,"children":889},{"className":72,"code":888,"language":26,"meta":74,"style":74},"func transferFunds(ctx context.Context, fromID, toID string, amount int) error {\n    tx, err := db.Begin(ctx)\n    if err != nil {\n        return err\n    }\n    defer tx.Rollback()  \u002F\u002F No-op if committed\n    \n    _, err = tx.Exec(ctx, `\n        UPDATE accounts SET balance = balance - $1 WHERE id = $2\n    `, amount, fromID)\n    if err != nil {\n        return err\n    }\n    \n    _, err = tx.Exec(ctx, `\n        UPDATE accounts SET balance = balance + $1 WHERE id = $2\n    `, amount, toID)\n    if err != nil {\n        return err\n    }\n    \n    return tx.Commit()\n}\n",[890],{"type":47,"tag":77,"props":891,"children":892},{"__ignoreMap":74},[893,901,909,916,924,931,939,947,955,963,971,978,985,992,999,1006,1014,1022,1029,1036,1043,1050,1058],{"type":47,"tag":81,"props":894,"children":895},{"class":83,"line":84},[896],{"type":47,"tag":81,"props":897,"children":898},{},[899],{"type":53,"value":900},"func transferFunds(ctx context.Context, fromID, toID string, amount int) error {\n",{"type":47,"tag":81,"props":902,"children":903},{"class":83,"line":93},[904],{"type":47,"tag":81,"props":905,"children":906},{},[907],{"type":53,"value":908},"    tx, err := db.Begin(ctx)\n",{"type":47,"tag":81,"props":910,"children":911},{"class":83,"line":103},[912],{"type":47,"tag":81,"props":913,"children":914},{},[915],{"type":53,"value":281},{"type":47,"tag":81,"props":917,"children":918},{"class":83,"line":112},[919],{"type":47,"tag":81,"props":920,"children":921},{},[922],{"type":53,"value":923},"        return err\n",{"type":47,"tag":81,"props":925,"children":926},{"class":83,"line":31},[927],{"type":47,"tag":81,"props":928,"children":929},{},[930],{"type":53,"value":299},{"type":47,"tag":81,"props":932,"children":933},{"class":83,"line":128},[934],{"type":47,"tag":81,"props":935,"children":936},{},[937],{"type":53,"value":938},"    defer tx.Rollback()  \u002F\u002F No-op if committed\n",{"type":47,"tag":81,"props":940,"children":941},{"class":83,"line":137},[942],{"type":47,"tag":81,"props":943,"children":944},{},[945],{"type":53,"value":946},"    \n",{"type":47,"tag":81,"props":948,"children":949},{"class":83,"line":248},[950],{"type":47,"tag":81,"props":951,"children":952},{},[953],{"type":53,"value":954},"    _, err = tx.Exec(ctx, `\n",{"type":47,"tag":81,"props":956,"children":957},{"class":83,"line":257},[958],{"type":47,"tag":81,"props":959,"children":960},{},[961],{"type":53,"value":962},"        UPDATE accounts SET balance = balance - $1 WHERE id = $2\n",{"type":47,"tag":81,"props":964,"children":965},{"class":83,"line":266},[966],{"type":47,"tag":81,"props":967,"children":968},{},[969],{"type":53,"value":970},"    `, amount, fromID)\n",{"type":47,"tag":81,"props":972,"children":973},{"class":83,"line":275},[974],{"type":47,"tag":81,"props":975,"children":976},{},[977],{"type":53,"value":281},{"type":47,"tag":81,"props":979,"children":980},{"class":83,"line":284},[981],{"type":47,"tag":81,"props":982,"children":983},{},[984],{"type":53,"value":923},{"type":47,"tag":81,"props":986,"children":987},{"class":83,"line":293},[988],{"type":47,"tag":81,"props":989,"children":990},{},[991],{"type":53,"value":299},{"type":47,"tag":81,"props":993,"children":994},{"class":83,"line":302},[995],{"type":47,"tag":81,"props":996,"children":997},{},[998],{"type":53,"value":946},{"type":47,"tag":81,"props":1000,"children":1001},{"class":83,"line":311},[1002],{"type":47,"tag":81,"props":1003,"children":1004},{},[1005],{"type":53,"value":954},{"type":47,"tag":81,"props":1007,"children":1008},{"class":83,"line":319},[1009],{"type":47,"tag":81,"props":1010,"children":1011},{},[1012],{"type":53,"value":1013},"        UPDATE accounts SET balance = balance + $1 WHERE id = $2\n",{"type":47,"tag":81,"props":1015,"children":1016},{"class":83,"line":328},[1017],{"type":47,"tag":81,"props":1018,"children":1019},{},[1020],{"type":53,"value":1021},"    `, amount, toID)\n",{"type":47,"tag":81,"props":1023,"children":1024},{"class":83,"line":337},[1025],{"type":47,"tag":81,"props":1026,"children":1027},{},[1028],{"type":53,"value":281},{"type":47,"tag":81,"props":1030,"children":1031},{"class":83,"line":346},[1032],{"type":47,"tag":81,"props":1033,"children":1034},{},[1035],{"type":53,"value":923},{"type":47,"tag":81,"props":1037,"children":1038},{"class":83,"line":355},[1039],{"type":47,"tag":81,"props":1040,"children":1041},{},[1042],{"type":53,"value":299},{"type":47,"tag":81,"props":1044,"children":1045},{"class":83,"line":364},[1046],{"type":47,"tag":81,"props":1047,"children":1048},{},[1049],{"type":53,"value":946},{"type":47,"tag":81,"props":1051,"children":1052},{"class":83,"line":373},[1053],{"type":47,"tag":81,"props":1054,"children":1055},{},[1056],{"type":53,"value":1057},"    return tx.Commit()\n",{"type":47,"tag":81,"props":1059,"children":1060},{"class":83,"line":382},[1061],{"type":47,"tag":81,"props":1062,"children":1063},{},[1064],{"type":53,"value":230},{"type":47,"tag":56,"props":1066,"children":1068},{"id":1067},"using-scan",[1069],{"type":53,"value":1070},"Using Scan",{"type":47,"tag":151,"props":1072,"children":1073},{},[1074,1076,1082,1084,1089],{"type":53,"value":1075},"The ",{"type":47,"tag":77,"props":1077,"children":1079},{"className":1078},[],[1080],{"type":53,"value":1081},"Scan",{"type":53,"value":1083}," method reads columns from query results into variables. Columns are mapped by position, not by name - the order of arguments to ",{"type":47,"tag":77,"props":1085,"children":1087},{"className":1086},[],[1088],{"type":53,"value":1081},{"type":53,"value":1090}," must match the order of columns in your SELECT statement.",{"type":47,"tag":70,"props":1092,"children":1094},{"className":72,"code":1093,"language":26,"meta":74,"style":74},"type User struct {\n    ID        string\n    Email     string\n    Name      string\n    CreatedAt time.Time\n}\n\n\u002F\u002F Single row with QueryRow\nfunc getUser(ctx context.Context, id string) (*User, error) {\n    var u User\n    err := db.QueryRow(ctx, `\n        SELECT id, email, name, created_at FROM users WHERE id = $1\n    `, id).Scan(&u.ID, &u.Email, &u.Name, &u.CreatedAt)\n    if err != nil {\n        return nil, err\n    }\n    return &u, nil\n}\n\n\u002F\u002F You can also scan into an inline struct\nfunc getItem(ctx context.Context, id int64) error {\n    var item struct {\n        ID    int64\n        Title string\n        Done  bool\n    }\n    err := db.QueryRow(ctx, `\n        SELECT id, title, done FROM items WHERE id = $1\n    `, id).Scan(&item.ID, &item.Title, &item.Done)\n    return err\n}\n",[1095],{"type":47,"tag":77,"props":1096,"children":1097},{"__ignoreMap":74},[1098,1105,1113,1121,1129,1137,1144,1151,1159,1166,1173,1180,1188,1196,1203,1210,1217,1224,1231,1238,1246,1254,1262,1270,1278,1286,1293,1301,1310,1319,1327],{"type":47,"tag":81,"props":1099,"children":1100},{"class":83,"line":84},[1101],{"type":47,"tag":81,"props":1102,"children":1103},{},[1104],{"type":53,"value":198},{"type":47,"tag":81,"props":1106,"children":1107},{"class":83,"line":93},[1108],{"type":47,"tag":81,"props":1109,"children":1110},{},[1111],{"type":53,"value":1112},"    ID        string\n",{"type":47,"tag":81,"props":1114,"children":1115},{"class":83,"line":103},[1116],{"type":47,"tag":81,"props":1117,"children":1118},{},[1119],{"type":53,"value":1120},"    Email     string\n",{"type":47,"tag":81,"props":1122,"children":1123},{"class":83,"line":112},[1124],{"type":47,"tag":81,"props":1125,"children":1126},{},[1127],{"type":53,"value":1128},"    Name      string\n",{"type":47,"tag":81,"props":1130,"children":1131},{"class":83,"line":31},[1132],{"type":47,"tag":81,"props":1133,"children":1134},{},[1135],{"type":53,"value":1136},"    CreatedAt time.Time\n",{"type":47,"tag":81,"props":1138,"children":1139},{"class":83,"line":128},[1140],{"type":47,"tag":81,"props":1141,"children":1142},{},[1143],{"type":53,"value":230},{"type":47,"tag":81,"props":1145,"children":1146},{"class":83,"line":137},[1147],{"type":47,"tag":81,"props":1148,"children":1149},{"emptyLinePlaceholder":97},[1150],{"type":53,"value":100},{"type":47,"tag":81,"props":1152,"children":1153},{"class":83,"line":248},[1154],{"type":47,"tag":81,"props":1155,"children":1156},{},[1157],{"type":53,"value":1158},"\u002F\u002F Single row with QueryRow\n",{"type":47,"tag":81,"props":1160,"children":1161},{"class":83,"line":257},[1162],{"type":47,"tag":81,"props":1163,"children":1164},{},[1165],{"type":53,"value":431},{"type":47,"tag":81,"props":1167,"children":1168},{"class":83,"line":266},[1169],{"type":47,"tag":81,"props":1170,"children":1171},{},[1172],{"type":53,"value":439},{"type":47,"tag":81,"props":1174,"children":1175},{"class":83,"line":275},[1176],{"type":47,"tag":81,"props":1177,"children":1178},{},[1179],{"type":53,"value":447},{"type":47,"tag":81,"props":1181,"children":1182},{"class":83,"line":284},[1183],{"type":47,"tag":81,"props":1184,"children":1185},{},[1186],{"type":53,"value":1187},"        SELECT id, email, name, created_at FROM users WHERE id = $1\n",{"type":47,"tag":81,"props":1189,"children":1190},{"class":83,"line":293},[1191],{"type":47,"tag":81,"props":1192,"children":1193},{},[1194],{"type":53,"value":1195},"    `, id).Scan(&u.ID, &u.Email, &u.Name, &u.CreatedAt)\n",{"type":47,"tag":81,"props":1197,"children":1198},{"class":83,"line":302},[1199],{"type":47,"tag":81,"props":1200,"children":1201},{},[1202],{"type":53,"value":281},{"type":47,"tag":81,"props":1204,"children":1205},{"class":83,"line":311},[1206],{"type":47,"tag":81,"props":1207,"children":1208},{},[1209],{"type":53,"value":290},{"type":47,"tag":81,"props":1211,"children":1212},{"class":83,"line":319},[1213],{"type":47,"tag":81,"props":1214,"children":1215},{},[1216],{"type":53,"value":299},{"type":47,"tag":81,"props":1218,"children":1219},{"class":83,"line":328},[1220],{"type":47,"tag":81,"props":1221,"children":1222},{},[1223],{"type":53,"value":545},{"type":47,"tag":81,"props":1225,"children":1226},{"class":83,"line":337},[1227],{"type":47,"tag":81,"props":1228,"children":1229},{},[1230],{"type":53,"value":230},{"type":47,"tag":81,"props":1232,"children":1233},{"class":83,"line":346},[1234],{"type":47,"tag":81,"props":1235,"children":1236},{"emptyLinePlaceholder":97},[1237],{"type":53,"value":100},{"type":47,"tag":81,"props":1239,"children":1240},{"class":83,"line":355},[1241],{"type":47,"tag":81,"props":1242,"children":1243},{},[1244],{"type":53,"value":1245},"\u002F\u002F You can also scan into an inline struct\n",{"type":47,"tag":81,"props":1247,"children":1248},{"class":83,"line":364},[1249],{"type":47,"tag":81,"props":1250,"children":1251},{},[1252],{"type":53,"value":1253},"func getItem(ctx context.Context, id int64) error {\n",{"type":47,"tag":81,"props":1255,"children":1256},{"class":83,"line":373},[1257],{"type":47,"tag":81,"props":1258,"children":1259},{},[1260],{"type":53,"value":1261},"    var item struct {\n",{"type":47,"tag":81,"props":1263,"children":1264},{"class":83,"line":382},[1265],{"type":47,"tag":81,"props":1266,"children":1267},{},[1268],{"type":53,"value":1269},"        ID    int64\n",{"type":47,"tag":81,"props":1271,"children":1272},{"class":83,"line":390},[1273],{"type":47,"tag":81,"props":1274,"children":1275},{},[1276],{"type":53,"value":1277},"        Title string\n",{"type":47,"tag":81,"props":1279,"children":1280},{"class":83,"line":399},[1281],{"type":47,"tag":81,"props":1282,"children":1283},{},[1284],{"type":53,"value":1285},"        Done  bool\n",{"type":47,"tag":81,"props":1287,"children":1288},{"class":83,"line":27},[1289],{"type":47,"tag":81,"props":1290,"children":1291},{},[1292],{"type":53,"value":299},{"type":47,"tag":81,"props":1294,"children":1296},{"class":83,"line":1295},27,[1297],{"type":47,"tag":81,"props":1298,"children":1299},{},[1300],{"type":53,"value":447},{"type":47,"tag":81,"props":1302,"children":1304},{"class":83,"line":1303},28,[1305],{"type":47,"tag":81,"props":1306,"children":1307},{},[1308],{"type":53,"value":1309},"        SELECT id, title, done FROM items WHERE id = $1\n",{"type":47,"tag":81,"props":1311,"children":1313},{"class":83,"line":1312},29,[1314],{"type":47,"tag":81,"props":1315,"children":1316},{},[1317],{"type":53,"value":1318},"    `, id).Scan(&item.ID, &item.Title, &item.Done)\n",{"type":47,"tag":81,"props":1320,"children":1322},{"class":83,"line":1321},30,[1323],{"type":47,"tag":81,"props":1324,"children":1325},{},[1326],{"type":53,"value":624},{"type":47,"tag":81,"props":1328,"children":1330},{"class":83,"line":1329},31,[1331],{"type":47,"tag":81,"props":1332,"children":1333},{},[1334],{"type":53,"value":230},{"type":47,"tag":56,"props":1336,"children":1338},{"id":1337},"sql-injection-protection",[1339],{"type":53,"value":1340},"SQL Injection Protection",{"type":47,"tag":151,"props":1342,"children":1343},{},[1344],{"type":53,"value":1345},"Always use parameterized queries:",{"type":47,"tag":70,"props":1347,"children":1349},{"className":72,"code":1348,"language":26,"meta":74,"style":74},"\u002F\u002F SAFE - values are parameterized\nvar u User\nerr := db.QueryRow(ctx, `\n    SELECT id, email, name FROM users WHERE email = $1\n`, email).Scan(&u.ID, &u.Email, &u.Name)\n\n\u002F\u002F WRONG - SQL injection risk\nquery := fmt.Sprintf(\"SELECT * FROM users WHERE email = '%s'\", email)\n",[1350],{"type":47,"tag":77,"props":1351,"children":1352},{"__ignoreMap":74},[1353,1361,1369,1377,1385,1393,1400,1408],{"type":47,"tag":81,"props":1354,"children":1355},{"class":83,"line":84},[1356],{"type":47,"tag":81,"props":1357,"children":1358},{},[1359],{"type":53,"value":1360},"\u002F\u002F SAFE - values are parameterized\n",{"type":47,"tag":81,"props":1362,"children":1363},{"class":83,"line":93},[1364],{"type":47,"tag":81,"props":1365,"children":1366},{},[1367],{"type":53,"value":1368},"var u User\n",{"type":47,"tag":81,"props":1370,"children":1371},{"class":83,"line":103},[1372],{"type":47,"tag":81,"props":1373,"children":1374},{},[1375],{"type":53,"value":1376},"err := db.QueryRow(ctx, `\n",{"type":47,"tag":81,"props":1378,"children":1379},{"class":83,"line":112},[1380],{"type":47,"tag":81,"props":1381,"children":1382},{},[1383],{"type":53,"value":1384},"    SELECT id, email, name FROM users WHERE email = $1\n",{"type":47,"tag":81,"props":1386,"children":1387},{"class":83,"line":31},[1388],{"type":47,"tag":81,"props":1389,"children":1390},{},[1391],{"type":53,"value":1392},"`, email).Scan(&u.ID, &u.Email, &u.Name)\n",{"type":47,"tag":81,"props":1394,"children":1395},{"class":83,"line":128},[1396],{"type":47,"tag":81,"props":1397,"children":1398},{"emptyLinePlaceholder":97},[1399],{"type":53,"value":100},{"type":47,"tag":81,"props":1401,"children":1402},{"class":83,"line":137},[1403],{"type":47,"tag":81,"props":1404,"children":1405},{},[1406],{"type":53,"value":1407},"\u002F\u002F WRONG - SQL injection risk\n",{"type":47,"tag":81,"props":1409,"children":1410},{"class":83,"line":248},[1411],{"type":47,"tag":81,"props":1412,"children":1413},{},[1414],{"type":53,"value":1415},"query := fmt.Sprintf(\"SELECT * FROM users WHERE email = '%s'\", email)\n",{"type":47,"tag":56,"props":1417,"children":1419},{"id":1418},"error-handling",[1420],{"type":53,"value":1421},"Error Handling",{"type":47,"tag":70,"props":1423,"children":1425},{"className":72,"code":1424,"language":26,"meta":74,"style":74},"import (\n    \"errors\"\n    \"encore.dev\u002Fstorage\u002Fsqldb\"\n    \"encore.dev\u002Fbeta\u002Ferrs\"\n)\n\nfunc getUser(ctx context.Context, id string) (*User, error) {\n    var u User\n    err := db.QueryRow(ctx, `\n        SELECT id, email, name FROM users WHERE id = $1\n    `, id).Scan(&u.ID, &u.Email, &u.Name)\n\n    if errors.Is(err, sqldb.ErrNoRows) {\n        return nil, &errs.Error{\n            Code:    errs.NotFound,\n            Message: \"user not found\",\n        }\n    }\n    if err != nil {\n        return nil, err\n    }\n    return &u, nil\n}\n",[1426],{"type":47,"tag":77,"props":1427,"children":1428},{"__ignoreMap":74},[1429,1437,1445,1453,1461,1469,1476,1483,1490,1497,1504,1511,1518,1525,1532,1539,1546,1553,1560,1567,1574,1581,1588],{"type":47,"tag":81,"props":1430,"children":1431},{"class":83,"line":84},[1432],{"type":47,"tag":81,"props":1433,"children":1434},{},[1435],{"type":53,"value":1436},"import (\n",{"type":47,"tag":81,"props":1438,"children":1439},{"class":83,"line":93},[1440],{"type":47,"tag":81,"props":1441,"children":1442},{},[1443],{"type":53,"value":1444},"    \"errors\"\n",{"type":47,"tag":81,"props":1446,"children":1447},{"class":83,"line":103},[1448],{"type":47,"tag":81,"props":1449,"children":1450},{},[1451],{"type":53,"value":1452},"    \"encore.dev\u002Fstorage\u002Fsqldb\"\n",{"type":47,"tag":81,"props":1454,"children":1455},{"class":83,"line":112},[1456],{"type":47,"tag":81,"props":1457,"children":1458},{},[1459],{"type":53,"value":1460},"    \"encore.dev\u002Fbeta\u002Ferrs\"\n",{"type":47,"tag":81,"props":1462,"children":1463},{"class":83,"line":31},[1464],{"type":47,"tag":81,"props":1465,"children":1466},{},[1467],{"type":53,"value":1468},")\n",{"type":47,"tag":81,"props":1470,"children":1471},{"class":83,"line":128},[1472],{"type":47,"tag":81,"props":1473,"children":1474},{"emptyLinePlaceholder":97},[1475],{"type":53,"value":100},{"type":47,"tag":81,"props":1477,"children":1478},{"class":83,"line":137},[1479],{"type":47,"tag":81,"props":1480,"children":1481},{},[1482],{"type":53,"value":431},{"type":47,"tag":81,"props":1484,"children":1485},{"class":83,"line":248},[1486],{"type":47,"tag":81,"props":1487,"children":1488},{},[1489],{"type":53,"value":439},{"type":47,"tag":81,"props":1491,"children":1492},{"class":83,"line":257},[1493],{"type":47,"tag":81,"props":1494,"children":1495},{},[1496],{"type":53,"value":447},{"type":47,"tag":81,"props":1498,"children":1499},{"class":83,"line":266},[1500],{"type":47,"tag":81,"props":1501,"children":1502},{},[1503],{"type":53,"value":455},{"type":47,"tag":81,"props":1505,"children":1506},{"class":83,"line":275},[1507],{"type":47,"tag":81,"props":1508,"children":1509},{},[1510],{"type":53,"value":463},{"type":47,"tag":81,"props":1512,"children":1513},{"class":83,"line":284},[1514],{"type":47,"tag":81,"props":1515,"children":1516},{"emptyLinePlaceholder":97},[1517],{"type":53,"value":100},{"type":47,"tag":81,"props":1519,"children":1520},{"class":83,"line":293},[1521],{"type":47,"tag":81,"props":1522,"children":1523},{},[1524],{"type":53,"value":478},{"type":47,"tag":81,"props":1526,"children":1527},{"class":83,"line":302},[1528],{"type":47,"tag":81,"props":1529,"children":1530},{},[1531],{"type":53,"value":486},{"type":47,"tag":81,"props":1533,"children":1534},{"class":83,"line":311},[1535],{"type":47,"tag":81,"props":1536,"children":1537},{},[1538],{"type":53,"value":494},{"type":47,"tag":81,"props":1540,"children":1541},{"class":83,"line":319},[1542],{"type":47,"tag":81,"props":1543,"children":1544},{},[1545],{"type":53,"value":502},{"type":47,"tag":81,"props":1547,"children":1548},{"class":83,"line":328},[1549],{"type":47,"tag":81,"props":1550,"children":1551},{},[1552],{"type":53,"value":370},{"type":47,"tag":81,"props":1554,"children":1555},{"class":83,"line":337},[1556],{"type":47,"tag":81,"props":1557,"children":1558},{},[1559],{"type":53,"value":299},{"type":47,"tag":81,"props":1561,"children":1562},{"class":83,"line":346},[1563],{"type":47,"tag":81,"props":1564,"children":1565},{},[1566],{"type":53,"value":281},{"type":47,"tag":81,"props":1568,"children":1569},{"class":83,"line":355},[1570],{"type":47,"tag":81,"props":1571,"children":1572},{},[1573],{"type":53,"value":290},{"type":47,"tag":81,"props":1575,"children":1576},{"class":83,"line":364},[1577],{"type":47,"tag":81,"props":1578,"children":1579},{},[1580],{"type":53,"value":299},{"type":47,"tag":81,"props":1582,"children":1583},{"class":83,"line":373},[1584],{"type":47,"tag":81,"props":1585,"children":1586},{},[1587],{"type":53,"value":545},{"type":47,"tag":81,"props":1589,"children":1590},{"class":83,"line":382},[1591],{"type":47,"tag":81,"props":1592,"children":1593},{},[1594],{"type":53,"value":230},{"type":47,"tag":56,"props":1596,"children":1598},{"id":1597},"guidelines",[1599],{"type":53,"value":1600},"Guidelines",{"type":47,"tag":765,"props":1602,"children":1603},{},[1604,1625,1637,1650,1655,1660,1665],{"type":47,"tag":769,"props":1605,"children":1606},{},[1607,1609,1615,1617,1623],{"type":53,"value":1608},"Always use parameterized queries (",{"type":47,"tag":77,"props":1610,"children":1612},{"className":1611},[],[1613],{"type":53,"value":1614},"$1",{"type":53,"value":1616},", ",{"type":47,"tag":77,"props":1618,"children":1620},{"className":1619},[],[1621],{"type":53,"value":1622},"$2",{"type":53,"value":1624},", etc.)",{"type":47,"tag":769,"props":1626,"children":1627},{},[1628,1630,1635],{"type":53,"value":1629},"Use ",{"type":47,"tag":77,"props":1631,"children":1633},{"className":1632},[],[1634],{"type":53,"value":1081},{"type":53,"value":1636}," to read query results - columns are mapped by position",{"type":47,"tag":769,"props":1638,"children":1639},{},[1640,1642,1648],{"type":53,"value":1641},"Check for ",{"type":47,"tag":77,"props":1643,"children":1645},{"className":1644},[],[1646],{"type":53,"value":1647},"sqldb.ErrNoRows",{"type":53,"value":1649}," when expecting a single row",{"type":47,"tag":769,"props":1651,"children":1652},{},[1653],{"type":53,"value":1654},"Migrations are applied automatically on startup",{"type":47,"tag":769,"props":1656,"children":1657},{},[1658],{"type":53,"value":1659},"Database names should be lowercase, descriptive",{"type":47,"tag":769,"props":1661,"children":1662},{},[1663],{"type":53,"value":1664},"Each service typically has its own database",{"type":47,"tag":769,"props":1666,"children":1667},{},[1668],{"type":53,"value":1669},"Use transactions for operations that must be atomic",{"type":47,"tag":1671,"props":1672,"children":1673},"style",{},[1674],{"type":53,"value":1675},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":1677,"total":1303},[1678,1692,1704,1722,1738,1750,1766],{"slug":1679,"name":1679,"fn":1680,"description":1681,"org":1682,"tags":1683,"stars":27,"repoUrl":28,"updatedAt":1691},"encore-api","build type-safe APIs with Encore","Define typed API endpoints in Encore.ts using `api(...)` from `encore.dev\u002Fapi`. Covers typed request\u002Fresponse interfaces, path\u002Fquery\u002Fheader\u002Fcookie params, request validation, and `APIError`. For raw endpoints (`api.raw()`) and inbound webhooks, use `encore-webhook` instead.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1684,1687,1688],{"name":1685,"slug":1686,"type":14},"API Development","api-development",{"name":9,"slug":8,"type":14},{"name":1689,"slug":1690,"type":14},"TypeScript","typescript","2026-04-06T18:09:46.044101",{"slug":1693,"name":1693,"fn":1694,"description":1695,"org":1696,"tags":1697,"stars":27,"repoUrl":28,"updatedAt":1703},"encore-auth","implement Encore.ts authentication","Protect Encore.ts endpoints with authentication and authorize callers. Covers `authHandler`, `Gateway`, `getAuthData`, and `auth: true`.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1698,1701,1702],{"name":1699,"slug":1700,"type":14},"Auth","auth",{"name":9,"slug":8,"type":14},{"name":1689,"slug":1690,"type":14},"2026-04-06T18:09:47.336322",{"slug":1705,"name":1705,"fn":1706,"description":1707,"org":1708,"tags":1709,"stars":27,"repoUrl":28,"updatedAt":1721},"encore-bucket","store files in Encore.ts buckets","Store unstructured files in Encore.ts using `Bucket` from `encore.dev\u002Fstorage\u002Fobjects` — uploads, images, documents, blobs.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1710,1713,1714,1717,1720],{"name":1711,"slug":1712,"type":14},"Backend","backend",{"name":9,"slug":8,"type":14},{"name":1715,"slug":1716,"type":14},"File Storage","file-storage",{"name":1718,"slug":1719,"type":14},"File Uploads","file-uploads",{"name":1689,"slug":1690,"type":14},"2026-05-16T05:59:52.813772",{"slug":1723,"name":1723,"fn":1724,"description":1725,"org":1726,"tags":1727,"stars":27,"repoUrl":28,"updatedAt":1737},"encore-cache","cache data in Redis with Encore.ts","Cache data in Redis from Encore.ts using `CacheCluster` and typed keyspaces from `encore.dev\u002Fstorage\u002Fcache`. Type-safe key\u002Fvalue access with TTLs, atomic increments, and per-keyspace data shapes.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1728,1729,1732,1733,1736],{"name":1711,"slug":1712,"type":14},{"name":1730,"slug":1731,"type":14},"Caching","caching",{"name":9,"slug":8,"type":14},{"name":1734,"slug":1735,"type":14},"Redis","redis",{"name":1689,"slug":1690,"type":14},"2026-05-16T05:59:56.808328",{"slug":1739,"name":1739,"fn":1740,"description":1741,"org":1742,"tags":1743,"stars":27,"repoUrl":28,"updatedAt":1749},"encore-code-review","review Encore.ts code","Review existing Encore.ts code for best practices and common anti-patterns.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1744,1747,1748],{"name":1745,"slug":1746,"type":14},"Code Review","code-review",{"name":9,"slug":8,"type":14},{"name":1689,"slug":1690,"type":14},"2026-04-06T18:10:01.123999",{"slug":1751,"name":1751,"fn":1752,"description":1753,"org":1754,"tags":1755,"stars":27,"repoUrl":28,"updatedAt":1765},"encore-cron","schedule recurring jobs in Encore.ts","Schedule periodic \u002F recurring work in Encore.ts using `CronJob` from `encore.dev\u002Fcron`. Covers `every: \"1h\"` interval syntax and `schedule: \"0 9 * * 1\"` cron expressions.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1756,1759,1760,1761,1764],{"name":1757,"slug":1758,"type":14},"Automation","automation",{"name":1711,"slug":1712,"type":14},{"name":9,"slug":8,"type":14},{"name":1762,"slug":1763,"type":14},"Scheduling","scheduling",{"name":1689,"slug":1690,"type":14},"2026-05-16T05:59:54.146651",{"slug":1767,"name":1767,"fn":1768,"description":1769,"org":1770,"tags":1771,"stars":27,"repoUrl":28,"updatedAt":1778},"encore-database","build Encore databases","Work with PostgreSQL in Encore.ts using `SQLDatabase` from `encore.dev\u002Fstorage\u002Fsqldb` — schema migrations and SQL queries.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1772,1773,1774,1777],{"name":16,"slug":17,"type":14},{"name":9,"slug":8,"type":14},{"name":1775,"slug":1776,"type":14},"ORM","orm",{"name":1689,"slug":1690,"type":14},"2026-04-06T18:09:54.823017",{"items":1780,"total":1303},[1781,1787,1793,1801,1809,1815,1823,1830,1847,1864,1874,1885],{"slug":1679,"name":1679,"fn":1680,"description":1681,"org":1782,"tags":1783,"stars":27,"repoUrl":28,"updatedAt":1691},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1784,1785,1786],{"name":1685,"slug":1686,"type":14},{"name":9,"slug":8,"type":14},{"name":1689,"slug":1690,"type":14},{"slug":1693,"name":1693,"fn":1694,"description":1695,"org":1788,"tags":1789,"stars":27,"repoUrl":28,"updatedAt":1703},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1790,1791,1792],{"name":1699,"slug":1700,"type":14},{"name":9,"slug":8,"type":14},{"name":1689,"slug":1690,"type":14},{"slug":1705,"name":1705,"fn":1706,"description":1707,"org":1794,"tags":1795,"stars":27,"repoUrl":28,"updatedAt":1721},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1796,1797,1798,1799,1800],{"name":1711,"slug":1712,"type":14},{"name":9,"slug":8,"type":14},{"name":1715,"slug":1716,"type":14},{"name":1718,"slug":1719,"type":14},{"name":1689,"slug":1690,"type":14},{"slug":1723,"name":1723,"fn":1724,"description":1725,"org":1802,"tags":1803,"stars":27,"repoUrl":28,"updatedAt":1737},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1804,1805,1806,1807,1808],{"name":1711,"slug":1712,"type":14},{"name":1730,"slug":1731,"type":14},{"name":9,"slug":8,"type":14},{"name":1734,"slug":1735,"type":14},{"name":1689,"slug":1690,"type":14},{"slug":1739,"name":1739,"fn":1740,"description":1741,"org":1810,"tags":1811,"stars":27,"repoUrl":28,"updatedAt":1749},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1812,1813,1814],{"name":1745,"slug":1746,"type":14},{"name":9,"slug":8,"type":14},{"name":1689,"slug":1690,"type":14},{"slug":1751,"name":1751,"fn":1752,"description":1753,"org":1816,"tags":1817,"stars":27,"repoUrl":28,"updatedAt":1765},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1818,1819,1820,1821,1822],{"name":1757,"slug":1758,"type":14},{"name":1711,"slug":1712,"type":14},{"name":9,"slug":8,"type":14},{"name":1762,"slug":1763,"type":14},{"name":1689,"slug":1690,"type":14},{"slug":1767,"name":1767,"fn":1768,"description":1769,"org":1824,"tags":1825,"stars":27,"repoUrl":28,"updatedAt":1778},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1826,1827,1828,1829],{"name":16,"slug":17,"type":14},{"name":9,"slug":8,"type":14},{"name":1775,"slug":1776,"type":14},{"name":1689,"slug":1690,"type":14},{"slug":1831,"name":1831,"fn":1832,"description":1833,"org":1834,"tags":1835,"stars":27,"repoUrl":28,"updatedAt":1846},"encore-frontend","connect frontend to Encore backend","Connect a frontend application (React, Next.js, Vue, Svelte, etc.) to an Encore.ts backend.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1836,1837,1840,1843],{"name":9,"slug":8,"type":14},{"name":1838,"slug":1839,"type":14},"Frontend","frontend",{"name":1841,"slug":1842,"type":14},"Next.js","next-js",{"name":1844,"slug":1845,"type":14},"React","react","2026-04-06T18:09:56.091006",{"slug":1848,"name":1848,"fn":1849,"description":1850,"org":1851,"tags":1852,"stars":27,"repoUrl":28,"updatedAt":1863},"encore-getting-started","build and run applications with Encore.ts","Bootstrap a brand-new Encore.ts project from zero. Only for first-time CLI install and `encore app create` — not for architecture or feature questions.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1853,1854,1855,1856,1859,1860],{"name":1685,"slug":1686,"type":14},{"name":1711,"slug":1712,"type":14},{"name":9,"slug":8,"type":14},{"name":1857,"slug":1858,"type":14},"Local Development","local-development",{"name":1689,"slug":1690,"type":14},{"name":1861,"slug":1862,"type":14},"Web Development","web-development","2026-04-06T18:10:04.885446",{"slug":1865,"name":1865,"fn":1866,"description":1867,"org":1868,"tags":1869,"stars":27,"repoUrl":28,"updatedAt":1873},"encore-go-api","build APIs with Encore Go","Define typed API endpoints in Encore Go using `\u002F\u002Fencore:api` annotations. Covers typed request\u002Fresponse structs, path\u002Fquery\u002Fheader\u002Fcookie params, and error returns. For raw endpoints (`\u002F\u002Fencore:api raw`) and inbound webhooks, use `encore-go-webhook` instead.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1870,1871,1872],{"name":1685,"slug":1686,"type":14},{"name":9,"slug":8,"type":14},{"name":25,"slug":26,"type":14},"2026-04-06T18:09:48.578781",{"slug":1875,"name":1875,"fn":1876,"description":1877,"org":1878,"tags":1879,"stars":27,"repoUrl":28,"updatedAt":1884},"encore-go-auth","implement authentication with Encore Go","Protect Encore Go endpoints with authentication and authorize callers. Covers `auth.AuthHandler`, `auth.UserID`, the `Authorization` header, and `\u002F\u002Fencore:api auth`.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1880,1881,1882,1883],{"name":1699,"slug":1700,"type":14},{"name":1711,"slug":1712,"type":14},{"name":9,"slug":8,"type":14},{"name":25,"slug":26,"type":14},"2026-04-06T18:09:51.065102",{"slug":1886,"name":1886,"fn":1887,"description":1888,"org":1889,"tags":1890,"stars":27,"repoUrl":28,"updatedAt":1898},"encore-go-bucket","store files in Encore Go buckets","Store unstructured files in Encore Go using `objects.NewBucket` from `encore.dev\u002Fstorage\u002Fobjects` — uploads, images, documents, blobs.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1891,1892,1893,1894,1895],{"name":1711,"slug":1712,"type":14},{"name":9,"slug":8,"type":14},{"name":1715,"slug":1716,"type":14},{"name":25,"slug":26,"type":14},{"name":1896,"slug":1897,"type":14},"Storage","storage","2026-05-16T06:00:03.633918"]