From b4c76a30ef857c3033d3800483cca74a9723e681 Mon Sep 17 00:00:00 2001 From: Ricardo Gonzalez Date: Thu, 11 Jun 2026 12:27:01 -0700 Subject: [PATCH] node server.ts example --- starter/node-server/.gitignore | 5 + starter/node-server/README.md | 36 +++ starter/node-server/package.json | 16 ++ starter/node-server/pnpm-lock.yaml | 329 ++++++++++++++++++++++++++ starter/node-server/src/api/routes.ts | 41 ++++ starter/node-server/src/pages/home.ts | 85 +++++++ starter/node-server/src/server.ts | 36 +++ starter/node-server/tsconfig.json | 11 + 8 files changed, 559 insertions(+) create mode 100644 starter/node-server/.gitignore create mode 100644 starter/node-server/README.md create mode 100644 starter/node-server/package.json create mode 100644 starter/node-server/pnpm-lock.yaml create mode 100644 starter/node-server/src/api/routes.ts create mode 100644 starter/node-server/src/pages/home.ts create mode 100644 starter/node-server/src/server.ts create mode 100644 starter/node-server/tsconfig.json diff --git a/starter/node-server/.gitignore b/starter/node-server/.gitignore new file mode 100644 index 0000000000..fb36784850 --- /dev/null +++ b/starter/node-server/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +.vercel +dist +*.log +.DS_Store diff --git a/starter/node-server/README.md b/starter/node-server/README.md new file mode 100644 index 0000000000..ff737817f1 --- /dev/null +++ b/starter/node-server/README.md @@ -0,0 +1,36 @@ +# Node.js Server Starter + +Deploy your Node.js HTTP server to Vercel with zero configuration. + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?demo-description=Deploy%20a%20Node.js%20HTTP%20server%20with%20zero%20configuration.&demo-title=Node.js%20Server%20Boilerplate&from=templates&project-name=Node.js%20Server%20Boilerplate&repository-name=node-server-boilerplate&repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fexamples%2Ftree%2Fmain%2Fstarter%2Fnode-server&skippable-integrations=1) + +Visit the [Node.js on Vercel documentation](https://vercel.com/docs/functions/runtimes/node-js) to learn more. + +## Getting Started + +Install the required dependencies: + +```bash +pnpm install +``` + +## Running Locally + +Start the development server on http://localhost:3000 + +```bash +pnpm dev +``` + +## Deploying to Vercel + +Deploy your project to Vercel with the following command: + +```bash +npm install -g vercel +vercel --prod +``` + +Or `git push` to your repository with our [git integration](https://vercel.com/docs/deployments/git). + +To view the source code for this template, [visit the example repository](https://github.com/vercel/examples/tree/main/starter/node-server). diff --git a/starter/node-server/package.json b/starter/node-server/package.json new file mode 100644 index 0000000000..d09451a71f --- /dev/null +++ b/starter/node-server/package.json @@ -0,0 +1,16 @@ +{ + "name": "node-server", + "repository": "https://github.com/vercel/examples.git", + "license": "MIT", + "private": true, + "type": "module", + "scripts": { + "dev": "tsx src/server.ts", + "type-check": "tsc --noEmit" + }, + "devDependencies": { + "@types/node": "^22.13.10", + "tsx": "^4.19.2", + "typescript": "^5.8.3" + } +} diff --git a/starter/node-server/pnpm-lock.yaml b/starter/node-server/pnpm-lock.yaml new file mode 100644 index 0000000000..caef05840f --- /dev/null +++ b/starter/node-server/pnpm-lock.yaml @@ -0,0 +1,329 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@types/node': + specifier: ^22.13.10 + version: 22.19.21 + tsx: + specifier: ^4.19.2 + version: 4.22.4 + typescript: + specifier: ^5.8.3 + version: 5.9.3 + +packages: + + '@esbuild/aix-ppc64@0.28.0': + resolution: {integrity: sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.0': + resolution: {integrity: sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.0': + resolution: {integrity: sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.0': + resolution: {integrity: sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.0': + resolution: {integrity: sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.0': + resolution: {integrity: sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.0': + resolution: {integrity: sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.0': + resolution: {integrity: sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.0': + resolution: {integrity: sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.0': + resolution: {integrity: sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.0': + resolution: {integrity: sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.0': + resolution: {integrity: sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.0': + resolution: {integrity: sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.0': + resolution: {integrity: sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.0': + resolution: {integrity: sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.0': + resolution: {integrity: sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.0': + resolution: {integrity: sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.0': + resolution: {integrity: sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.0': + resolution: {integrity: sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.0': + resolution: {integrity: sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.0': + resolution: {integrity: sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.0': + resolution: {integrity: sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.0': + resolution: {integrity: sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.0': + resolution: {integrity: sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.0': + resolution: {integrity: sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.0': + resolution: {integrity: sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@types/node@22.19.21': + resolution: {integrity: sha512-VMeFBSCKQKmm2swI2kW51SFusDqekC6q9trBCvJ/JliDchFSuoYYKN7yVNjPthP1HKZcx3U1gI/wTcEBjEFKTA==} + + esbuild@0.28.0: + resolution: {integrity: sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==} + engines: {node: '>=18'} + hasBin: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + tsx@4.22.4: + resolution: {integrity: sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==} + engines: {node: '>=18.0.0'} + hasBin: true + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + +snapshots: + + '@esbuild/aix-ppc64@0.28.0': + optional: true + + '@esbuild/android-arm64@0.28.0': + optional: true + + '@esbuild/android-arm@0.28.0': + optional: true + + '@esbuild/android-x64@0.28.0': + optional: true + + '@esbuild/darwin-arm64@0.28.0': + optional: true + + '@esbuild/darwin-x64@0.28.0': + optional: true + + '@esbuild/freebsd-arm64@0.28.0': + optional: true + + '@esbuild/freebsd-x64@0.28.0': + optional: true + + '@esbuild/linux-arm64@0.28.0': + optional: true + + '@esbuild/linux-arm@0.28.0': + optional: true + + '@esbuild/linux-ia32@0.28.0': + optional: true + + '@esbuild/linux-loong64@0.28.0': + optional: true + + '@esbuild/linux-mips64el@0.28.0': + optional: true + + '@esbuild/linux-ppc64@0.28.0': + optional: true + + '@esbuild/linux-riscv64@0.28.0': + optional: true + + '@esbuild/linux-s390x@0.28.0': + optional: true + + '@esbuild/linux-x64@0.28.0': + optional: true + + '@esbuild/netbsd-arm64@0.28.0': + optional: true + + '@esbuild/netbsd-x64@0.28.0': + optional: true + + '@esbuild/openbsd-arm64@0.28.0': + optional: true + + '@esbuild/openbsd-x64@0.28.0': + optional: true + + '@esbuild/openharmony-arm64@0.28.0': + optional: true + + '@esbuild/sunos-x64@0.28.0': + optional: true + + '@esbuild/win32-arm64@0.28.0': + optional: true + + '@esbuild/win32-ia32@0.28.0': + optional: true + + '@esbuild/win32-x64@0.28.0': + optional: true + + '@types/node@22.19.21': + dependencies: + undici-types: 6.21.0 + + esbuild@0.28.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.0 + '@esbuild/android-arm': 0.28.0 + '@esbuild/android-arm64': 0.28.0 + '@esbuild/android-x64': 0.28.0 + '@esbuild/darwin-arm64': 0.28.0 + '@esbuild/darwin-x64': 0.28.0 + '@esbuild/freebsd-arm64': 0.28.0 + '@esbuild/freebsd-x64': 0.28.0 + '@esbuild/linux-arm': 0.28.0 + '@esbuild/linux-arm64': 0.28.0 + '@esbuild/linux-ia32': 0.28.0 + '@esbuild/linux-loong64': 0.28.0 + '@esbuild/linux-mips64el': 0.28.0 + '@esbuild/linux-ppc64': 0.28.0 + '@esbuild/linux-riscv64': 0.28.0 + '@esbuild/linux-s390x': 0.28.0 + '@esbuild/linux-x64': 0.28.0 + '@esbuild/netbsd-arm64': 0.28.0 + '@esbuild/netbsd-x64': 0.28.0 + '@esbuild/openbsd-arm64': 0.28.0 + '@esbuild/openbsd-x64': 0.28.0 + '@esbuild/openharmony-arm64': 0.28.0 + '@esbuild/sunos-x64': 0.28.0 + '@esbuild/win32-arm64': 0.28.0 + '@esbuild/win32-ia32': 0.28.0 + '@esbuild/win32-x64': 0.28.0 + + fsevents@2.3.3: + optional: true + + tsx@4.22.4: + dependencies: + esbuild: 0.28.0 + optionalDependencies: + fsevents: 2.3.3 + + typescript@5.9.3: {} + + undici-types@6.21.0: {} diff --git a/starter/node-server/src/api/routes.ts b/starter/node-server/src/api/routes.ts new file mode 100644 index 0000000000..3d1ac98fe2 --- /dev/null +++ b/starter/node-server/src/api/routes.ts @@ -0,0 +1,41 @@ +import type { IncomingMessage, ServerResponse } from 'node:http' + +function sendJson(response: ServerResponse, status: number, data: unknown) { + response.writeHead(status, { 'Content-Type': 'application/json' }) + response.end(JSON.stringify(data)) +} + +export function handleApiRequest( + request: IncomingMessage, + response: ServerResponse, + url: URL +) { + if (request.method === 'GET' && url.pathname === '/api/data') { + sendJson(response, 200, { + data: [ + { id: 1, name: 'Sample Item 1', value: 100 }, + { id: 2, name: 'Sample Item 2', value: 200 }, + { id: 3, name: 'Sample Item 3', value: 300 }, + ], + total: 3, + timestamp: '2024-01-01T00:00:00Z', + }) + return + } + + const itemMatch = url.pathname.match(/^\/api\/items\/(\d+)$/) + if (request.method === 'GET' && itemMatch) { + const itemId = Number(itemMatch[1]) + sendJson(response, 200, { + item: { + id: itemId, + name: `Sample Item ${itemId}`, + value: itemId * 100, + }, + timestamp: '2024-01-01T00:00:00Z', + }) + return + } + + sendJson(response, 404, { error: 'Not Found' }) +} diff --git a/starter/node-server/src/pages/home.ts b/starter/node-server/src/pages/home.ts new file mode 100644 index 0000000000..31b4ce2344 --- /dev/null +++ b/starter/node-server/src/pages/home.ts @@ -0,0 +1,85 @@ +export const homePage = ` + + + + + + server.ts + + + + +
+ +
+
+
+

