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.
Scanned 9/7/2026
Install to Claude Code
npx -y skills add OrcaQubits/agentic-commerce-skills-plugins --skill node-backend --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Node Backend?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/orcaqubits-node-backend-d605f4e7)More formats (shields.io, HTML) on the badges page.
---
name: node-backend
description: >
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.
---
# 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.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!