encore-migrate

When migrating existing Node.js applications to Encore.ts, follow these transformation patterns:

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "encore-migrate" with this command: npx skills add encoredev/skills/encoredev-skills-encore-migrate

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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

encore-auth

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

encore-code-review

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

encore-service

No summary provided by upstream source.

Repository SourceNeeds Review