# Node Backend

> Build Node.js backends for Medusa v2 — Express middleware chain, MikroORM entity patterns, PostgreSQL connection pooling, async patterns, error handling, and logging. Use when working with Medusa's Node.js runtime.

- Skill: `orcaqubits/node-backend` (Agent Skill)
- Install (CLI): `npx skillmds@latest add orcaqubits/node-backend`
- Raw SKILL.md: https://api.skillmd.com/api/skills/orcaqubits/node-backend/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: OrcaQubits (https://skillmd.com/u/orcaqubits)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/orcaqubits/node-backend

---


# Node.js Backend Patterns for Medusa v2

## Before writing code

**Fetch live docs**:
1. Web-search `site:docs.medusajs.com api routes middlewares express` for middleware configuration
2. Web-search `site:mikro-orm.io docs` for MikroORM entity and query patterns
3. Web-search `site:docs.medusajs.com error handling MedusaError` for error types
4. Fetch `https://docs.medusajs.com/learn/fundamentals/api-routes` for API route conventions
5. Web-search `site:node.org docs async_hooks performance` for Node.js async best practices

## Express Middleware Chain

### Medusa Request Lifecycle

Medusa v2 is built on Express. Every HTTP request flows through a defined middleware chain:

```
Incoming Request
├── 1. CORS middleware
├── 2. Body parser (JSON)
├── 3. Cookie parser / session
├── 4. Authentication middleware
├── 5. Custom middlewares (middlewares.ts)
├── 6. Route handler (route.ts)
└── 7. Error handler
```

### Custom Middleware Registration

Define custom middleware in `src/api/middlewares.ts`:

```ts
// Fetch live docs for defineMiddlewares
// and MiddlewareRoute type signatures
import { defineMiddlewares } from "@medusajs/medusa"
```

| Middleware Property | Purpose |
|--------------------|---------|
| `matcher` | Route pattern to match (glob or regex) |
| `method` | HTTP method filter (GET, POST, etc.) |
| `middlewares` | Array of Express middleware functions |
| `bodyParser` | Configure or disable body parsing |
| `authenticate` | Apply auth requirements |

### Middleware Execution Order

Middleware executes in the order defined in the `routes` array. Global middleware (matcher `*`) runs before route-specific middleware.

## MikroORM Entity Patterns

### DML Abstraction Layer

Medusa v2 abstracts MikroORM behind DML. However, understanding the underlying ORM helps with advanced queries:

| DML Layer | MikroORM Layer | Purpose |
|-----------|---------------|---------|
| `model.define()` | `@Entity()` decorator | Define database entity |
| DML fields | `@Property()` decorators | Column definitions |
| DML relationships | `@OneToMany()`, `@ManyToOne()` | Relationship mappings |
| DML `.index()` | `@Index()` decorator | Database indexes |
| Generated migrations | MikroORM migration files | Schema changes |

### Query Patterns via Service Methods

Module services generated by `MedusaService` provide typed query methods:

| Operation | Service Method | Underlying ORM Action |
|-----------|---------------|----------------------|
| Find many | `list(filters, config)` | `em.find()` with criteria |
| Find one | `retrieve(id, config)` | `em.findOneOrFail()` |
| Create | `create(data)` | `em.create()` + `em.persist()` |
| Update | `update(data)` | `em.assign()` + `em.flush()` |
| Delete | `delete(id)` | `em.removeAndFlush()` |
| Soft delete | `softDelete(id)` | Set `deleted_at` timestamp |

MikroORM uses the Unit of Work pattern — changes are tracked and flushed together per request.

## PostgreSQL Connection Pooling

### Default Connection Settings

Medusa uses MikroORM's built-in PostgreSQL driver with connection pooling:

| Setting | Default | Production Recommendation |
|---------|---------|--------------------------|
| Pool minimum | 2 | 5-10 |
| Pool maximum | 10 | 20-50 (based on load) |
| Idle timeout | 30s | 10-30s |
| Connection timeout | 5s | 5-10s |
| SSL mode | Disabled | `require` or `verify-full` |

### Pool Management

