Labs ICT
Pro Login

Apollo Server Setup

3 min read | GraphQL Tutorial

Want the full learning experience?

Get structured courses, certificates, projects, and instructor support with LabsICT Pro.

Explore Pro Courses

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.