A production-ready Express.js application with TypeScript, TypeORM, Graphql, PostgreSQL, and cookie-based authentication.
- ✅ TypeScript with strict mode
- ✅ Express.js with best practices middleware (helmet, morgan)
- ✅ TypeORM with PostgreSQL
- ✅ GraphQL with Apollo Server
- ✅ Keyset-based pagination for efficient data fetching
- ✅ Cookie-based session authentication (REST + GraphQL)
- ✅ Per-resolver authentication for GraphQL
- ✅ Service layer architecture
- ✅ Swagger UI for REST API documentation
- ✅ Docker Compose for development
- ✅ Hot-reload development environment
- ✅ Comprehensive unit tests (Jest)
- Node.js 18+ (for local development)
- Docker & Docker Compose (recommended)
# Start PostgreSQL and app with hot-reload
docker-compose up
# The app will be available at http://localhost:3000# Install dependencies
npm install
# Start PostgreSQL (manually or via Docker)
docker-compose up postgres
# Run development server
npm run devCopy .env and configure:
The GraphQL endpoint is available at http://localhost:3000/graphql
- Apollo Sandbox: Visit
http://localhost:3000/graphqlin your browser for an interactive GraphQL playground - Introspection: Enabled for exploring the schema
Public Queries (No authentication required):
# Get all posts with pagination
posts(limit: Int, afterId: Int, beforeId: Int): PostConnection!
# Get a single post by ID
post(id: Int!): PostProtected Queries (Authentication required):
# Get posts created by the logged-in user
myPosts(limit: Int, afterId: Int, beforeId: Int): PostConnection!Protected Mutations (Authentication required):
# Create a new post
createPost(title: String!, content: String!, published: Boolean): Post!
# Update an existing post
updatePost(id: Int!, title: String, content: String, published: Boolean): Post
# Delete a post
deletePost(id: Int!): Boolean!Get posts with pagination:
query GetPosts {
posts(limit: 10) {
nodes {
id
title
content
published
author {
email
}
}
pageInfo {
hasNextPage
hasPreviousPage
endId
startId
}
totalCount
}
}Create a post (requires authentication):
mutation CreatePost {
createPost(title: "My First Post", content: "This is the content", published: true) {
id
title
content
createdAt
}
}Pagination - Next Page:
query GetNextPage {
posts(limit: 10, afterId: 45) {
# Use endId from previous response
nodes {
id
title
}
pageInfo {
hasNextPage
endId
}
}
}-
Keyset Pagination: Efficient pagination using ID-based cursors instead of offset
- Better performance for large datasets
- No skipped or duplicate items when data changes
- Arguments:
limit,afterId(forward),beforeId(backward)
-
Per-Resolver Authentication: Fine-grained access control
- Public queries work without authentication
- Protected queries/mutations require login
- Uses
requireAuthwrapper for GraphQL resolvers
-
Schema-Based Security: Only defined fields can be queried
- Sensitive fields (like passwords) are never exposed
- GraphQL validates queries before execution
-
Session-Based Auth: Works seamlessly with REST endpoints
- Same session cookie used for REST and GraphQL
- Login via REST
/auth/login, use GraphQL endpoints - Must send
credentials: 'include'in fetch requests
Interactive API documentation available at: http://localhost:3000/api-docs
GET /user/profile- Get user profile (requires authentication)
src/
├── config/ # Configuration files (TypeORM, etc.)
├── entity/ # TypeORM entities (User, Post)
├── middleware/ # Express middleware (auth, service injection)
├── controllers/ # REST controllers (routing-controllers)
│ ├── AuthController.ts
│ ├── UserController.ts
│ └── HealthcheckController.ts
├── services/ # Business logic layer (UserService, PostService)
├── graphql/
│ ├── schema.ts # GraphQL type definitions
│ ├── resolvers/ # GraphQL resolvers
│ │ ├── post.resolver.ts
│ │ └── index.ts
│ ├── types/ # Pagination utilities
│ └── utils/ # Auth wrapper for resolvers
├── dto/ # Data Transfer Objects with validation
├── types/ # TypeScript type definitions
└── index.ts # Application entry point
npm run dev # Start development server with hot-reload
npm run build # Build for production
npm start # Run production build
npm test # Run unit tests
npm run test:watch # Run tests in watch mode
npm run test:coverage # Run tests with coverage report
npm run docker:up # Start Docker containers
npm run docker:down # Stop Docker containers- User registers via
/auth/register - User logs in via
/auth/login→ Session cookie is set - Browser automatically sends cookie with subsequent requests
- Protected routes use
requireAuthmiddleware to verify session - User logs out via
/auth/logout→ Session cookie is cleared
- Login using REST endpoint
/auth/login(sets session cookie) - GraphQL requests automatically include session cookie
- Public queries (e.g.,
posts,post) work without authentication - Protected queries/mutations (e.g.,
myPosts,createPost) check for authentication - Per-resolver auth using
requireAuthwrapper throwsUNAUTHENTICATEDerror if not logged in
Important: Always include credentials: 'include' in fetch requests to send session cookies!
This project includes comprehensive unit tests for all authentication components.
Run tests:
npm test # Run all tests
npm run test:watch # Watch mode for development
npm run test:coverage # Generate coverage reportSee TESTING.md for detailed testing documentation.
- Set
NODE_ENV=production - Use strong
SESSION_SECRET - Enable HTTPS (session cookies will be secure-only)
- Set
synchronize: falsein TypeORM config (use migrations) - Disable GraphQL introspection in production
- Set up proper CORS policies
- Use environment-specific database credentials
- REST API: Simple CRUD operations, authentication, Swagger docs
- GraphQL API: Complex queries, flexible data fetching, real-time needs
- Shared Authentication: Same session cookie works for both
- Controllers handle HTTP concerns
- Services contain business logic
- Services are reused by both REST and GraphQL
- Easy to test in isolation
- GraphQL schema defines queryable fields
- Sensitive fields (passwords) never exposed
- Validation happens before resolvers execute
- Type-safe queries and mutations
- 🌐 App: http://localhost:3000
- 📊 GraphQL Playground: http://localhost:3000/graphql
- 📚 Swagger UI: http://localhost:3000/api-docs
- ❤️ Health Check: http://localhost:3000/healthcheck
- Backend: Express 5 + TypeScript
- Database: PostgreSQL 15 with TypeORM
- GraphQL: Apollo Server 5
- Authentication: Cookie-based sessions (stateless)
- Validation: class-validator
- API Docs: Swagger UI (routing-controllers-openapi)
- Testing: Jest + ts-jest
- Dev Tools: ts-node-dev, Docker Compose
- Swagger UI - REST API interactive docs
- Apollo Sandbox - GraphQL interactive playground
MIT