Skip to content

Publish with Vercel #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ yarn-error.log
.idea

#env
.env
.env

dist/
.vercel
2 changes: 2 additions & 0 deletions .next/trace
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[{"name":"next-dev","duration":375778,"timestamp":48743038978,"id":1,"tags":{},"startTime":1755273250317,"traceId":"67ad9cb661b8ac33"}]
[{"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"}]
186 changes: 96 additions & 90 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,84 +4,100 @@ Backend API for the **Speed Puzzle** app.

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

---

## Tech stack
## What changed (serverless-ready)

- Express (REST API)
- MongoDB Node.js driver
- TypeScript + `tsx` (watch/dev runner)
- `dotenv` for env vars
- `bcryptjs` for password hashing
- `@faker-js/faker` for seed data
This repo is now configured to run as **Vercel Serverless Functions**:

- The Express app lives in **`/api/index.ts`** and **exports the app** (`export default app`), no `app.listen()`.
- On cold start, we **await DB initialization** before serving requests.
- API routes return **500** on internal errors (instead of 406) and log a helpful message.
- A diagnostic route `GET /__debug` is available during testing.

---

## Project structure

```text
api/
index.ts # Express app exported for Vercel Functions
src/
constants/events.ts
controllers/Global.ts
services/
MongoDB.ts
MongoDB.ts # MongoDB client (Node driver)
Users.ts
Scores.ts
index.ts
scripts/
seed.ts
```

> Note: The seeder populates 15 users with 1–3 scores each (200–500).

---

## Setup
## Environment variables

1. **Install**
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):

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

2. **Environment** — create a `.env` at the project root:
> 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

---

## Local development

```bash
MONGODB_URI="mongodb://localhost:27017"
API_PORT=3000
npm install
npm run dev # uses `vercel dev` to emulate the platform
```

3. **Run**
**Seed demo data**

```bash
# dev (watch)
npm run dev

# prod
npm run build
npm start
npm run seed # inserts users + scores
npm run seed -- --reset # wipe & reseed
```

4. **Seed demo data**
> The app runs as a Vercel Function locally; you don’t need `npm start` for serverless.

---

## Deployment (Vercel)

**One-time**:

```bash
# inserts 15 users with 1–3 scores each (values 200–500)
npm run seed
npm i -g vercel
vercel login
```

**Deploy**:

# wipe & reseed
npm run seed -- --reset
```bash
vercel # Preview deployment
vercel --prod # Production deployment
```

**Vercel project settings** (for an API-only app):

- **Framework Preset**: **Other**
- **Build Command**: _empty_
- **Output Directory**: _empty_

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

---

## REST API

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

### GET `/`

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

---
### GET `/__debug` (temporary)

Returns a few runtime facts to confirm env setup.

```json
{ "hasMongoURI": true, "vercelEnv": "production", "nodeVersion": "v20.x" }
```

### POST `/adduser`

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

```json
{
"userName": "Ada Lovelace",
"password": "SeedUser#2025",
"score": 420
}
{ "userName": "Ada Lovelace", "password": "SeedUser#2025", "score": 420 }
```

**Responses**

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

**cURL**

```bash
curl -s -X POST http://localhost:3000/adduser \
-H 'content-type: application/json' \
-d '{"userName":"Ada Lovelace","password":"SeedUser#2025","score":420}' | jq .
```

---

### POST `/users/:userName/scores`

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

**cURL**

```bash
curl -s -X POST 'http://localhost:3000/users/Ada%20Lovelace/scores' \
-H 'content-type: application/json' \
-d '{"value":451}' | jq .
```

---

### POST `/score`

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

**cURL**

```bash
curl -i -X POST http://localhost:3000/score \
-H 'content-type: application/json' \
-d '{"score":300}'
```

---

### GET `/users`

List all users (public fields only).
Expand All @@ -190,14 +178,6 @@ List all users (public fields only).
]
```

**cURL**

```bash
curl -s http://localhost:3000/users | jq .
```

---

### GET `/scores/top?limit=10`

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

```json
[
{
"score": 497,
"user": { "_id": "665e...", "userName": "Noah Smith", "createdAt": 172..., "updatedAt": 172... }
}
{ "score": 497, "user": { "_id": "665e...", "userName": "Noah Smith", "createdAt": 172..., "updatedAt": 172... } }
]
```

**cURL**
---

```bash
curl -s 'http://localhost:3000/scores/top?limit=5' | jq .
```
## Serverless notes (important)

- **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
- **Cold start readiness**: We use an async `ready` promise to **await `initDB()`** on first request.
- **MongoDB Node driver**: `serverSelectionTimeoutMS` defaults to **30000ms**; we set shorter timeouts to fail fast during testing. citeturn0search14turn0search4
- **Connection reuse**: Prefer a **singleton client** cached across invocations to avoid reconnect storms in serverless environments. (See `MongoDB.ts`.)

---

## Notes
## Troubleshooting

### "Missing public directory" during deploy

Your project is configured like a static site. For an API-only app, clear **Build Command** and **Output Directory** (Project → Settings). citeturn0search1

### `MongoServerSelectionError` / `ReplicaSetNoPrimary` / timeouts

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

### Env variables not picked up

Set them in **Vercel → Project → Settings → Environment Variables** (correct environment), then redeploy.

### Seeing 406 on errors

We now return **500** for server errors. If you still see 406, ensure your routes aren’t catching and rethrowing as 406.

---

## Scripts

```json
{
"dev": "vercel dev",
"build": "echo \"No build step for Vercel Functions\"",
"seed": "tsx src/scripts/seed.ts"
}
```

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

---

Expand Down
Loading