server.ts

+
+
import { createServer } from 'node:http'
+
+const server = createServer((request, response) => {
+  response.writeHead(200, { 'Content-Type': 'application/json' })
+  response.end(JSON.stringify({ Node: 'on Vercel' }))
+})
+
+server.listen(3000)
+
+
+ +
+
+

Sample Data

+

Access sample JSON data through our REST API. Perfect for testing and development purposes.

+ Get Data → +
+
+
+ + +` diff --git a/starter/node-server/src/server.ts b/starter/node-server/src/server.ts new file mode 100644 index 0000000000..cee321d08e --- /dev/null +++ b/starter/node-server/src/server.ts @@ -0,0 +1,36 @@ +import { createServer } from 'node:http' +import { handleApiRequest } from './api/routes.js' +import { homePage } from './pages/home.js' + +const port = Number(process.env.PORT ?? 3000) + +const server = createServer((request, response) => { + const url = new URL( + request.url ?? '/', + `http://${request.headers.host ?? 'localhost'}` + ) + + if (request.method === 'GET' && url.pathname === '/health') { + response.writeHead(200, { 'Content-Type': 'application/json' }) + response.end(JSON.stringify({ status: 'ok' })) + return + } + + if (request.method === 'GET' && url.pathname === '/') { + response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }) + response.end(homePage) + return + } + + if (url.pathname.startsWith('/api/')) { + handleApiRequest(request, response, url) + return + } + + response.writeHead(404, { 'Content-Type': 'application/json' }) + response.end(JSON.stringify({ error: 'Not Found' })) +}) + +server.listen(port, () => { + console.log(`Server listening on http://localhost:${port}`) +}) diff --git a/starter/node-server/tsconfig.json b/starter/node-server/tsconfig.json new file mode 100644 index 0000000000..4413ff9dcd --- /dev/null +++ b/starter/node-server/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "skipLibCheck": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"] +}