Infer Deploy Url
Utilities for inferring deploy url from auto-injected env vars on platform like Vercel or Netlify.
Example Usage
// constants.ts
import { getInferredDeployUrl } from './infer-deploy-url';
export const APP_BASE_URL = getInferredDeployUrl(process.env);
// ⏬ when browser
// ""
// ⏬ when server + no recognized deploy service
// http://localhost:3000
// ⏬ when server + production deploy on vercel
// https://my-project.vercel.app
// ⏬ when server + deploy preview on vercel
// https://my-project-4567g34536d.vercel.app
// ⏬ when server + production deploy on netlify
// https://my-project.netlify.app
// ⏬ when server + deploy preview on netlify
// https://deploy-preview-1--petsof.netlify.app
Dependencies
No dependencies
Auto Install
npx shadcn@latest add https://shadcn-registry-ts.vercel.app/r/util-infer-deploy-url.json
Manual Install
infer-deploy-url.ts
/** Which git branch must be considered `production` */
const GIT_BRANCH_PRODUCTION = 'main';
/**
* Get the server base URL by inferring the environment.
*
* If called from `browser` -> returns empty string (relative path usage).
* If called from `server` -> try to recognize the URL from env vars injected by the deploy service, if nothing found -> fallback to `localhost:3000`.
*
* NOTE: recognized deploy services: `netlify.com`, `vercel.com`
*/
export function getInferredDeployUrl(processEnv: NodeJS.ProcessEnv) {
// if browser...
if (isBrowser()) {
// browser should use relative path
return '';
}
// if is server..
return (
inferDeployUrlVercel(processEnv)
|| inferDeployUrlNetlify(processEnv)
|| `http://localhost:${processEnv.PORT ?? 3000}`
);
}
type InferDeployUrl = (processEnv: NodeJS.ProcessEnv) => string | null;
/** Get Vercel deploy url (`string`) if this app is built on Vercel, or `null` otherwise */
const inferDeployUrlVercel: InferDeployUrl = (processEnv) => {
// @see https://vercel.com/docs/environment-variables/system-environment-variables
//
// - VERCEL=1 if deployed on Vercel
// - VERCEL_GIT_COMMIT_REF:
// - i.e. `main`
// - is the git branch of the deployment
// - VERCEL_PROJECT_PRODUCTION_URL:
// - i.e. `my-project.vercel.app`
// - is the fixed production url of the project on Vercel
// - does not change across deployments
// - if custom domain is used, this is the custom domain
// - if no custom domain is used, this is the production url auto-assigned by Vercel
// - VERCEL_URL:
// - i.e. `my-project-4535c432342x4234.vercel.app`
// - is the immutable deplyment url.
// - changes on each deployment
// if we are on Vercel...
if (processEnv.VERCEL === '1') {
// ...and this git branch is PRODUCTION and we have a Vercel Production Url -> return it
if (processEnv.VERCEL_GIT_COMMIT_REF === GIT_BRANCH_PRODUCTION && processEnv.VERCEL_PROJECT_PRODUCTION_URL) {
return `https://${processEnv.VERCEL_PROJECT_PRODUCTION_URL}`;
}
// ...and this git branch is not PRODUCTION and we have a Vercel Deploy Preview Url or Branch Deploy Url -> return it
if (processEnv.VERCEL_GIT_COMMIT_REF !== GIT_BRANCH_PRODUCTION && processEnv.VERCEL_URL) {
return `https://${processEnv.VERCEL_URL}`;
}
}
// if not deployed on Vercel -> return null
return null;
};
/** Get Netlify deploy url (`string`) if this app is built on Netlify, or `null` otherwise */
const inferDeployUrlNetlify: InferDeployUrl = (processEnv) => {
// @see https://docs.netlify.com/build/configure-builds/environment-variables/
//
// - NETLIFY=true if deployed on netlify
// - BRANCH:
// - i.e. `main`
// - is the git branch of the deployment
// - URL:
// - i.e. `https://my-project.netlify.app`
// - is the fixed production url of the project on Vercel
// - does not change across deployments
// - if custom domain is used, this is the custom domain
// - if no custom domain is used, this is the auto-assigned by netlify production url
// - DEPLOY_URL:
// - i.e. `https://5b243e66dd6a547b4fee73ae--petsof.netlify.app`
// - is the immutable deplyment url.
// - changes on each deployment
// - DEPLOY_PRIME_URL:
// - i.e. `https://deploy-preview-1--petsof.netlify.app`
// - is the deplyment url of the gruoup (deploy preview for the same PR keep the same url on new commits re-deploy, ...).
// - changes when the group changees
// if we are on Netlify...
if (processEnv.NETLIFY === 'true') {
// ...and this git branch is PRODUCTION and we have a Netlify Production Url -> return it
if (processEnv.BRANCH === 'main' && processEnv.URL) {
return processEnv.URL;
}
// ...and this git branch is not PRODUCTION and we have a Netlify Deploy Preview or Branch Deploy Url -> return it
if (processEnv.BRANCH !== 'main' && processEnv.DEPLOY_PRIME_URL) {
return processEnv.DEPLOY_PRIME_URL;
}
if (processEnv.BRANCH !== 'main' && processEnv.DEPLOY_URL) {
return processEnv.DEPLOY_URL;
}
}
// if not deployed on Netlify -> return null
return null;
};
// utils
function isBrowser() {
return typeof window !== 'undefined';
}Test
infer-deploy-url.test.ts
import { describe, it, expect } from 'vitest';
import { getInferredDeployUrl } from './infer-deploy-url';
describe('getInferredDeployUrl', () => {
it('do it - browser', () => {
// @ts-ignore
global.window = {};
expect(getInferredDeployUrl({
NODE_ENV: 'production',
})).toBe("");
expect(getInferredDeployUrl({
NODE_ENV: 'development',
})).toBe("");
// @ts-ignore
delete global.window;
});
it('do it - server + no recognized deploy service', () => {
expect(getInferredDeployUrl({
NODE_ENV: 'production',
})).toBe("http://localhost:3000");
expect(getInferredDeployUrl({
NODE_ENV: 'development',
})).toBe("http://localhost:3000");
});
it('do it - server + vercel', () => {
// production
expect(getInferredDeployUrl({
NODE_ENV: 'production',
VERCEL: '1',
VERCEL_GIT_COMMIT_REF: 'main',
VERCEL_PROJECT_PRODUCTION_URL: 'my-project.vercel.app',
VERCEL_URL: 'my-project-4535c432342x4234.vercel.app',
})).toBe('https://my-project.vercel.app');
// deploy preview
expect(getInferredDeployUrl({
NODE_ENV: 'production',
VERCEL: '1',
VERCEL_GIT_COMMIT_REF: 'feature-1',
VERCEL_PROJECT_PRODUCTION_URL: 'my-project.vercel.app',
VERCEL_URL: 'my-project-4567g34536d.vercel.app',
})).toBe('https://my-project-4567g34536d.vercel.app');
});
it('do it - server + netlify', () => {
// production
expect(getInferredDeployUrl({
NODE_ENV: 'production',
NETLIFY: 'true',
BRANCH: 'main',
URL: 'https://my-project.netlify.app',
DEPLOY_URL: 'https://5b243e66dd6a547b4fee73ae--petsof.netlify.app',
DEPLOY_PRIME_URL: 'https://deploy-preview-1--petsof.netlify.app',
})).toBe('https://my-project.netlify.app');
// deploy preview
expect(getInferredDeployUrl({
NODE_ENV: 'production',
NETLIFY: 'true',
BRANCH: 'feature-1',
URL: 'https://my-project.netlify.app',
DEPLOY_URL: 'https://5b243e66dd6a547b4fee73ae--petsof.netlify.app',
DEPLOY_PRIME_URL: 'https://deploy-preview-1--petsof.netlify.app',
})).toBe('https://deploy-preview-1--petsof.netlify.app');
});
});
Command Palette
Search for a command to run...