Skip to content

Commit e5413a5

Browse files
Merge pull request #11 from numericgraphics/feature/06_use-vercel
Publish with Vercel
2 parents 02a5c3a + b782210 commit e5413a5

File tree

10 files changed

+285
-247
lines changed

10 files changed

+285
-247
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,7 @@ yarn-error.log
1313
.idea
1414

1515
#env
16-
.env
16+
.env
17+
18+
dist/
19+
.vercel

.next/trace

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[{"name":"next-dev","duration":375778,"timestamp":48743038978,"id":1,"tags":{},"startTime":1755273250317,"traceId":"67ad9cb661b8ac33"}]
2+
[{"name":"generate-buildid","duration":97,"timestamp":48766044509,"id":4,"parentId":1,"tags":{},"startTime":1755273273323,"traceId":"6b2fe1b57754ff3a"},{"name":"load-custom-routes","duration":160,"timestamp":48766044646,"id":5,"parentId":1,"tags":{},"startTime":1755273273323,"traceId":"6b2fe1b57754ff3a"},{"name":"next-build","duration":47457,"timestamp":48765998214,"id":1,"tags":{"buildMode":"default","isTurboBuild":"false","version":"15.4.6"},"startTime":1755273273276,"traceId":"6b2fe1b57754ff3a"}]

README.md

Lines changed: 96 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -4,84 +4,100 @@ Backend API for the **Speed Puzzle** app.
44

55
- **Language**: TypeScript
66
- **Framework**: Express (Node.js)
7-
- **Database**: MongoDB
8-
- **Data model**
9-
- `users`: `{ _id, userName, password(hashed), createdAt, updatedAt }`
10-
- `scores`: `{ _id, userId (ref users._id), value, createdAt }`
7+
- **Runtime**: Vercel Functions (Node.js)
8+
- **Database**: MongoDB Atlas
119

1210
---
1311

14-
## Tech stack
12+
## What changed (serverless-ready)
1513

16-
- Express (REST API)
17-
- MongoDB Node.js driver
18-
- TypeScript + `tsx` (watch/dev runner)
19-
- `dotenv` for env vars
20-
- `bcryptjs` for password hashing
21-
- `@faker-js/faker` for seed data
14+
This repo is now configured to run as **Vercel Serverless Functions**:
15+
16+
- The Express app lives in **`/api/index.ts`** and **exports the app** (`export default app`), no `app.listen()`.
17+
- On cold start, we **await DB initialization** before serving requests.
18+
- API routes return **500** on internal errors (instead of 406) and log a helpful message.
19+
- A diagnostic route `GET /__debug` is available during testing.
2220

2321
---
2422

2523
## Project structure
2624

2725
```text
26+
api/
27+
index.ts # Express app exported for Vercel Functions
2828
src/
2929
constants/events.ts
3030
controllers/Global.ts
3131
services/
32-
MongoDB.ts
32+
MongoDB.ts # MongoDB client (Node driver)
3333
Users.ts
3434
Scores.ts
35-
index.ts
3635
scripts/
3736
seed.ts
3837
```
3938

40-
> Note: The seeder populates 15 users with 1–3 scores each (200–500).
41-
4239
---
4340

44-
## Setup
41+
## Environment variables
4542

46-
1. **Install**
43+
Create these for **local dev** in a `.env` file at the project root and set the same keys in **Vercel → Project → Settings → Environment Variables** (Production scope):
4744

4845
```bash
49-
npm install
46+
MONGODB_URI="mongodb+srv://<user>:<pass>@<cluster>.mongodb.net/?retryWrites=true&w=majority&appName=<YourApp>"
47+
API_PORT=3000 # used only for local non-serverless runs
5048
```
5149

52-
2. **Environment** — create a `.env` at the project root:
50+
> For Vercel → Atlas connectivity, Atlas recommends allowing **`0.0.0.0/0`** (all IPs) because Vercel uses **dynamic egress IPs**. Tighten later with Secure Compute / private networking if required. citeturn0search2turn0search7
51+
52+
---
53+
54+
## Local development
5355

5456
```bash
55-
MONGODB_URI="mongodb://localhost:27017"
56-
API_PORT=3000
57+
npm install
58+
npm run dev # uses `vercel dev` to emulate the platform
5759
```
5860

59-
3. **Run**
61+
**Seed demo data**
6062

6163
```bash
62-
# dev (watch)
63-
npm run dev
64-
65-
# prod
66-
npm run build
67-
npm start
64+
npm run seed # inserts users + scores
65+
npm run seed -- --reset # wipe & reseed
6866
```
6967

70-
4. **Seed demo data**
68+
> The app runs as a Vercel Function locally; you don’t need `npm start` for serverless.
69+
70+
---
71+
72+
## Deployment (Vercel)
73+
74+
**One-time**:
7175

7276
```bash
73-
# inserts 15 users with 1–3 scores each (values 200–500)
74-
npm run seed
77+
npm i -g vercel
78+
vercel login
79+
```
80+
81+
**Deploy**:
7582

76-
# wipe & reseed
77-
npm run seed -- --reset
83+
```bash
84+
vercel # Preview deployment
85+
vercel --prod # Production deployment
7886
```
7987

88+
**Vercel project settings** (for an API-only app):
89+
90+
- **Framework Preset**: **Other**
91+
- **Build Command**: _empty_
92+
- **Output Directory**: _empty_
93+
94+
This avoids the “Missing public directory” error that applies to static sites. If you previously set an Output Directory (e.g., `public/`), clear it. citeturn0search1
95+
8096
---
8197

8298
## REST API
8399

84-
**Base URL:** `http://localhost:3000`
100+
**Base URL (prod):** `https://<your-deployment>.vercel.app`
85101

86102
### GET `/`
87103

