Overview

Build a GraphQL API with Apollo Server. Best for complex data requirements and flexible client queries.

Features

  • Apollo Server 4 setup
  • Express middleware integration
  • Context for authentication
  • Introspection control

Installation

npx hanma add graphql-server

Dependencies

@apollo/server, graphql

Code

import { ApolloServer } from "@apollo/server";
import { ApolloServerPluginDrainHttpServer } from "@apollo/server/plugin/drainHttpServer";
import express from "express";
import http from "http";
import cors from "cors";

import type { RequestHandler } from "express";
import { expressMiddleware } from "@apollo/server/express4";

/**
 * Apollo ships Express-5 typed middleware
 * but runs on Express-4.
 * This adapter fixes the type universe.
 */
export function apolloExpress4(...args: Parameters<typeof expressMiddleware>) {
  return expressMiddleware(...args) as unknown as RequestHandler;
}


let app: express.Express | null = null;
let httpServer: http.Server | null = null;
let apolloServer: ApolloServer | null = null;

/* ---------------- Schema ---------------- */

const typeDefs = `#graphql
  type Query {
    hello: String
    health: HealthStatus
  }

  type HealthStatus {
    status: String!
    timestamp: String!
  }
`;

const resolvers = {
  Query: {
    hello: () => "Hello from GraphQL!",
    health: () => ({
      status: "ok",
      timestamp: new Date().toISOString(),
    }),
  },
};

export interface GraphQLContext {}

/* ---------------- Start ---------------- */

export async function startGraphQLServer(port = 4000) {
  if (httpServer) {
    console.warn("GraphQL already running");
    return;
  }

  app = express();
  httpServer = http.createServer(app);

  apolloServer = new ApolloServer<GraphQLContext>({
    typeDefs,
    resolvers,
    introspection: process.env.NODE_ENV !== "production",
    plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
  });

  await apolloServer.start();

  app.use(
    "/graphql",
    cors<cors.CorsRequest>(),
    express.json(),
    apolloExpress4(apolloServer, {
      context: async () => ({}),
    }),
  );

  app.get("/health", (_req, res) => {
    res.json({ status: "ok", timestamp: new Date().toISOString() });
  });

  await new Promise<void>((resolve) => httpServer!.listen({ port }, resolve));

  console.log(`GraphQL running at http://localhost:${port}/graphql`);
}

/* ---------------- Shutdown ---------------- */

export async function shutdownGraphQLServer(signal: string) {
  console.log(`Shutdown signal received: ${signal}`);

  try {
    if (apolloServer) {
      await apolloServer.stop();
      apolloServer = null;
    }

    if (httpServer) {
      await new Promise<void>((resolve, reject) => {
        httpServer!.close((err) => (err ? reject(err) : resolve()));
      });
      httpServer = null;
    }

    app = null;
    console.log("GraphQL shutdown complete");
    process.exit(0);
  } catch (err) {
    console.error("Shutdown failed", err);
    process.exit(1);
  }
}

Usage

import { createApolloServer } from './libs/servers/graphql-server';

const server = await createApolloServer();
await server.start();
app.use('/graphql', expressMiddleware(server));

Dependencies

Frequently Asked Questions

How do I install Apollo GraphQL Server?
Run the following command: npx hanma add graphql-server
What dependencies are required?
This snippet requires: @apollo/server, graphql. Make sure to install them before using the snippet.
How do I use this snippet?
Copy the code and paste it into your project. The snippet will be saved to libs/servers/graphql-server.ts.
Can I customize this snippet?
Yes! All snippets are copy-paste code that you own. Feel free to modify them to fit your needs.
Does this work with other frameworks?
This snippet is designed for the specific framework. Check the other framework pages for equivalent implementations.