CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-astro

Astro is a modern site builder with web best practices, performance, and DX front-of-mind.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

ssr-and-app.mddocs/

SSR and Application

Astro's SSR (Server-Side Rendering) application class provides the runtime for deploying Astro sites to server environments through adapters.

Capabilities

App Class

The main application class for server-side rendering in production.

/**
 * Main SSR application class
 * Created from build output for production deployment
 */
class App {
  constructor(manifest: SSRManifest, streaming?: boolean);
}

Render Request

Renders an incoming request to a Response.

/**
 * Renders a request to a Response
 * @param request - Incoming HTTP request
 * @param options - Rendering options
 * @returns Promise resolving to HTTP Response
 */
render(request: Request, options?: RenderOptions): Promise<Response>;

interface RenderOptions {
  /**
   * Route data for the request (from match())
   */
  routeData?: RouteData;

  /**
   * Locals object for context
   */
  locals?: App.Locals;

  /**
   * Client IP address
   */
  clientAddress?: string;

  /**
   * Add Set-Cookie header to response
   * @default true
   */
  addCookieHeader?: boolean;
}
import { App } from 'astro/app';
import manifest from './manifest.mjs';

const app = new App(manifest);

export default {
  async fetch(request) {
    const response = await app.render(request, {
      clientAddress: request.headers.get('CF-Connecting-IP'),
      locals: {},
    });
    return response;
  },
};

Match Route

Matches a request to a route.

/**
 * Matches a request to a route
 * @param request - Incoming HTTP request
 * @returns RouteData if matched, undefined otherwise
 */
match(request: Request): RouteData | undefined;
import { App } from 'astro/app';
import manifest from './manifest.mjs';

const app = new App(manifest);

export default {
  async fetch(request) {
    // Match the route
    const routeData = app.match(request);

    if (!routeData) {
      return new Response('Not Found', { status: 404 });
    }

    // Render with matched route
    return app.render(request, { routeData });
  },
};

Get Adapter Logger

Returns an adapter-specific logger instance.

/**
 * Gets the adapter logger
 * @returns Integration logger for adapter use
 */
getAdapterLogger(): AstroIntegrationLogger;
import { App } from 'astro/app';
import manifest from './manifest.mjs';

const app = new App(manifest);
const logger = app.getAdapterLogger();

logger.info('Adapter initialized');
logger.warn('Custom warning from adapter');

Get Allowed Domains

Returns the list of allowed domains for remote images and forwarded hosts.

/**
 * Gets the allowed domains from manifest
 * @returns Array of remote patterns for allowed domains
 */
getAllowedDomains(): Partial<RemotePattern>[];
import { App } from 'astro/app';
import manifest from './manifest.mjs';

const app = new App(manifest);
const allowedDomains = app.getAllowedDomains();
console.log('Allowed domains:', allowedDomains);

Remove Base

Removes the configured base path from a pathname.

/**
 * Removes base path from pathname
 * @param pathname - Full pathname including base
 * @returns Pathname without base prefix
 */
removeBase(pathname: string): string;
import { App } from 'astro/app';
import manifest from './manifest.mjs';

const app = new App(manifest);
// If base is '/docs', this removes it
const withoutBase = app.removeBase('/docs/guide/intro');
// Result: 'guide/intro'

Set Cookie Headers

Extracts Set-Cookie headers from a Response object as an instance method.

/**
 * Extracts Set-Cookie headers from Response (instance method)
 * @param response - Web API Response
 * @returns Array of cookie strings
 */
setCookieHeaders(response: Response): string[];
import { App } from 'astro/app';
import manifest from './manifest.mjs';

const app = new App(manifest);
const response = await app.render(request);
const cookies = app.setCookieHeaders(response);
console.log('Cookies to set:', cookies);

Get Set-Cookie From Response (Static)

Extracts Set-Cookie headers from a Response object. This is a static method on the App class.

/**
 * Extracts Set-Cookie headers from Response
 * @param response - Web API Response
 * @returns Array of cookie strings
 */
static getSetCookieFromResponse(response: Response): string[];
import { App } from 'astro/app';

const response = await app.render(request);
const cookies = App.getSetCookieFromResponse(response);
console.log('Cookies to set:', cookies);

Validate Forwarded Host (Static)

