Skip to content

sedna16/api-project

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

API Project

Starter package only — this repository is a minimal template to bootstrap your own API. Clone it, configure Supabase and Vercel, then extend routes, auth, and schema for your product. It is not a production-ready application as-is.

Basic Express.js API deployed on Vercel only, with Supabase, optional JWT auth, Zod validation, rate limiting, and axios for external HTTP.

Prerequisites

  • Node.js 20+
  • Supabase project
  • Vercel account
  • Vercel CLI (optional, for local dev): npm i -g vercel

Setup

  1. Clone the repo and install dependencies:
npm install
  1. Copy environment variables:
cp .env.example .env
  1. Fill in Supabase credentials from Project Settings → API:

    • SUPABASE_URL
    • SUPABASE_ANON_KEY (publishable / anon key)
    • SUPABASE_SERVICE_ROLE_KEY (server only — never expose to clients)
  2. Create the database table and RLS policies. Run supabase/schema.sql in the Supabase SQL Editor.

  3. Local development (recommended — matches Vercel serverless):

vercel link
vercel env pull
npm run dev

Optional fallback without Vercel CLI (may differ slightly from production):

npm run dev:local

Environment variables

Variable Required Description
SUPABASE_URL Yes Supabase project URL
SUPABASE_ANON_KEY Yes Anon key for JWT validation and RLS-scoped queries
SUPABASE_SERVICE_ROLE_KEY Yes Service role (server only; reserved for admin tasks)
RATE_LIMIT_WINDOW_MS No Rate limit window (default 900000 = 15 min)
RATE_LIMIT_MAX No Max requests per window per IP (default 100)
PORT No Port for dev:local only (default 3000)

Set the same variables in the Vercel dashboard for Production and Preview, or use vercel env pull.

Authentication (optional)

Endpoints under /api/* accept an optional Bearer token. Auth is never required unless you use ?mine=true on GET /api/items.

Scenario Behavior
No Authorization header Anonymous request; req.user is unset
Valid Authorization: Bearer <token> User attached; Supabase client uses caller JWT (RLS applies)
Invalid or expired token 401 Unauthorized

Obtaining a Supabase access token

  1. Dashboard: Auth → Users → create a user, then use the Supabase client or Auth API to sign in.
  2. Client SDK: supabase.auth.signInWithPassword({ email, password }) and read session.access_token.

Example:

curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" https://YOUR_DEPLOYMENT.vercel.app/api/me

Rate limiting

Global limit: 100 requests / 15 minutes / IP (configurable via env).

On Vercel, limits apply per serverless instance, not globally across all regions. For strict distributed limits in production, consider Upstash Ratelimit.

Deploy to Vercel

  1. Push the repository to GitHub.
  2. Import the project in Vercel (framework preset: Other).
  3. Set environment variables in the Vercel dashboard.
  4. Deploy.

vercel.json rewrites all routes to api/index.js, which exports the Express app.

Smoke tests

Replace BASE with your deployment URL (e.g. https://api-project.vercel.app).

# Health (no Supabase auth needed for route; env must still be set on server)
curl "$BASE/health"

# Auth status (anonymous)
curl "$BASE/api/me"

# List items
curl "$BASE/api/items"

# Create item (anonymous)
curl -X POST "$BASE/api/items" \
  -H "Content-Type: application/json" \
  -d '{"name":"Test item"}'

# With authentication
curl "$BASE/api/me" -H "Authorization: Bearer YOUR_TOKEN"
curl "$BASE/api/items?mine=true" -H "Authorization: Bearer YOUR_TOKEN"
curl -X POST "$BASE/api/items" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"name":"My item"}'

# External proxy
curl "$BASE/api/external"

API reference

Base URL: your Vercel deployment or http://localhost:3000 when using dev:local.

Errors use: { "error": "message", "details": ... } (details optional).


GET /health

Liveness check. No authentication.

Response 200

{ "status": "ok" }
curl http://localhost:3000/health

GET /api/me

Returns whether the caller is authenticated.

Headers (optional): Authorization: Bearer <supabase_access_token>

Response 200 (anonymous)

{ "authenticated": false }

Response 200 (authenticated)

{
  "authenticated": true,
  "user": { "id": "uuid", "email": "user@example.com" }
}

Response 401 — invalid token when Authorization header is sent

{ "error": "Invalid or expired token" }

GET /api/items

List items from Supabase.

Headers (optional): Authorization: Bearer <token>

Query parameters

Param Type Description
mine true When true, return only items owned by the authenticated user. Requires valid Bearer token.

Response 200

{
  "items": [
    {
      "id": "uuid",
      "name": "Example",
      "user_id": null,
      "created_at": "2026-01-01T00:00:00.000Z"
    }
  ]
}

Response 401?mine=true without valid token

curl http://localhost:3000/api/items
curl "http://localhost:3000/api/items?mine=true" -H "Authorization: Bearer TOKEN"

POST /api/items

Create an item.

Headers: Content-Type: application/json
Headers (optional): Authorization: Bearer <token>

Body

{ "name": "Item name" }
Field Type Rules
name string Required, 1–200 characters

When authenticated, user_id is set to the caller. When anonymous, user_id is null.

Response 201

{
  "item": {
    "id": "uuid",
    "name": "Item name",
    "user_id": "uuid-or-null",
    "created_at": "2026-01-01T00:00:00.000Z"
  }
}

Response 400 — validation error

curl -X POST http://localhost:3000/api/items \
  -H "Content-Type: application/json" \
  -d '{"name":"Hello"}'

GET /api/external

Fetches a random quote from Quotable via axios (5s timeout).

Headers (optional): Authorization: Bearer <token> (auth middleware runs; behavior is unchanged)

Response 200

{
  "source": "quotable",
  "data": { "_id": "...", "content": "...", "author": "..." }
}

Response 502 / 504 — upstream failure or timeout

curl http://localhost:3000/api/external

Common errors

Status When
400 Request validation failed (Zod)
401 Invalid Bearer token, or GET /api/items?mine=true without authentication
404 Unknown route
429 Rate limit exceeded
500 Server or Supabase error
502 / 504 Upstream failure on GET /api/external

Security

  • Never commit .env or expose SUPABASE_SERVICE_ROLE_KEY to clients.
  • The anon key is used server-side for JWT validation and RLS-scoped queries.
  • Enable RLS on all public tables (see supabase/schema.sql).

Project structure

api/           Vercel serverless entry
src/           Express app, routes, middleware
supabase/      SQL schema for items table + RLS
vercel.json    Vercel rewrites and function config

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors