0
# Server Utilities
1
2
Server lifecycle management utilities including graceful shutdown patterns, configuration file loading, version detection, and Node.js module utilities.
3
4
## Capabilities
5
6
### Graceful Server Shutdown
7
8
Enhanced HTTP server with graceful shutdown capabilities that properly handles existing connections during server termination.
9
10
```typescript { .api }
11
import { Server } from "http";
12
13
/**
14
* Abstract class providing graceful shutdown functionality for HTTP servers
15
*/
16
abstract class GracefulShutdownServer {
17
/** Indicates if the server is currently shutting down */
18
public isShuttingDown: boolean;
19
20
/** Abstract method that must be implemented by concrete classes */
21
public abstract shutdown(timeout?: number): Promise<void>;
22
23
/**
24
* Creates a graceful shutdown server from an existing HTTP server
25
* @param originalServer - The HTTP server to enhance
26
* @param waitingResponseTime - Time to wait between connection checks (default: 200ms)
27
* @returns Enhanced server with graceful shutdown capabilities
28
*/
29
public static create<T extends Server>(
30
originalServer: T,
31
waitingResponseTime?: number
32
): T & GracefulShutdownServer;
33
}
34
```
35
36
**Usage Examples:**
37
38
```typescript
39
import { GracefulShutdownServer } from "medusa-core-utils";
40
import { createServer } from "http";
41
import express from "express";
42
43
// Create Express app and HTTP server
44
const app = express();
45
const httpServer = createServer(app);
46
47
// Enhance server with graceful shutdown
48
const server = GracefulShutdownServer.create(httpServer);
49
50
// Start server
51
server.listen(3000, () => {
52
console.log("Server running on port 3000");
53
});
54
55
// Handle shutdown signals
56
const gracefulShutdown = async (signal: string) => {
57
console.log(`Received ${signal}. Starting graceful shutdown...`);
58
59
try {
60
// Stop accepting new connections and wait for existing ones to finish
61
await server.shutdown(30000); // 30 second timeout
62
console.log("Server shut down gracefully");
63
process.exit(0);
64
} catch (error) {
65
console.error("Error during shutdown:", error);
66
process.exit(1);
67
}
68
};
69
70
// Register signal handlers
71
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
72
process.on("SIGINT", () => gracefulShutdown("SIGINT"));
73
74
// Check shutdown status
75
app.get("/health", (req, res) => {
76
if (server.isShuttingDown) {
77
res.status(503).json({ status: "shutting down" });
78
} else {
79
res.json({ status: "healthy" });
80
}
81
});
82
83
// With custom waiting time for faster shutdown cycles
84
const fastShutdownServer = GracefulShutdownServer.create(httpServer, 50);
85
```
86
87
### Configuration File Loading
88
89
Dynamic configuration file loading with error handling and support for various module formats.
90
91
```typescript { .api }
92
/**
93
* Attempts to resolve and load a configuration file from a directory
94
* @param rootDir - The directory to search for the config file
95
* @param configName - The name of the config file (without extension)
96
* @returns Object containing the loaded config, file path, and any errors
97
*/
98
function getConfigFile<TConfig = unknown>(
99
rootDir: string,
100
configName: string
101
): {
102
configModule: TConfig;
103
configFilePath: string;
104
error: any
105
};
106
```
107
108
**Usage Examples:**
109
110
```typescript
111
import { getConfigFile } from "medusa-core-utils";
112
import path from "path";
113
114
// Load application configuration
115
const { configModule, configFilePath, error } = getConfigFile(
116
process.cwd(),
117
"medusa-config"
118
);
119
120
if (error) {
121
console.log("No config file found, using defaults");
122
// Handle missing config gracefully
123
} else {
124
console.log(`Loaded config from: ${configFilePath}`);
125
console.log("Config:", configModule);
126
}
127
128
// Type-safe configuration loading
129
interface AppConfig {
130
database: {
131
host: string;
132
port: number;
133
};
134
redis: {
135
url: string;
136
};
137
server: {
138
port: number;
139
};
140
}
141
142
function loadAppConfig(): AppConfig {
143
const { configModule, error } = getConfigFile<AppConfig>(
144
process.cwd(),
145
"app-config"
146
);
147
148
if (error || !configModule) {
149
// Return default configuration
150
return {
151
database: {
152
host: "localhost",
153
port: 5432
154
},
155
redis: {
156
url: "redis://localhost:6379"
157
},
158
server: {
159
port: 3000
160
}
161
};
162
}
163
164
return configModule;
165
}
166
167
// Multi-environment config loading
168
function loadEnvironmentConfig(env: string = "development") {
169
const configName = `config.${env}`;
170
const { configModule, error } = getConfigFile(
171
path.join(process.cwd(), "config"),
172
configName
173
);
174
175
if (error) {
176
console.warn(`Failed to load ${configName}, falling back to default config`);
177
return getConfigFile(process.cwd(), "config").configModule;
178
}
179
180
return configModule;
181
}
182
183
// Plugin configuration loading
184
function loadPluginConfigs(pluginDir: string): Record<string, any> {
185
const fs = require("fs");
186
const plugins = {};
187
188
try {
189
const files = fs.readdirSync(pluginDir);
190
191
for (const file of files) {
192
if (file.endsWith("-config.js")) {
193
const pluginName = file.replace("-config.js", "");
194
const { configModule, error } = getConfigFile(pluginDir, file.replace(".js", ""));
195
196
if (!error && configModule) {
197
plugins[pluginName] = configModule;
198
}
199
}
200
}
201
} catch (error) {
202
console.warn("Failed to load plugin configs:", error.message);
203
}
204
205
return plugins;
206
}
207
```
208
209
### Version Detection
210
211
Utility to detect the installed version of the Medusa framework.
212
213
```typescript { .api }
214
/**
215
* Gets the version of the installed @medusajs/medusa package
216
* @returns Version string or empty string if not found
217
*/
218
function getMedusaVersion(): string;
219
```
220
221
**Usage Examples:**
222
223
```typescript
224
import { getMedusaVersion } from "medusa-core-utils";
225
226
// Get current Medusa version
227
const version = getMedusaVersion();
228
229
if (version) {
230
console.log(`Medusa version: ${version}`);
231
} else {
232
console.log("Medusa package not found");
233
}
234
235
// Version compatibility checking
236
function checkCompatibility() {
237
const currentVersion = getMedusaVersion();
238
239
if (!currentVersion) {
240
throw new Error("Medusa package not installed");
241
}
242
243
const [major, minor] = currentVersion.split('.').map(Number);
244
245
if (major < 1 || (major === 1 && minor < 2)) {
246
throw new Error(`Unsupported Medusa version: ${currentVersion}. Minimum required: 1.2.0`);
247
}
248
249
return currentVersion;
250
}
251
252
// System information gathering
253
function getSystemInfo() {
254
return {
255
nodeVersion: process.version,
256
medusaVersion: getMedusaVersion(),
257
platform: process.platform,
258
arch: process.arch,
259
environment: process.env.NODE_ENV || "development"
260
};
261
}
262
263
// Health check endpoint
264
app.get("/health", (req, res) => {
265
const medusaVersion = getMedusaVersion();
266
267
res.json({
268
status: "healthy",
269
timestamp: new Date().toISOString(),
270
versions: {
271
node: process.version,
272
medusa: medusaVersion || "not-installed"
273
}
274
});
275
});
276
```
277
278
### Module Require Utilities
279
280
Cross-platform utility for creating require functions from specific file paths.
281
282
```typescript { .api }
283
/**
284
* Creates a require function from a specific file path
285
* Polyfill for Node's Module.createRequire (added in Node v10.12.0)
286
* @param filename - The file path to create require function for
287
* @returns require function scoped to the given path
288
*/
289
function createRequireFromPath(filename: string): NodeRequire;
290
```
291
292
**Usage Examples:**
293
294
```typescript
295
import { createRequireFromPath } from "medusa-core-utils";
296
import path from "path";
297
298
// Create require function for a specific directory
299
const pluginDir = path.join(process.cwd(), "plugins", "my-plugin");
300
const pluginRequire = createRequireFromPath(path.join(pluginDir, "index.js"));
301
302
// Use the scoped require to load modules relative to plugin directory
303
try {
304
const pluginConfig = pluginRequire("./config.json");
305
const pluginHelpers = pluginRequire("./helpers");
306
307
console.log("Plugin loaded successfully");
308
} catch (error) {
309
console.error("Failed to load plugin:", error);
310
}
311
312
// Dynamic module loading from different contexts
313
function loadModuleFromPath(modulePath: string, fromDirectory: string) {
314
const requireFromPath = createRequireFromPath(
315
path.join(fromDirectory, "package.json")
316
);
317
318
try {
319
return requireFromPath(modulePath);
320
} catch (error) {
321
throw new Error(`Failed to load module ${modulePath} from ${fromDirectory}: ${error.message}`);
322
}
323
}
324
325
// Plugin system implementation
326
class PluginLoader {
327
private pluginDir: string;
328
private pluginRequire: NodeRequire;
329
330
constructor(pluginDirectory: string) {
331
this.pluginDir = pluginDirectory;
332
this.pluginRequire = createRequireFromPath(
333
path.join(pluginDirectory, "index.js")
334
);
335
}
336
337
loadPlugin(pluginName: string) {
338
try {
339
const pluginMain = this.pluginRequire(`./${pluginName}`);
340
const pluginPackage = this.pluginRequire(`./${pluginName}/package.json`);
341
342
return {
343
name: pluginName,
344
version: pluginPackage.version,
345
main: pluginMain,
346
path: path.join(this.pluginDir, pluginName)
347
};
348
} catch (error) {
349
throw new Error(`Failed to load plugin ${pluginName}: ${error.message}`);
350
}
351
}
352
}
353
354
// Cross-Node.js version compatibility
355
function createRequire(filename: string) {
356
// Use native createRequire if available (Node.js 12+)
357
if (require.createRequire) {
358
return require.createRequire(filename);
359
}
360
361
// Fallback to custom implementation for older Node.js
362
return createRequireFromPath(filename);
363
}