Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web/document server #1129

Merged
merged 21 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
38b70de
refactor(web/server): simplify env handling and remove utils
suddenlyGiovanni Dec 17, 2024
fcb81b4
feat(web): update biome config for server-specific rules
suddenlyGiovanni Dec 17, 2024
858008e
refactor(web): improve server configuration and startup logic
suddenlyGiovanni Dec 17, 2024
bc84873
docs(web): add server setup documentation
suddenlyGiovanni Dec 17, 2024
63ab289
refactor(web): reorganize biome.json overrides
suddenlyGiovanni Dec 17, 2024
111141e
docs(web/server): add details on middleware and dev server
suddenlyGiovanni Dec 17, 2024
df731c0
feat(web): refactor server to export `run` and return `http.Server`
suddenlyGiovanni Dec 17, 2024
25ddf0b
feat(web): add msw for API mocking support
suddenlyGiovanni Dec 17, 2024
f043950
feat(web): add MSW setup for testing environment
suddenlyGiovanni Dec 17, 2024
9ee8b43
test(web): add initial test placeholders for server setup
suddenlyGiovanni Dec 17, 2024
b17494d
feat(web-server): improve error handling and graceful shutdown
suddenlyGiovanni Dec 17, 2024
db22118
test(web/server): add test for missing server build path
suddenlyGiovanni Dec 17, 2024
e1b1af3
fix(pnpm-lock): remove deprecated warnings from lockfile
suddenlyGiovanni Dec 17, 2024
21f2686
Apply suggestions from code review
suddenlyGiovanni Dec 18, 2024
68b8b43
fix(web): set NODE_ENV to production in start script
suddenlyGiovanni Dec 18, 2024
ce049cd
feat(web): add environment-specific .env files
suddenlyGiovanni Dec 18, 2024
eb2ca75
chore(web): adjust Node.js options in scripts and env files
suddenlyGiovanni Dec 18, 2024
43fa362
refactor(web): simplify server build configuration
suddenlyGiovanni Dec 18, 2024
7568e0e
test(web): remove obsolete test for missing build path argument
suddenlyGiovanni Dec 18, 2024
075f338
fix(web): update Dockerfile to correct CMD instruction
suddenlyGiovanni Dec 18, 2024
dcb300e
feat(web/server): enhance middleware and add graceful shutdown
suddenlyGiovanni Dec 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/web/.development.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
NODE_ENV=development
NODE_OPTIONS="--trace-warnings --inspect --experimental-strip-types --experimental-transform-types --watch-preserve-output"
2 changes: 2 additions & 0 deletions apps/web/.production.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
NODE_ENV=production
NODE_OPTIONS=--trace-warnings --inspect --experimental-strip-types --experimental-transform-types
2 changes: 1 addition & 1 deletion apps/web/Dockerfile
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [hadolint] <DL3018> reported by reviewdog 🐶
Pin versions in apk add. Instead of apk add <package> use apk add <package>=<version>

RUN apk add --no-cache jq

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [hadolint] <DL3059> reported by reviewdog 🐶
Multiple consecutive RUN instructions. Consider consolidation.

RUN jq -r .packageManager package.json > pnpm-version.txt

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [hadolint] <SC2046> reported by reviewdog 🐶
Quote this to prevent word splitting.

RUN corepack enable \

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [hadolint] <DL3059> reported by reviewdog 🐶
Multiple consecutive RUN instructions. Consider consolidation.

RUN pnpm dlx turbo prune --docker "@suddenlygiovanni/web"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [hadolint] <SC2164> reported by reviewdog 🐶
Use 'cd ... || exit' or 'cd ... || return' in case cd fails.

RUN cd ./apps/web

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [hadolint] <DL3003> reported by reviewdog 🐶
Use WORKDIR to switch to a directory

RUN cd ./apps/web

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [hadolint] <DL3059> reported by reviewdog 🐶
Multiple consecutive RUN instructions. Consider consolidation.

RUN pnpm --filter @suddenlygiovanni/web --prod --no-optional deploy pruned

Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,4 @@ USER node

EXPOSE 5173

