A production-ready GraphQL API built with Node.js, TypeScript, Apollo Server 4, Express, and MongoDB. This project demonstrates full CRUD operations with persistent data storage, proper error handling, and graceful shutdown mechanisms.
- ✅ TypeScript - Full type safety with TypeScript 5.3.3
- ✅ GraphQL API - Apollo Server 4.10.0 with Express integration
- ✅ Full CRUD Operations - Create, Read, Update, Delete with complete validation
- ✅ MongoDB Integration - MongoDB 6.20.0 with native driver
- ✅ Health Check Endpoint - Monitor server status at
/health - ✅ Graceful Shutdown - Proper cleanup of database connections on SIGINT/SIGTERM
- ✅ Database Indexes - Automatic index creation for optimal performance
- ✅ Singleton Pattern - Efficient database connection management
- ✅ Service Layer Architecture - Clean separation of concerns with DAO pattern
- ✅ Environment Configuration - Flexible configuration via
.envfile - ✅ API Testing Suite - Bruno API collection for comprehensive testing
- Features
- Prerequisites
- Quick Start
- Installation
- Running the Server
- API Endpoints
- GraphQL Schema
- Example Queries and Mutations
- Project Structure
- Database
- Testing
- Error Handling
- Additional Documentation
- Node.js (v16 or higher)
- npm or yarn
- MongoDB (installed and running)
# 1. Install dependencies
npm install
# 2. Start MongoDB (macOS with Homebrew)
brew services start mongodb-community
# 3. Create .env file with MongoDB connection
echo "MONGODB_URI=mongodb://localhost:27017?appName=devrel-graphql" > .env
# 4. Start development server
npm run dev:watch
# 5. Open GraphQL Playground
# Visit http://localhost:3000/graphql in your browser- Install dependencies:
npm install- Make sure MongoDB is running:
# macOS (if installed via Homebrew)
brew services start mongodb-community
# Or manually
mongod --dbpath /path/to/data/directory- Configure environment variables:
Create a
.envfile in the root directory (required for MongoDB connection):
# Required
MONGODB_URI=mongodb://localhost:27017?appName=devrel-graphql
# Optional (defaults shown)
DB_NAME=graphql_example
PORT=3000Environment Variables:
MONGODB_URI- MongoDB connection string (required)DB_NAME- Database name (optional, default:graphql_example)PORT- Server port (optional, default:3000)
Start the server with tsx for fast TypeScript execution:
npm run devAutomatically restart the server when files change:
npm run dev:watchBuild and run the compiled JavaScript:
# Build the TypeScript code
npm run build
# Start the server
npm startThe server will start at http://localhost:3000/graphql and initialize:
- MongoDB connection with automatic retry
- Database indexes (email uniqueness, createdAt sorting)
- GraphQL playground interface
- Health check endpoint
- GraphQL Playground:
http://localhost:3000/graphql - Health Check:
http://localhost:3000/health
type User {
id: ID!
name: String!
email: String!
age: Int
createdAt: String!
}# Get all users
users: [User!]!
# Get a specific user by ID
user(id: ID!): User# Create a new user
createUser(name: String!, email: String!, age: Int): User!
# Update an existing user
updateUser(id: ID!, name: String, email: String, age: Int): User
# Delete a user
deleteUser(id: ID!): Boolean!query {
users {
id
name
email
age
createdAt
}
}query {
user(id: "MONGODB_OBJECT_ID") {
id
name
email
age
createdAt
}
}Note: Replace MONGODB_OBJECT_ID with an actual MongoDB ObjectId (24-character hex string). You can get IDs from the users query.
mutation {
createUser(name: "Alice Williams", email: "alice@example.com", age: 25) {
id
name
email
age
createdAt
}
}mutation {
updateUser(id: "MONGODB_OBJECT_ID", name: "John Updated", age: 31) {
id
name
email
age
createdAt
}
}mutation {
deleteUser(id: "MONGODB_OBJECT_ID")
}graphql_example/
├── src/
│ ├── database/
│ │ ├── connection.ts # MongoDB connection singleton
│ │ └── userService.ts # User data access layer (DAO pattern)
│ ├── resolvers/
│ │ └── index.ts # GraphQL resolvers
│ ├── schema/
│ │ └── typeDefs.ts # GraphQL type definitions
│ ├── types/
│ │ └── index.ts # TypeScript interfaces
│ └── index.ts # Server entry point
├── bruno/ # Bruno API testing collection
│ ├── bruno.json
│ ├── Create User.bru
│ ├── Get Users.bru
│ ├── Get Single User.bru
│ ├── Update Single User.bru
│ └── Delete Single User.bru
├── dist/ # Compiled JavaScript (generated)
├── package.json
├── tsconfig.json
├── AGENTS.md # AI agent context and documentation
├── MONGODB_SETUP.md # Detailed MongoDB setup guide
└── README.md
This project follows a layered architecture:
- Presentation Layer: GraphQL resolvers handle API requests
- Service Layer: UserService manages business logic and data access
- Data Layer: MongoDB connection singleton manages database connections
- Singleton Pattern: Database connection management
- DAO Pattern: UserService acts as Data Access Object
- Service Layer Pattern: Clear separation between GraphQL and database logic
The application uses MongoDB for persistent data storage. The database is automatically initialized when the server starts, including index creation for optimal performance.
Document Structure:
{
_id: ObjectId, // MongoDB primary key (exposed as 'id' in GraphQL)
name: String, // User's full name (required)
email: String, // User's email (required, unique)
age: Number, // User's age (optional)
createdAt: Date // Timestamp of creation (auto-generated)
}Indexes:
{ email: 1 }- Unique ascending index for email uniqueness validation{ createdAt: -1 }- Descending index for efficient chronological queries
Index Benefits:
- Email uniqueness is enforced at the database level
- Fast lookups by email
- Efficient sorting and filtering by creation date
To automatically recompile TypeScript on file changes:
npm run watchnpm run dev- Start development server with tsxnpm run dev:watch- Start development server with auto-reload on file changesnpm run build- Compile TypeScript to JavaScript indist/directorynpm start- Run compiled production server fromdist/npm run watch- Watch TypeScript files and recompile on changes
- Node.js (v16+) - JavaScript runtime environment
- TypeScript (5.3.3) - Typed superset of JavaScript
- Apollo Server (4.10.0) - GraphQL server implementation
- Express (4.18.2) - Minimal web framework for Node.js
- GraphQL (16.8.1) - Query language for APIs
- MongoDB (6.20.0) - NoSQL document database
- tsx (4.20.6) - TypeScript execution and development tool
- dotenv - Environment variable management
- Bruno - API testing and documentation
- cors - Cross-Origin Resource Sharing
- body-parser - Request body parsing middleware
This project includes a comprehensive API testing suite using Bruno, an open-source API client.
The bruno/ directory contains pre-configured API tests for all operations:
- Get Users - Query all users
- Get Single User - Query user by ID
- Create User - Create new user with validation
- Update Single User - Update existing user
- Delete Single User - Delete user by ID
To use the Bruno collection:
- Install Bruno from usebruno.com
- Open Bruno and load the collection from the
bruno/directory - Ensure the server is running on
http://localhost:3000 - Execute requests directly from Bruno
The API implements comprehensive error handling:
- Invalid ObjectId: Returns
nullfor queries,falsefor deletions - Duplicate Email: MongoDB throws error due to unique index
- Missing Required Fields: GraphQL validation prevents invalid requests
- Connection Errors: Server logs errors and implements graceful shutdown
- No authentication or authorization implemented
- No input validation beyond TypeScript types
- No pagination for the
usersquery (returns all records) - No DataLoader implementation (potential N+1 query issues in complex schemas)
- No GraphQL subscriptions support
- MONGODB_SETUP.md - Detailed MongoDB installation and configuration guide
- AGENTS.md - Comprehensive AI agent context and architectural documentation
ISC