06.01 — Stack Vercel Edge#

package.json#

{
  "name": "tests-workers",
  "version": "1.0.0",
  "scripts": {
    "vercel-build": "echo 'Build skipped'"
  },
  "packageManager": "pnpm@11.2.2",
  "dependencies": {
    "@upstash/redis": "^1.36.2",
    "otplib": "^13.2.1"
  },
  "devDependencies": {
    "@types/node": "^25.2.0",
    "next": "^16.1.6",
    "@vercel/node": "^5.5.28",
    "typescript": "^5.9.3"
  },
  "license": "ISC"
}
LibRôle
@upstash/redis (runtime)Client Redis REST (pas TCP). Compatible Edge.
otplib (runtime)Génération TOTP RFC 6238.
next (dev seulement)Types uniquement (NextRequest). Pas d’app Next.js.
@vercel/node (dev seulement)Types Vercel.
@types/node (dev seulement)Types Node 22+.

Note : aucune dépendance runtime au-delà de @upstash/redis + otplib.
Le bundle est microscopique → cold start < 100ms.

vercel.json#

{
  "version": 2,
  "buildCommand": "echo 'No build required'",
  "outputDirectory": ".",
  "framework": null
}
ChampValeurEffet
version: 2Vercel v2 — 
buildCommand: "echo 'No build required'"Pas de buildTypeScript est transpilé à la volée par le runtime Vercel
outputDirectory: "."Racine du repoPas de dist/ à produire
framework: nullPas de framework détectéVercel ne fait pas d’auto-config

Le runtime Edge#

Chaque endpoint déclare :

export const config = { runtime: "edge" };
AspectEdge FunctionsNode Serverless
Cold start< 50ms200-500ms
Mémoire~128 MBjusqu’à 3 GB
API NodeLimité (pas de fs, child_process)Full Node
GeoCo-localisée à l’utilisateurRégion unique
fetch, crypto.getRandomValues, URLNativeNative
TarificationMoins cherPlus cher

Pour 3 endpoints qui ne font qu’HTTP + Redis, Edge est le bon choix.

Pourquoi next est en devDependencies#

import type { NextRequest } from "next/server";

tests-workers n’utilise Next.js que pour le type NextRequest. Pas d’app Next.js. C’est de l’emprunt de typage : Vercel publie les types NextRequest parce qu’ils sont compatibles avec les Edge Functions.

Au runtime, l’objet est juste une Request (Standard Web), mais typé NextRequest pour avoir l’autocomplétion des helpers searchParams, etc.

Pourquoi Upstash plutôt qu’un Redis classique#

Redis classique (TCP)Upstash (REST)
Connexion persistanteHTTP par requête
Pas compatible EdgeCompatible Edge
Plus rapide en multi-opsPlus simple en single-op
Self-hosted ou managedManaged (Cloudflare-style)

L’Edge runtime n’a pas de socket TCP. Upstash expose Redis via HTTP+REST.

API_SECRET#

const API_SECRET = process.env.API_SECRET;

Lu au démarrage du Worker (cold start).
Stocké en mémoire pour la durée de vie d’un worker (qui peut servir N requêtes).

UPSTASH_REDIS_REST_*#

// lib/redis.ts
const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL,
  token: process.env.UPSTASH_REDIS_REST_TOKEN,
});
  1. UPSTASH_REDIS_REST_URL : l’URL HTTPS du Redis Upstash.
  2. UPSTASH_REDIS_REST_TOKEN : le token Bearer pour s’authentifier.

vercel env add ... (cf. README).

Pourquoi seulement 3 endpoints#

EndpointPourquoi
/api/otp?_user=<u>Génère un OTP pour le test login MFA
/api/otp-historyPermet à Ocarina de retrouver l’OTP qu’Igoristan vient de générer
/api/corsicadex?id=NFournit des données pour la page « Pokédex corse »

Pas de webhook, pas de polling, pas de DB autre que Redis. Le strict minimum.