Building a GraphQL Server with Node.js

intermediate
15 min

Building a GraphQL Server with Node.js

Time to stop reading and start running code. In this lesson we build a complete, working GraphQL server in Node.js using Apollo Server 4: schema, resolvers, in-memory data, and mutations. By the end you will have a playground at http://localhost:4000 answering real queries.

Project Setup

bash
mkdir graphql-blog && cd graphql-blog npm init -y npm install @apollo/server graphql

Add "type": "module" to package.json so we can use ES imports. Apollo Server 4 is a lean library: @apollo/server plus the reference graphql implementation is all we need. (Alternatives like graphql-yoga or Mercurius for Fastify follow the same shape; skills transfer directly.)

Define the Schema

Create index.js and start with type definitions, the same SDL from earlier lessons:

js
import { ApolloServer } from "@apollo/server"; import { startStandaloneServer } from "@apollo/server/standalone"; const typeDefs = `#graphql type User { id: ID!, name: String!, posts: [Post!]! } type Post { id: ID!, title: String!, published: Boolean!, author: User! } type Query { users: [User!]! post(id: ID!): Post } type Mutation { createPost(title: String!, authorId: ID!): Post! } `;

The #graphql comment tag enables syntax highlighting in most editors.

Add Data and Resolvers

For learning purposes an in-memory store is perfect; swapping in Postgres or Mongo later only changes resolver bodies, never the schema:

js
const users = [ { id: "1", name: "Ada" }, { id: "2", name: "Grace" }, ]; const posts = [ { id: "1", title: "On Engines", published: true, authorId: "1" }, { id: "2", title: "Compilers 101", published: true, authorId: "2" }, ]; const resolvers = { Query: { users: () => users, post: (_, { id }) => posts.find((p) => p.id === id) ?? null, }, Mutation: { createPost: (_, { title, authorId }) => { const post = { id: String(posts.length + 1), title, published: false, authorId }; posts.push(post); return post; }, }, User: { posts: (user) => posts.filter((p) => p.authorId === user.id), }, Post: { author: (post) => users.find((u) => u.id === post.authorId), }, };

Note how User.posts and Post.author bridge the foreign key: the raw records store authorId, but the API exposes real object edges.

Start the Server

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

Run node index.js, open http://localhost:4000, and Apollo Sandbox loads with your schema fully introspected. Try:

graphql
mutation { createPost(title: "Hello GraphQL", authorId: "1") { id title } }

then query { users { name posts { title } } } to see the new post attached to Ada.

Where to Go from Here

  • Replace arrays with a database client passed via context.
  • Split typeDefs and resolvers into modules per domain as the schema grows.
  • Add validation and the payload/error pattern from lesson 5 to createPost.

Key Takeaways

  • A minimal Apollo Server needs only typeDefs, resolvers, and startStandaloneServer.
  • Keep raw records (with foreign keys) separate from API shape; relationship resolvers bridge the two.
  • The built-in sandbox gives you introspection, docs, and a query IDE for free.
  • Swapping the data layer never changes the schema, which is the whole point of the abstraction.

Next lesson: Fragments and Reusable Query Parts, a client-side technique for keeping large queries maintainable.

Building a GraphQL Server with Node.js - GraphQL | CodeYourCraft | CodeYourCraft