or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

error-handling.mdhttp-integration.mdindex.mdplugins.mdserver-lifecycle.mdtypes.md
tile.json

http-integration.mddocs/

HTTP Integration

HTTP request handling, standalone server creation, and framework integration capabilities for Apollo Server.

Capabilities

Standalone Server

Ready-to-use HTTP server implementation for Apollo Server with built-in CORS and body parsing.

/**
 * Starts Apollo Server as a standalone HTTP server
 * @param server - Apollo Server instance
 * @param options - Server configuration and listen options
 * @returns Promise resolving to server URL
 */
function startStandaloneServer(
  server: ApolloServer<BaseContext>,
  options?: StartStandaloneServerOptions<BaseContext> & {
    listen?: ListenOptions;
  }
): Promise<{ url: string }>;

/**
 * Starts Apollo Server with custom context type
 * @param server - Apollo Server instance with custom context
 * @param options - Server configuration with required context function
 * @returns Promise resolving to server URL
 */
function startStandaloneServer<TContext extends BaseContext>(
  server: ApolloServer<TContext>,
  options: WithRequired<StartStandaloneServerOptions<TContext>, 'context'> & {
    listen?: ListenOptions;
  }
): Promise<{ url: string }>;

/**
 * Options for standalone server
 */
interface StartStandaloneServerOptions<TContext extends BaseContext> {
  /** Context function receiving HTTP request/response */
  context?: ContextFunction<
    [StandaloneServerContextFunctionArgument],
    TContext
  >;
}

/**
 * Context function argument for standalone server
 */
interface StandaloneServerContextFunctionArgument {
  /** Node.js HTTP request object */
  req: IncomingMessage;
  /** Node.js HTTP response object */
  res: ServerResponse;
}

HTTP Request Types

Types for handling HTTP-specific GraphQL requests and responses.

/**
 * HTTP-specific GraphQL request information
 */
interface HTTPGraphQLRequest {
  /** HTTP method (GET, POST, etc.) */
  method: string;
  /** Request headers */
  headers: HeaderMap;
  /** URL search parameters */
  search: string;
  /** Request body */
  body: unknown;
}

/**
 * HTTP response headers and status
 */
interface HTTPGraphQLHead {
  /** HTTP status code */
  status?: number;
  /** Response headers */
  headers: HeaderMap;
}

/**
 * HTTP GraphQL response body
 */
type HTTPGraphQLResponseBody = 
  | { kind: 'complete'; string: string }
  | { kind: 'chunked'; asyncIterator: AsyncIterator<string> };

/**
 * Complete HTTP GraphQL response
 */
interface HTTPGraphQLResponse {
  /** HTTP status code */
  status?: number;
  /** Response headers */
  headers: HeaderMap;
  /** Response body */
  body: HTTPGraphQLResponseBody;
}

HTTP Request Execution

Execute GraphQL requests through HTTP with complete request/response handling.

/**
 * Executes a GraphQL request via HTTP protocol
 * @param options - HTTP request and context configuration
 * @returns Promise resolving to HTTP GraphQL response
 */
executeHTTPGraphQLRequest(options: {
  httpGraphQLRequest: HTTPGraphQLRequest;
  context: ContextThunk<TContext>;
}): Promise<HTTPGraphQLResponse>;

HeaderMap Utility

Case-insensitive HTTP header management utility extending JavaScript Map.

/**
 * Case-insensitive HTTP header map
 * Extends Map<string, string> with case-insensitive key handling
 */
class HeaderMap extends Map<string, string> {
  /**
   * Sets a header value (key normalized to lowercase)
   * @param key - Header name
   * @param value - Header value
   * @returns this for chaining
   */
  set(key: string, value: string): this;

  /**
   * Gets a header value by name (case-insensitive)
   * @param key - Header name
   * @returns Header value or undefined
   */
  get(key: string): string | undefined;

  /**
   * Deletes a header by name (case-insensitive)
   * @param key - Header name
   * @returns true if header existed and was deleted
   */
  delete(key: string): boolean;

  /**
   * Checks if header exists by name (case-insensitive)
   * @param key - Header name
   * @returns true if header exists
   */
  has(key: string): boolean;
}

Context Types

Types for managing GraphQL execution context in HTTP environments.

/**
 * Base context type that all contexts must extend
 */
type BaseContext = {};

/**
 * Function type for integration authors to create context
 * @template TIntegrationSpecificArgs - Arguments provided by the integration
 * @template TContext - The context type to return
 */
type ContextFunction<
  TIntegrationSpecificArgs extends ReadonlyArray<any>,
  TContext extends BaseContext
> = (...args: TIntegrationSpecificArgs) => Promise<TContext>;

/**
 * Internal context thunk type
 */