Validates a forwarded host header against allowed host patterns. This is a static method on the App class.

/**
 * Validates forwarded host header
 * Checks if forwarded host matches allowed host patterns
 * @param forwardedHost - Forwarded host value from X-Forwarded-Host header
 * @param allowedHosts - Array of allowed host patterns (supports wildcards)
 * @param protocol - Protocol (http or https)
 * @returns Whether the host is valid
 */
static validateForwardedHost(
  forwardedHost: string,
  allowedDomains?: Partial<RemotePattern>[],
  protocol?: string
): boolean;

interface RemotePattern {
  protocol?: string;
  hostname: string;
  port?: string;
  pathname?: string;
}
import { App } from 'astro/app';

const forwardedHost = request.headers.get('x-forwarded-host');
if (forwardedHost && !App.validateForwardedHost(
  forwardedHost,
  ['example.com', '*.example.com']
)) {
  return new Response('Invalid host', { status: 403 });
}

Deserialize Manifest

Deserializes a manifest from build output.

/**
 * Deserializes a manifest from build output
 * @param manifest - Serialized manifest
 * @returns Deserialized SSR manifest
 */
function deserializeManifest(
  manifest: SerializedSSRManifest
): SSRManifest;

Node.js App

Node.js-specific exports for Node adapters.

// From astro/app/node
import { NodeApp } from 'astro/app/node';
import type { NodeAppOptions } from 'astro/app/node';

The Node.js app provides additional functionality for Node.js-based adapters, including middleware integration and response handling.

Types

SSRManifest

interface SSRManifest {
  /**
   * Base URL for the site
   */
  base: string;

  /**
   * Trailing slash behavior
   */
  trailingSlash: 'always' | 'never' | 'ignore';

  /**
   * Compress HTML output
   */
  compressHTML: boolean;

  /**
   * Assets configuration
   */
  assets: Set<string>;

  /**
   * Assets prefix for CDN
   */
  assetsPrefix?: string | Record<string, string>;

  /**
   * Entry modules
   */
  entryModules: Record<string, string>;

  /**
   * Routes
   */
  routes: RouteInfo[];

  /**
   * Renderers for framework components
   */
  renderers: SSRLoadedRenderer[];

  /**
   * Client directives
   */
  clientDirectives: Map<string, ClientDirective>;

  /**
   * Component metadata
   */
  componentMetadata: Map<string, ComponentMetadata>;

  /**
   * Inlined scripts
   */
  inlinedScripts: Map<string, string>;

  /**
   * i18n configuration
   */
  i18n?: I18NConfig;

  /**
   * Middleware function
   */
  middleware?: () => AstroMiddlewareInstance;

  /**
   * Check origin header
   */
  checkOrigin: boolean;
}

RouteData

interface RouteData {
  /**
   * Route pattern (e.g., '/blog/[slug]')
   */
  route: string;

  /**
   * Route type: 'page', 'endpoint', or 'redirect'
   */
  type: 'page' | 'endpoint' | 'redirect';

  /**
   * URL pattern for matching
   */
  pattern: RegExp;

  /**
   * Route parameter names
   */
  params: string[];

  /**
   * Component path
   */
  component: string;

  /**
   * Generate function for static paths
   */
  generate: (data: any) => string;

  /**
   * Path segments
   */
  segments: string[][];

  /**
   * Whether route is prerendered
   */
  prerender: boolean;

  /**
   * Fallback routes for i18n
   */
  fallbackRoutes: RouteData[];

  /**
   * Whether route is an index
   */
  isIndex: boolean;

  /**
   * Route origin
   */
  origin: 'internal' | 'external';
}

RouteInfo

interface RouteInfo {
  /**
   * Route data
   */
  routeData: RouteData;

  /**
   * File path
   */
  file: string;

  /**
   * Linked stylesheets
   */
  links: string[];

  /**
   * Linked styles
   */
  styles: StyleInfo[];

  /**
   * Linked scripts
   */
  scripts: ScriptInfo[];
}

Adapter Integration

Adapters use the App class to integrate Astro with deployment platforms.

Adapter Interface

interface AstroAdapter {
  /**
   * Adapter name
   */
  name: string;

  /**
   * Server entrypoint
   */
  serverEntrypoint?: string;

