Labs ICT
Pro Login

Apollo Server Setup

Apollo Server Setup

Apollo Server is the most popular GraphQL server for Node.js. It's production-ready, well-documented, and has a rich ecosystem of tools.

Let's set up a complete Apollo Server project from scratch.

Project Setup

Create a new project and install the necessary packages:

// Create project
mkdir apollo-server-demo
cd apollo-server-demo
npm init -y

// Install dependencies
npm install @apollo/server graphql

// For development
npm install -D nodemon

Apollo Server 4 has a modular design that works with any Node.js framework.

Complete Server Setup

Here's a complete server with schema, resolvers, and context:

const { ApolloServer } = require('@apollo/server');
const { startStandaloneServer } = require('@apollo/server/standalone');

// Schema
const typeDefs = `
  type User {
    id: ID!
    name: String!
    email: String!
    posts: [Post!]!
  }

  type Post {
    id: ID!
    title: String!
    content: String!
    author: User!
  }

  type Query {
    user(id: ID!): User
    users: [User!]!
    post(id: ID!): Post
  }

  type Mutation {
    createUser(name: String!, email: String!): User!
    createPost(title: String!, content: String!, authorId: ID!): Post!
  }
`;

// Sample data
const users = [
  { id: '1', name: 'Alice', email: 'alice@example.com' },
  { id: '2', name: 'Bob', email: 'bob@example.com' },
];

const posts = [
  { id: '1', title: 'First Post', content: 'Hello world!', authorId: '1' },
  { id: '2', title: 'Second Post', content: 'GraphQL is great!', authorId: '1' },
];

// Resolvers
const resolvers = {
  Query: {
    user: (parent, args) => users.find(u => u.id === args.id),
    users: () => users,
    post: (parent, args) => posts.find(p => p.id === args.id),
  },
  
  User: {
    posts: (parent) => posts.filter(p => p.authorId === parent.id),
  },
  
  Post: {
    author: (parent) => users.find(u => u.id === parent.authorId),
  },
  
  Mutation: {
    createUser: (parent, args) => {
      const user = { id: String(users.length + 1), ...args };
      users.push(user);
      return user;
    },
    createPost: (parent, args) => {
      const post = { id: String(posts.length + 1), ...args };
      posts.push(post);
      return post;
    },
  },
};

// Start server
async function startServer() {
  const server = new ApolloServer({ typeDefs, resolvers });
  const { url } = await startStandaloneServer(server, {
    listen: { port: 4000 },
  });
  console.log(`Server ready at ${url}`);
}

startServer();

This gives you a working GraphQL server with users and posts.

Adding Context

For real applications, add context for database connections and authentication:

const { startStandaloneServer } = require('@apollo/server/standalone');

async function startServer() {
  const server = new ApolloServer({ typeDefs, resolvers });
  
  const { url } = await startStandaloneServer(server, {
    listen: { port: 4000 },
    context: async ({ req }) => {
      // Get token from headers
      const token = req.headers.authorization || '';
      
      // Get user from token (simplified)
      const user = token ? await getUserFromToken(token) : null;
      
      return {
        user,
        db: database,
      };
    },
  });
  
  console.log(`Server ready at ${url}`);
}

The context runs for every request and provides shared resources to all resolvers.

Production Considerations

Before going to production, consider these important settings:

const server = new ApolloServer({
  typeDefs,
  resolvers,
  
  // Security settings
  introspection: process.env.NODE_ENV !== 'production',
  
  // Error handling
  formatError: (formattedError, error) => {
    // Don't expose internal errors in production
    if (process.env.NODE_ENV === 'production') {
      return { message: 'Internal server error' };
    }
    return formattedError;
  },
  
  // Plugins for logging, metrics, etc.
  plugins: [
    {
      requestDidStart(requestContext) {
        console.log('Request started:', requestContext.request.query);
        return {
          didEncounterErrors(ctx) {
            console.error('Errors:', ctx.errors);
          },
        };
      },
    },
  ],
});

Always disable introspection in production and handle errors carefully.