Package Exports
- @signmax/remix-base
- @signmax/remix-base/client
- @signmax/remix-base/growthbook
- @signmax/remix-base/instrumentation
- @signmax/remix-base/load_context
- @signmax/remix-base/logger
- @signmax/remix-base/metrics
- @signmax/remix-base/middleware
- @signmax/remix-base/secrets
- @signmax/remix-base/server
- @signmax/remix-base/test/helpers
- @signmax/remix-base/util
Readme
@signmax/remix-base
Server and shared utilities for React Router 7 projects.
This package provides a production-ready server setup and common utilities for React Router 7 (formerly Remix) applications, including Express server configuration, middleware, logging, metrics, and AWS integration.
Installation
npm install @signmax/remix-baseUsage
Basic Server Setup
The simplest setup with defaults:
import { serveApp } from "@signmax/remix-base"
import type { ServerBuild } from "react-router"
const build = () => import("./build/server/index.js") as Promise<ServerBuild>
await serveApp(build)Complete Example with Custom Context
Here's a complete example showing how to set up the server with custom middleware, load context, and Sentry instrumentation:
import { serveApp } from "@signmax/remix-base"
import { getLoadContext, type LoadContext } from "@signmax/remix-base/load_context"
import { startMetrics } from "@signmax/remix-base/metrics"
import { deviceKeyMiddleware, requestMiddleware } from "@signmax/remix-base/middleware"
import { type AppLoadContext, type ServerBuild } from "react-router"
// Initialize Sentry if configured
if (process.env.SENTRY_DSN) {
void import("@signmax/remix-base/instrumentation").then(({ init }) => init())
}
// Start Prometheus metrics server
await startMetrics()
// Development server setup
const viteDevServer =
process.env.NODE_ENV === "development"
? await import("vite").then(vite =>
vite.createServer({
server: { middlewareMode: true },
})
)
: null
const build: () => Promise<ServerBuild> = viteDevServer
? () => viteDevServer.ssrLoadModule("virtual:react-router/server-build") as Promise<ServerBuild>
: async () => import("../build/server/index.js")
// Extend the base load context with your app-specific properties
declare module "react-router" {
interface AppLoadContext extends LoadContext {
// Add your custom properties here
locale: string
userId?: string
}
}
await serveApp(
build,
viteDevServer?.middlewares,
[requestMiddleware, deviceKeyMiddleware /* add your custom middleware */],
(req, res) => {
const baseContext = getLoadContext(req, res)
const context: AppLoadContext = {
...baseContext,
locale: req.cookies.locale || "en",
userId: req.cookies.user_id,
}
// Optionally update GrowthBook attributes with user data
if (context.growthbook) {
context.growthbook.updateAttributes({
userId: context.userId,
locale: context.locale,
})
}
return context
}
)This automatically includes:
- CloudFront IP trust proxy configuration (production only)
- Compression
- Cookie parsing
- Logging with Pino
- Sentry error tracking
- Security headers (Helmet, CSP)
- No-index middleware
- Static asset serving with proper cache headers
Middleware
Import individual middleware as needed:
import {
cspMiddleware,
deviceKeyMiddleware,
endingSlashMiddleware,
helmetMiddleware,
noIndexMiddleware,
sentryIPMiddleware,
} from "@signmax/remix-base/middleware"Utilities
import { headers, timing } from "@signmax/remix-base/util"Includes utilities for:
- Browser detection
- Request header handling
- Server timing metrics
- Revision tracking
Logger
Access the pre-configured Pino logger:
import logger from "@signmax/remix-base/logger"
logger.info("Application started")
logger.error({ err }, "Error occurred")Metrics
Prometheus metrics collection:
import { metrics } from "@signmax/remix-base/metrics"API Client
GraphQL client utilities:
import { createClient } from "@signmax/remix-base/client"Test Helpers
Testing utilities for your test suite:
import { mockRequest, mockResponse } from "@signmax/remix-base/test/helpers"AWS Secrets Manager
Optional utility for loading secrets from AWS Secrets Manager:
import { loadSecrets, type Secrets } from "@signmax/remix-base/secrets"
// Load secrets with defaults (uses AWS_SECRET_NAME or app/env/${APP_ENV})
const secrets = await loadSecrets()
// Or specify custom options
const secrets = await loadSecrets({
secretName: "my-app/production",
region: "us-east-1",
})
// Type the secrets by extending the Secrets interface
interface MySecrets extends Secrets {
apiKey: string
dbPassword: string
}
const secrets = await loadSecrets<MySecrets>()
// secrets.apiKey is typed as stringFeatures
- Production-ready Express server with sensible defaults
- CloudFront IP updater for proper client IP tracking behind AWS CloudFront
- Security middleware including Helmet and CSP
- Logging with Pino (structured JSON logging)
- Metrics with Prometheus client
- Sentry integration for error tracking and profiling
- AWS Secrets Manager integration
- GrowthBook feature flag support
- TypeScript first-class support
Environment Variables
The package respects common environment variables:
NODE_ENV- Environment (production/development)PORT- Server port (default: 4000)APP_ENV- Application environment name (defaults to NODE_ENV)API_HOST- GraphQL API host (default: localhost)API_PORT- GraphQL API port (default: 3000)API_PATH- GraphQL API path (default: /graphql)SHARED_SECRET- Shared secret for API authenticationSHARED_SECRET_HEADER- Header name for shared secret (default: x-shared-secret)BUILD_DIR- Client build directory (default: build/client)ASSETS_DIR- Assets directory (default: ${BUILD_DIR}/assets)AWS_SECRET_NAME- AWS Secrets Manager secret name (default:app/env/${APP_ENV})AWS_REGION- AWS region (default: eu-central-1)SENTRY_DSN- Sentry error tracking
Configuration
Server Options
You can customize the server behavior using either the traditional parameters or an options object:
import { serveApp, type ServeAppOptions } from "@signmax/remix-base"
// Using options object for fine-grained control
const options: ServeAppOptions = {
build,
devServer: viteDevServer?.middlewares,
middleware: [requestMiddleware, deviceKeyMiddleware],
getLoadContext: (req, res) => getLoadContext(req, res),
port: 4000,
buildDir: "dist/client",
assetsDir: "dist/client/assets",
enableCloudFrontIpUpdater: true,
enableSentryIPMiddleware: true,
enableEndingSlashMiddleware: true,
enableHelmetMiddleware: true,
enableNoIndexMiddleware: false, // Disable for sites that should be indexed
}
await serveApp(options)Sentry Configuration
Customize Sentry initialization:
import { init, type SentryConfig } from "@signmax/remix-base/instrumentation"
const sentryConfig: SentryConfig = {
denyUrls: [/\/health/, /\/metrics/],
tracesSampleRate: 0.1,
profilesSampleRate: 0.05,
}
if (process.env.SENTRY_DSN) {
init(sentryConfig)
}GrowthBook (Optional)
GrowthBook is an optional dependency for feature flagging. To use it:
- Install the optional dependency:
npm install @growthbook/growthbook eventsource- Create a GrowthBook instance:
import { createGrowthBook } from "@signmax/remix-base/growthbook"
import { getLoadContext } from "@signmax/remix-base/load_context"
const growthbook = await createGrowthBook({
apiHost: "https://cdn.growthbook.io",
clientKey: "your-client-key",
timeout: 3000,
streaming: true,
})
const customGetLoadContext = (req, res) => getLoadContext(req, res, { growthbook })
await serveApp(build, undefined, [], customGetLoadContext)Device Key Middleware
Customize the device key cookie name:
import { createDeviceKeyMiddleware } from "@signmax/remix-base/middleware"
const deviceKey = createDeviceKeyMiddleware({
cookieName: "my_device_id",
maxAge: 1000 * 60 * 60 * 24 * 365, // 1 year
})
await serveApp(build, undefined, [deviceKey])Custom API Headers
Pass additional headers to your GraphQL API by configuring the client:
import { createRequest, createResponseMiddleware } from "@signmax/remix-base/api/client"
const requestFn = createRequest(req, res, createResponseMiddleware(req, res), {
passthroughHeaders: ["x-custom-auth", "x-tenant-id"],
})Set includeDefaultPassthroughHeaders to false to replace the defaults instead of extending them.
Publishing
This package is published to npm whenever a GitHub Release is created.
How to release:
- Update version in
package.json - Update
CHANGELOG.md - Commit and push changes
- Create a Git tag
vX.Y.Zand a GitHub Release
License
MIT