Back to list
encoredev

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 require with import
  • 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.ts in 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