[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-encore-encore-go-code-review":3,"mdc--4snbeo-key":34,"related-repo-encore-encore-go-code-review":1517,"related-org-encore-encore-go-code-review":1620},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":21,"repoUrl":22,"updatedAt":23,"license":24,"forks":25,"topics":26,"repo":29,"sourceUrl":32,"mdContent":33},"encore-go-code-review","review Encore Go code","Review existing Encore Go code for best practices and common anti-patterns.",{"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,17,18],{"name":14,"slug":15,"type":16},"Code Review","code-review","tag",{"name":9,"slug":8,"type":16},{"name":19,"slug":20,"type":16},"Go","go",26,"https:\u002F\u002Fgithub.com\u002Fencoredev\u002Fskills","2026-04-06T18:10:03.633271",null,5,[27,28],"claude-code","skills",{"repoUrl":22,"stars":21,"forks":25,"topics":30,"description":31},[27,28],"Agent Skills for development with Encore.","https:\u002F\u002Fgithub.com\u002Fencoredev\u002Fskills\u002Ftree\u002FHEAD\u002Fencore\u002Fgo-code-review","---\nname: encore-go-code-review\ndescription: Review existing Encore Go code for best practices and common anti-patterns.\nwhen_to_use: >-\n  User is reviewing a pull request, auditing existing code, or checking for Encore-specific anti-patterns before merging — infrastructure declared inside functions, missing service files, wrong import paths, raw `errors.New(...)` thrown instead of `errs.B`, untyped APIs, panicking in handlers. SKIP for greenfield code being actively written. Trigger phrases: \"audit\", \"review\", \"before merge\", \"PR review\", \"anti-patterns\", \"code smell\", \"lint this\".\n---\n\n# Encore Go Code Review\n\n## Instructions\n\nWhen reviewing Encore Go code, check for these common issues:\n\n## Critical Issues\n\n### 1. Infrastructure Inside Functions\n\n```go\n\u002F\u002F WRONG: Infrastructure declared inside function\nfunc setup() {\n    db := sqldb.NewDatabase(\"mydb\", sqldb.DatabaseConfig{...})\n    topic := pubsub.NewTopic[*Event](\"events\", pubsub.TopicConfig{...})\n}\n\n\u002F\u002F CORRECT: Package level declaration\nvar db = sqldb.NewDatabase(\"mydb\", sqldb.DatabaseConfig{\n    Migrations: \".\u002Fmigrations\",\n})\n\nvar topic = pubsub.NewTopic[*Event](\"events\", pubsub.TopicConfig{\n    DeliveryGuarantee: pubsub.AtLeastOnce,\n})\n```\n\n### 2. Missing Context Parameter\n\n```go\n\u002F\u002F WRONG: Missing context\n\u002F\u002Fencore:api public method=GET path=\u002Fusers\u002F:id\nfunc GetUser(params *GetUserParams) (*User, error) {\n    \u002F\u002F ...\n}\n\n\u002F\u002F CORRECT: Context as first parameter\n\u002F\u002Fencore:api public method=GET path=\u002Fusers\u002F:id\nfunc GetUser(ctx context.Context, params *GetUserParams) (*User, error) {\n    \u002F\u002F ...\n}\n```\n\n### 3. SQL Injection Risk\n\n```go\n\u002F\u002F WRONG: String interpolation\nquery := fmt.Sprintf(\"SELECT * FROM users WHERE email = '%s'\", email)\nrows, err := db.Query(ctx, query)\n\n\u002F\u002F CORRECT: Parameterized query\nrows, err := sqldb.Query[User](ctx, db, `\n    SELECT * FROM users WHERE email = $1\n`, email)\n```\n\n### 4. Wrong Return Types\n\n```go\n\u002F\u002F WRONG: Returning non-pointer struct\n\u002F\u002Fencore:api public method=GET path=\u002Fusers\u002F:id\nfunc GetUser(ctx context.Context, params *GetUserParams) (User, error) {\n    \u002F\u002F ...\n}\n\n\u002F\u002F CORRECT: Return pointer to struct\n\u002F\u002Fencore:api public method=GET path=\u002Fusers\u002F:id\nfunc GetUser(ctx context.Context, params *GetUserParams) (*User, error) {\n    \u002F\u002F ...\n}\n```\n\n### 5. Ignoring Errors\n\n```go\n\u002F\u002F WRONG: Ignoring error\nuser, _ := sqldb.QueryRow[User](ctx, db, query, id)\n\n\u002F\u002F CORRECT: Handle error\nuser, err := sqldb.QueryRow[User](ctx, db, query, id)\nif err != nil {\n    return nil, err\n}\n```\n\n## Warning Issues\n\n### 6. Not Checking for ErrNoRows\n\n```go\n\u002F\u002F RISKY: Returns nil without proper error\nfunc getUser(ctx context.Context, id string) (*User, error) {\n    user, err := sqldb.QueryRow[User](ctx, db, `\n        SELECT * FROM users WHERE id = $1\n    `, id)\n    if err != nil {\n        return nil, err  \u002F\u002F ErrNoRows returns generic error\n    }\n    return user, nil\n}\n\n\u002F\u002F BETTER: Check for not found specifically\nimport \"errors\"\n\nfunc getUser(ctx context.Context, id string) (*User, error) {\n    user, err := sqldb.QueryRow[User](ctx, db, `\n        SELECT * FROM users WHERE id = $1\n    `, id)\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 user, nil\n}\n```\n\n### 7. Public Internal Endpoints\n\n```go\n\u002F\u002F CHECK: Should this cron endpoint be public?\n\u002F\u002Fencore:api public method=POST path=\u002Finternal\u002Fcleanup\nfunc CleanupJob(ctx context.Context) error {\n    \u002F\u002F ...\n}\n\n\u002F\u002F BETTER: Use private for internal endpoints\n\u002F\u002Fencore:api private\nfunc CleanupJob(ctx context.Context) error {\n    \u002F\u002F ...\n}\n```\n\n### 8. Non-Idempotent Subscription Handlers\n\n```go\n\u002F\u002F RISKY: Not idempotent (pubsub has at-least-once delivery)\nvar _ = pubsub.NewSubscription(OrderCreated, \"process-order\",\n    pubsub.SubscriptionConfig[*OrderCreatedEvent]{\n        Handler: func(ctx context.Context, event *OrderCreatedEvent) error {\n            return chargeCustomer(ctx, event.OrderID)  \u002F\u002F Could charge twice!\n        },\n    },\n)\n\n\u002F\u002F SAFER: Check before processing\nvar _ = pubsub.NewSubscription(OrderCreated, \"process-order\",\n    pubsub.SubscriptionConfig[*OrderCreatedEvent]{\n        Handler: func(ctx context.Context, event *OrderCreatedEvent) error {\n            order, err := getOrder(ctx, event.OrderID)\n            if err != nil {\n                return err\n            }\n            if order.Status != \"pending\" {\n                return nil  \u002F\u002F Already processed\n            }\n            return chargeCustomer(ctx, event.OrderID)\n        },\n    },\n)\n```\n\n### 9. Not Closing Query Rows\n\n```go\n\u002F\u002F WRONG: Rows not closed\nfunc listUsers(ctx context.Context) ([]*User, error) {\n    rows, err := sqldb.Query[User](ctx, db, `SELECT * FROM users`)\n    if err != nil {\n        return nil, err\n    }\n    \u002F\u002F Missing: defer rows.Close()\n    \n    var users []*User\n    for rows.Next() {\n        users = append(users, rows.Value())\n    }\n    return users, nil\n}\n\n\u002F\u002F CORRECT: Always close rows\nfunc listUsers(ctx context.Context) ([]*User, error) {\n    rows, err := sqldb.Query[User](ctx, db, `SELECT * FROM users`)\n    if err != nil {\n        return nil, err\n    }\n    defer rows.Close()\n    \n    var users []*User\n    for rows.Next() {\n        users = append(users, rows.Value())\n    }\n    return users, rows.Err()\n}\n```\n\n## Review Checklist\n\n- [ ] All infrastructure at package level\n- [ ] All API endpoints have `context.Context` as first parameter\n- [ ] SQL uses parameterized queries (`$1`, `$2`, etc.)\n- [ ] Response types are pointers\n- [ ] Errors are handled, not ignored\n- [ ] `sqldb.ErrNoRows` checked where appropriate\n- [ ] Internal endpoints use `private` not `public`\n- [ ] Subscription handlers are idempotent\n- [ ] Query rows are closed with `defer rows.Close()`\n- [ ] Migrations follow naming convention (`1_name.up.sql`)\n\n## Output Format\n\nWhen reviewing, report issues as:\n\n```\n[CRITICAL] [file:line] Description of issue\n[WARNING] [file:line] Description of concern  \n[GOOD] Notable good practice observed\n```\n",{"data":35,"body":37},{"name":4,"description":6,"when_to_use":36},"User is reviewing a pull request, auditing existing code, or checking for Encore-specific anti-patterns before merging — infrastructure declared inside functions, missing service files, wrong import paths, raw `errors.New(...)` thrown instead of `errs.B`, untyped APIs, panicking in handlers. SKIP for greenfield code being actively written. Trigger phrases: \"audit\", \"review\", \"before merge\", \"PR review\", \"anti-patterns\", \"code smell\", \"lint this\".",{"type":38,"children":39},"root",[40,48,55,61,67,74,211,217,307,313,383,389,476,482,551,557,563,804,810,899,905,1096,1102,1324,1330,1490,1496,1501,1511],{"type":41,"tag":42,"props":43,"children":44},"element","h1",{"id":4},[45],{"type":46,"value":47},"text","Encore Go Code Review",{"type":41,"tag":49,"props":50,"children":52},"h2",{"id":51},"instructions",[53],{"type":46,"value":54},"Instructions",{"type":41,"tag":56,"props":57,"children":58},"p",{},[59],{"type":46,"value":60},"When reviewing Encore Go code, check for these common issues:",{"type":41,"tag":49,"props":62,"children":64},{"id":63},"critical-issues",[65],{"type":46,"value":66},"Critical Issues",{"type":41,"tag":68,"props":69,"children":71},"h3",{"id":70},"_1-infrastructure-inside-functions",[72],{"type":46,"value":73},"1. Infrastructure Inside Functions",{"type":41,"tag":75,"props":76,"children":80},"pre",{"className":77,"code":78,"language":20,"meta":79,"style":79},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F WRONG: Infrastructure declared inside function\nfunc setup() {\n    db := sqldb.NewDatabase(\"mydb\", sqldb.DatabaseConfig{...})\n    topic := pubsub.NewTopic[*Event](\"events\", pubsub.TopicConfig{...})\n}\n\n\u002F\u002F CORRECT: Package level declaration\nvar db = sqldb.NewDatabase(\"mydb\", sqldb.DatabaseConfig{\n    Migrations: \".\u002Fmigrations\",\n})\n\nvar topic = pubsub.NewTopic[*Event](\"events\", pubsub.TopicConfig{\n    DeliveryGuarantee: pubsub.AtLeastOnce,\n})\n","",[81],{"type":41,"tag":82,"props":83,"children":84},"code",{"__ignoreMap":79},[85,96,105,114,123,131,141,150,159,168,177,185,194,203],{"type":41,"tag":86,"props":87,"children":90},"span",{"class":88,"line":89},"line",1,[91],{"type":41,"tag":86,"props":92,"children":93},{},[94],{"type":46,"value":95},"\u002F\u002F WRONG: Infrastructure declared inside function\n",{"type":41,"tag":86,"props":97,"children":99},{"class":88,"line":98},2,[100],{"type":41,"tag":86,"props":101,"children":102},{},[103],{"type":46,"value":104},"func setup() {\n",{"type":41,"tag":86,"props":106,"children":108},{"class":88,"line":107},3,[109],{"type":41,"tag":86,"props":110,"children":111},{},[112],{"type":46,"value":113},"    db := sqldb.NewDatabase(\"mydb\", sqldb.DatabaseConfig{...})\n",{"type":41,"tag":86,"props":115,"children":117},{"class":88,"line":116},4,[118],{"type":41,"tag":86,"props":119,"children":120},{},[121],{"type":46,"value":122},"    topic := pubsub.NewTopic[*Event](\"events\", pubsub.TopicConfig{...})\n",{"type":41,"tag":86,"props":124,"children":125},{"class":88,"line":25},[126],{"type":41,"tag":86,"props":127,"children":128},{},[129],{"type":46,"value":130},"}\n",{"type":41,"tag":86,"props":132,"children":134},{"class":88,"line":133},6,[135],{"type":41,"tag":86,"props":136,"children":138},{"emptyLinePlaceholder":137},true,[139],{"type":46,"value":140},"\n",{"type":41,"tag":86,"props":142,"children":144},{"class":88,"line":143},7,[145],{"type":41,"tag":86,"props":146,"children":147},{},[148],{"type":46,"value":149},"\u002F\u002F CORRECT: Package level declaration\n",{"type":41,"tag":86,"props":151,"children":153},{"class":88,"line":152},8,[154],{"type":41,"tag":86,"props":155,"children":156},{},[157],{"type":46,"value":158},"var db = sqldb.NewDatabase(\"mydb\", sqldb.DatabaseConfig{\n",{"type":41,"tag":86,"props":160,"children":162},{"class":88,"line":161},9,[163],{"type":41,"tag":86,"props":164,"children":165},{},[166],{"type":46,"value":167},"    Migrations: \".\u002Fmigrations\",\n",{"type":41,"tag":86,"props":169,"children":171},{"class":88,"line":170},10,[172],{"type":41,"tag":86,"props":173,"children":174},{},[175],{"type":46,"value":176},"})\n",{"type":41,"tag":86,"props":178,"children":180},{"class":88,"line":179},11,[181],{"type":41,"tag":86,"props":182,"children":183},{"emptyLinePlaceholder":137},[184],{"type":46,"value":140},{"type":41,"tag":86,"props":186,"children":188},{"class":88,"line":187},12,[189],{"type":41,"tag":86,"props":190,"children":191},{},[192],{"type":46,"value":193},"var topic = pubsub.NewTopic[*Event](\"events\", pubsub.TopicConfig{\n",{"type":41,"tag":86,"props":195,"children":197},{"class":88,"line":196},13,[198],{"type":41,"tag":86,"props":199,"children":200},{},[201],{"type":46,"value":202},"    DeliveryGuarantee: pubsub.AtLeastOnce,\n",{"type":41,"tag":86,"props":204,"children":206},{"class":88,"line":205},14,[207],{"type":41,"tag":86,"props":208,"children":209},{},[210],{"type":46,"value":176},{"type":41,"tag":68,"props":212,"children":214},{"id":213},"_2-missing-context-parameter",[215],{"type":46,"value":216},"2. Missing Context Parameter",{"type":41,"tag":75,"props":218,"children":220},{"className":77,"code":219,"language":20,"meta":79,"style":79},"\u002F\u002F WRONG: Missing context\n\u002F\u002Fencore:api public method=GET path=\u002Fusers\u002F:id\nfunc GetUser(params *GetUserParams) (*User, error) {\n    \u002F\u002F ...\n}\n\n\u002F\u002F CORRECT: Context as first parameter\n\u002F\u002Fencore:api public method=GET path=\u002Fusers\u002F:id\nfunc GetUser(ctx context.Context, params *GetUserParams) (*User, error) {\n    \u002F\u002F ...\n}\n",[221],{"type":41,"tag":82,"props":222,"children":223},{"__ignoreMap":79},[224,232,240,248,256,263,270,278,285,293,300],{"type":41,"tag":86,"props":225,"children":226},{"class":88,"line":89},[227],{"type":41,"tag":86,"props":228,"children":229},{},[230],{"type":46,"value":231},"\u002F\u002F WRONG: Missing context\n",{"type":41,"tag":86,"props":233,"children":234},{"class":88,"line":98},[235],{"type":41,"tag":86,"props":236,"children":237},{},[238],{"type":46,"value":239},"\u002F\u002Fencore:api public method=GET path=\u002Fusers\u002F:id\n",{"type":41,"tag":86,"props":241,"children":242},{"class":88,"line":107},[243],{"type":41,"tag":86,"props":244,"children":245},{},[246],{"type":46,"value":247},"func GetUser(params *GetUserParams) (*User, error) {\n",{"type":41,"tag":86,"props":249,"children":250},{"class":88,"line":116},[251],{"type":41,"tag":86,"props":252,"children":253},{},[254],{"type":46,"value":255},"    \u002F\u002F ...\n",{"type":41,"tag":86,"props":257,"children":258},{"class":88,"line":25},[259],{"type":41,"tag":86,"props":260,"children":261},{},[262],{"type":46,"value":130},{"type":41,"tag":86,"props":264,"children":265},{"class":88,"line":133},[266],{"type":41,"tag":86,"props":267,"children":268},{"emptyLinePlaceholder":137},[269],{"type":46,"value":140},{"type":41,"tag":86,"props":271,"children":272},{"class":88,"line":143},[273],{"type":41,"tag":86,"props":274,"children":275},{},[276],{"type":46,"value":277},"\u002F\u002F CORRECT: Context as first parameter\n",{"type":41,"tag":86,"props":279,"children":280},{"class":88,"line":152},[281],{"type":41,"tag":86,"props":282,"children":283},{},[284],{"type":46,"value":239},{"type":41,"tag":86,"props":286,"children":287},{"class":88,"line":161},[288],{"type":41,"tag":86,"props":289,"children":290},{},[291],{"type":46,"value":292},"func GetUser(ctx context.Context, params *GetUserParams) (*User, error) {\n",{"type":41,"tag":86,"props":294,"children":295},{"class":88,"line":170},[296],{"type":41,"tag":86,"props":297,"children":298},{},[299],{"type":46,"value":255},{"type":41,"tag":86,"props":301,"children":302},{"class":88,"line":179},[303],{"type":41,"tag":86,"props":304,"children":305},{},[306],{"type":46,"value":130},{"type":41,"tag":68,"props":308,"children":310},{"id":309},"_3-sql-injection-risk",[311],{"type":46,"value":312},"3. SQL Injection Risk",{"type":41,"tag":75,"props":314,"children":316},{"className":77,"code":315,"language":20,"meta":79,"style":79},"\u002F\u002F WRONG: String interpolation\nquery := fmt.Sprintf(\"SELECT * FROM users WHERE email = '%s'\", email)\nrows, err := db.Query(ctx, query)\n\n\u002F\u002F CORRECT: Parameterized query\nrows, err := sqldb.Query[User](ctx, db, `\n    SELECT * FROM users WHERE email = $1\n`, email)\n",[317],{"type":41,"tag":82,"props":318,"children":319},{"__ignoreMap":79},[320,328,336,344,351,359,367,375],{"type":41,"tag":86,"props":321,"children":322},{"class":88,"line":89},[323],{"type":41,"tag":86,"props":324,"children":325},{},[326],{"type":46,"value":327},"\u002F\u002F WRONG: String interpolation\n",{"type":41,"tag":86,"props":329,"children":330},{"class":88,"line":98},[331],{"type":41,"tag":86,"props":332,"children":333},{},[334],{"type":46,"value":335},"query := fmt.Sprintf(\"SELECT * FROM users WHERE email = '%s'\", email)\n",{"type":41,"tag":86,"props":337,"children":338},{"class":88,"line":107},[339],{"type":41,"tag":86,"props":340,"children":341},{},[342],{"type":46,"value":343},"rows, err := db.Query(ctx, query)\n",{"type":41,"tag":86,"props":345,"children":346},{"class":88,"line":116},[347],{"type":41,"tag":86,"props":348,"children":349},{"emptyLinePlaceholder":137},[350],{"type":46,"value":140},{"type":41,"tag":86,"props":352,"children":353},{"class":88,"line":25},[354],{"type":41,"tag":86,"props":355,"children":356},{},[357],{"type":46,"value":358},"\u002F\u002F CORRECT: Parameterized query\n",{"type":41,"tag":86,"props":360,"children":361},{"class":88,"line":133},[362],{"type":41,"tag":86,"props":363,"children":364},{},[365],{"type":46,"value":366},"rows, err := sqldb.Query[User](ctx, db, `\n",{"type":41,"tag":86,"props":368,"children":369},{"class":88,"line":143},[370],{"type":41,"tag":86,"props":371,"children":372},{},[373],{"type":46,"value":374},"    SELECT * FROM users WHERE email = $1\n",{"type":41,"tag":86,"props":376,"children":377},{"class":88,"line":152},[378],{"type":41,"tag":86,"props":379,"children":380},{},[381],{"type":46,"value":382},"`, email)\n",{"type":41,"tag":68,"props":384,"children":386},{"id":385},"_4-wrong-return-types",[387],{"type":46,"value":388},"4. Wrong Return Types",{"type":41,"tag":75,"props":390,"children":392},{"className":77,"code":391,"language":20,"meta":79,"style":79},"\u002F\u002F WRONG: Returning non-pointer struct\n\u002F\u002Fencore:api public method=GET path=\u002Fusers\u002F:id\nfunc GetUser(ctx context.Context, params *GetUserParams) (User, error) {\n    \u002F\u002F ...\n}\n\n\u002F\u002F CORRECT: Return pointer to struct\n\u002F\u002Fencore:api public method=GET path=\u002Fusers\u002F:id\nfunc GetUser(ctx context.Context, params *GetUserParams) (*User, error) {\n    \u002F\u002F ...\n}\n",[393],{"type":41,"tag":82,"props":394,"children":395},{"__ignoreMap":79},[396,404,411,419,426,433,440,448,455,462,469],{"type":41,"tag":86,"props":397,"children":398},{"class":88,"line":89},[399],{"type":41,"tag":86,"props":400,"children":401},{},[402],{"type":46,"value":403},"\u002F\u002F WRONG: Returning non-pointer struct\n",{"type":41,"tag":86,"props":405,"children":406},{"class":88,"line":98},[407],{"type":41,"tag":86,"props":408,"children":409},{},[410],{"type":46,"value":239},{"type":41,"tag":86,"props":412,"children":413},{"class":88,"line":107},[414],{"type":41,"tag":86,"props":415,"children":416},{},[417],{"type":46,"value":418},"func GetUser(ctx context.Context, params *GetUserParams) (User, error) {\n",{"type":41,"tag":86,"props":420,"children":421},{"class":88,"line":116},[422],{"type":41,"tag":86,"props":423,"children":424},{},[425],{"type":46,"value":255},{"type":41,"tag":86,"props":427,"children":428},{"class":88,"line":25},[429],{"type":41,"tag":86,"props":430,"children":431},{},[432],{"type":46,"value":130},{"type":41,"tag":86,"props":434,"children":435},{"class":88,"line":133},[436],{"type":41,"tag":86,"props":437,"children":438},{"emptyLinePlaceholder":137},[439],{"type":46,"value":140},{"type":41,"tag":86,"props":441,"children":442},{"class":88,"line":143},[443],{"type":41,"tag":86,"props":444,"children":445},{},[446],{"type":46,"value":447},"\u002F\u002F CORRECT: Return pointer to struct\n",{"type":41,"tag":86,"props":449,"children":450},{"class":88,"line":152},[451],{"type":41,"tag":86,"props":452,"children":453},{},[454],{"type":46,"value":239},{"type":41,"tag":86,"props":456,"children":457},{"class":88,"line":161},[458],{"type":41,"tag":86,"props":459,"children":460},{},[461],{"type":46,"value":292},{"type":41,"tag":86,"props":463,"children":464},{"class":88,"line":170},[465],{"type":41,"tag":86,"props":466,"children":467},{},[468],{"type":46,"value":255},{"type":41,"tag":86,"props":470,"children":471},{"class":88,"line":179},[472],{"type":41,"tag":86,"props":473,"children":474},{},[475],{"type":46,"value":130},{"type":41,"tag":68,"props":477,"children":479},{"id":478},"_5-ignoring-errors",[480],{"type":46,"value":481},"5. Ignoring Errors",{"type":41,"tag":75,"props":483,"children":485},{"className":77,"code":484,"language":20,"meta":79,"style":79},"\u002F\u002F WRONG: Ignoring error\nuser, _ := sqldb.QueryRow[User](ctx, db, query, id)\n\n\u002F\u002F CORRECT: Handle error\nuser, err := sqldb.QueryRow[User](ctx, db, query, id)\nif err != nil {\n    return nil, err\n}\n",[486],{"type":41,"tag":82,"props":487,"children":488},{"__ignoreMap":79},[489,497,505,512,520,528,536,544],{"type":41,"tag":86,"props":490,"children":491},{"class":88,"line":89},[492],{"type":41,"tag":86,"props":493,"children":494},{},[495],{"type":46,"value":496},"\u002F\u002F WRONG: Ignoring error\n",{"type":41,"tag":86,"props":498,"children":499},{"class":88,"line":98},[500],{"type":41,"tag":86,"props":501,"children":502},{},[503],{"type":46,"value":504},"user, _ := sqldb.QueryRow[User](ctx, db, query, id)\n",{"type":41,"tag":86,"props":506,"children":507},{"class":88,"line":107},[508],{"type":41,"tag":86,"props":509,"children":510},{"emptyLinePlaceholder":137},[511],{"type":46,"value":140},{"type":41,"tag":86,"props":513,"children":514},{"class":88,"line":116},[515],{"type":41,"tag":86,"props":516,"children":517},{},[518],{"type":46,"value":519},"\u002F\u002F CORRECT: Handle error\n",{"type":41,"tag":86,"props":521,"children":522},{"class":88,"line":25},[523],{"type":41,"tag":86,"props":524,"children":525},{},[526],{"type":46,"value":527},"user, err := sqldb.QueryRow[User](ctx, db, query, id)\n",{"type":41,"tag":86,"props":529,"children":530},{"class":88,"line":133},[531],{"type":41,"tag":86,"props":532,"children":533},{},[534],{"type":46,"value":535},"if err != nil {\n",{"type":41,"tag":86,"props":537,"children":538},{"class":88,"line":143},[539],{"type":41,"tag":86,"props":540,"children":541},{},[542],{"type":46,"value":543},"    return nil, err\n",{"type":41,"tag":86,"props":545,"children":546},{"class":88,"line":152},[547],{"type":41,"tag":86,"props":548,"children":549},{},[550],{"type":46,"value":130},{"type":41,"tag":49,"props":552,"children":554},{"id":553},"warning-issues",[555],{"type":46,"value":556},"Warning Issues",{"type":41,"tag":68,"props":558,"children":560},{"id":559},"_6-not-checking-for-errnorows",[561],{"type":46,"value":562},"6. Not Checking for ErrNoRows",{"type":41,"tag":75,"props":564,"children":566},{"className":77,"code":565,"language":20,"meta":79,"style":79},"\u002F\u002F RISKY: Returns nil without proper error\nfunc getUser(ctx context.Context, id string) (*User, error) {\n    user, err := sqldb.QueryRow[User](ctx, db, `\n        SELECT * FROM users WHERE id = $1\n    `, id)\n    if err != nil {\n        return nil, err  \u002F\u002F ErrNoRows returns generic error\n    }\n    return user, nil\n}\n\n\u002F\u002F BETTER: Check for not found specifically\nimport \"errors\"\n\nfunc getUser(ctx context.Context, id string) (*User, error) {\n    user, err := sqldb.QueryRow[User](ctx, db, `\n        SELECT * FROM users WHERE id = $1\n    `, id)\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 user, nil\n}\n",[567],{"type":41,"tag":82,"props":568,"children":569},{"__ignoreMap":79},[570,578,586,594,602,610,618,626,634,642,649,656,664,672,679,687,695,703,711,720,729,738,747,756,764,772,780,788,796],{"type":41,"tag":86,"props":571,"children":572},{"class":88,"line":89},[573],{"type":41,"tag":86,"props":574,"children":575},{},[576],{"type":46,"value":577},"\u002F\u002F RISKY: Returns nil without proper error\n",{"type":41,"tag":86,"props":579,"children":580},{"class":88,"line":98},[581],{"type":41,"tag":86,"props":582,"children":583},{},[584],{"type":46,"value":585},"func getUser(ctx context.Context, id string) (*User, error) {\n",{"type":41,"tag":86,"props":587,"children":588},{"class":88,"line":107},[589],{"type":41,"tag":86,"props":590,"children":591},{},[592],{"type":46,"value":593},"    user, err := sqldb.QueryRow[User](ctx, db, `\n",{"type":41,"tag":86,"props":595,"children":596},{"class":88,"line":116},[597],{"type":41,"tag":86,"props":598,"children":599},{},[600],{"type":46,"value":601},"        SELECT * FROM users WHERE id = $1\n",{"type":41,"tag":86,"props":603,"children":604},{"class":88,"line":25},[605],{"type":41,"tag":86,"props":606,"children":607},{},[608],{"type":46,"value":609},"    `, id)\n",{"type":41,"tag":86,"props":611,"children":612},{"class":88,"line":133},[613],{"type":41,"tag":86,"props":614,"children":615},{},[616],{"type":46,"value":617},"    if err != nil {\n",{"type":41,"tag":86,"props":619,"children":620},{"class":88,"line":143},[621],{"type":41,"tag":86,"props":622,"children":623},{},[624],{"type":46,"value":625},"        return nil, err  \u002F\u002F ErrNoRows returns generic error\n",{"type":41,"tag":86,"props":627,"children":628},{"class":88,"line":152},[629],{"type":41,"tag":86,"props":630,"children":631},{},[632],{"type":46,"value":633},"    }\n",{"type":41,"tag":86,"props":635,"children":636},{"class":88,"line":161},[637],{"type":41,"tag":86,"props":638,"children":639},{},[640],{"type":46,"value":641},"    return user, nil\n",{"type":41,"tag":86,"props":643,"children":644},{"class":88,"line":170},[645],{"type":41,"tag":86,"props":646,"children":647},{},[648],{"type":46,"value":130},{"type":41,"tag":86,"props":650,"children":651},{"class":88,"line":179},[652],{"type":41,"tag":86,"props":653,"children":654},{"emptyLinePlaceholder":137},[655],{"type":46,"value":140},{"type":41,"tag":86,"props":657,"children":658},{"class":88,"line":187},[659],{"type":41,"tag":86,"props":660,"children":661},{},[662],{"type":46,"value":663},"\u002F\u002F BETTER: Check for not found specifically\n",{"type":41,"tag":86,"props":665,"children":666},{"class":88,"line":196},[667],{"type":41,"tag":86,"props":668,"children":669},{},[670],{"type":46,"value":671},"import \"errors\"\n",{"type":41,"tag":86,"props":673,"children":674},{"class":88,"line":205},[675],{"type":41,"tag":86,"props":676,"children":677},{"emptyLinePlaceholder":137},[678],{"type":46,"value":140},{"type":41,"tag":86,"props":680,"children":682},{"class":88,"line":681},15,[683],{"type":41,"tag":86,"props":684,"children":685},{},[686],{"type":46,"value":585},{"type":41,"tag":86,"props":688,"children":690},{"class":88,"line":689},16,[691],{"type":41,"tag":86,"props":692,"children":693},{},[694],{"type":46,"value":593},{"type":41,"tag":86,"props":696,"children":698},{"class":88,"line":697},17,[699],{"type":41,"tag":86,"props":700,"children":701},{},[702],{"type":46,"value":601},{"type":41,"tag":86,"props":704,"children":706},{"class":88,"line":705},18,[707],{"type":41,"tag":86,"props":708,"children":709},{},[710],{"type":46,"value":609},{"type":41,"tag":86,"props":712,"children":714},{"class":88,"line":713},19,[715],{"type":41,"tag":86,"props":716,"children":717},{},[718],{"type":46,"value":719},"    if errors.Is(err, sqldb.ErrNoRows) {\n",{"type":41,"tag":86,"props":721,"children":723},{"class":88,"line":722},20,[724],{"type":41,"tag":86,"props":725,"children":726},{},[727],{"type":46,"value":728},"        return nil, &errs.Error{\n",{"type":41,"tag":86,"props":730,"children":732},{"class":88,"line":731},21,[733],{"type":41,"tag":86,"props":734,"children":735},{},[736],{"type":46,"value":737},"            Code:    errs.NotFound,\n",{"type":41,"tag":86,"props":739,"children":741},{"class":88,"line":740},22,[742],{"type":41,"tag":86,"props":743,"children":744},{},[745],{"type":46,"value":746},"            Message: \"user not found\",\n",{"type":41,"tag":86,"props":748,"children":750},{"class":88,"line":749},23,[751],{"type":41,"tag":86,"props":752,"children":753},{},[754],{"type":46,"value":755},"        }\n",{"type":41,"tag":86,"props":757,"children":759},{"class":88,"line":758},24,[760],{"type":41,"tag":86,"props":761,"children":762},{},[763],{"type":46,"value":633},{"type":41,"tag":86,"props":765,"children":767},{"class":88,"line":766},25,[768],{"type":41,"tag":86,"props":769,"children":770},{},[771],{"type":46,"value":617},{"type":41,"tag":86,"props":773,"children":774},{"class":88,"line":21},[775],{"type":41,"tag":86,"props":776,"children":777},{},[778],{"type":46,"value":779},"        return nil, err\n",{"type":41,"tag":86,"props":781,"children":783},{"class":88,"line":782},27,[784],{"type":41,"tag":86,"props":785,"children":786},{},[787],{"type":46,"value":633},{"type":41,"tag":86,"props":789,"children":791},{"class":88,"line":790},28,[792],{"type":41,"tag":86,"props":793,"children":794},{},[795],{"type":46,"value":641},{"type":41,"tag":86,"props":797,"children":799},{"class":88,"line":798},29,[800],{"type":41,"tag":86,"props":801,"children":802},{},[803],{"type":46,"value":130},{"type":41,"tag":68,"props":805,"children":807},{"id":806},"_7-public-internal-endpoints",[808],{"type":46,"value":809},"7. Public Internal Endpoints",{"type":41,"tag":75,"props":811,"children":813},{"className":77,"code":812,"language":20,"meta":79,"style":79},"\u002F\u002F CHECK: Should this cron endpoint be public?\n\u002F\u002Fencore:api public method=POST path=\u002Finternal\u002Fcleanup\nfunc CleanupJob(ctx context.Context) error {\n    \u002F\u002F ...\n}\n\n\u002F\u002F BETTER: Use private for internal endpoints\n\u002F\u002Fencore:api private\nfunc CleanupJob(ctx context.Context) error {\n    \u002F\u002F ...\n}\n",[814],{"type":41,"tag":82,"props":815,"children":816},{"__ignoreMap":79},[817,825,833,841,848,855,862,870,878,885,892],{"type":41,"tag":86,"props":818,"children":819},{"class":88,"line":89},[820],{"type":41,"tag":86,"props":821,"children":822},{},[823],{"type":46,"value":824},"\u002F\u002F CHECK: Should this cron endpoint be public?\n",{"type":41,"tag":86,"props":826,"children":827},{"class":88,"line":98},[828],{"type":41,"tag":86,"props":829,"children":830},{},[831],{"type":46,"value":832},"\u002F\u002Fencore:api public method=POST path=\u002Finternal\u002Fcleanup\n",{"type":41,"tag":86,"props":834,"children":835},{"class":88,"line":107},[836],{"type":41,"tag":86,"props":837,"children":838},{},[839],{"type":46,"value":840},"func CleanupJob(ctx context.Context) error {\n",{"type":41,"tag":86,"props":842,"children":843},{"class":88,"line":116},[844],{"type":41,"tag":86,"props":845,"children":846},{},[847],{"type":46,"value":255},{"type":41,"tag":86,"props":849,"children":850},{"class":88,"line":25},[851],{"type":41,"tag":86,"props":852,"children":853},{},[854],{"type":46,"value":130},{"type":41,"tag":86,"props":856,"children":857},{"class":88,"line":133},[858],{"type":41,"tag":86,"props":859,"children":860},{"emptyLinePlaceholder":137},[861],{"type":46,"value":140},{"type":41,"tag":86,"props":863,"children":864},{"class":88,"line":143},[865],{"type":41,"tag":86,"props":866,"children":867},{},[868],{"type":46,"value":869},"\u002F\u002F BETTER: Use private for internal endpoints\n",{"type":41,"tag":86,"props":871,"children":872},{"class":88,"line":152},[873],{"type":41,"tag":86,"props":874,"children":875},{},[876],{"type":46,"value":877},"\u002F\u002Fencore:api private\n",{"type":41,"tag":86,"props":879,"children":880},{"class":88,"line":161},[881],{"type":41,"tag":86,"props":882,"children":883},{},[884],{"type":46,"value":840},{"type":41,"tag":86,"props":886,"children":887},{"class":88,"line":170},[888],{"type":41,"tag":86,"props":889,"children":890},{},[891],{"type":46,"value":255},{"type":41,"tag":86,"props":893,"children":894},{"class":88,"line":179},[895],{"type":41,"tag":86,"props":896,"children":897},{},[898],{"type":46,"value":130},{"type":41,"tag":68,"props":900,"children":902},{"id":901},"_8-non-idempotent-subscription-handlers",[903],{"type":46,"value":904},"8. Non-Idempotent Subscription Handlers",{"type":41,"tag":75,"props":906,"children":908},{"className":77,"code":907,"language":20,"meta":79,"style":79},"\u002F\u002F RISKY: Not idempotent (pubsub has at-least-once delivery)\nvar _ = pubsub.NewSubscription(OrderCreated, \"process-order\",\n    pubsub.SubscriptionConfig[*OrderCreatedEvent]{\n        Handler: func(ctx context.Context, event *OrderCreatedEvent) error {\n            return chargeCustomer(ctx, event.OrderID)  \u002F\u002F Could charge twice!\n        },\n    },\n)\n\n\u002F\u002F SAFER: Check before processing\nvar _ = pubsub.NewSubscription(OrderCreated, \"process-order\",\n    pubsub.SubscriptionConfig[*OrderCreatedEvent]{\n        Handler: func(ctx context.Context, event *OrderCreatedEvent) error {\n            order, err := getOrder(ctx, event.OrderID)\n            if err != nil {\n                return err\n            }\n            if order.Status != \"pending\" {\n                return nil  \u002F\u002F Already processed\n            }\n            return chargeCustomer(ctx, event.OrderID)\n        },\n    },\n)\n",[909],{"type":41,"tag":82,"props":910,"children":911},{"__ignoreMap":79},[912,920,928,936,944,952,960,968,976,983,991,998,1005,1012,1020,1028,1036,1044,1052,1060,1067,1075,1082,1089],{"type":41,"tag":86,"props":913,"children":914},{"class":88,"line":89},[915],{"type":41,"tag":86,"props":916,"children":917},{},[918],{"type":46,"value":919},"\u002F\u002F RISKY: Not idempotent (pubsub has at-least-once delivery)\n",{"type":41,"tag":86,"props":921,"children":922},{"class":88,"line":98},[923],{"type":41,"tag":86,"props":924,"children":925},{},[926],{"type":46,"value":927},"var _ = pubsub.NewSubscription(OrderCreated, \"process-order\",\n",{"type":41,"tag":86,"props":929,"children":930},{"class":88,"line":107},[931],{"type":41,"tag":86,"props":932,"children":933},{},[934],{"type":46,"value":935},"    pubsub.SubscriptionConfig[*OrderCreatedEvent]{\n",{"type":41,"tag":86,"props":937,"children":938},{"class":88,"line":116},[939],{"type":41,"tag":86,"props":940,"children":941},{},[942],{"type":46,"value":943},"        Handler: func(ctx context.Context, event *OrderCreatedEvent) error {\n",{"type":41,"tag":86,"props":945,"children":946},{"class":88,"line":25},[947],{"type":41,"tag":86,"props":948,"children":949},{},[950],{"type":46,"value":951},"            return chargeCustomer(ctx, event.OrderID)  \u002F\u002F Could charge twice!\n",{"type":41,"tag":86,"props":953,"children":954},{"class":88,"line":133},[955],{"type":41,"tag":86,"props":956,"children":957},{},[958],{"type":46,"value":959},"        },\n",{"type":41,"tag":86,"props":961,"children":962},{"class":88,"line":143},[963],{"type":41,"tag":86,"props":964,"children":965},{},[966],{"type":46,"value":967},"    },\n",{"type":41,"tag":86,"props":969,"children":970},{"class":88,"line":152},[971],{"type":41,"tag":86,"props":972,"children":973},{},[974],{"type":46,"value":975},")\n",{"type":41,"tag":86,"props":977,"children":978},{"class":88,"line":161},[979],{"type":41,"tag":86,"props":980,"children":981},{"emptyLinePlaceholder":137},[982],{"type":46,"value":140},{"type":41,"tag":86,"props":984,"children":985},{"class":88,"line":170},[986],{"type":41,"tag":86,"props":987,"children":988},{},[989],{"type":46,"value":990},"\u002F\u002F SAFER: Check before processing\n",{"type":41,"tag":86,"props":992,"children":993},{"class":88,"line":179},[994],{"type":41,"tag":86,"props":995,"children":996},{},[997],{"type":46,"value":927},{"type":41,"tag":86,"props":999,"children":1000},{"class":88,"line":187},[1001],{"type":41,"tag":86,"props":1002,"children":1003},{},[1004],{"type":46,"value":935},{"type":41,"tag":86,"props":1006,"children":1007},{"class":88,"line":196},[1008],{"type":41,"tag":86,"props":1009,"children":1010},{},[1011],{"type":46,"value":943},{"type":41,"tag":86,"props":1013,"children":1014},{"class":88,"line":205},[1015],{"type":41,"tag":86,"props":1016,"children":1017},{},[1018],{"type":46,"value":1019},"            order, err := getOrder(ctx, event.OrderID)\n",{"type":41,"tag":86,"props":1021,"children":1022},{"class":88,"line":681},[1023],{"type":41,"tag":86,"props":1024,"children":1025},{},[1026],{"type":46,"value":1027},"            if err != nil {\n",{"type":41,"tag":86,"props":1029,"children":1030},{"class":88,"line":689},[1031],{"type":41,"tag":86,"props":1032,"children":1033},{},[1034],{"type":46,"value":1035},"                return err\n",{"type":41,"tag":86,"props":1037,"children":1038},{"class":88,"line":697},[1039],{"type":41,"tag":86,"props":1040,"children":1041},{},[1042],{"type":46,"value":1043},"            }\n",{"type":41,"tag":86,"props":1045,"children":1046},{"class":88,"line":705},[1047],{"type":41,"tag":86,"props":1048,"children":1049},{},[1050],{"type":46,"value":1051},"            if order.Status != \"pending\" {\n",{"type":41,"tag":86,"props":1053,"children":1054},{"class":88,"line":713},[1055],{"type":41,"tag":86,"props":1056,"children":1057},{},[1058],{"type":46,"value":1059},"                return nil  \u002F\u002F Already processed\n",{"type":41,"tag":86,"props":1061,"children":1062},{"class":88,"line":722},[1063],{"type":41,"tag":86,"props":1064,"children":1065},{},[1066],{"type":46,"value":1043},{"type":41,"tag":86,"props":1068,"children":1069},{"class":88,"line":731},[1070],{"type":41,"tag":86,"props":1071,"children":1072},{},[1073],{"type":46,"value":1074},"            return chargeCustomer(ctx, event.OrderID)\n",{"type":41,"tag":86,"props":1076,"children":1077},{"class":88,"line":740},[1078],{"type":41,"tag":86,"props":1079,"children":1080},{},[1081],{"type":46,"value":959},{"type":41,"tag":86,"props":1083,"children":1084},{"class":88,"line":749},[1085],{"type":41,"tag":86,"props":1086,"children":1087},{},[1088],{"type":46,"value":967},{"type":41,"tag":86,"props":1090,"children":1091},{"class":88,"line":758},[1092],{"type":41,"tag":86,"props":1093,"children":1094},{},[1095],{"type":46,"value":975},{"type":41,"tag":68,"props":1097,"children":1099},{"id":1098},"_9-not-closing-query-rows",[1100],{"type":46,"value":1101},"9. Not Closing Query Rows",{"type":41,"tag":75,"props":1103,"children":1105},{"className":77,"code":1104,"language":20,"meta":79,"style":79},"\u002F\u002F WRONG: Rows not closed\nfunc listUsers(ctx context.Context) ([]*User, error) {\n    rows, err := sqldb.Query[User](ctx, db, `SELECT * FROM users`)\n    if err != nil {\n        return nil, err\n    }\n    \u002F\u002F Missing: defer rows.Close()\n    \n    var users []*User\n    for rows.Next() {\n        users = append(users, rows.Value())\n    }\n    return users, nil\n}\n\n\u002F\u002F CORRECT: Always close rows\nfunc listUsers(ctx context.Context) ([]*User, error) {\n    rows, err := sqldb.Query[User](ctx, db, `SELECT * FROM users`)\n    if err != nil {\n        return nil, err\n    }\n    defer rows.Close()\n    \n    var users []*User\n    for rows.Next() {\n        users = append(users, rows.Value())\n    }\n    return users, rows.Err()\n}\n",[1106],{"type":41,"tag":82,"props":1107,"children":1108},{"__ignoreMap":79},[1109,1117,1125,1133,1140,1147,1154,1162,1170,1178,1186,1194,1201,1209,1216,1223,1231,1238,1245,1252,1259,1266,1274,1281,1288,1295,1302,1309,1317],{"type":41,"tag":86,"props":1110,"children":1111},{"class":88,"line":89},[1112],{"type":41,"tag":86,"props":1113,"children":1114},{},[1115],{"type":46,"value":1116},"\u002F\u002F WRONG: Rows not closed\n",{"type":41,"tag":86,"props":1118,"children":1119},{"class":88,"line":98},[1120],{"type":41,"tag":86,"props":1121,"children":1122},{},[1123],{"type":46,"value":1124},"func listUsers(ctx context.Context) ([]*User, error) {\n",{"type":41,"tag":86,"props":1126,"children":1127},{"class":88,"line":107},[1128],{"type":41,"tag":86,"props":1129,"children":1130},{},[1131],{"type":46,"value":1132},"    rows, err := sqldb.Query[User](ctx, db, `SELECT * FROM users`)\n",{"type":41,"tag":86,"props":1134,"children":1135},{"class":88,"line":116},[1136],{"type":41,"tag":86,"props":1137,"children":1138},{},[1139],{"type":46,"value":617},{"type":41,"tag":86,"props":1141,"children":1142},{"class":88,"line":25},[1143],{"type":41,"tag":86,"props":1144,"children":1145},{},[1146],{"type":46,"value":779},{"type":41,"tag":86,"props":1148,"children":1149},{"class":88,"line":133},[1150],{"type":41,"tag":86,"props":1151,"children":1152},{},[1153],{"type":46,"value":633},{"type":41,"tag":86,"props":1155,"children":1156},{"class":88,"line":143},[1157],{"type":41,"tag":86,"props":1158,"children":1159},{},[1160],{"type":46,"value":1161},"    \u002F\u002F Missing: defer rows.Close()\n",{"type":41,"tag":86,"props":1163,"children":1164},{"class":88,"line":152},[1165],{"type":41,"tag":86,"props":1166,"children":1167},{},[1168],{"type":46,"value":1169},"    \n",{"type":41,"tag":86,"props":1171,"children":1172},{"class":88,"line":161},[1173],{"type":41,"tag":86,"props":1174,"children":1175},{},[1176],{"type":46,"value":1177},"    var users []*User\n",{"type":41,"tag":86,"props":1179,"children":1180},{"class":88,"line":170},[1181],{"type":41,"tag":86,"props":1182,"children":1183},{},[1184],{"type":46,"value":1185},"    for rows.Next() {\n",{"type":41,"tag":86,"props":1187,"children":1188},{"class":88,"line":179},[1189],{"type":41,"tag":86,"props":1190,"children":1191},{},[1192],{"type":46,"value":1193},"        users = append(users, rows.Value())\n",{"type":41,"tag":86,"props":1195,"children":1196},{"class":88,"line":187},[1197],{"type":41,"tag":86,"props":1198,"children":1199},{},[1200],{"type":46,"value":633},{"type":41,"tag":86,"props":1202,"children":1203},{"class":88,"line":196},[1204],{"type":41,"tag":86,"props":1205,"children":1206},{},[1207],{"type":46,"value":1208},"    return users, nil\n",{"type":41,"tag":86,"props":1210,"children":1211},{"class":88,"line":205},[1212],{"type":41,"tag":86,"props":1213,"children":1214},{},[1215],{"type":46,"value":130},{"type":41,"tag":86,"props":1217,"children":1218},{"class":88,"line":681},[1219],{"type":41,"tag":86,"props":1220,"children":1221},{"emptyLinePlaceholder":137},[1222],{"type":46,"value":140},{"type":41,"tag":86,"props":1224,"children":1225},{"class":88,"line":689},[1226],{"type":41,"tag":86,"props":1227,"children":1228},{},[1229],{"type":46,"value":1230},"\u002F\u002F CORRECT: Always close rows\n",{"type":41,"tag":86,"props":1232,"children":1233},{"class":88,"line":697},[1234],{"type":41,"tag":86,"props":1235,"children":1236},{},[1237],{"type":46,"value":1124},{"type":41,"tag":86,"props":1239,"children":1240},{"class":88,"line":705},[1241],{"type":41,"tag":86,"props":1242,"children":1243},{},[1244],{"type":46,"value":1132},{"type":41,"tag":86,"props":1246,"children":1247},{"class":88,"line":713},[1248],{"type":41,"tag":86,"props":1249,"children":1250},{},[1251],{"type":46,"value":617},{"type":41,"tag":86,"props":1253,"children":1254},{"class":88,"line":722},[1255],{"type":41,"tag":86,"props":1256,"children":1257},{},[1258],{"type":46,"value":779},{"type":41,"tag":86,"props":1260,"children":1261},{"class":88,"line":731},[1262],{"type":41,"tag":86,"props":1263,"children":1264},{},[1265],{"type":46,"value":633},{"type":41,"tag":86,"props":1267,"children":1268},{"class":88,"line":740},[1269],{"type":41,"tag":86,"props":1270,"children":1271},{},[1272],{"type":46,"value":1273},"    defer rows.Close()\n",{"type":41,"tag":86,"props":1275,"children":1276},{"class":88,"line":749},[1277],{"type":41,"tag":86,"props":1278,"children":1279},{},[1280],{"type":46,"value":1169},{"type":41,"tag":86,"props":1282,"children":1283},{"class":88,"line":758},[1284],{"type":41,"tag":86,"props":1285,"children":1286},{},[1287],{"type":46,"value":1177},{"type":41,"tag":86,"props":1289,"children":1290},{"class":88,"line":766},[1291],{"type":41,"tag":86,"props":1292,"children":1293},{},[1294],{"type":46,"value":1185},{"type":41,"tag":86,"props":1296,"children":1297},{"class":88,"line":21},[1298],{"type":41,"tag":86,"props":1299,"children":1300},{},[1301],{"type":46,"value":1193},{"type":41,"tag":86,"props":1303,"children":1304},{"class":88,"line":782},[1305],{"type":41,"tag":86,"props":1306,"children":1307},{},[1308],{"type":46,"value":633},{"type":41,"tag":86,"props":1310,"children":1311},{"class":88,"line":790},[1312],{"type":41,"tag":86,"props":1313,"children":1314},{},[1315],{"type":46,"value":1316},"    return users, rows.Err()\n",{"type":41,"tag":86,"props":1318,"children":1319},{"class":88,"line":798},[1320],{"type":41,"tag":86,"props":1321,"children":1322},{},[1323],{"type":46,"value":130},{"type":41,"tag":49,"props":1325,"children":1327},{"id":1326},"review-checklist",[1328],{"type":46,"value":1329},"Review Checklist",{"type":41,"tag":1331,"props":1332,"children":1335},"ul",{"className":1333},[1334],"contains-task-list",[1336,1349,1366,1391,1400,1409,1426,1449,1458,1473],{"type":41,"tag":1337,"props":1338,"children":1341},"li",{"className":1339},[1340],"task-list-item",[1342,1347],{"type":41,"tag":1343,"props":1344,"children":1346},"input",{"disabled":137,"type":1345},"checkbox",[],{"type":46,"value":1348}," All infrastructure at package level",{"type":41,"tag":1337,"props":1350,"children":1352},{"className":1351},[1340],[1353,1356,1358,1364],{"type":41,"tag":1343,"props":1354,"children":1355},{"disabled":137,"type":1345},[],{"type":46,"value":1357}," All API endpoints have ",{"type":41,"tag":82,"props":1359,"children":1361},{"className":1360},[],[1362],{"type":46,"value":1363},"context.Context",{"type":46,"value":1365}," as first parameter",{"type":41,"tag":1337,"props":1367,"children":1369},{"className":1368},[1340],[1370,1373,1375,1381,1383,1389],{"type":41,"tag":1343,"props":1371,"children":1372},{"disabled":137,"type":1345},[],{"type":46,"value":1374}," SQL uses parameterized queries (",{"type":41,"tag":82,"props":1376,"children":1378},{"className":1377},[],[1379],{"type":46,"value":1380},"$1",{"type":46,"value":1382},", ",{"type":41,"tag":82,"props":1384,"children":1386},{"className":1385},[],[1387],{"type":46,"value":1388},"$2",{"type":46,"value":1390},", etc.)",{"type":41,"tag":1337,"props":1392,"children":1394},{"className":1393},[1340],[1395,1398],{"type":41,"tag":1343,"props":1396,"children":1397},{"disabled":137,"type":1345},[],{"type":46,"value":1399}," Response types are pointers",{"type":41,"tag":1337,"props":1401,"children":1403},{"className":1402},[1340],[1404,1407],{"type":41,"tag":1343,"props":1405,"children":1406},{"disabled":137,"type":1345},[],{"type":46,"value":1408}," Errors are handled, not ignored",{"type":41,"tag":1337,"props":1410,"children":1412},{"className":1411},[1340],[1413,1416,1418,1424],{"type":41,"tag":1343,"props":1414,"children":1415},{"disabled":137,"type":1345},[],{"type":46,"value":1417}," ",{"type":41,"tag":82,"props":1419,"children":1421},{"className":1420},[],[1422],{"type":46,"value":1423},"sqldb.ErrNoRows",{"type":46,"value":1425}," checked where appropriate",{"type":41,"tag":1337,"props":1427,"children":1429},{"className":1428},[1340],[1430,1433,1435,1441,1443],{"type":41,"tag":1343,"props":1431,"children":1432},{"disabled":137,"type":1345},[],{"type":46,"value":1434}," Internal endpoints use ",{"type":41,"tag":82,"props":1436,"children":1438},{"className":1437},[],[1439],{"type":46,"value":1440},"private",{"type":46,"value":1442}," not ",{"type":41,"tag":82,"props":1444,"children":1446},{"className":1445},[],[1447],{"type":46,"value":1448},"public",{"type":41,"tag":1337,"props":1450,"children":1452},{"className":1451},[1340],[1453,1456],{"type":41,"tag":1343,"props":1454,"children":1455},{"disabled":137,"type":1345},[],{"type":46,"value":1457}," Subscription handlers are idempotent",{"type":41,"tag":1337,"props":1459,"children":1461},{"className":1460},[1340],[1462,1465,1467],{"type":41,"tag":1343,"props":1463,"children":1464},{"disabled":137,"type":1345},[],{"type":46,"value":1466}," Query rows are closed with ",{"type":41,"tag":82,"props":1468,"children":1470},{"className":1469},[],[1471],{"type":46,"value":1472},"defer rows.Close()",{"type":41,"tag":1337,"props":1474,"children":1476},{"className":1475},[1340],[1477,1480,1482,1488],{"type":41,"tag":1343,"props":1478,"children":1479},{"disabled":137,"type":1345},[],{"type":46,"value":1481}," Migrations follow naming convention (",{"type":41,"tag":82,"props":1483,"children":1485},{"className":1484},[],[1486],{"type":46,"value":1487},"1_name.up.sql",{"type":46,"value":1489},")",{"type":41,"tag":49,"props":1491,"children":1493},{"id":1492},"output-format",[1494],{"type":46,"value":1495},"Output Format",{"type":41,"tag":56,"props":1497,"children":1498},{},[1499],{"type":46,"value":1500},"When reviewing, report issues as:",{"type":41,"tag":75,"props":1502,"children":1506},{"className":1503,"code":1505,"language":46},[1504],"language-text","[CRITICAL] [file:line] Description of issue\n[WARNING] [file:line] Description of concern  \n[GOOD] Notable good practice observed\n",[1507],{"type":41,"tag":82,"props":1508,"children":1509},{"__ignoreMap":79},[1510],{"type":46,"value":1505},{"type":41,"tag":1512,"props":1513,"children":1514},"style",{},[1515],{"type":46,"value":1516},"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":1518,"total":790},[1519,1533,1545,1563,1579,1589,1605],{"slug":1520,"name":1520,"fn":1521,"description":1522,"org":1523,"tags":1524,"stars":21,"repoUrl":22,"updatedAt":1532},"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},[1525,1528,1529],{"name":1526,"slug":1527,"type":16},"API Development","api-development",{"name":9,"slug":8,"type":16},{"name":1530,"slug":1531,"type":16},"TypeScript","typescript","2026-04-06T18:09:46.044101",{"slug":1534,"name":1534,"fn":1535,"description":1536,"org":1537,"tags":1538,"stars":21,"repoUrl":22,"updatedAt":1544},"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},[1539,1542,1543],{"name":1540,"slug":1541,"type":16},"Auth","auth",{"name":9,"slug":8,"type":16},{"name":1530,"slug":1531,"type":16},"2026-04-06T18:09:47.336322",{"slug":1546,"name":1546,"fn":1547,"description":1548,"org":1549,"tags":1550,"stars":21,"repoUrl":22,"updatedAt":1562},"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},[1551,1554,1555,1558,1561],{"name":1552,"slug":1553,"type":16},"Backend","backend",{"name":9,"slug":8,"type":16},{"name":1556,"slug":1557,"type":16},"File Storage","file-storage",{"name":1559,"slug":1560,"type":16},"File Uploads","file-uploads",{"name":1530,"slug":1531,"type":16},"2026-05-16T05:59:52.813772",{"slug":1564,"name":1564,"fn":1565,"description":1566,"org":1567,"tags":1568,"stars":21,"repoUrl":22,"updatedAt":1578},"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},[1569,1570,1573,1574,1577],{"name":1552,"slug":1553,"type":16},{"name":1571,"slug":1572,"type":16},"Caching","caching",{"name":9,"slug":8,"type":16},{"name":1575,"slug":1576,"type":16},"Redis","redis",{"name":1530,"slug":1531,"type":16},"2026-05-16T05:59:56.808328",{"slug":1580,"name":1580,"fn":1581,"description":1582,"org":1583,"tags":1584,"stars":21,"repoUrl":22,"updatedAt":1588},"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},[1585,1586,1587],{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},{"name":1530,"slug":1531,"type":16},"2026-04-06T18:10:01.123999",{"slug":1590,"name":1590,"fn":1591,"description":1592,"org":1593,"tags":1594,"stars":21,"repoUrl":22,"updatedAt":1604},"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},[1595,1598,1599,1600,1603],{"name":1596,"slug":1597,"type":16},"Automation","automation",{"name":1552,"slug":1553,"type":16},{"name":9,"slug":8,"type":16},{"name":1601,"slug":1602,"type":16},"Scheduling","scheduling",{"name":1530,"slug":1531,"type":16},"2026-05-16T05:59:54.146651",{"slug":1606,"name":1606,"fn":1607,"description":1608,"org":1609,"tags":1610,"stars":21,"repoUrl":22,"updatedAt":1619},"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},[1611,1614,1615,1618],{"name":1612,"slug":1613,"type":16},"Database","database",{"name":9,"slug":8,"type":16},{"name":1616,"slug":1617,"type":16},"ORM","orm",{"name":1530,"slug":1531,"type":16},"2026-04-06T18:09:54.823017",{"items":1621,"total":790},[1622,1628,1634,1642,1650,1656,1664,1671,1688,1705,1715,1726],{"slug":1520,"name":1520,"fn":1521,"description":1522,"org":1623,"tags":1624,"stars":21,"repoUrl":22,"updatedAt":1532},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1625,1626,1627],{"name":1526,"slug":1527,"type":16},{"name":9,"slug":8,"type":16},{"name":1530,"slug":1531,"type":16},{"slug":1534,"name":1534,"fn":1535,"description":1536,"org":1629,"tags":1630,"stars":21,"repoUrl":22,"updatedAt":1544},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1631,1632,1633],{"name":1540,"slug":1541,"type":16},{"name":9,"slug":8,"type":16},{"name":1530,"slug":1531,"type":16},{"slug":1546,"name":1546,"fn":1547,"description":1548,"org":1635,"tags":1636,"stars":21,"repoUrl":22,"updatedAt":1562},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1637,1638,1639,1640,1641],{"name":1552,"slug":1553,"type":16},{"name":9,"slug":8,"type":16},{"name":1556,"slug":1557,"type":16},{"name":1559,"slug":1560,"type":16},{"name":1530,"slug":1531,"type":16},{"slug":1564,"name":1564,"fn":1565,"description":1566,"org":1643,"tags":1644,"stars":21,"repoUrl":22,"updatedAt":1578},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1645,1646,1647,1648,1649],{"name":1552,"slug":1553,"type":16},{"name":1571,"slug":1572,"type":16},{"name":9,"slug":8,"type":16},{"name":1575,"slug":1576,"type":16},{"name":1530,"slug":1531,"type":16},{"slug":1580,"name":1580,"fn":1581,"description":1582,"org":1651,"tags":1652,"stars":21,"repoUrl":22,"updatedAt":1588},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1653,1654,1655],{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},{"name":1530,"slug":1531,"type":16},{"slug":1590,"name":1590,"fn":1591,"description":1592,"org":1657,"tags":1658,"stars":21,"repoUrl":22,"updatedAt":1604},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1659,1660,1661,1662,1663],{"name":1596,"slug":1597,"type":16},{"name":1552,"slug":1553,"type":16},{"name":9,"slug":8,"type":16},{"name":1601,"slug":1602,"type":16},{"name":1530,"slug":1531,"type":16},{"slug":1606,"name":1606,"fn":1607,"description":1608,"org":1665,"tags":1666,"stars":21,"repoUrl":22,"updatedAt":1619},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1667,1668,1669,1670],{"name":1612,"slug":1613,"type":16},{"name":9,"slug":8,"type":16},{"name":1616,"slug":1617,"type":16},{"name":1530,"slug":1531,"type":16},{"slug":1672,"name":1672,"fn":1673,"description":1674,"org":1675,"tags":1676,"stars":21,"repoUrl":22,"updatedAt":1687},"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},[1677,1678,1681,1684],{"name":9,"slug":8,"type":16},{"name":1679,"slug":1680,"type":16},"Frontend","frontend",{"name":1682,"slug":1683,"type":16},"Next.js","next-js",{"name":1685,"slug":1686,"type":16},"React","react","2026-04-06T18:09:56.091006",{"slug":1689,"name":1689,"fn":1690,"description":1691,"org":1692,"tags":1693,"stars":21,"repoUrl":22,"updatedAt":1704},"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},[1694,1695,1696,1697,1700,1701],{"name":1526,"slug":1527,"type":16},{"name":1552,"slug":1553,"type":16},{"name":9,"slug":8,"type":16},{"name":1698,"slug":1699,"type":16},"Local Development","local-development",{"name":1530,"slug":1531,"type":16},{"name":1702,"slug":1703,"type":16},"Web Development","web-development","2026-04-06T18:10:04.885446",{"slug":1706,"name":1706,"fn":1707,"description":1708,"org":1709,"tags":1710,"stars":21,"repoUrl":22,"updatedAt":1714},"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},[1711,1712,1713],{"name":1526,"slug":1527,"type":16},{"name":9,"slug":8,"type":16},{"name":19,"slug":20,"type":16},"2026-04-06T18:09:48.578781",{"slug":1716,"name":1716,"fn":1717,"description":1718,"org":1719,"tags":1720,"stars":21,"repoUrl":22,"updatedAt":1725},"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},[1721,1722,1723,1724],{"name":1540,"slug":1541,"type":16},{"name":1552,"slug":1553,"type":16},{"name":9,"slug":8,"type":16},{"name":19,"slug":20,"type":16},"2026-04-06T18:09:51.065102",{"slug":1727,"name":1727,"fn":1728,"description":1729,"org":1730,"tags":1731,"stars":21,"repoUrl":22,"updatedAt":1739},"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},[1732,1733,1734,1735,1736],{"name":1552,"slug":1553,"type":16},{"name":9,"slug":8,"type":16},{"name":1556,"slug":1557,"type":16},{"name":19,"slug":20,"type":16},{"name":1737,"slug":1738,"type":16},"Storage","storage","2026-05-16T06:00:03.633918"]