Files
esdp/serverless/README.md

193 lines
5.6 KiB
Markdown

# Serverless TypeScript Project
This folder contains AWS Lambda functions deployed via the Serverless Framework, written in TypeScript.
## Setup
This project is configured with TypeScript, ESLint, and Prettier independently from the main project.
### Prerequisites
- Node.js 22.x or higher
- npm or yarn
### Installation
```bash
npm install
```
## Development
### Available Scripts
- `npm run build` - Compile TypeScript to JavaScript
- `npm run type-check` - Run TypeScript type checking without emitting files
- `npm run lint` - Run ESLint on TypeScript files
- `npm run lint:fix` - Run ESLint with auto-fix
- `npm run format` - Format code with Prettier
- `npm run format:check` - Check if code is formatted correctly
### Project Structure
```
src/
handlers/ # Lambda function handlers
vehicleType.ts # Vehicle type lookup handler
scrub.ts # Estimate scrubbing handler
emsupload.ts # File upload presigned URL handler
lib/ # Shared library code
vehicleTypes/ # Vehicle type utilities
db.ts # Database utilities
```
## Deployment
The project uses `serverless-esbuild` for bundling TypeScript code for deployment.
```bash
# 1) Deploy shared infra (VPC + DB + Proxy + Secret) once
sls deploy --config serverless.infra.yml --stage shared
# 2) Deploy the application Lambdas per stage (dev/alpha/beta/prod)
sls deploy --stage dev
sls deploy --stage prod
```
## RDS + Drizzle (Postgres)
This project provisions a **shared private** Postgres RDS instance (one DB for all app stages) and the networking needed for Lambdas to reach it.
If you want direct access from your laptop, the infra stack also allows inbound Postgres from a single admin IP CIDR (default is the current value in `serverless.infra.yml`).
### What gets created
Defined in `serverless.infra.yml`:
- A dedicated VPC with 2 public subnets + 2 private subnets
- A NAT Gateway for private-subnet egress
- Security groups:
- Lambda SG (egress all)
- RDS Proxy SG (allows inbound `5432` only from the Lambda SG)
- RDS SG (allows inbound `5432` only from the Proxy SG)
- An RDS Postgres instance:
- `DeletionProtection: true`
- `DeletionPolicy: Snapshot` / `UpdateReplacePolicy: Snapshot`
- `AutoMinorVersionUpgrade: true` (minor updates)
- Publicly accessible (restricted to a single `admin_cidr`)
- An RDS Proxy in the same VPC (Lambdas connect to the proxy, proxy connects to RDS)
- A Secrets Manager secret for the RDS master user (generated password)
Note: RDS + NAT Gateway incur AWS costs.
### Direct DB access (your IP only)
Infra config includes an `admin_cidr` parameter (e.g. `70.36.57.88/32`) that is allowed to connect to port `5432`. The RDS instance is placed in public subnets so it can be reached directly, but security groups restrict it to that single CIDR.
To change it, edit `stages.shared.params.admin_cidr` in `serverless.infra.yml` and redeploy infra:
```bash
cd serverless
sls deploy --config serverless.infra.yml --stage shared
```
### Drizzle files
- Schema: `src/db/schema/**/*.ts`
- Drizzle config: `drizzle.config.ts`
- Generated migrations (SQL): `drizzle/` (packaged for Lambda)
### Install deps
```bash
cd serverless
npm install
```
### Generate a migration (no DB connection required)
Drizzle can generate SQL migrations by diffing your schema files.
```bash
cd serverless
npm run db:generate
```
This writes SQL into `serverless/drizzle/`.
### Deploy the database
```bash
cd serverless
sls deploy --config serverless.infra.yml --stage shared
```
RDS creation can take several minutes.
### Apply migrations (recommended: run inside AWS)
Because the RDS instance is in private subnets, it is not directly reachable from your laptop by default.
The repo includes an internal migration Lambda (`dbMigrate`) that runs in the same VPC as the database.
```bash
cd serverless
sls invoke -f dbMigrate --stage dev
```
### Sanity check connectivity
```bash
cd serverless
sls invoke -f dbPing --stage dev
```
### Local migrations (optional)
If you set up network access to the private RDS instance (e.g., via Client VPN or an SSM tunnel through a bastion host), you can run Drizzle migrations locally:
```bash
export DATABASE_URL='postgres://USER:PASSWORD@HOST:5432/esdp'
npm run db:migrate
```
### Deletion protection behavior
- `sls remove --config serverless.infra.yml --stage shared` will **not** be able to delete the DB instance while deletion protection is enabled.
- To intentionally remove it later, you must first disable deletion protection in `serverless.infra.yml` and redeploy, then remove the infra stack.
### Single shared DB
The DB name comes from the `shared` stage param `db_name` in `serverless.infra.yml` (defaults to `esdp`). All app stages connect to this same DB via CloudFormation outputs.
## Local Development
```bash
# Run serverless offline
sls dev
```
## Configuration
- **TypeScript**: Configured in `tsconfig.json`
- **ESLint**: Configured in `eslint.config.mjs` (ESM flat config)
- **Prettier**: Configured in `.prettierrc.json`
- **Serverless (app)**: Configured in `serverless.yml`
- **Serverless (infra)**: Configured in `serverless.infra.yml`
## Code Quality
This project enforces:
- TypeScript strict mode
- ESLint rules for TypeScript
- Prettier formatting
- No unused variables or parameters (except those prefixed with `_`)
## Environment Variables
See `serverless.yml` for environment-specific configuration.
How To Deploy
Deploy infra once (shared DB + networking): cd serverless && sls deploy --config serverless.infra.yml --stage shared
Deploy/update Lambdas per environment without touching DB/VPC: cd serverless && sls deploy --stage dev (and similarly --stage prod, etc.)