Skip to content

Commit bd77ab2

Browse files
author
claudfuen
committed
feat: update Dockerfile and application to support database URL as build argument
- Added support for passing DATABASE_URL as a build argument in the Dockerfile. - Updated the image build process to include database dependency and build-time arguments. - Refactored TodoApp component to remove direct database URL prop, simplifying its interface. - Enhanced API response types for better type safety and clarity in handling responses.
1 parent 475c81f commit bd77ab2

File tree

11 files changed

+58
-8949
lines changed

11 files changed

+58
-8949
lines changed

apps/infra/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as aws from "@pulumi/aws";
22
import * as awsx from "@pulumi/awsx";
3-
import * as pulumi from "@pulumi/pulumi";
43
import * as command from "@pulumi/command";
4+
import * as pulumi from "@pulumi/pulumi";
55
import * as random from "@pulumi/random";
66

77
// ==========================================
@@ -317,11 +317,16 @@ const repo = new awsx.ecr.Repository("pathfinder-repo", {
317317
},
318318
});
319319

320-
// Always build Docker image locally and push to ECR
320+
// Build Docker image with database dependency and build-time args
321321
const imageUri = new awsx.ecr.Image("pathfinder-image", {
322322
repositoryUrl: repo.url,
323323
context: "../web",
324324
platform: "linux/amd64", // Required for AWS Fargate
325+
args: {
326+
DATABASE_URL: pulumi.interpolate`postgresql://${db.username}:${db.password}@${db.endpoint}/${db.dbName}`,
327+
},
328+
}, {
329+
dependsOn: [db] // Ensure database is fully created before building image
325330
}).imageUri;
326331

327332
// Fargate Service with init container for migrations

apps/web/Dockerfile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ RUN bun install --frozen-lockfile
1010
FROM oven/bun:1.2.18-alpine AS builder
1111
WORKDIR /app
1212

13-
# Skip environment validation during build (DATABASE_URL is runtime-only)
14-
ENV SKIP_ENV_VALIDATION=true
13+
# Accept DATABASE_URL as build argument
14+
ARG DATABASE_URL
15+
ENV DATABASE_URL=$DATABASE_URL
1516

1617
# Copy dependencies from previous stage
1718
COPY --from=deps /app/node_modules ./node_modules

apps/web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"pg": "^8.16.3",
2828
"react": "^19.0.0",
2929
"react-dom": "^19.0.0",
30+
"tsx": "^4.20.3",
3031
"zod": "^3.25.76"
3132
},
3233
"devDependencies": {

apps/web/src/app/components/TodoApp.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@ import type { Todo } from "@/types/api";
55
import { useState } from "react";
66

77
type TodoAppProps = {
8-
databaseUrl: string;
98
initialTodos: Todo[];
109
};
1110

12-
export default function TodoApp({ databaseUrl, initialTodos }: TodoAppProps) {
11+
export default function TodoApp({ initialTodos }: TodoAppProps) {
1312
const [todos, setTodos] = useState<Todo[]>(initialTodos);
1413
const [newTodo, setNewTodo] = useState("");
1514
const [loading, setLoading] = useState(false);

apps/web/src/app/page.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@ export default async function Home() {
1010
.from(todos)
1111
.orderBy(desc(todos.createdAt));
1212

13+
console.log("initialTodos", initialTodos);
14+
1315
return (
1416
<div>
15-
<TodoApp
16-
databaseUrl={env.DATABASE_URL}
17-
initialTodos={initialTodos}
18-
/>
17+
<TodoApp initialTodos={initialTodos} />
1918
<div
2019
style={{
2120
position: "fixed",

apps/web/src/db/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as schema from "./schema";
66
// Create a connection pool using validated environment variables
77
const pool = new Pool({
88
connectionString: env.DATABASE_URL,
9-
ssl: env.NODE_ENV === "production" ? { rejectUnauthorized: false } : false,
9+
// ssl: env.NODE_ENV === "production" ? { rejectUnauthorized: false } : false,
1010
});
1111

1212
// Create the database instance

apps/web/src/env.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ export const env = createEnv({
1313
// NEXT_PUBLIC_*: z.string(),
1414
},
1515
// For Next.js >= 13.4.4, you only need to destructure client variables:
16-
experimental__runtimeEnv: {
17-
// No client env vars currently
16+
runtimeEnv: {
17+
DATABASE_URL: process.env.DATABASE_URL,
18+
NODE_ENV: process.env.NODE_ENV,
1819
},
1920
// Allow skipping validation for development scenarios
2021
skipValidation: !!process.env.SKIP_ENV_VALIDATION,

apps/web/src/types/api.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,21 @@ import type { InferSelectModel } from "drizzle-orm";
55
export type Todo = InferSelectModel<typeof todos>;
66

77
// API Response types
8-
export interface ApiResponse<T = any> {
8+
export interface ApiResponse<T = unknown> {
99
success: boolean;
1010
data?: T;
1111
error?: string;
1212
message?: string;
1313
}
1414

15-
export interface TodosResponse extends ApiResponse<Todo[]> {}
16-
export interface TodoResponse extends ApiResponse<Todo> {}
17-
export interface DeleteResponse extends ApiResponse {
15+
export interface TodosResponse extends ApiResponse<Todo[]> {
16+
data: Todo[];
17+
}
18+
19+
export interface TodoResponse extends ApiResponse<Todo> {
20+
data: Todo;
21+
}
22+
23+
export interface DeleteResponse extends ApiResponse<never> {
1824
message: string;
1925
}

apps/web/test-db.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const { Pool } = require('pg');
2+
3+
async function testConnection() {
4+
const pool = new Pool({
5+
connectionString: process.env.DATABASE_URL,
6+
});
7+
8+
try {
9+
const client = await pool.connect();
10+
console.log('✅ Database connection successful');
11+
12+
const result = await client.query('SELECT * FROM todos LIMIT 1');
13+
console.log('✅ Query successful:', result.rowCount, 'rows');
14+
15+
client.release();
16+
await pool.end();
17+
} catch (error) {
18+
console.error('❌ Database error:', error.message);
19+
console.error('Connection string:', process.env.DATABASE_URL);
20+
process.exit(1);
21+
}
22+
}
23+
24+
testConnection();

bun.lock

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)