A minimal Node.js GraphQL server using Envelop for plugin-based architecture.
This project extends a pure Node.js GraphQL server with:
- Envelop plugin system for middleware-like extensions
- Validation cache to optimize repeated queries
- Custom logging and timing of GraphQL operations
- Basic authentication via Bearer token
- Embedded GraphiQL UI
- Fully written in TypeScript
- Install dependencies:
npm install
- Run the server:
npm run dev
- Open GraphiQL UI:
| Path | Method | Description |
|---|---|---|
/graphql |
POST | Main GraphQL endpoint with Envelop middleware |
/ |
GET | Interactive GraphiQL UI |
query Hello {
hello
}
query GetUsers {
users {
id
name
email
}
}
query GetUser {
user(id: "2") {
id
name
}
}
mutation SendMessage {
sendMessage(message: "Hello from GraphiQL") {
message
timestamp
}
}
curl -X POST http://localhost:8080/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer secret-token" \
-d "{\"query\":\"query Hello { hello }\",\"operationName\":\"Hello\"}"
curl -X POST http://localhost:8080/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer secret-token" \
-d "{\"query\":\"query GetUsers { users { id name email } }\",\"operationName\":\"GetUsers\"}"
curl -X POST http://localhost:8080/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer secret-token" \
-d "{\"query\":\"query GetUser { user(id: \\\"2\\\") { id name email } }\",\"operationName\":\"GetUser\"}"
curl -X POST http://localhost:8080/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer secret-token" \
-d "{\"query\":\"mutation SendMessage { sendMessage(message: \\\"Hello from curl\\\") { message timestamp } }\",\"operationName\":\"SendMessage\"}"
When running the server and executing queries or mutations, you will see logs like:
➡️ Incoming GraphQL request: Hello
⏱️ GraphQL [Hello] executed in 2ms
✅ Request executed successfully
➡️ Incoming GraphQL request: GetUsers
⏱️ GraphQL [GetUsers] executed in 1ms
✅ Request executed successfully
➡️ Incoming GraphQL request: GetUser
⏱️ GraphQL [GetUser] executed in 0ms
✅ Request executed successfully
➡️ Incoming GraphQL request: SendMessage
⏱️ GraphQL [SendMessage] executed in 1ms
✅ Request executed successfully
This demonstrates:
- Operation names appear instead of
anonymous - Execution time is logged for each request
- Success or errors are immediately visible
- Defines GraphQL types, queries, and mutations using SDL (
buildSchema) - User type:
id,name,email - Message type:
message,timestamp - Queries:
hello,users,user(id: ID!) - Mutation:
sendMessage(message: String!) - Resolvers: functions in
rootreturn static data for queries and generate timestamps for mutations
- useEngine: integrates GraphQL execution engine
- useValidationCache: caches validation results for repeated queries
- useTiming: logs execution time of GraphQL requests
- useLogger: logs incoming requests, operation names, and errors or success
- useAuth: reads
Authorizationheader and attachesuserto context; blocks unauthenticated requests
- Uses native http module for serving GraphQL and GraphiQL UI
- GET
/serves GraphiQL interface - POST
/graphqlparses JSON body, executes GraphQL queries using Envelop, and returns JSON responses - Fully written in TypeScript with typed context (
MyContext)
-
Move secret token to environment variable
Avoid hardcoding the Bearer token in the code. Useprocess.env.AUTH_TOKENfor security. -
Add role-based authorization
ExtenduseAuthplugin to handle multiple roles (admin, user, guest) for more granular access control. -
Implement rate limiting
Prevent abuse of the GraphQL endpoint by limiting request rate per IP or user. -
Enable logging to file
Instead of only console logging, save logs to a file or use a logging library likewinstonfor production monitoring. -
Add input validation
Validate mutation inputs and query arguments before execution to avoid runtime errors and improve security. -
Add more Envelop plugins
Consider using plugins for depth limiting, query complexity, and error masking to improve security and performance. -
Set up environment-specific configuration
Use.envor a configuration library to manage different environments (development, staging, production). -
Enable CORS
Allow cross-origin requests if the server will be accessed from different frontends. -
Add tests
Write unit and integration tests for resolvers, plugins, and the server endpoints. -
Add GraphQL subscriptions (optional)
If real-time features are needed, implement subscriptions using WebSocket transport with Envelop.
- Node.js >=22 – JavaScript runtime
- graphql 16.x – Official GraphQL implementation for Node.js
- http – Native HTTP server
- GraphiQL – Embedded interactive IDE
- Envelop – Plugin system for GraphQL
- TypeScript – Strict type-checking and compiler options
- Render.com – Zero-config deployment