eddev

Serverless

Run the public eddev frontend on Vercel while WordPress owns content and routing.

eddev serverless mode runs the public React frontend on a JavaScript host such as Vercel, while WordPress remains the CMS and routing authority.

In production, the usual shape is:

  • cms.website.com points at the WordPress origin, usually behind Cloudflare.
  • website.com points at the Vercel deployment.
  • Vercel receives public page requests, asks WordPress for the matched route data, then server-renders the React view.
  • WordPress still resolves the route, executes the view/block GraphQL files, owns /wp-admin, and provides runtime query and mutation endpoints.

The Vercel app is not a separate router. It is a rendering and API layer in front of the WordPress origin.

Deploying To Vercel

For a normal project:

  1. Import the site's GitHub repository into Vercel.
  2. Add SITE_URL in Vercel project environment variables.
  3. Set SITE_URL to the WordPress origin, including the protocol, for example https://cms.staging.sff.org.au/.
  4. If the WordPress origin uses eddev Access Control, add SITE_API_KEY in Vercel as well.
  5. Deploy.

During the build, eddev reads SITE_URL and uses it as the origin for WordPress route data, global app data, named GraphQL query hooks, mutations, plugin assets, uploads, and proxied WordPress/admin requests.

Do not point SITE_URL at the Vercel frontend. It should point at the WordPress origin, usually the cms. host.

Request Flow

When a visitor requests a page from the Vercel deployment:

  1. The serverless app normalizes the URL and asks WordPress for route data with ?_props=1 and _ssr=1.
  2. WordPress resolves the request through the normal template hierarchy, runs the paired view GraphQL file, and returns the JSON route payload.
  3. The serverless app replaces origin URLs where configured, fetches app data, and renders the matching React view.
  4. Browser navigation continues through eddev's route data endpoints instead of loading a whole WordPress HTML page.

Runtime GraphQL hooks follow the same split:

  • browser-side query hooks on Vercel call /_data/query/{queryName}
  • browser-side mutations on Vercel call /_data/mutation/{mutationName}
  • the serverless app forwards those to the WordPress REST endpoints under /wp-json/ed/v1/query/ and /wp-json/ed/v1/mutation/

Server routes from server/routes/ are deployed with the serverless app. See RPC Functions for route authoring and React usage.

Basic Configuration

Serverless behaviour is configured in ed.config.json. The serverless key controls deployment and routing behaviour, while the cache key controls how both the WordPress and JavaScript server sides cache data.

{
  "$schema": ".ed.config.schema.json",
  "version": "2",
  "serverless": {
    "enabled": true,
    "uploads": "remote",
    "plugins": "remote",
    "admin": "hide",
    "themeAssets": ["assets/**/*"],
    "originProtection": {
      "requireLogin": false
    },
    "endpoints": {
      "cms.website.com": "website.com"
    }
  },
  "cache": {
    "*": {
      "serverless": {
        "isr": true,
        "dataCache": "in-memory"
      },
      "wordpress": {
        "cacheHeaders": true,
        "transients": true
      },
      "pageDataTTL": 300,
      "appDataTTL": 300,
      "queryHooksTTL": 300
    }
  }
}

serverless

OptionWhat It Does
enabledEnables serverless deployment support. This is normally true.
uploadsControls whether /wp-content/uploads/** URLs are left as remote origin URLs or proxied through the serverless deployment. Use remote by default.
pluginsControls whether /wp-content/plugins/** URLs are left as remote origin URLs or proxied through the serverless deployment. Use remote by default.
adminproxy forwards WordPress admin/API URLs through the deployment. hide returns a 404 for /wp-admin, /wp-json, /wp-login.php, GraphQL, and PHP-style URLs in production.
originProtection.requireLoginForce-enables origin protection from config. This prevents non-logged-in visitors from accessing the WordPress origin frontend, while still allowing the serverless frontend to fetch with an API key.
themeAssetsStatic theme asset folders to include in the serverless deployment. The starter theme uses ["assets/**/*"].
endpointsMaps WordPress hostnames to serverless hostnames. This tells the WordPress-hosted SPA/admin where to call serverless endpoints such as RPC functions.
cors.originsAdditional allowed CORS origins. WordPress and serverless hosts from endpoints are already allowed.
cspContent Security Policy settings for the serverless app, including autodetected origins, common origins, nonces, and directive values.

serverless.endpoints values are hostnames, not full URLs:

{
  "serverless": {
    "endpoints": {
      "cms.staging.sff.org.au": "sff-staging.vercel.app",
      "cms.sff.org.au": "www.sff.org.au"
    }
  }
}

