@everystack/server
v0.2.0
Published
Server runtime primitives for Lambda — event adapters, routing, SSR, image processing
Readme
@everystack/server
Lambda runtime primitives for building serverless applications with SST. Provides event adapters, routing, database connection, SSR, image processing, migrations, and background job handling.
Install
pnpm add @everystack/serverEntry Points
| Export | Purpose |
|--------|---------|
| @everystack/server | Lambda handler, router, event adapters, logging |
| @everystack/server/db | SST Resource-linked database connection |
| @everystack/server/ssr | Expo web SSR bundle serving |
| @everystack/server/image | On-demand image resizing via Sharp |
| @everystack/server/migrate | Drizzle migration runner |
| @everystack/server/worker | SQS job queue consumer |
| @everystack/server/stubs | esbuild bundling helpers for client packages |
Quick Start
import { createLambdaHandler } from '@everystack/server';
import { createDb, getJwtSecret } from '@everystack/server/db';
import { runMigrations } from '@everystack/server/migrate';
import { createWorkerHandler } from '@everystack/server/worker';
import * as schema from '../db/schema';
const { db } = createDb(schema);
// API Lambda
export const handler = createLambdaHandler({
init: async () => ({
api: createApiHandler(db),
}),
routes: (h) => [
{ path: '/api', handler: h.api },
],
onAction: async (action, payload) => {
// CLI invocations via IAM-authed Lambda invoke
if (action === 'migrate') return runMigrations(db, './drizzle');
if (action === 'seed') return runSeed(db);
},
});
// Worker Lambda
export const worker = createWorkerHandler(async () => ({
'email:send': async (payload) => { /* ... */ },
'image:process': async (payload) => { /* ... */ },
}));Main Export (@everystack/server)
createLambdaHandler(options)
The primary entry point. Wraps initialization, routing, and error handling into a Lambda handler.
interface LambdaHandlerOptions {
init: () => Promise<Record<string, Handler>>;
routes: (handlers: Record<string, Handler>) => Route[];
fallback?: (handlers: Record<string, Handler>) => Handler;
onAction?: (action: string, payload: unknown, handlers: Record<string, Handler>) => Promise<unknown>;
}Behavior:
- Lazy-initializes handlers once, caches across warm invocations
- Detects CLI invocations (events with
_actionfield) and dispatches toonAction - Sets Cache-Control:
private, no-store(authenticated) orpublic, s-maxage=300(public GET) - Guards response size (5MB max for Function URLs)
- Adds
x-request-idfrom Lambda trace or UUID
createRouter(routes, fallback?)
Path/method dispatcher.
interface Route {
path: string; // URL prefix match
method?: string; // HTTP method filter (optional)
exact?: boolean; // Exact path match (default: prefix)
handler: Handler;
}Routes match by prefix unless exact: true. First match wins.
eventToRequest(event)
Converts AWS API Gateway V2 events to Web Standard Request objects.
responseToResult(response)
Converts Web Standard Response to API Gateway V2 results. Auto-detects and base64-encodes binary content.
log(level, message, meta?)
Structured JSON logging to stdout with ISO timestamp.
Database (@everystack/server/db)
SST Resource-linked database helpers. All credentials come from SST's Resource linking — no env vars needed.
createDb(schema, options?)
Lazy singleton Drizzle connection via postgres.js.
const { db } = createDb(schema, { maxConnections: 1 });Pool defaults: max 1, idle timeout 60s, connect timeout 5s, max lifetime 5min, SSL required.
getDatabaseUrl()
Returns PostgreSQL connection URL from SST Resources.
getJwtSecret()
Returns JWT secret as Uint8Array from Resource.JwtSecret.value.
Web SSR (@everystack/server/ssr)
Serves Expo web builds deployed via @everystack/cli.
getWebHandler(db, storage)
Returns a Request→Response handler for the latest web release.
- Queries database for latest
platform='web'release - Downloads and extracts brotli-compressed tar archive to
/tmp/web-build/ - Caches handler with 60s TTL (re-queries for new releases)
- Returns
nullif no web release exists
downloadAndExtract(storage, storagePrefix)
Downloads {prefix}/bundle.tar.br from storage, decompresses with brotli, extracts to /tmp/web-build/.
extractTar(data, destDir)
Pure JavaScript POSIX tar extractor. Required because Lambda doesn't include the tar binary.
Image Processing (@everystack/server/image)
On-demand image resizing via Sharp (provided as Lambda layer).
createImageHandler(config)
interface ImageHandlerConfig {
bucket: string; // S3 bucket name
region?: string; // AWS region (default: AWS_REGION env)
pathPrefix?: string; // URL prefix (default: '/media/')
}URL format: /media/{key}?w=400&h=300&fit=cover&fm=webp&q=80&dpr=2
Behavior:
- No transforms → 302 redirect to presigned S3 URL (avoids 6MB Lambda limit)
- With transforms → fetch from S3, process with Sharp, return with immutable cache headers
parseParams(query)
Validates URL query params into transform options:
| Param | Range | Description |
|-------|-------|-------------|
| w | 1–4096 | Width |
| h | 1–4096 | Height |
| fit | cover, contain, fill, inside, outside | Resize fit |
| fm | webp, jpeg, png, avif | Output format |
| q | 1–100 | Quality |
| dpr | 1–3 | Device pixel ratio (multiplies w/h) |
Migrations (@everystack/server/migrate)
runMigrations(db, migrationsFolder)
Runs Drizzle migrations from the specified folder. Called via onAction handler (CLI → IAM → Lambda invoke).
- Returns
{ success: true, message: 'Migrations applied' } - Throws on error (caller handles response)
Worker (@everystack/server/worker)
createWorkerHandler(init)
SQS Lambda handler with type-based job dispatch.
type JobHandler = (payload: any, job: { id: string; type: string }) => Promise<void>;
const worker = createWorkerHandler(async () => ({
'email:send': async (payload) => { /* ... */ },
'image:process': async (payload) => { /* ... */ },
}));- Lazy-initializes handlers, caches across warm invocations
- Parses SQS message body as
{ jobId, type, payload } - Returns
batchItemFailuresfor partial failure handling (failed messages retry)
Stubs (@everystack/server/stubs)
clientStubs
Array of client-side package names to exclude from Lambda bundles via esbuild external.
stubsDir
Path to no-op module stubs. Used with SST copyFiles to prevent "module not found" errors when server code transitively imports client packages.
// sst.config.ts
import { clientStubs, stubsDir } from '@everystack/server/stubs';
new sst.aws.Function('Api', {
handler: 'server/api.handler',
nodejs: {
esbuild: { external: clientStubs },
},
copyFiles: [{ from: stubsDir, to: 'stubs' }],
});Peer Dependencies
sst— Resource linking for credentialsdrizzle-orm— Database ORMpostgres— PostgreSQL driversharp— Image processing (optional, Lambda layer)@aws-sdk/client-s3— S3 operations (image handler)@aws-sdk/s3-request-presigner— Presigned URLs (image handler)@everystack/cli— Storage adapter (SSR handler)
Part of everystack — a self-hosted application stack for Expo apps on AWS.
License
MIT
