Pricing Service: Node.js Architecture Deep Dive
Executive Summary
The pricing-service is a serverless GraphQL API built with TypeScript that runs entirely on the Node.js runtime within AWS Lambda functions. This document provides a comprehensive technical explanation of how Node.js is used throughout the architecture, from GraphQL resolvers to event-driven processing.
Table of Contents
- Node.js Runtime Foundation
- TypeScript to Node.js Compilation
- GraphQL API with AWS AppSync
- Lambda Functions on Node.js Runtime
- Event-Driven Architecture
- Code Structure and Patterns
- Deployment and Execution
- Performance Optimization
- Development Workflow
Node.js Runtime Foundation
What is Node.js in This Context?
Node.js is the JavaScript runtime environment that executes all the TypeScript code in the pricing-service. While the code is written in TypeScript, it compiles to JavaScript and runs on Node.js.
Why Node.js?
- Serverless Native: AWS Lambda natively supports Node.js runtime (versions 14.x, 16.x, 18.x, 20.x)
- TypeScript Compatibility: TypeScript compiles to JavaScript, which Node.js executes
- Event-Driven: Node.js's non-blocking I/O model is perfect for serverless, event-driven architectures
- GraphQL Ecosystem: Rich ecosystem of GraphQL libraries for Node.js
- Performance: Single-digit millisecond latency requirements met with Node.js's efficient execution
Runtime Environment
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā AWS Lambda Execution Environment ā
ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā Node.js Runtime (18.x) ā ā
ā ā ā ā
ā ā āāāāāāāāāāāāāāāāāāāāāāāāā ā ā
ā ā ā Compiled JavaScript ā ā ā
ā ā ā (from TypeScript) ā ā ā
ā ā āāāāāāāāāāāāāāāāāāāāāāāāā ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
TypeScript to Node.js Compilation
Compilation Process
The pricing-service is written in TypeScript but executes as JavaScript on Node.js:
// Source: pricing-service/src/resolvers/pricing.ts (TypeScript)
export const getPrice = async (
parent: any,
args: { storeId: string; productId: string },
context: AppSyncContext
): Promise<Price> => {
const { storeId, productId } = args;
const price = await dynamoDB.get({
TableName: 'prices',
Key: { storeId, productId }
});
return price.Item as Price;
};
Compilation Step:
tsc --target ES2020 --module commonjs --outDir dist
Result (JavaScript for Node.js):
// Compiled: dist/resolvers/pricing.js (JavaScript)
exports.getPrice = async (parent, args, context) => {
const { storeId, productId } = args;
const price = await dynamoDB.get({
TableName: 'prices',
Key: { storeId, productId }
});
return price.Item;
};
TypeScript Configuration
// tsconfig.json
{
"compilerOptions": {
"target": "ES2020", // Node.js 14+ compatible
"module": "commonjs", // Node.js module system
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Node.js Module System
The compiled JavaScript uses CommonJS modules (Node.js standard):
// CommonJS exports (Node.js)
module.exports = { getPrice };
// or
exports.getPrice = getPrice;
// CommonJS imports (Node.js)
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
const dynamoDB = require('./dynamoDB');
GraphQL API with AWS AppSync
AWS AppSync Architecture
AWS AppSync is a managed GraphQL service that uses Node.js Lambda functions as resolvers:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Client Request (GraphQL) ā
āāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā AWS AppSync (Managed) ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā GraphQL Schema Definition ā ā
ā ā - Query: getPrice, listPrices ā ā
ā ā - Mutation: updatePrice, createPrice ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā ā
ā ā¼ ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā Resolver Mapping ā ā
ā ā Query.getPrice ā Lambda Function ā ā
ā ā Mutation.updatePrice ā Lambda Function ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
āāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā AWS Lambda (Node.js Runtime) ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā TypeScript Resolver Code ā ā
ā ā (Compiled to JavaScript) ā ā
ā ā ā ā
ā ā exports.handler = async (event) => { ā ā
ā ā // GraphQL resolver logic ā ā
ā ā return await getPrice(event); ā ā
ā ā }; ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
āāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā DynamoDB ā
ā - prices table ā
ā - store_info table ā
ā - price_changes table ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
GraphQL Schema Definition
# schema.graphql
type Query {
getPrice(storeId: ID!, productId: ID!): Price
listPrices(storeId: ID!, filter: PriceFilter): [Price]
}
type Mutation {
updatePrice(input: UpdatePriceInput!): Price
createPrice(input: CreatePriceInput!): Price
}
type Price {
storeId: ID!
productId: ID!
amount: Float!
currency: String!
effectiveDate: String!
expirationDate: String
}
Resolver Implementation (Node.js)
// src/resolvers/pricing.ts
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, GetCommand, QueryCommand } from '@aws-sdk/lib-dynamodb';
import type { AppSyncResolverEvent } from 'aws-lambda';
const client = new DynamoDBClient({ region: 'us-east-1' });
const docClient = DynamoDBDocumentClient.from(client);
export const getPrice = async (
event: AppSyncResolverEvent<{ storeId: string; productId: string }>
) => {
const { storeId, productId } = event.arguments;
// Node.js async/await for non-blocking I/O
const result = await docClient.send(
new GetCommand({
TableName: process.env.PRICES_TABLE!,
Key: { storeId, productId }
})
);
return result.Item;
};
// Lambda handler (Node.js entry point)
export const handler = async (event: AppSyncResolverEvent) => {
const { fieldName, arguments: args } = event.info;
switch (fieldName) {
case 'getPrice':
return await getPrice(event);
case 'listPrices':
return await listPrices(event);
case 'updatePrice':
return await updatePrice(event);
default:
throw new Error(`Unknown field: ${fieldName}`);
}
};
AppSync Resolver Configuration
// infrastructure/appsync-resolver.ts (AWS CDK)
import * as appsync from 'aws-cdk-lib/aws-appsync';
import * as lambda from 'aws-cdk-lib/aws-lambda';
const resolverFunction = new lambda.Function(this, 'PricingResolver', {
runtime: lambda.Runtime.NODEJS_18_X, // Node.js 18 runtime
handler: 'index.handler', // Node.js entry point
code: lambda.Code.fromAsset('dist'), // Compiled JavaScript
environment: {
PRICES_TABLE: pricesTable.tableName,
NODE_ENV: 'production'
}
});
const api = new appsync.GraphqlApi(this, 'PricingApi', {
name: 'pricing-service',
schema: appsync.Schema.fromAsset('schema.graphql'),
authorizationConfig: {
defaultAuthorization: {
authorizationType: appsync.AuthorizationType.USER_POOL,
userPoolConfig: {
userPool: cognitoUserPool
}
}
}
});
// Attach Lambda resolver to GraphQL field
const getPriceDataSource = api.addLambdaDataSource(
'getPriceDataSource',
resolverFunction
);
getPriceDataSource.createResolver({
typeName: 'Query',
fieldName: 'getPrice'
});
Lambda Functions on Node.js Runtime
Lambda Function Structure
Each GraphQL resolver and event handler is a Node.js Lambda function:
// src/lambda/pricing-resolver/index.ts
import type { AppSyncResolverEvent, Context } from 'aws-lambda';
// Node.js Lambda handler signature
export const handler = async (
event: AppSyncResolverEvent,
context: Context
): Promise<any> => {
// Node.js execution context
console.log('Event:', JSON.stringify(event, null, 2));
console.log('Context:', context.requestId);
try {
// Resolver logic
const result = await resolveQuery(event);
return result;
} catch (error) {
console.error('Error:', error);
throw error;
}
};
Lambda Runtime Configuration
// infrastructure/lambda-stack.ts (AWS CDK)
import * as lambda from 'aws-cdk-lib/aws-lambda';
export class PricingServiceStack extends Stack {
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props);
// Node.js 18.x runtime
const resolverLambda = new lambda.Function(this, 'PricingResolver', {
runtime: lambda.Runtime.NODEJS_18_X, // Node.js runtime
handler: 'index.handler', // handler.exportedFunction
code: lambda.Code.fromAsset('dist'), // Compiled JavaScript
timeout: Duration.seconds(30), // Node.js execution timeout
memorySize: 512, // Memory allocation
environment: {
NODE_ENV: 'production',
PRICES_TABLE: pricesTable.tableName,
STORE_INFO_TABLE: storeInfoTable.tableName
}
});
}
}
Node.js Execution Model
Lambda Invocation Flow:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā 1. Request arrives at AppSync ā
āāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāā
ā
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā 2. AppSync invokes Lambda ā
ā (Node.js runtime starts) ā
āāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāā
ā
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā 3. Node.js loads handler module ā
ā - require('./index.js') ā
ā - Execute handler function ā
āāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāā
ā
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā 4. Handler executes (async) ā
ā - await DynamoDB query ā
ā - Process data ā
ā - Return result ā
āāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāā
ā
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā 5. Result returned to AppSync ā
ā - Node.js runtime may persist ā
ā for warm start ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Cold Start vs Warm Start
Cold Start (Node.js initialization):
- Lambda service starts Node.js runtime
- Loads handler module (
require('./index.js')) - Executes handler function
- Time: ~100-500ms (first invocation)
Warm Start (Node.js already loaded):
- Reuses existing Node.js runtime
- Handler module already in memory
- Executes handler function immediately
- Time: <10ms (subsequent invocations)
Optimization for Single-Digit Millisecond Latency:
- Provisioned concurrency for critical paths
- Module caching in Node.js
- Connection pooling for DynamoDB
- Minimal dependencies to reduce cold start
Event-Driven Architecture
DynamoDB Streams + Lambda (Node.js)
The pricing-service uses DynamoDB Streams to trigger Node.js Lambda functions for event processing:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā DynamoDB Table (prices) ā
ā - Price update written ā
āāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāā
ā
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā DynamoDB Streams ā
ā - Captures table changes ā
ā - Streams events to Lambda ā
āāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāā
ā
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Lambda Function (Node.js) ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā exports.handler = async ā ā
ā ā (event) => { ā ā
ā ā // Process stream record ā ā
ā ā // Schedule price change ā ā
ā ā // Update store info ā ā
ā ā } ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
āāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāā
ā
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Event Processing ā
ā - Schedule price changes ā
ā - Update 8,000+ stores ā
ā - Send notifications ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Stream Handler Implementation (Node.js)
// src/lambda/stream-processor/index.ts
import type { DynamoDBStreamEvent, Context } from 'aws-lambda';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBStreamsClient } from '@aws-sdk/client-dynamodb-streams';
export const handler = async (
event: DynamoDBStreamEvent,
context: Context
): Promise<void> => {
// Node.js processes stream records asynchronously
for (const record of event.Records) {
if (record.eventName === 'INSERT' || record.eventName === 'MODIFY') {
const newImage = record.dynamodb?.NewImage;
if (newImage) {
// Process price change event
await processPriceChange(newImage);
// Schedule price update for stores
await schedulePriceUpdate(newImage);
}
}
}
};
async function processPriceChange(priceRecord: any) {
// Node.js async I/O for DynamoDB operations
const storeId = priceRecord.storeId.S;
const productId = priceRecord.productId.S;
const newPrice = parseFloat(priceRecord.amount.N);
// Update store pricing cache
await updateStoreCache(storeId, productId, newPrice);
// Trigger notifications
await sendPriceChangeNotification(storeId, productId, newPrice);
}
async function schedulePriceUpdate(priceRecord: any) {
// Node.js event scheduling
const effectiveDate = new Date(priceRecord.effectiveDate.S);
const now = new Date();
if (effectiveDate > now) {
// Schedule future price change
await scheduleLambdaInvocation(effectiveDate, priceRecord);
} else {
// Apply immediately
await applyPriceChange(priceRecord);
}
}
Event-Driven Lambda Configuration
// infrastructure/stream-processor.ts (AWS CDK)
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
const streamProcessor = new lambda.Function(this, 'StreamProcessor', {
runtime: lambda.Runtime.NODEJS_18_X, // Node.js runtime
handler: 'index.handler',
code: lambda.Code.fromAsset('dist/stream-processor'),
timeout: Duration.minutes(5),
memorySize: 1024,
environment: {
STORE_INFO_TABLE: storeInfoTable.tableName,
NOTIFICATION_TOPIC: notificationTopic.topicArn
}
});
// Enable DynamoDB Streams
pricesTable.enableStream(dynamodb.StreamViewType.NEW_AND_OLD_IMAGES);
// Attach Lambda to stream
streamProcessor.addEventSource(
new lambdaEventSources.DynamoEventSource(pricesTable, {
startingPosition: lambda.StartingPosition.LATEST,
batchSize: 100, // Process up to 100 records per invocation
bisectBatchOnError: true,
retryAttempts: 3
})
);
Code Structure and Patterns
Project Structure
pricing-service/
āāā src/
ā āāā resolvers/ # GraphQL resolvers (Node.js)
ā ā āāā pricing.ts
ā ā āāā store.ts
ā ā āāā index.ts
ā āāā lambda/ # Lambda handlers (Node.js)
ā ā āāā pricing-resolver/
ā ā ā āāā index.ts # AppSync resolver handler
ā ā āāā stream-processor/
ā ā ā āāā index.ts # DynamoDB stream handler
ā ā āāā scheduler/
ā ā āāā index.ts # Price change scheduler
ā āāā services/ # Business logic (Node.js)
ā ā āāā pricing-service.ts
ā ā āāā dynamo-service.ts
ā ā āāā notification-service.ts
ā āāā utils/ # Utilities (Node.js)
ā ā āāā logger.ts
ā ā āāā errors.ts
ā ā āāā validation.ts
ā āāā types/ # TypeScript types
ā āāā pricing.ts
ā āāā graphql.ts
āāā dist/ # Compiled JavaScript (Node.js)
āāā infrastructure/ # AWS CDK (TypeScript)
ā āāā app.ts
ā āāā pricing-stack.ts
ā āāā lambda-stack.ts
āāā package.json # Node.js dependencies
āāā tsconfig.json # TypeScript config
āāā schema.graphql # GraphQL schema
Node.js Patterns Used
1. Async/Await Pattern
// Node.js async/await for non-blocking I/O
export const getPrice = async (storeId: string, productId: string) => {
// Non-blocking DynamoDB query
const result = await dynamoDB.get({
TableName: 'prices',
Key: { storeId, productId }
});
return result.Item;
};
2. Promise Chaining
// Node.js Promise chains for complex operations
export const updatePrice = async (input: UpdatePriceInput) => {
return dynamoDB
.put({ TableName: 'prices', Item: input })
.then(() => dynamoDB.get({ TableName: 'prices', Key: { ... } }))
.then(result => result.Item)
.catch(error => {
logger.error('Price update failed', error);
throw error;
});
};
3. Error Handling
// Node.js error handling patterns
export const handler = async (event: AppSyncResolverEvent) => {
try {
return await processRequest(event);
} catch (error) {
// Node.js error logging
console.error('Handler error:', error);
// Return GraphQL error format
throw new Error(`Failed to process request: ${error.message}`);
}
};
4. Module Exports (CommonJS)
// Node.js module exports
export const handler = async (event: any) => {
// Handler logic
};
// Compiled to:
// exports.handler = async (event) => { ... };
5. Environment Variables
// Node.js process.env access
const PRICES_TABLE = process.env.PRICES_TABLE!;
const NODE_ENV = process.env.NODE_ENV || 'development';
// Used in Lambda environment configuration
DRF-Equivalent Patterns (Django REST Framework)
The service uses DRF-equivalent patterns in TypeScript/Node.js:
// Django-like serializer pattern in Node.js
class PriceSerializer {
static validate(data: any): PriceInput {
if (!data.storeId) throw new ValidationError('storeId required');
if (!data.productId) throw new ValidationError('productId required');
if (data.amount <= 0) throw new ValidationError('amount must be positive');
return data;
}
static toRepresentation(price: Price): PriceOutput {
return {
storeId: price.storeId,
productId: price.productId,
amount: price.amount,
currency: price.currency,
effectiveDate: price.effectiveDate.toISOString()
};
}
}
// Django-like viewset pattern in Node.js
class PriceViewSet {
async retrieve(storeId: string, productId: string): Promise<Price> {
const price = await this.service.getPrice(storeId, productId);
if (!price) throw new NotFoundError('Price not found');
return PriceSerializer.toRepresentation(price);
}
async list(storeId: string, filters?: PriceFilter): Promise<Price[]> {
const prices = await this.service.listPrices(storeId, filters);
return prices.map(PriceSerializer.toRepresentation);
}
async create(input: CreatePriceInput): Promise<Price> {
const validated = PriceSerializer.validate(input);
const price = await this.service.createPrice(validated);
return PriceSerializer.toRepresentation(price);
}
}
Deployment and Execution
Build Process
# 1. Install Node.js dependencies
npm install
# 2. Compile TypeScript to JavaScript (for Node.js)
npm run build
# Runs: tsc (TypeScript Compiler)
# Output: dist/ (JavaScript files for Node.js)
# 3. Package for Lambda deployment
npm run package
# Creates: lambda-package.zip (Node.js code + dependencies)
# 4. Deploy with AWS CDK
cdk deploy
# Deploys Lambda functions with Node.js runtime
Package.json (Node.js Dependencies)
{
"name": "pricing-service",
"version": "1.0.0",
"scripts": {
"build": "tsc",
"package": "zip -r lambda-package.zip dist node_modules",
"deploy": "cdk deploy"
},
"dependencies": {
"@aws-sdk/client-dynamodb": "^3.0.0",
"@aws-sdk/lib-dynamodb": "^3.0.0",
"@aws-sdk/client-appsync": "^3.0.0",
"graphql": "^16.0.0"
},
"devDependencies": {
"@types/node": "^18.0.0",
"@types/aws-lambda": "^8.10.0",
"typescript": "^5.0.0"
},
"engines": {
"node": ">=18.0.0"
}
}
AWS CDK Deployment
// infrastructure/pricing-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
export class PricingServiceStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Deploy Lambda function with Node.js runtime
const resolverFunction = new lambda.Function(this, 'PricingResolver', {
runtime: lambda.Runtime.NODEJS_18_X, // Node.js 18.x
handler: 'index.handler', // Node.js entry point
code: lambda.Code.fromAsset('dist'), // Compiled JavaScript
timeout: cdk.Duration.seconds(30),
memorySize: 512,
environment: {
NODE_ENV: 'production',
PRICES_TABLE: pricesTable.tableName
}
});
}
}
Execution Flow
1. Developer writes TypeScript code
ā
2. TypeScript compiles to JavaScript
ā
3. JavaScript packaged with Node.js dependencies
ā
4. AWS CDK deploys to Lambda
ā
5. Lambda uses Node.js 18.x runtime
ā
6. GraphQL request arrives at AppSync
ā
7. AppSync invokes Lambda (Node.js)
ā
8. Node.js executes handler function
ā
9. Handler queries DynamoDB (async)
ā
10. Result returned to AppSync
ā
11. Response sent to client
Performance Optimization
Node.js-Specific Optimizations
1. Connection Pooling
// Reuse DynamoDB client across Lambda invocations (warm start)
const dynamoClient = new DynamoDBClient({
region: 'us-east-1',
maxAttempts: 3
});
// Connection pool persists in warm Lambda containers
export const docClient = DynamoDBDocumentClient.from(dynamoClient);
2. Module Caching
// Modules loaded once and cached by Node.js
// Subsequent invocations reuse cached modules
import { dynamoClient } from './dynamo-client'; // Cached
import { logger } from './logger'; // Cached
3. Async Concurrency
// Node.js processes multiple async operations concurrently
export const batchUpdatePrices = async (updates: PriceUpdate[]) => {
// All operations run concurrently (non-blocking)
const results = await Promise.all(
updates.map(update => updatePrice(update))
);
return results;
};
4. Memory Management
// Node.js garbage collection optimization
// - Use object pooling for frequently created objects
// - Avoid memory leaks with proper cleanup
// - Monitor memory usage in CloudWatch
export const handler = async (event: any) => {
// Process request
const result = await processRequest(event);
// Explicit cleanup (helps Node.js GC)
event = null;
return result;
};
Single-Digit Millisecond Latency
Achieved through:
- Warm Lambda containers (Node.js already loaded)
- Connection pooling (reused DynamoDB connections)
- Efficient Node.js execution (V8 engine optimizations)
- Minimal dependencies (faster cold starts)
- Provisioned concurrency (always-warm containers)
Development Workflow
Local Development (Node.js)
# 1. Install Node.js (v18+)
node --version # v18.x.x
# 2. Install dependencies
npm install
# 3. Run TypeScript compiler in watch mode
npm run dev
# Compiles TypeScript ā JavaScript on file changes
# 4. Test locally with Node.js
npm run test
# Runs tests in Node.js environment
# 5. Run Lambda function locally (Node.js)
sam local invoke PricingResolver
# or
node dist/lambda/pricing-resolver/index.js
Testing Node.js Code
// tests/pricing.test.ts
import { handler } from '../src/lambda/pricing-resolver';
import type { AppSyncResolverEvent } from 'aws-lambda';
describe('Pricing Resolver', () => {
it('should get price', async () => {
const event: AppSyncResolverEvent = {
arguments: { storeId: '123', productId: '456' },
info: { fieldName: 'getPrice' }
};
// Test Node.js handler
const result = await handler(event, {} as Context);
expect(result).toBeDefined();
expect(result.storeId).toBe('123');
});
});
Debugging Node.js Lambda
// Enable Node.js debugging
export const handler = async (event: any) => {
// Node.js console.log (appears in CloudWatch)
console.log('Event:', JSON.stringify(event, null, 2));
// Node.js error logging
console.error('Error:', error);
// Structured logging
logger.info('Processing request', { storeId: event.arguments.storeId });
};
Summary
The pricing-service is built entirely on Node.js through:
- TypeScript ā JavaScript compilation for Node.js runtime
- AWS Lambda with Node.js 18.x runtime
- GraphQL resolvers executing as Node.js functions
- Event-driven processing via Node.js Lambda handlers
- Async/await patterns leveraging Node.js non-blocking I/O
- CommonJS modules (Node.js module system)
- Performance optimizations specific to Node.js execution
The service demonstrates production-grade Node.js expertise at scale:
- Millions of daily requests handled by Node.js Lambda functions
- Single-digit millisecond latency through Node.js optimizations
- 8,000+ stores served by Node.js-powered GraphQL API
- Event-driven architecture built on Node.js async capabilities
This architecture showcases deep understanding of Node.js runtime characteristics, serverless execution models, and high-performance TypeScript/JavaScript development patterns.