You can use "*" as a fallback key, but explicit hostnames are better for production and staging because they make the CMS-to-frontend relationship clear.

The WordPress PHP layer injects window.SERVERLESS_ENDPOINT into the WordPress frontend and admin when it can match the current WordPress host in serverless.endpoints. The RPC client uses that value when the site is running in WordPress-hosted SPA mode, so incorrect endpoint mappings usually show up as RPC calls going to the wrong host.

cache

The cache object is a map of WordPress origin hostnames to cache settings. Use "*" for the default, and add host-specific entries when staging and production need different behaviour.

Cache host keys are matched as hostnames. Ports are ignored, exact hostnames win over wildcard patterns, wildcard patterns like "*.website.com" are supported, and "*" is used as the fallback.

OptionWhat It Does
pageDataTTLSeconds to cache route data and rendered page responses.
appDataTTLSeconds to cache global app data from views/_app.graphql.
queryHooksTTLSeconds to cache named runtime GraphQL query hook responses.
serverless.isrEnables serverless response caching/ISR for rendered pages, route data, and named query data. Set it to false for dynamic, no-store serverless responses.
serverless.dataCacheControls the serverless origin-data cache. Use in-memory for the normal LRU/SWR cache, or none to fetch WordPress every time the serverless function needs data.
wordpress.cacheHeadersAllows WordPress data responses to emit cache headers.
wordpress.transientsAllows WordPress to cache data in transients.

serverless.isr and serverless.dataCache control different cache layers:

ConfigurationBehaviour
isr: true, dataCache: "in-memory"Normal production mode. Serverless responses can be cached, and serverless revalidation/data requests are protected by an in-memory data cache.
isr: true, dataCache: "none"Serverless responses can still be cached, but every revalidation fetch goes back to WordPress.
isr: false, dataCache: "in-memory"Serverless responses are dynamic/no-store, but repeated WordPress data fetches can still use the serverless in-memory cache.
isr: false, dataCache: "none"Fully dynamic serverless mode. Responses are no-store and serverless data fetches go back to WordPress every time.

Queries and mutations are handled differently:

  • route data and app data use the page/app TTLs
  • named query hooks use queryHooksTTL
  • mutation responses are not cached
  • requests with varied auth/session headers avoid the shared named-query data cache

Individual GraphQL query files can still set # ttl: 300 or # nocache; see GraphQL caching.

Origin Access Control

Use eddev's Access Control feature to protect the WordPress origin. Do not use WP Engine's password protection for this, because Vercel still needs to fetch WordPress route data, GraphQL results, uploads, and REST endpoints without receiving a human password prompt.

In WordPress:

  1. Open Settings → Access Control.
  2. Generate an API key.
  3. Enable API key protection if it is not force-enabled by config.
  4. Optionally enable simple password protection for people who should be able to preview the CMS-hosted frontend directly.
  5. Add the generated key to Vercel as SITE_API_KEY.

When SITE_API_KEY is set, eddev adds it to server-to-server WordPress requests as the x-ed-api-key header. The PHP origin checks that header before allowing access.

Access Control allows:

  • logged-in WordPress users
  • requests with a valid eddev API key
  • users with the configured basic-auth credentials, when password protection is enabled

Everyone else receives an Access Denied page or a password prompt, depending on the settings.

An API key only allows a request to reach the origin. It does not grant WordPress permissions or log the request in as a user.

For stricter projects, set serverless.originProtection.requireLogin to true. That force-enables API-key protection from committed config, so the WordPress origin cannot accidentally be left public through an admin setting.

Domain Setup

The common production setup is:

HostPoints ToPurpose
cms.website.comWP Engine or another WordPress host, often proxied through CloudflareWordPress admin, CMS preview, GraphQL, REST, uploads, and route data origin
website.com / www.website.comVercelPublic server-rendered frontend and serverless routes

Keep the WordPress origin reachable by the Vercel deployment. If Cloudflare sits in front of cms.website.com, make sure it passes the x-ed-api-key header and does not add its own interactive challenge to server-to-server requests.

Deployment Checklist

  • SITE_URL in Vercel points to the WordPress origin, for example https://cms.website.com/.
  • SITE_API_KEY is set in Vercel when Access Control is enabled.
  • The same API key exists in WordPress under Settings → Access Control.
  • serverless.endpoints maps each WordPress hostname to the correct Vercel/public hostname.
  • serverless.admin is set intentionally, usually hide for public deployments.
  • cache["*"] or a host-specific cache entry sets realistic TTLs for route data, app data, and runtime query hooks.
  • Cloudflare or host-level protection does not block Vercel's server-to-server origin requests.

On this page