← Back to list

encore-migrate
by encoredev
Agent Skills for development with Encore.
⭐ 10🍴 1📅 Jan 23, 2026
SKILL.md
name: encore-migrate description: Migrate Express/Fastify apps to Encore.ts.
Migrate to Encore.ts
Instructions
When migrating existing Node.js applications to Encore.ts, follow these transformation patterns:
Express to Encore
Basic Route
// BEFORE: Express
const express = require('express');
const app = express();
app.get('/users/:id', async (req, res) => {
const user = await getUser(req.params.id);
res.json(user);
});
app.listen(3000);
// AFTER: Encore
import { api } from "encore.dev/api";
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);
}
);
POST with Body
// BEFORE: Express
app.post('/users', async (req, res) => {
const { email, name } = req.body;
const user = await createUser(email, name);
res.status(201).json(user);
});
// AFTER: Encore
interface CreateUserRequest {
email: string;
name: string;
}
export const createUser = api(
{ method: "POST", path: "/users", expose: true },
async (req: CreateUserRequest): Promise<User> => {
return await insertUser(req.email, req.name);
}
);
Query Parameters
// BEFORE: Express
app.get('/users', async (req, res) => {
const { limit, offset } = req.query;
const users = await listUsers(Number(limit), Number(offset));
res.json(users);
});
// AFTER: Encore
import { Query, api } from "encore.dev/api";
interface ListUsersRequest {
limit?: Query<number>;
offset?: Query<number>;
}
export const listUsers = api(
{ method: "GET", path: "/users", expose: true },
async ({ limit = 10, offset = 0 }: ListUsersRequest): Promise<{ users: User[] }> => {
return { users: await fetchUsers(limit, offset) };
}
);
Headers
// BEFORE: Express
app.post('/webhook', async (req, res) => {
const signature = req.headers['x-signature'];
// verify...
});
// AFTER: Encore
import { Header, api } from "encore.dev/api";
interface WebhookRequest {
signature: Header<"X-Signature">;
payload: any;
}
export const webhook = api(
{ method: "POST", path: "/webhook", expose: true },
async ({ signature, payload }: WebhookRequest): Promise<void> => {
// verify signature...
}
);
Raw Request Access (Webhooks)
// BEFORE: Express
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['stripe-signature'];
const event = stripe.webhooks.constructEvent(req.body, sig, secret);
res.sendStatus(200);
});
// AFTER: Encore
export const stripeWebhook = api.raw(
{ expose: true, path: "/webhooks/stripe", method: "POST" },
async (req, res) => {
const sig = req.headers["stripe-signature"];
const chunks: Buffer[] = [];
for await (const chunk of req) {
chunks.push(chunk);
}
const body = Buffer.concat(chunks);
const event = stripe.webhooks.constructEvent(body, sig, secret);
res.writeHead(200);
res.end();
}
);
Middleware
// BEFORE: Express
app.use((req, res, next) => {
console.log(`${req.method} ${req.path}`);
next();
});
// AFTER: Encore
import { Service } from "encore.dev/service";
import { middleware } from "encore.dev/api";
const logMiddleware = middleware(
{ target: { all: true } },
async (req, next) => {
console.log(`${req.requestMeta?.method} ${req.requestMeta?.path}`);
return next(req);
}
);
export default new Service("my-service", {
middlewares: [logMiddleware],
});
Error Handling
// BEFORE: Express
app.get('/users/:id', async (req, res) => {
const user = await getUser(req.params.id);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json(user);
});
// AFTER: Encore
import { APIError, api } from "encore.dev/api";
export const getUser = api(
{ method: "GET", path: "/users/:id", expose: true },
async ({ id }: GetUserRequest): Promise<User> => {
const user = await findUser(id);
if (!user) {
throw APIError.notFound("user not found");
}
return user;
}
);
Database Migration
// BEFORE: Express with pg
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const result = await pool.query('SELECT * FROM users WHERE id = $1', [id]);
// AFTER: Encore
import { SQLDatabase } from "encore.dev/storage/sqldb";
const db = new SQLDatabase("users", {
migrations: "./migrations",
});
const user = await db.queryRow<User>`
SELECT * FROM users WHERE id = ${id}
`;
Cron Jobs
// BEFORE: Node with node-cron
import cron from 'node-cron';
cron.schedule('0 * * * *', () => {
cleanupExpiredSessions();
});
// AFTER: Encore
import { CronJob } from "encore.dev/cron";
import { api } from "encore.dev/api";
export const cleanupSessions = api(
{ expose: false },
async (): Promise<void> => {
// cleanup logic
}
);
const _ = new CronJob("cleanup-sessions", {
title: "Cleanup expired sessions",
schedule: "0 * * * *",
endpoint: cleanupSessions,
});
Migration Checklist
- Replace
requirewithimport - Remove
app.listen()- Encore handles this - Convert routes to
api()functions - Define TypeScript interfaces for request/response
- Replace manual validation with Encore's type validation
- Convert error responses to
APIError - Move database connection to
SQLDatabase - Convert cron jobs to
CronJob - Move env vars to
secret()for sensitive values - Create
encore.service.tsin each service directory
Score
Total Score
60/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
✓LICENSE
ライセンスが設定されている
+10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
✓最近の活動
1ヶ月以内に更新
+10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
○言語
プログラミング言語が設定されている
0/5
✓タグ
1つ以上のタグが設定されている
+5
Reviews
💬
Reviews coming soon
