Node.js adapter for Astro framework enabling server-side rendering and standalone server deployments
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Server creation and management utilities for HTTP/HTTPS servers with graceful shutdown, networking features, and logging capabilities.
Creates HTTP or HTTPS server with SSL support, graceful shutdown capabilities, and network interface detection.
/**
* Creates HTTP/HTTPS server with SSL support via environment variables
* Includes graceful shutdown and server-destroy integration
* @param listener - Request listener function for handling requests
* @param host - Host to bind server to
* @param port - Port to bind server to
* @returns PreviewServer instance with control methods and server access
*/
function createServer(
listener: http.RequestListener,
host: string,
port: number
): PreviewServer & { server: http.Server | https.Server };
interface PreviewServer {
/** Host server is bound to */
host: string;
/** Port server is listening on */
port: number;
/** Promise that resolves when server is closed */
closed(): Promise<void>;
/** Gracefully stop the server */
stop(): Promise<void>;
}Usage Examples:
import { createServer } from "@astrojs/node/standalone.js";
import { createStandaloneHandler } from "@astrojs/node/standalone.js";
const handler = createStandaloneHandler(app, options);
const { server, host, port, stop } = createServer(handler, "localhost", 8080);
// Server is ready to accept connections
console.log(`Server created on ${host}:${port}`);
// Graceful shutdown
process.on('SIGTERM', async () => {
await stop();
process.exit(0);
});HTTPS Configuration:
# Environment variables for SSL
export SERVER_CERT_PATH="/path/to/certificate.pem"
export SERVER_KEY_PATH="/path/to/private-key.pem"Creates and starts a complete standalone server with automatic configuration and logging.
/**
* Creates and starts standalone HTTP/HTTPS server
* Automatically resolves host/port from environment and options
* @param app - NodeApp instance for request handling
* @param options - Resolved adapter options including host/port configuration
* @returns Server instance with done promise for shutdown handling
*/
function standalone(
app: NodeApp,
options: Options
): {
server: PreviewServer & { server: http.Server | https.Server };
done: Promise<void>;
};Usage Examples:
import { NodeApp } from "astro/app/node";
import standalone from "@astrojs/node/standalone.js";
const app = new NodeApp(manifest);
const { server, done } = standalone(app, options);
// Wait for server to close
await done;Environment Variable Resolution:
options.port (default: 8080)options.hostResolves host configuration for server binding with sensible defaults.
/**
* Resolves host value for server binding
* @param host - Host configuration from options (boolean or string)
* @returns Resolved host string for server.listen()
*/
function hostOptions(host: Options['host']): string;
/**
* Resolves host configuration for HTTP server binding used in logging and preview
* @param host - Host configuration from options or CLI arguments
* @returns Resolved host string for server operations
*/
function getResolvedHostForHttpServer(host: string | boolean | undefined): string | undefined;Host Resolution Logic:
true: Returns '0.0.0.0' (listen on all interfaces)false: Returns 'localhost' (secure default)string: Returns the provided host valueUsage Examples:
import { hostOptions } from "@astrojs/node/standalone.js";
// Configuration examples
console.log(hostOptions(true)); // '0.0.0.0'
console.log(hostOptions(false)); // 'localhost'
console.log(hostOptions('127.0.0.1')); // '127.0.0.1'
console.log(hostOptions('::1')); // '::1'Automatic HTTPS server creation when certificate files are provided:
if (process.env.SERVER_CERT_PATH && process.env.SERVER_KEY_PATH) {
httpServer = https.createServer(
{
key: fs.readFileSync(process.env.SERVER_KEY_PATH),
cert: fs.readFileSync(process.env.SERVER_CERT_PATH),
},
listener,
);
} else {
httpServer = http.createServer(listener);
}Certificate Requirements:
SERVER_CERT_PATH and SERVER_KEY_PATH must be setCertificate Formats:
# Development with self-signed certificates
export SERVER_CERT_PATH="./certs/dev-cert.pem"
export SERVER_KEY_PATH="./certs/dev-key.pem"
npm run preview
# Production with CA-signed certificates
export SERVER_CERT_PATH="/etc/ssl/certs/domain.pem"
export SERVER_KEY_PATH="/etc/ssl/private/domain.key"
node dist/server.jsUses server-destroy library for graceful connection termination:
import enableDestroy from 'server-destroy';
const httpServer = http.createServer(listener);
enableDestroy(httpServer);
// Graceful shutdown capability
async stop() {
await new Promise((resolve, reject) => {
httpServer.destroy((err) => (err ? reject(err) : resolve(undefined)));
});
}Shutdown Process:
// Resolves when server is closed
const closed = new Promise<void>((resolve, reject) => {
httpServer.addListener('close', resolve);
httpServer.addListener('error', reject);
});Lifecycle Events:
Comprehensive network address detection and logging with local and network interface discovery:
/**
* Logs server listening information with network address detection
* @param logger - Astro integration logger for formatted output
* @param server - HTTP/HTTPS server instance
* @param configuredHost - Host configuration for address resolution
* @returns Promise that resolves when logging is complete
*/
function logListeningOn(
logger: AstroIntegrationLogger,
server: http.Server | https.Server,
configuredHost: string | boolean | undefined
): Promise<void>;Network Interface Detection:
os.networkInterfaces()Example Logging Output:
# Wildcard host (0.0.0.0 or ::)
Server listening on
local: http://localhost:8080
network: http://192.168.1.100:8080
# Specific host
Server listening on http://localhost:8080const wildcardHosts = new Set(['0.0.0.0', '::', '0000:0000:0000:0000:0000:0000:0000:0000']);
if (host === undefined || wildcardHosts.has(host)) {
// Show both local and network addresses
} else {
// Show only the configured address
}Common server creation and startup errors:
// Port already in use
server.on('error', (err) => {
if (err.code === 'EADDRINUSE') {
console.error(`Port ${port} is already in use`);
}
});
// Permission denied (privileged ports)
if (err.code === 'EACCES') {
console.error(`Permission denied binding to port ${port}`);
}HTTPS-specific error handling:
try {
const cert = fs.readFileSync(process.env.SERVER_CERT_PATH);
const key = fs.readFileSync(process.env.SERVER_KEY_PATH);
} catch (err) {
console.error('Failed to read SSL certificate files:', err.message);
process.exit(1);
}Common SSL Errors:
Graceful shutdown error handling:
async stop() {
try {
await new Promise((resolve, reject) => {
httpServer.destroy((err) => (err ? reject(err) : resolve(undefined)));
});
} catch (err) {
console.error('Error during server shutdown:', err);
throw err;
}
}// Graceful shutdown handling
process.on('SIGTERM', async () => {
console.log('Received SIGTERM, shutting down gracefully');
await server.stop();
process.exit(0);
});
process.on('SIGINT', async () => {
console.log('Received SIGINT, shutting down gracefully');
await server.stop();
process.exit(0);
});import { createStandaloneHandler } from "@astrojs/node/standalone.js";
const handler = createStandaloneHandler(app, options);
// Wrap handler with health check
const healthCheckHandler = (req, res) => {
if (req.url === '/health') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok', timestamp: Date.now() }));
return;
}
return handler(req, res);
};
const server = createServer(healthCheckHandler, host, port);// Trust proxy headers for load balancer setups
const handler = (req, res) => {
// Handle X-Forwarded-For, X-Forwarded-Proto headers
req.headers['x-forwarded-host'] = req.headers['x-forwarded-host'] || req.headers.host;
return standaloneHandler(req, res);
};