Skip to content

Commit c1acc0d

Browse files
committed
address PR review: input validation, error handling, env guard, dynamic port, generate scripts
1 parent acbb0be commit c1acc0d

6 files changed

Lines changed: 59 additions & 31 deletions

File tree

deployment-platforms/rest-express-docker-aws-ec2/.github/workflows/deploy.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
push:
55
branches:
66
- main
7+
- latest
78

89
jobs:
910
deploy:

deployment-platforms/rest-express-docker-aws-ec2/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
"version": "1.0.0",
44
"license": "MIT",
55
"scripts": {
6-
"build": "tsc",
7-
"dev": "tsx src/index.ts",
6+
"build": "prisma generate && tsc",
7+
"dev": "prisma generate && tsx src/index.ts",
88
"start": "node dist/index.js"
99
},
1010
"dependencies": {

deployment-platforms/rest-express-docker-aws-ec2/src/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ app.use(express.json())
99
app.use(postRouter)
1010
app.use(userRouter)
1111

12-
app.listen(3000, () =>
13-
console.log('Server ready at: http://localhost:3000'),
12+
const PORT = process.env.PORT || 3000
13+
app.listen(PORT, () =>
14+
console.log(`Server ready at: http://localhost:${PORT}`),
1415
)
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { PrismaClient } from '../../prisma/generated/client'
22
import { PrismaPg } from '@prisma/adapter-pg'
33

4-
const pool = new PrismaPg({ connectionString: process.env.DATABASE_URL! })
4+
const databaseUrl = process.env.DATABASE_URL
5+
if (!databaseUrl) {
6+
throw new Error('Missing DATABASE_URL environment variable')
7+
}
8+
9+
const pool = new PrismaPg({ connectionString: databaseUrl })
510
export const prisma = new PrismaClient({ adapter: pool })

deployment-platforms/rest-express-docker-aws-ec2/src/routes/post.routes.ts

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ import { prisma } from '../lib/prisma'
44

55
export const postRouter = Router()
66

7+
function parsePostId(id: string | string[], res: Response): number | null {
8+
const postId = Number(id)
9+
if (!Number.isInteger(postId) || postId <= 0) {
10+
res.status(400).json({ error: `Invalid post ID: ${id}` })
11+
return null
12+
}
13+
return postId
14+
}
15+
716
// GET /feed — all published posts
817
postRouter.get('/feed', async (_req: Request, res: Response) => {
918
const posts = await prisma.post.findMany({
@@ -15,25 +24,25 @@ postRouter.get('/feed', async (_req: Request, res: Response) => {
1524

1625
// GET /post/:id — single post by id
1726
postRouter.get('/post/:id', async (req: Request, res: Response) => {
18-
const { id } = req.params
19-
const postId = Number(id)
20-
if (!Number.isInteger(postId)) {
21-
res.status(400).json({ error: `Invalid post ID: ${id}` })
22-
return
23-
}
27+
const postId = parsePostId(req.params.id, res)
28+
if (postId === null) return
2429
const post = await prisma.post.findUnique({
2530
where: { id: postId },
2631
})
2732
if (!post) {
28-
res.status(404).json({ error: `Post with ID ${id} not found` })
33+
res.status(404).json({ error: `Post with ID ${req.params.id} not found` })
2934
return
3035
}
3136
res.json(post)
3237
})
3338

3439
// POST /post — create post
3540
postRouter.post('/post', async (req: Request, res: Response) => {
36-
const { title, content, authorEmail } = req.body
41+
const { title, authorEmail, content } = req.body
42+
if (!title || !authorEmail) {
43+
res.status(400).json({ error: 'title and authorEmail are required' })
44+
return
45+
}
3746
const post = await prisma.post.create({
3847
data: {
3948
title,
@@ -46,12 +55,8 @@ postRouter.post('/post', async (req: Request, res: Response) => {
4655

4756
// PUT /publish/:id — publish a post
4857
postRouter.put('/publish/:id', async (req: Request, res: Response) => {
49-
const { id } = req.params
50-
const postId = Number(id)
51-
if (!Number.isInteger(postId)) {
52-
res.status(400).json({ error: `Invalid post ID: ${id}` })
53-
return
54-
}
58+
const postId = parsePostId(req.params.id, res)
59+
if (postId === null) return
5560
try {
5661
const post = await prisma.post.update({
5762
where: { id: postId },
@@ -63,7 +68,7 @@ postRouter.put('/publish/:id', async (req: Request, res: Response) => {
6368
error instanceof Prisma.PrismaClientKnownRequestError &&
6469
error.code === 'P2025'
6570
) {
66-
res.status(404).json({ error: `Post with ID ${id} not found` })
71+
res.status(404).json({ error: `Post with ID ${req.params.id} not found` })
6772
return
6873
}
6974
res.status(500).json({ error: 'Internal server error' })
@@ -72,12 +77,8 @@ postRouter.put('/publish/:id', async (req: Request, res: Response) => {
7277

7378
// DELETE /post/:id — delete post
7479
postRouter.delete('/post/:id', async (req: Request, res: Response) => {
75-
const { id } = req.params
76-
const postId = Number(id)
77-
if (!Number.isInteger(postId)) {
78-
res.status(400).json({ error: `Invalid post ID: ${id}` })
79-
return
80-
}
80+
const postId = parsePostId(req.params.id, res)
81+
if (postId === null) return
8182
try {
8283
const post = await prisma.post.delete({
8384
where: { id: postId },
@@ -88,7 +89,7 @@ postRouter.delete('/post/:id', async (req: Request, res: Response) => {
8889
error instanceof Prisma.PrismaClientKnownRequestError &&
8990
error.code === 'P2025'
9091
) {
91-
res.status(404).json({ error: `Post with ID ${id} not found` })
92+
res.status(404).json({ error: `Post with ID ${req.params.id} not found` })
9293
return
9394
}
9495
res.status(500).json({ error: 'Internal server error' })
Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,33 @@
11
import { Router, Request, Response } from 'express'
2+
import { Prisma } from '../../prisma/generated/client'
23
import { prisma } from '../lib/prisma'
34

45
export const userRouter = Router()
56

67
// POST /user — create user
78
userRouter.post('/user', async (req: Request, res: Response) => {
89
const { email, name } = req.body
9-
const user = await prisma.user.create({
10-
data: { email, name },
11-
})
12-
res.json(user)
10+
if (!email) {
11+
res.status(400).json({ error: 'email is required' })
12+
return
13+
}
14+
if (typeof email !== 'string' || !email.includes('@')) {
15+
res.status(400).json({ error: 'email must be a valid email address' })
16+
return
17+
}
18+
try {
19+
const user = await prisma.user.create({
20+
data: { email, name },
21+
})
22+
res.json(user)
23+
} catch (error: unknown) {
24+
if (
25+
error instanceof Prisma.PrismaClientKnownRequestError &&
26+
error.code === 'P2002'
27+
) {
28+
res.status(409).json({ error: `Email ${email} is already in use` })
29+
return
30+
}
31+
res.status(500).json({ error: 'Internal server error' })
32+
}
1333
})

0 commit comments

Comments
 (0)