@@ -91,7 +107,13 @@ Health check.
91107
200 OK → "<h1>Hello world</h1>"
92108
```
93109

94-
---
110+
### GET `/__debug` (temporary)
111+
112+
Returns a few runtime facts to confirm env setup.
113+
114+
```json
115+
{ "hasMongoURI": true, "vercelEnv": "production", "nodeVersion": "v20.x" }
116+
```
95117

96118
### POST `/adduser`
97119

@@ -100,28 +122,14 @@ Create a user aligned with the mobile schema (optionally with an initial score).
100122
**Body**
101123

102124
```json
103-
{
104-
"userName": "Ada Lovelace",
105-
"password": "SeedUser#2025",
106-
"score": 420
107-
}
125+
{ "userName": "Ada Lovelace", "password": "SeedUser#2025", "score": 420 }
108126
```
109127

110128
**Responses**
111129

112130
- `200 OK` → array of users (public fields only)
113131
- `409 Conflict` → "User Already Exist."
114132

115-
**cURL**
116-
117-
```bash
118-
curl -s -X POST http://localhost:3000/adduser \
119-
-H 'content-type: application/json' \
120-
-d '{"userName":"Ada Lovelace","password":"SeedUser#2025","score":420}' | jq .
121-
```
122-
123-
---
124-
125133
### POST `/users/:userName/scores`
126134

127135
Add a score for an existing user.
@@ -138,16 +146,6 @@ Add a score for an existing user.
138146
- `400 Bad Request` if `value` is not a number
139147
- `409 Conflict` if rejected by the acceptance rule
140148

141-
**cURL**
142-
143-
```bash
144-
curl -s -X POST 'http://localhost:3000/users/Ada%20Lovelace/scores' \
145-
-H 'content-type: application/json' \
146-
-d '{"value":451}' | jq .
147-
```
148-
149-
---
150-
151149
### POST `/score`
152150

153151
Check a raw score against the global minimum across all scores. **Does not persist**.
@@ -163,16 +161,6 @@ Check a raw score against the global minimum across all scores. **Does not persi
163161
- `200 OK` if accepted
164162
- `409 Conflict` if rejected
165163

166-
**cURL**
167-
168-
```bash
169-
curl -i -X POST http://localhost:3000/score \
170-
-H 'content-type: application/json' \
171-
-d '{"score":300}'
172-
```
173-
174-
---
175-
176164
### GET `/users`
177165

178166
List all users (public fields only).
@@ -190,14 +178,6 @@ List all users (public fields only).
190178
]
191179
```
192180

193-
**cURL**
194-
195-
```bash
196-
curl -s http://localhost:3000/users | jq .
197-
```
198-
199-
---
200-
201181
### GET `/scores/top?limit=10`
202182

203183
Top `limit` scores (default 10). Each item returns `{ score, user }`.
@@ -206,26 +186,52 @@ Top `limit` scores (default 10). Each item returns `{ score, user }`.
206186

207187
```json
208188
[
209-
{
210-
"score": 497,
211-
"user": { "_id": "665e...", "userName": "Noah Smith", "createdAt": 172..., "updatedAt": 172... }
212-
}
189+
{ "score": 497, "user": { "_id": "665e...", "userName": "Noah Smith", "createdAt": 172..., "updatedAt": 172... } }
213190
]
214191
```
215192

216-
**cURL**
193+
---
217194

218-
```bash
219-
curl -s 'http://localhost:3000/scores/top?limit=5' | jq .
220-
```
195+
## Serverless notes (important)
196+
197+
- **Express on Vercel**: Files under `/api` become functions; export the Express app and let Vercel handle the server. Don’t call `app.listen()`. citeturn0search0turn0search3
198+
- **Cold start readiness**: We use an async `ready` promise to **await `initDB()`** on first request.
199+
- **MongoDB Node driver**: `serverSelectionTimeoutMS` defaults to **30000ms**; we set shorter timeouts to fail fast during testing. citeturn0search14turn0search4
200+
- **Connection reuse**: Prefer a **singleton client** cached across invocations to avoid reconnect storms in serverless environments. (See `MongoDB.ts`.)
221201

222202
---
223203

224-
## Notes
204+
## Troubleshooting
205+
206+
### "Missing public directory" during deploy
207+
208+
Your project is configured like a static site. For an API-only app, clear **Build Command** and **Output Directory** (Project → Settings). citeturn0search1
209+
210+
### `MongoServerSelectionError` / `ReplicaSetNoPrimary` / timeouts
211+
212+
Usually network access to Atlas. Ensure Atlas Network Access allows your deployment to connect. For Vercel, allow **`0.0.0.0/0`** during testing (use strong creds), or adopt a fixed-egress solution for production. Also verify you use the **SRV** URI (`mongodb+srv://…`). citeturn0search2turn0search7
213+
214+
### Env variables not picked up
215+
216+
Set them in **Vercel → Project → Settings → Environment Variables** (correct environment), then redeploy.
217+
218+
### Seeing 406 on errors
219+
220+
We now return **500** for server errors. If you still see 406, ensure your routes aren’t catching and rethrowing as 406.
221+
222+
---
223+
224+
## Scripts
225+
226+
```json
227+
{
228+
"dev": "vercel dev",
229+
"build": "echo \"No build step for Vercel Functions\"",
230+
"seed": "tsx src/scripts/seed.ts"
231+
}
232+
```
225233

226-
- Passwords are hashed with `bcryptjs` (10 rounds).
227-
- Seeding uses Faker’s `person.fullName()` to generate human names.
228-
- `tsx` provides fast TypeScript execution with watch mode for development.
234+
> Vercel builds TypeScript in `/api` automatically; a real `build` step isn’t required for this API-only project. citeturn0search3
229235
230236
---
231237

0 commit comments

Comments
 (0)