Skip to content

Cloud-Jack/graphql-demo

Repository files navigation

Express + TypeScript + Graphql Demo

A production-ready Express.js application with TypeScript, TypeORM, Graphql, PostgreSQL, and cookie-based authentication.

Features

  • ✅ 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)

Prerequisites

  • Node.js 18+ (for local development)
  • Docker & Docker Compose (recommended)

Getting Started

Option 1: Docker (Recommended)

# Start PostgreSQL and app with hot-reload
docker-compose up

# The app will be available at http://localhost:3000

Option 2: Local Development

# Install dependencies
npm install

# Start PostgreSQL (manually or via Docker)
docker-compose up postgres

# Run development server
npm run dev

Environment Variables

Copy .env and configure:

API Endpoints

GraphQL API

The GraphQL endpoint is available at http://localhost:3000/graphql

Exploring the API

  • Apollo Sandbox: Visit http://localhost:3000/graphql in your browser for an interactive GraphQL playground
  • Introspection: Enabled for exploring the schema

GraphQL Schema Overview

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!): Post

Protected 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!

Example GraphQL Queries

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
    }
  }
}

GraphQL Features

  • 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 requireAuth wrapper 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

REST API

Swagger Documentation

Interactive API documentation available at: http://localhost:3000/api-docs

Protected Routes

  • GET /user/profile - Get user profile (requires authentication)

Project Structure

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

Scripts

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

Authentication Flow

REST Authentication

  1. User registers via /auth/register
  2. User logs in via /auth/login → Session cookie is set
  3. Browser automatically sends cookie with subsequent requests
  4. Protected routes use requireAuth middleware to verify session
  5. User logs out via /auth/logout → Session cookie is cleared

GraphQL Authentication

  1. Login using REST endpoint /auth/login (sets session cookie)
  2. GraphQL requests automatically include session cookie
  3. Public queries (e.g., posts, post) work without authentication
  4. Protected queries/mutations (e.g., myPosts, createPost) check for authentication
  5. Per-resolver auth using requireAuth wrapper throws UNAUTHENTICATED error if not logged in

Important: Always include credentials: 'include' in fetch requests to send session cookies!

Testing

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 report

See TESTING.md for detailed testing documentation.

Production Considerations

  • Set NODE_ENV=production
  • Use strong SESSION_SECRET
  • Enable HTTPS (session cookies will be secure-only)
  • Set synchronize: false in TypeORM config (use migrations)
  • Disable GraphQL introspection in production
  • Set up proper CORS policies
  • Use environment-specific database credentials

Architecture Highlights

GraphQL + REST Coexistence

  • 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

Service Layer Pattern

  • Controllers handle HTTP concerns
  • Services contain business logic
  • Services are reused by both REST and GraphQL
  • Easy to test in isolation

Schema-First Security

  • GraphQL schema defines queryable fields
  • Sensitive fields (passwords) never exposed
  • Validation happens before resolvers execute
  • Type-safe queries and mutations

Quick Reference

Endpoints

Key Technologies

  • 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

Documentation

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors