GraphRAG Demo

May 12, 2026 · View on GitHub

Architecture Overview

ComponentServicePurpose
FrontendS3 + CloudFrontReact SPA hosting (Vite + Cloudscape)
Query APILambda (Docker)GraphRAG and Vector RAG queries, per-query results
Upload APILambda (Docker)Document upload, MD5 dedup, S3 storage, graph indexing
Graph StoreNeptune AnalyticsKnowledge graph (lexical graph)
Vector StoreAmazon S3 VectorsVector embeddings for similarity search
AuthCognitoUser Pool + Identity Pool, SigV4 credentials
Document StorageS3Raw documents under private/{user_id}/{tenant_id}/documents/
Document RegistryDynamoDBDocument metadata + MD5 dedup index (GSI)

Prerequisites

Required Tools

node --version    # 18+ required
npm --version
docker --version  # For Lambda container images
aws --version
cdk --version     # npm install -g aws-cdk

AWS Configuration

aws configure
aws sts get-caller-identity

Required IAM permissions: CloudFormation, Lambda, S3, DynamoDB, CloudFront, Cognito, Neptune Analytics, Bedrock, IAM role creation.

Enable Bedrock Models

In the AWS Console → Amazon BedrockModel access, enable the models you plan to use.

Defaults (see Configuration to override):

  • Claude Sonnet 4.6 (us.anthropic.claude-sonnet-4-6) — extraction and response generation
  • Cohere Embed English v3 (cohere.embed-english-v3) — vector embeddings (1024 dimensions)

If you set different models via .env, grant access to those instead. Whichever embedding model you pick, the EMBEDDINGS_DIMENSIONS value must match the model's output dimensions.

Deployment

1. Clone and Install

git clone <your-repo-url>
cd graphrag-neptune-analytics
cd infra
npm install

2. Bootstrap CDK (First Time Only)

cdk bootstrap

3. Deploy

cdk deploy

This deploys:

  • Neptune Analytics graph (128 GB, public connectivity)
  • Amazon S3 Vectors bucket (auto-created by the toolkit on first indexing)
  • Document Processor Lambda (Docker, 3 GB RAM, 15 min timeout) with Function URL
  • Query Handler Lambda (Docker, 3 GB RAM, 5 min timeout) with Function URL
  • S3 buckets for documents and frontend
  • DynamoDB table with MD5 GSI for document registry
  • Cognito User Pool and Identity Pool
  • CloudFront distribution with SPA error handling
  • IAM roles: authenticated users get lambda:InvokeFunctionUrl + lambda:InvokeFunction
  • Frontend built and deployed to S3 with appconfig.json runtime config

Deployment time: ~15-20 minutes (Neptune Analytics creation is the bottleneck).

4. Note the Outputs

GraphRAGStack.FrontendURL = https://xxxxxxxxxx.cloudfront.net
GraphRAGStack.QueryApiURL = https://xxx.lambda-url.us-west-2.on.aws/
GraphRAGStack.UploadApiURL = https://xxx.lambda-url.us-west-2.on.aws/
GraphRAGStack.DocumentsBucketName = graphragstack-documentsbucket-xxx
GraphRAGStack.NeptuneGraphId = g-xxxxxxxxxx
GraphRAGStack.UserPoolId = us-west-2_xxxxxxxxx
GraphRAGStack.UserPoolClientId = xxxxxxxxxxxxxxxxxxxxxxxxxx
GraphRAGStack.IdentityPoolId = us-west-2:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Frontend Configuration

The frontend is automatically configured during deployment. CDK generates appconfig.json and deploys it to S3:

{
  "queryApiUrl": "<QueryApiURL>",
  "uploadApiUrl": "<UploadApiURL>",
  "region": "us-west-2",
  "bucketName": "<DocumentsBucketName>",
  "documentTableName": "<DocumentRegistryTableName>",
  "auth": {
    "user_pool_id": "<UserPoolId>",
    "user_pool_client_id": "<UserPoolClientId>",
    "identity_pool_id": "<IdentityPoolId>"
  }
}

No manual configuration needed for production deployments.

API Endpoints

Document Processor Lambda

EndpointMethodDescription
/upload-jsonPOSTUpload document (base64 JSON body). Query params: tenant_id, user_id
/documentsGETList user's documents. Query param: user_id
/documentsDELETEDelete a document. Query params: user_id, s3_path
/reset-graphPOSTReset Neptune graph + clean DynamoDB records. Query params: tenant_id, user_id
/healthGETHealth check

Query Handler Lambda

EndpointMethodDescription
/queryPOSTQuery both Vector RAG and GraphRAG. Body: { query, tenant_id }. Returns responses, vector_chunks, graph_nodes, graph_links
/healthGETHealth check

Supported File Types

  • .txt — plain text, chunked with SentenceSplitter (512 chars, 50 overlap)
  • .md — Markdown, parsed with MarkdownNodeParser then SentenceSplitter

PDF is not supported.

Multi-Tenancy

  • Each tenant's data is isolated via the graphrag-toolkit's tenant_id mechanism
  • Tenant ID is hashed (md5(tenant_id)[:10]) before passing to the toolkit
  • S3 path: private/{user_id}/{tenant_id}/documents/
  • MD5 dedup is scoped to user + tenant + file content
  • Graph reset cleans up both Neptune data and DynamoDB records for the tenant

Updating the Deployment

After code changes (Lambda or frontend):

cd infra
cdk deploy

CDK rebuilds Docker images and the frontend automatically.

Cleanup

cd infra
cdk destroy

This removes all resources including Neptune Analytics (and all graph data), Lambda functions, S3 buckets, DynamoDB table, Cognito pools, and CloudFront distribution.

If cdk destroy fails: empty S3 buckets manually in the console, then delete the CloudFormation stack.

Troubleshooting

Lambda Function URL 403 Errors

Since October 2025, Lambda Function URLs require both permissions:

  • lambda:InvokeFunctionUrl
  • lambda:InvokeFunction

The CDK stack includes both on the Cognito authenticated role. If you see 403 errors, verify the role has both permissions.

CORS Errors

Lambda Function URLs handle CORS via their built-in configuration (not application middleware). The stack configures allowedOrigins: ['*'] with the required SigV4 headers. Do not add custom CORS middleware in the Lambda apps.

Neptune Analytics Connection

Neptune Analytics is configured with publicConnectivity: true. Lambda execution roles have neptune-graph:* permissions. The graph ID is passed via environment variables.

Bedrock Access Denied

Go to AWS Console → Bedrock → Model access → enable the models listed in your .env (or the defaults: Claude Sonnet 4.6 and Cohere Embed English v3). Wait a few minutes for propagation.

"Already Processed" on Upload

The dedup check uses md5(user_id + tenant_id + file_content) against a DynamoDB GSI. If you reset the graph and want to re-upload the same files, the reset now also cleans up DynamoDB records for that tenant. If stale records remain, delete them via the UI's document list or directly in DynamoDB.

Cost Optimization

  • Destroy the stack when not actively using it — Neptune Analytics charges ~$12.80/hour for 128 GB minimum
  • Lambda and Bedrock are pay-per-use
  • S3/DynamoDB/CloudFront costs are minimal for demo usage