Back to skills
SkillHub ClubShip Full StackFull Stack
encore-code-review
Review Encore.ts code for best practices and anti-patterns.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Stars
19
Hot score
87
Updated
March 20, 2026
Overall rating
C1.7
Composite score
1.7
Best-practice grade
B84.0
Install command
npx @skill-hub/cli install encoredev-skills-code-review
Repository
encoredev/skills
Skill path: encore/code-review
Review Encore.ts code for best practices and anti-patterns.
Open repositoryBest for
Primary workflow: Ship Full Stack.
Technical facets: Full Stack.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: encoredev.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install encore-code-review into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/encoredev/skills before adding encore-code-review to shared team environments
- Use encore-code-review for development workflows
Works across
Claude CodeCodex CLIGemini CLIOpenCode
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: encore-code-review
description: Review Encore.ts code for best practices and anti-patterns.
---
# Encore Code Review
## Instructions
When reviewing Encore.ts code, check for these common issues:
## Critical Issues
### 1. Infrastructure Inside Functions
```typescript
// WRONG: Infrastructure declared inside function
async function setup() {
const db = new SQLDatabase("mydb", { migrations: "./migrations" });
const topic = new Topic<Event>("events", { deliveryGuarantee: "at-least-once" });
}
// CORRECT: Package level declaration
const db = new SQLDatabase("mydb", { migrations: "./migrations" });
const topic = new Topic<Event>("events", { deliveryGuarantee: "at-least-once" });
```
### 2. Using require() Instead of import
```typescript
// WRONG
const { api } = require("encore.dev/api");
// CORRECT
import { api } from "encore.dev/api";
```
### 3. Wrong Service Import Pattern
```typescript
// WRONG: Direct import from another service
import { getUser } from "../user/api";
// CORRECT: Use ~encore/clients
import { user } from "~encore/clients";
const result = await user.getUser({ id });
```
### 4. Missing Error Handling
```typescript
// WRONG: Returning null for not found
const user = await db.queryRow`SELECT * FROM users WHERE id = ${id}`;
if (!user) return null;
// CORRECT: Throw APIError
import { APIError } from "encore.dev/api";
const user = await db.queryRow`SELECT * FROM users WHERE id = ${id}`;
if (!user) {
throw APIError.notFound("user not found");
}
```
### 5. SQL Injection Risk
```typescript
// WRONG: String concatenation
await db.query(`SELECT * FROM users WHERE email = '${email}'`);
// CORRECT: Template literal with automatic escaping
await db.queryRow`SELECT * FROM users WHERE email = ${email}`;
```
## Warning Issues
### 6. Missing Type Annotations
```typescript
// WEAK: No explicit types
export const getUser = api(
{ method: "GET", path: "/users/:id", expose: true },
async ({ id }) => {
return await findUser(id);
}
);
// BETTER: Explicit request/response types
interface GetUserRequest { id: string; }
interface User { id: string; email: string; name: string; }
export const getUser = api(
{ method: "GET", path: "/users/:id", expose: true },
async ({ id }: GetUserRequest): Promise<User> => {
return await findUser(id);
}
);
```
### 7. Exposed Internal Endpoints
```typescript
// CHECK: Should this cron endpoint be exposed?
export const cleanupJob = api(
{ expose: true }, // Probably should be false
async () => { /* ... */ }
);
```
### 8. Non-Idempotent Subscription Handlers
```typescript
// RISKY: Not idempotent (pubsub has at-least-once delivery)
const _ = new Subscription(orderCreated, "process-order", {
handler: async (event) => {
await chargeCustomer(event.orderId); // Could charge twice!
},
});
// SAFER: Check before processing
const _ = new Subscription(orderCreated, "process-order", {
handler: async (event) => {
const order = await getOrder(event.orderId);
if (order.status !== "pending") return; // Already processed
await chargeCustomer(event.orderId);
},
});
```
### 9. Secrets Called at Module Level
```typescript
// WRONG: Secret accessed at startup
const stripeKey = secret("StripeKey");
const client = new Stripe(stripeKey()); // Called during import
// CORRECT: Access inside functions
const stripeKey = secret("StripeKey");
async function charge() {
const client = new Stripe(stripeKey()); // Called at runtime
}
```
## Review Checklist
- [ ] All infrastructure at package level
- [ ] Using ES6 imports, not require()
- [ ] Cross-service calls use `~encore/clients`
- [ ] Proper error handling with APIError
- [ ] SQL uses template literals
- [ ] Request/response types defined
- [ ] Internal endpoints have `expose: false`
- [ ] Subscription handlers are idempotent
- [ ] Secrets accessed inside functions, not at import time
- [ ] Migrations follow naming convention (001_name.up.sql)
## Output Format
When reviewing, report issues as:
```
[CRITICAL] [file:line] Description of issue
[WARNING] [file:line] Description of concern
[GOOD] Notable good practice observed
```