  /**
   * Previewable adapter
   */
  previewEntrypoint?: string;

  /**
   * Exports for the entrypoint
   */
  exports?: string[];

  /**
   * Additional arguments for entrypoint
   */
  args?: any;

  /**
   * Adapter features
   */
  supportedAstroFeatures?: AstroAdapterFeatures;
}

Adapter Features

interface AstroAdapterFeatures {
  /**
   * Static assets support
   */
  staticAssets?: 'stable' | 'experimental' | 'unsupported';

  /**
   * Hybrid rendering support
   */
  hybrid?: 'stable' | 'experimental' | 'unsupported';

  /**
   * Server output support
   */
  serverOutput?: 'stable' | 'experimental' | 'unsupported';

  /**
   * Environment variables support
   */
  envGetSecret?: 'stable' | 'experimental' | 'unsupported';

  /**
   * i18n domains support
   */
  i18nDomains?: 'stable' | 'experimental' | 'unsupported';
}

Example Adapter Implementation

// Example adapter for a custom platform
import type { AstroAdapter, AstroIntegration } from 'astro';

export default function myAdapter(): AstroIntegration {
  return {
    name: 'my-adapter',
    hooks: {
      'astro:config:setup': ({ updateConfig }) => {
        updateConfig({
          vite: {
            // Vite config
          },
        });
      },
      'astro:config:done': ({ setAdapter }) => {
        setAdapter({
          name: 'my-adapter',
          serverEntrypoint: 'my-adapter/server.js',
          supportedAstroFeatures: {
            staticAssets: 'stable',
            hybrid: 'stable',
            serverOutput: 'stable',
          },
        });
      },
      'astro:build:done': async ({ dir }) => {
        // Post-build processing
      },
    },
  };
}

Server Entrypoint Example

// Server entrypoint for adapter
import { App } from 'astro/app';
import { polyfill } from '@astrojs/webapi';

polyfill(globalThis, {
  exclude: 'window document',
});

export function createExports(manifest) {
  const app = new App(manifest);

  const handler = async (request) => {
    // Get client IP from platform headers
    const clientAddress =
      request.headers.get('x-forwarded-for') ||
      request.headers.get('x-real-ip');

    // Render the request
    const response = await app.render(request, {
      clientAddress,
    });

    return response;
  };

  return { handler };
}

Platform-Specific Integrations

Cloudflare Workers

import { App } from 'astro/app';

export default {
  async fetch(request, env, ctx) {
    const app = new App(manifest);

    const response = await app.render(request, {
      clientAddress: request.headers.get('CF-Connecting-IP'),
      locals: { env, ctx },
    });

    return response;
  },
};

Vercel Edge

import { App } from 'astro/app';

export default async function handler(request) {
  const app = new App(manifest);

  return app.render(request, {
    clientAddress: request.headers.get('x-forwarded-for'),
  });
}

Netlify Functions

import { App } from 'astro/app';

const app = new App(manifest);

export const handler = async (event, context) => {
  const request = createRequestFromEvent(event);

  const response = await app.render(request, {
    clientAddress: event.headers['client-ip'],
  });

  return createResponseForNetlify(response);
};

Node.js Server

import { NodeApp } from 'astro/app/node';
import { createServer } from 'http';

const app = new NodeApp(manifest);

createServer(async (req, res) => {
  const request = NodeApp.createRequest(req);
  const response = await app.render(request);

  await NodeApp.writeResponse(response, res);
}).listen(3000);

Locals Type Safety

Define types for locals in src/env.d.ts:

/// <reference types="astro/client" />

declare namespace App {
  interface Locals {
    // Platform-specific context
    env?: {
      DATABASE_URL: string;
      API_KEY: string;
    };

    // User data
    user?: {
      id: string;
      name: string;
    };

    // Request metadata
    requestId?: string;
    startTime?: number;
  }
}

Node App Methods

The NodeApp class provides additional static methods for Node.js adapters.

class NodeApp extends App {
  constructor(manifest: SSRManifest);

  /**
   * Creates a Web API Request from Node.js IncomingMessage
   * @param req - Node.js request object
   * @param options - Request creation options
   * @returns Web API Request
   */
  static createRequest(
    req: import('http').IncomingMessage,
    options?: {
      skipBody?: boolean;
      allowedDomains?: Array<Partial<RemotePattern>>;
    }
  ): Request;

