Skip to content
Santekno.com | Tech Tutorials and Trends
EN
📖 0%
09 Jul 2025 · 6 min read ·Article 9 / 125
Go

9 Building a Basic HTTP Server for a GraphQL Endpoint

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

9 Building a Basic HTTP Server for a GraphQL Endpoint

Danger
Implementing GraphQL no longer requires a large framework. We can build a basic HTTP server to handle GraphQL queries efficiently and in a way that’s easy to understand. This article will guide you from scratch all the way to a simple, ready-to-use GraphQL HTTP server, complete with code examples, query simulations, and a flow diagram.

Introduction

GraphQL has become the new standard for modern APIs. Many developers want to try out or deploy a GraphQL endpoint without the hassle of heavy frameworks like Apollo Server or Relay. Sometimes all we need is a simple endpoint for a prototype, for mocking a backend, or for learning by doing. Whatever the reason, the core concept is the same: we need an HTTP server capable of receiving requests and responding with data based on a GraphQL query.

This article walks through how to build a basic HTTP server for GraphQL using Node.js and the graphql-js library. Our focus is not on fancy features, but on the core concept and implementation of a GraphQL endpoint: a request comes in over HTTP, the query is processed, and a response is sent back.


1. Prerequisites

To follow this tutorial, all you need is:

  • Basic knowledge of JavaScript/Node.js
  • Node.js >= v14
  • The graphql library from npm

Installing GraphQL:

bash
1npm init -y
2npm install graphql

For the HTTP server, we can simply use Node’s built-in http module.


2. A Minimalist HTTP Server Structure

The main goal is to accept a POST request at /graphql, read the query from the request body, then process it and send the result back. There’s no need for complicated routing.

javascript
 1const http = require('http');
 2const { graphql, buildSchema } = require('graphql');
 3
 4// 1. Define schema & resolver
 5const schema = buildSchema(`
 6  type Query {
 7    hello: String
 8  }
 9`);
10const rootValue = {
11  hello: () => 'Hello, world!'
12};
13
14// 2. HTTP server declaration
15const server = http.createServer(async (req, res) => {
16  if (req.method === 'POST' && req.url === '/graphql') {
17    let body = '';
18    req.on('data', chunk => body += chunk);
19    req.on('end', async () => {
20      try {
21        const { query, variables } = JSON.parse(body);
22
23        // 3. Core: GraphQL execution
24        const result = await graphql({
25          schema,
26          source: query,
27          rootValue,
28          variableValues: variables
29        });
30
31        // Response
32        res.writeHead(200, { 'Content-Type': 'application/json' });
33        res.end(JSON.stringify(result));
34      } catch (err) {
35        res.writeHead(400);
36        res.end(JSON.stringify({ errors: [{ message: err.message }] }));
37      }
38    });
39  } else {
40    res.writeHead(404);
41    res.end();
42  }
43});
44
45server.listen(4000, () => {
46  console.log('GraphQL server running at http://localhost:4000/graphql');
47});

3. How Does the Flow Work?

Let’s visualize the HTTP request-response flow for this endpoint with a mermaid diagram:

MERMAID
sequenceDiagram
    client->>server: POST /graphql {query, variables}
    server->>server: Parse body JSON
    server->>server: graphql({schema, query, rootValue})
    server->>server: Execute resolver
    server->>client: HTTP 200 {data, errors}

In broad strokes:

  1. The client sends a POST request to the /graphql endpoint.
  2. The server reads the query from the body (JSON).
  3. The query is executed using the graphql function.
  4. The result (data, and/or errors) is sent back to the client.

4. Simulating a Request

To try out this endpoint, use cURL or a GraphQL client like Insomnia/Postman.

bash
1curl -X POST http://localhost:4000/graphql \
2  -H "Content-Type: application/json" \
3  -d '{"query": "{ hello }"}'

The expected response:

json
1{
2  "data": {
3    "hello": "Hello, world!"
4  }
5}

5. Adding Queries & Parameters

The schema and resolvers can be expanded easily. For example, adding a query with an argument.

javascript
1const schema = buildSchema(`
2  type Query {
3    hello(name: String): String
4  }
5`);
6
7const rootValue = {
8  hello: ({ name }) => `Hello, ${name || "world"}!`
9};

Request:

json
1{
2  "query": "{ hello(name: \"Agus\") }"
3}

Response:

json
1{
2  "data": {
3    "hello": "Hello, Agus!"
4  }
5}

6. Handling Errors

Suppose the query is invalid:

json
1{
2  "query": "{ foo }"
3}

Output:

json
1{
2  "errors": [
3    {
4      "message": "Cannot query field \"foo\" on type \"Query\"."
5    }
6  ]
7}

7. Table Breakdown: Request & Response Structure

HTTP MethodPathContent-TypeBody (JSON)Response (JSON)
POST/graphqlapplication/json{ “query”: “…”, “variables”: { … } }{ “data”: {…}, “errors”: […] }

8. Expanding the Schema Further

GraphQL encourages an expressive schema. Here’s an example of a simple yet slightly more complex schema and resolver.

javascript
 1const schema = buildSchema(`
 2  type User {
 3    id: ID
 4    name: String
 5    age: Int
 6  }
 7  type Query {
 8    user(id: ID!): User
 9    users: [User]
10  }
11`);
12
13const users = [
14  { id: "1", name: "Agus", age: 30 },
15  { id: "2", name: "Budi", age: 25 }
16];
17
18const rootValue = {
19  user: ({ id }) => users.find(u => u.id === id),
20  users: () => users
21};

Query:

json
1{
2  "query": "{ users { name, age } }"
3}

Response:

json
1{
2  "data": {
3    "users": [
4      { "name": "Agus", "age": 30 },
5      { "name": "Budi", "age": 25 }
6    ]
7  }
8}

9. Production Tips & Conclusion

This basic HTTP server is well suited for prototyping, POCs, or internal tools. For a production deployment, at a minimum you should add:

  • Payload and body-size validation (security)
  • Logging and error tracking
  • CORS support if cross-origin access is needed
  • Rate limiting if it’s publicly accessible

That said, the core logic doesn’t change much: accept query, execute, respond. You can embed this code in a service/worker without much overhead.

💡 Conclusion

Without a large framework, we can build a lightweight, easy-to-understand GraphQL HTTP server. It starts from simple HTTP request handling, parsing, query execution, all the way to returning a response. The key takeaway is to understand the fundamental GraphQL flow, not just to rely on ready-made tools.

Danger
Next: You can extend this server with features like authentication, schema federation, or connecting resolvers to a database.

References


Hope this is helpful, and happy hacking with GraphQL—from engineers, for engineers!

Related Articles

💬 Comments