type ContextThunk<TContext extends BaseContext> = () => Promise<TContext>;

Usage Examples

Basic Standalone Server

import { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone";

const server = new ApolloServer({
  typeDefs: `
    type Query {
      hello: String
    }
  `,
  resolvers: {
    Query: {
      hello: () => "Hello world!",
    },
  },
});

// Start server on default port 4000
const { url } = await startStandaloneServer(server);
console.log(`๐Ÿš€ Server running at: ${url}`);

Standalone Server with Custom Port and Context

import { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone";

interface MyContext {
  userId?: string;
  userAgent: string;
}

const server = new ApolloServer<MyContext>({
  typeDefs,
  resolvers,
});

const { url } = await startStandaloneServer(server, {
  listen: { port: 8000 },
  context: async ({ req, res }) => {
    // Extract user ID from authorization header
    const token = req.headers.authorization?.replace('Bearer ', '');
    const userId = token ? await verifyToken(token) : undefined;
    
    return {
      userId,
      userAgent: req.headers['user-agent'] || 'unknown',
    };
  },
});

console.log(`๐Ÿš€ Server running at: ${url}`);

Working with Headers

import { HeaderMap } from "@apollo/server";

// Create and manipulate headers
const headers = new HeaderMap();
headers.set('Content-Type', 'application/json');
headers.set('CACHE-CONTROL', 'no-cache'); // Case insensitive

console.log(headers.get('content-type')); // 'application/json'
console.log(headers.get('Cache-Control')); // 'no-cache'
console.log(headers.has('CONTENT-TYPE')); // true

// Iterate over headers
for (const [key, value] of headers) {
  console.log(`${key}: ${value}`);
}

Framework Integration Context

// Example for Express integration (conceptual)
import express from 'express';
import { ApolloServer } from "@apollo/server";

interface ExpressContext {
  req: express.Request;
  res: express.Response;
  user?: User;
}

const server = new ApolloServer<ExpressContext>({
  typeDefs,
  resolvers,
});

// In your Express route handler
app.post('/graphql', async (req, res) => {
  const httpGraphQLResponse = await server.executeHTTPGraphQLRequest({
    httpGraphQLRequest: {
      method: req.method,
      headers: new HeaderMap(Object.entries(req.headers) as [string, string][]),
      search: req.url.split('?')[1] || '',
      body: req.body,
    },
    context: async () => ({
      req,
      res,
      user: req.user, // From authentication middleware
    }),
  });

  // Handle response
  res.status(httpGraphQLResponse.status || 200);
  for (const [key, value] of httpGraphQLResponse.headers) {
    res.setHeader(key, value);
  }
  
  if (httpGraphQLResponse.body.kind === 'complete') {
    res.send(httpGraphQLResponse.body.string);
  } else {
    // Handle chunked response for subscriptions/streaming
    for await (const chunk of httpGraphQLResponse.body.asyncIterator) {
      res.write(chunk);
    }
    res.end();
  }
});

Custom HTTP Server Integration

import http from 'http';
import { ApolloServer } from "@apollo/server";
import { HeaderMap } from "@apollo/server";

const server = new ApolloServer({ typeDefs, resolvers });
await server.start();

const httpServer = http.createServer(async (req, res) => {
  if (req.url !== '/graphql') {
    res.writeHead(404);
    res.end('Not found');
    return;
  }

  // Convert Node.js headers to HeaderMap
  const headers = new HeaderMap();
  for (const [key, value] of Object.entries(req.headers)) {
    if (value !== undefined) {
      headers.set(key, Array.isArray(value) ? value.join(', ') : value);
    }
  }

  // Get request body
  let body = '';
  req.on('data', (chunk) => { body += chunk; });
  req.on('end', async () => {
    try {
      const httpGraphQLResponse = await server.executeHTTPGraphQLRequest({
        httpGraphQLRequest: {
          method: req.method!.toUpperCase(),
          headers,
          search: new URL(req.url!, `http://${req.headers.host}`).search,
          body: body ? JSON.parse(body) : undefined,
        },
        context: async () => ({}),
      });

      // Send response
      res.writeHead(httpGraphQLResponse.status || 200, 
        Object.fromEntries(httpGraphQLResponse.headers));
      
      if (httpGraphQLResponse.body.kind === 'complete') {
        res.end(httpGraphQLResponse.body.string);
      } else {
        for await (const chunk of httpGraphQLResponse.body.asyncIterator) {
          res.write(chunk);
        }
        res.end();
      }
    } catch (error) {
      res.writeHead(500);
      res.end('Internal server error');
    }
  });
});

httpServer.listen(4000, () => {
  console.log('๐Ÿš€ Custom server running at http://localhost:4000/graphql');
});