CMD ["node", "--experimental-strip-types", "--experimental-transform-types", "server/server.ts", "build/server/index.js"]
CMD ["node", "--experimental-strip-types", "--experimental-transform-types", "server/server.ts"]
21 changes: 21 additions & 0 deletions apps/web/biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,27 @@
}
},
"overrides": [
{
"include": ["./server/**/*.ts"],
"linter": {
"rules": {
"style": {
"useNamingConvention": "info"
},
"suspicious": {
"noConsole": "off",
"noConsoleLog": "off"
},
"correctness": {
"noNodejsModules": "off"
},
"nursery": {
"useExplicitType": "off"
}
}
}
},

{
"include": ["app/routes/**/*.tsx"],
"linter": {
Expand Down
7 changes: 7 additions & 0 deletions apps/web/mocks/handlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { http, type HttpHandler } from 'msw'

export const handlers = [
http.all('*', ({ request }) => {
console.log(request.method, request.url)
}),
] satisfies HttpHandler[]
5 changes: 5 additions & 0 deletions apps/web/mocks/node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { setupServer } from 'msw/node'

import { handlers } from './handlers.ts'

export const server = setupServer(...handlers)
5 changes: 3 additions & 2 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@vitest/ui": "3.0.0-beta.2",
"babel-plugin-react-compiler": "19.0.0-beta-df7b47d-20241124",
"globals": "15.13.0",
"msw": "2.7.0",
"react-router-devtools": "2.0.0-beta.0",
"tailwindcss": "4.0.0-beta.8",
"tailwindcss-animate": "1.0.7",
Expand All @@ -69,11 +70,11 @@
"check": "biome check .",
"check:fix": "biome check --apply .",
"clean": "scripty",
"dev": "NODE_ENV=development node --watch-path=./server --watch-preserve-output --inspect --experimental-network-inspection --experimental-strip-types --experimental-transform-types --trace-warnings server/server.ts build/server/index.js",
"dev": "node --env-file=.development.env --experimental-network-inspection server/server.ts",
"format": "biome check --vcs-enabled=true --vcs-use-ignore-file=true --formatter-enabled=true --linter-enabled=false --organize-imports-enabled=true .",
"format:write": "biome check --vcs-enabled=true --vcs-use-ignore-file=true --formatter-enabled=true --linter-enabled=false --organize-imports-enabled=true --write .",
"lint": "biome ci --vcs-enabled=true --vcs-use-ignore-file=true --vcs-root='../../' --formatter-enabled=false --linter-enabled=true --organize-imports-enabled=false --no-errors-on-unmatched .",
"start": "node --experimental-strip-types --experimental-transform-types server/server.ts build/server/index.js",
"start": "node --env-file=.production.env server/server.ts",
"test": "vitest",
"test:unit": "vitest",
"typecheck": "react-router typegen && tsc"
Expand Down
1 change: 1 addition & 0 deletions apps/web/react-router.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Config } from '@react-router/dev/config'
export default {
appDirectory: 'src',
serverModuleFormat: 'esm',
serverBuildFile: 'index.js',
buildDirectory: 'build',
ssr: true,
future: {
Expand Down
150 changes: 150 additions & 0 deletions apps/web/server/server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Server Setup Documentation

## Overview

The server is an Express application that serves a React Router build. It supports both development and production environments, dynamically configuring the server port and host.

## Key Components

### Configuration (`Config` class)

- Manages environment variables and server build path.
- Uses `effect` library for schema validation.

### Source Map Support

- Uses `source-map-support` to enhance error stack traces.

### Express Application

- Uses `compression` for response compression.
- Uses `morgan` for HTTP request logging.
- Serves static files and handles all incoming requests.

### Development Mode

- Uses `vite` for development server with middleware mode.
- Dynamically loads and handles requests using Vite's SSR capabilities.

### Production Mode

- Serves static assets with caching.
- Loads the server build from the specified path.

### Request Handling

- Uses `react-router` for handling requests and responses.
- Custom request handler created using `react-router-express`.

## Middleware Behavior

### Compression Middleware (`compression`)

- Compresses response bodies for all requests.
- Adds `Content-Encoding` headers and compresses the body.

### Morgan Middleware (`morgan`)

- Logs HTTP requests.
- Does not modify the `Request` or `Response` but logs details like method, URL, status, and response time.

### Static File Serving (`express.static`)

- Serves static files from the specified directories.
- Adds appropriate headers (e.g., `Cache-Control`) and serves files.

### Vite Dev Server Middleware (`viteDevServer.middlewares`)

- Handles requests in development mode.
- Attaches custom middlewares and acts as a handler function for custom HTTP servers.
- Provides features like HMR, module loading, and development-specific optimizations.

## Vite Dev Server in Development Mode

- A connect app instance that attaches custom middlewares to the dev server.
- Acts as a handler function for custom HTTP servers or as middleware in connect-style Node.js frameworks.
- Provides HMR, module loading, and development-specific optimizations.

## `viteDevServer.ssrLoadModule('./server/app.ts')` and `app.ts`

### `ssrLoadModule`

- Loads a given URL as an instantiated module for SSR.

### `app.ts`

- Sets up the Express application and request handler.
- Defines a request handler using `createRequestHandler` from `react-router-express`.
- Configures the request handler to load the server build and provide context values.

## Handling OS Signals for Graceful Shutdown

- Listens for termination signals (`SIGTERM` and `SIGINT`).
- Closes the server instance and cleans up resources when a signal is received.

## Additional Details

### Configuration

- Dynamically configures the port and host based on environment variables or default settings.

### Source Map Support

- Enhances error stack traces by retrieving source maps for better debugging.

### Request Handling

- Uses `react-router` to handle requests and responses, providing SSR capabilities.

## External Dependencies

- `compression`: Middleware for response compression.
- `effect`: Library for schema validation.
- `express`: Web framework for building the server.
- `get-port`: Utility to get an available port.
- `morgan`: HTTP request logger.
- `source-map-support`: Enhances error stack traces.
- `vite`: Development server and build tool.
- `react-router`: Library for handling routing and SSR.

# Unit Tests

## Objectives

- Validate the configuration schema and default values.
- Ensure the server starts correctly in both development and production modes.
- Verify middleware and request handling logic.
- Test static file serving and caching behavior.
- Validate error handling and logging.

## Test Cases

### Configuration Tests

- Validate default values for `NODE_ENV`, `PORT`, and `HOST`.
- Test schema validation for different configurations.

### Server Initialization Tests

- Ensure the server starts on the specified port and host.
- Verify the correct middleware is applied based on the environment.

### Middleware Tests

- Test `compression` middleware for response compression.
- Verify `morgan` logs HTTP requests correctly.

### Request Handling Tests

- Test request handling in development mode using Vite.
- Verify request handling in production mode with static file serving.

### Static File Serving Tests

- Ensure static assets are served with correct caching headers.
- Test serving of client-side assets from the build directory.

### Error Handling Tests

- Verify error handling and logging for different scenarios.
- Test source map support for enhanced error stack traces.
63 changes: 63 additions & 0 deletions apps/web/server/server.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { type MockInstance, afterAll, afterEach, beforeEach, describe, it, vi } from 'vitest'

describe('server setup', () => {
let consoleErrorSpy: MockInstance<(...args: unknown[]) => void>

beforeEach(() => {
vi.stubEnv('NODE_ENV', undefined)
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
vi.spyOn(process, 'exit').mockImplementation((code?: number) => {
throw new Error(`process.exit(${code})`)
})
})

afterEach(() => {
vi.resetAllMocks()
vi.unstubAllEnvs()
vi.unstubAllGlobals()
})

afterAll(() => {
vi.restoreAllMocks()
})

describe('configuration tests', () => {
it.todo('should validate default values for NODE_ENV, PORT, and HOST')

it.todo('should test schema validation for different configurations')
})

describe('server Initialization Tests', () => {
it.todo('should ensure the server starts on the specified port and host')

it.todo('should verify the correct middleware is applied based on the environment')
})

describe('middleware Tests', () => {
it.todo('should test compression middleware for response compression')

it.todo('should verify morgan logs HTTP requests correctly')
})

describe('request Handling Tests', () => {
it.todo('should test request handling in development mode using Vite')

it.todo('should verify request handling in production mode with static file serving')
})

describe('static File Serving Tests', () => {
it.todo('should ensure static assets are served with correct caching headers')

it.todo('should test serving of client-side assets from the build directory')
})

describe('error Handling Tests', () => {
it.todo('should verify error handling and logging for different scenarios')

it.todo('should test source map support for enhanced error stack traces')
})

describe('graceful Shutdown Tests', () => {
it.todo('should handle OS signals to gracefully shut down the server')
})
})
Loading
Loading