- Configure via `DATABASE_URL` query parameters or `medusa-config.ts` database options
- Track active vs idle connections via PostgreSQL's `pg_stat_activity`
- Set pool max below PostgreSQL `max_connections` (reserve for admin)
- Use PgBouncer for external pooling in high-concurrency scenarios

## Async Patterns

### Async/Await Best Practices

| Pattern | Use Case | Avoid |
|---------|----------|-------|
| `async/await` | Sequential async operations | Callback-based patterns |
| `Promise.all()` | Parallel independent operations | Sequential awaits for independent tasks |
| `Promise.allSettled()` | Parallel ops where some may fail | `Promise.all()` when partial success is OK |
| `for await...of` | Async iterators, streams | Manual iterator consumption |
| `try/catch` in async | Error handling in async functions | Unhandled promise rejections |

### Medusa Workflow Steps

Workflow steps are inherently async. Each step receives context and returns a `StepResponse`:

```ts
// Fetch live docs for createStep async
// signature and StepResponse typing
```

### Event-Driven Async (Subscribers)

Subscribers handle events asynchronously. They execute in the worker process when running in separate mode:

| Concept | Implementation |
|---------|---------------|
| Event emission | Automatic on entity changes or manual via event bus |
| Subscriber registration | Export `config` with `event` and `handler` function |
| Execution context | Runs in worker process (async from request) |
| Error handling | Subscriber errors do not affect the triggering request |

## Error Handling

### Medusa Error Types

| Error Class | HTTP Status | Use Case |
|------------|-------------|----------|
| `MedusaError` | Varies | Base error class with type codes |
| `NOT_FOUND` | 404 | Entity not found |
| `INVALID_DATA` | 400 | Validation failure |
| `UNAUTHORIZED` | 401 | Authentication required |
| `NOT_ALLOWED` | 403 | Insufficient permissions |
| `CONFLICT` | 409 | Duplicate or conflicting state |
| `UNEXPECTED_STATE` | 500 | Internal logic error |

### Error Handling in API Routes

```ts
// Fetch live docs for MedusaError import
// and error type enum values
import { MedusaError } from "@medusajs/framework/utils"
```

### Error Handling Strategy

- Throw `MedusaError` with appropriate type in service methods
- Medusa's error handler middleware converts errors to HTTP responses
- Never swallow errors silently — log and re-throw or handle explicitly
- Use `try/catch` in workflow steps and return compensation data on failure

## Logging

### Built-in Logger

Medusa provides a structured logger accessible via the DI container:

| Log Level | Method | Use Case |
|-----------|--------|----------|
| `error` | `logger.error()` | Unrecoverable errors, exceptions |
| `warn` | `logger.warn()` | Degraded state, deprecated usage |
| `info` | `logger.info()` | Significant events, startup, shutdown |
| `debug` | `logger.debug()` | Detailed diagnostic information |

Resolve the logger from the container via `container.resolve("logger")`. Log at appropriate levels, include contextual data (entity IDs, operation names), and configure structured JSON output in production.

## Node.js Runtime Considerations

| Concern | Recommendation |
|---------|---------------|
| **Node.js version** | Use Node.js 20+ (LTS) for Medusa v2 |
| **Memory** | Set `--max-old-space-size` for large catalogs |
| **Event loop** | Avoid blocking the event loop with synchronous operations |
| **Graceful shutdown** | Handle `SIGTERM` to close connections and finish in-flight requests |
| **Clustering** | Use multiple server instances (not Node cluster) behind a load balancer |

## Best Practices

- **Middleware discipline** — define all custom middleware in `src/api/middlewares.ts`; order middleware from general to specific; use route matchers to scope middleware narrowly
- **Database access** — always go through module services, never access the entity manager directly; use `list` with filters instead of raw queries; leverage soft deletes for data preservation
- **Async safety** — always `await` async operations; use `Promise.all` for parallel independent work; handle errors in every `try/catch` block with meaningful error types
- **Performance** — configure connection pool size for expected concurrency; use Redis for caching frequently accessed data; avoid N+1 queries by using service `config.relations` to eager-load relationships

Fetch the Medusa API route and middleware documentation for exact handler signatures, error types, and logging configuration before implementing.