  interface RemotePattern {
    protocol?: string;
    hostname: string;
    port?: string;
    pathname?: string;
  }

  /**
   * Writes a Web API Response to Node.js ServerResponse
   * @param response - Web API Response
   * @param res - Node.js response object
   * @returns Promise that resolves when writing completes
   */
  static writeResponse(
    response: Response,
    res: import('http').ServerResponse
  ): Promise<void>;

  /**
   * Extracts Set-Cookie headers from Response
   * @param response - Web API Response
   * @returns Array of cookie strings
   */
  static getSetCookieFromResponse(response: Response): string[];

  /**
   * Validates forwarded host header
   * Checks if forwarded host matches allowed hosts
   * @param forwardedHost - Forwarded host value
   * @param allowedHosts - Array of allowed host patterns
   * @returns Whether the host is valid
   */
  static validateForwardedHost(
    forwardedHost: string,
    allowedHosts: string[]
  ): boolean;

  /**
   * Sets custom headers map for pathname-based header injection
   * @param headers - Headers configuration by pathname
   */
  setHeadersMap(headers: NodeAppHeadersJson): void;
}

type NodeAppHeadersJson = Array<{
  pathname: string;
  headers: Array<{
    key: string;
    value: string;
  }>;
}>;

Usage Examples:

import { NodeApp } from 'astro/app/node';
import { createServer } from 'http';
import { loadManifest } from 'astro/app/node';

const manifest = await loadManifest('./dist/server/manifest.json');
const app = new NodeApp(manifest);

const server = createServer(async (req, res) => {
  // Validate forwarded host
  const forwardedHost = req.headers['x-forwarded-host'];
  if (forwardedHost && !NodeApp.validateForwardedHost(forwardedHost, ['example.com', '*.example.com'])) {
    res.statusCode = 403;
    res.end('Invalid host');
    return;
  }

  // Create request with custom headers
  const request = NodeApp.createRequest(req, {
    headers: {
      'x-custom-header': 'value',
    },
  });

  // Render the page
  const response = await app.render(request);

  // Extract cookies before writing response
  const cookies = NodeApp.getSetCookieFromResponse(response);
  console.log('Setting cookies:', cookies);

  // Write response
  await NodeApp.writeResponse(response, res);
});

server.listen(3000);

Node.js Manifest Utilities

Utilities for loading manifests in Node.js environments.

/**
 * Loads SSR manifest from filesystem
 * @param manifestPath - Path to manifest.json file
 * @returns Promise resolving to deserialized manifest
 */
async function loadManifest(rootFolder: URL): Promise<SSRManifest>;

/**
 * Loads NodeApp with manifest from filesystem
 * Convenience function combining loadManifest and NodeApp constructor
 * @param rootFolder - URL to the root folder containing the manifest
 * @returns Promise resolving to NodeApp instance
 */
async function loadApp(rootFolder: URL): Promise<NodeApp>;

/**
 * Applies Node.js polyfills for Web APIs
 * Required for some server environments
 * Polyfills: fetch, Headers, Request, Response, crypto
 */
function applyPolyfills(): void;
import { loadApp, applyPolyfills } from 'astro/app/node';
import { createServer } from 'http';

// Apply polyfills if needed
applyPolyfills();

// Load app from manifest
const app = await loadApp('./dist/server/manifest.json');

createServer(async (req, res) => {
  const request = app.constructor.createRequest(req);
  const response = await app.render(request);
  await app.constructor.writeResponse(response, res);
}).listen(3000);

Module Imports

// Base App class
import { App, deserializeManifest } from 'astro/app';
import type { RenderOptions, SSRManifest, SerializedSSRManifest } from 'astro/app';

// Node.js-specific exports
import { NodeApp, loadManifest, loadApp, applyPolyfills } from 'astro/app/node';
import type { NodeAppOptions } from 'astro/app/node';

docs

assets.md

cli-and-build.md

configuration.md

container.md

content-collections.md

content-loaders.md

dev-toolbar.md

environment.md

i18n.md

index.md

integrations.md

middleware.md

server-actions.md

ssr-and-app.md

transitions.md

tile.json