> ## Documentation Index
> Fetch the complete documentation index at: https://developer.upsun.com/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript/Node.js

> All you need to know about configuring a new Javascript/Node.js project with Upsun

export const DynamicCodeBlock = ({language = 'yaml', filename, icon, lines, wrap, expandable, highlight, focus, children}) => {
  const STORAGE_KEY = 'upsun_versions_cache';
  const COMPOSABLE_STORAGE_KEY = 'upsun_composable_cache';
  const CACHE_TTL = 5 * 60 * 1000;
  const API_URL = 'https://meta.upsun.com/images';
  const COMPOSABLE_API_URL = 'https://meta.upsun.com/composable';
  const DEBUG_PREFIX = '[DynamicCodeBlock cache]';
  const [versionData, setVersionData] = useState(null);
  const [versionError, setVersionError] = useState(false);
  const [composableData, setComposableData] = useState(null);
  const [composableError, setComposableError] = useState(false);
  useEffect(() => {
    const fetchData = async () => {
      let cachedData = null;
      let cachedEtag = null;
      if (typeof localStorage !== 'undefined') {
        try {
          const cached = localStorage.getItem(STORAGE_KEY);
          if (cached) {
            const parsed = JSON.parse(cached);
            cachedData = parsed?.data || null;
            cachedEtag = parsed?.etag || null;
            if (cachedData && Date.now() - parsed.timestamp < CACHE_TTL) {
              return cachedData;
            }
          }
        } catch (err) {
          console.error('Failed to load from cache:', err);
        }
      }
      const requestHeaders = cachedEtag ? {
        'If-None-Match': cachedEtag
      } : {};
      console.debug(`${DEBUG_PREFIX} revalidating`, {
        storageKey: STORAGE_KEY,
        hasCachedData: Boolean(cachedData),
        hasCachedEtag: Boolean(cachedEtag)
      });
      const response = await fetch(API_URL, {
        headers: requestHeaders
      });
      if (response.status === 304 && cachedData) {
        console.debug(`${DEBUG_PREFIX} revalidated (304)`, {
          storageKey: STORAGE_KEY
        });
        if (typeof localStorage !== 'undefined') {
          try {
            const etag = response.headers.get('etag') || cachedEtag;
            localStorage.setItem(STORAGE_KEY, JSON.stringify({
              data: cachedData,
              etag,
              timestamp: Date.now()
            }));
          } catch (err) {
            console.error('Failed to refresh cache metadata:', err);
          }
        }
        return cachedData;
      }
      if (!response.ok) throw new Error(`API request failed: ${response.statusText}`);
      const data = await response.json();
      const etag = response.headers.get('etag');
      console.debug(`${DEBUG_PREFIX} refreshed (200)`, {
        storageKey: STORAGE_KEY,
        etag
      });
      if (typeof localStorage !== 'undefined') {
        try {
          localStorage.setItem(STORAGE_KEY, JSON.stringify({
            data,
            etag,
            timestamp: Date.now()
          }));
        } catch (err) {
          console.error('Failed to cache data:', err);
        }
      }
      return data;
    };
    fetchData().then(data => setVersionData(data)).catch(err => console.error('Failed to fetch version data:', err));
  }, []);
  const findHighestVersion = versionsMap => {
    if (!versionsMap || Object.keys(versionsMap).length === 0) return null;
    const entries = Object.entries(versionsMap);
    const active = entries.filter(([, v]) => v.upsun && v.upsun.status === 'supported' || v.upsun && v.upsun.status === 'deprecated');
    const candidates = active.length > 0 ? active : entries;
    let [highestName] = candidates[0];
    for (let i = 1; i < candidates.length; i++) {
      const [currentName] = candidates[i];
      const cp = currentName.split('.').map(Number);
      const hp = highestName.split('.').map(Number);
      for (let j = 0; j < Math.max(cp.length, hp.length); j++) {
        if ((cp[j] || 0) > (hp[j] || 0)) {
          highestName = currentName;
          break;
        } else if ((cp[j] || 0) < (hp[j] || 0)) {
          break;
        }
      }
    }
    return highestName;
  };
  const getVersion = (lang, requestedVersion = 'latest') => {
    if (lang === 'composable') {
      if (!composableData || !composableData.versions || Object.keys(composableData.versions).length === 0) return null;
      if (requestedVersion && requestedVersion !== 'latest') {
        return (requestedVersion in composableData.versions) ? requestedVersion : null;
      }
      return findHighestVersion(composableData.versions);
    }
    if (!versionData) return null;
    const imageData = versionData[lang];
    if (!imageData || !imageData.versions || Object.keys(imageData.versions).length === 0) {
      return null;
    }
    if (requestedVersion && requestedVersion !== 'latest') {
      return (requestedVersion in imageData.versions) ? requestedVersion : null;
    }
    return findHighestVersion(imageData.versions);
  };
  let code = typeof children === 'string' ? children : String(children || '');
  const codeLines = code.split('\n');
  while (codeLines.length > 0 && codeLines[0].trim() === '') codeLines.shift();
  while (codeLines.length > 0 && codeLines[codeLines.length - 1].trim() === '') codeLines.pop();
  if (codeLines.length > 0) {
    const indents = codeLines.filter(line => line.trim().length > 0).map(line => line.match(/^[ \t]*/)[0].length);
    const minIndent = Math.min(...indents);
    code = codeLines.map(line => line.slice(minIndent)).join('\n');
  }
  code = code.replace(/\{\{version:(.*?)\}\}/g, (match, params) => {
    const parts = params.split(':');
    const lang = parts[0];
    const ver = parts[1] || 'latest';
    const isComposable = lang === 'composable';
    const hasError = isComposable ? composableError : versionError;
    const dataReady = isComposable ? composableData !== null : versionData !== null;
    if (hasError) return '(unavailable)';
    if (dataReady) {
      const resolvedVersion = getVersion(lang, ver);
      return resolvedVersion || match;
    }
    return '...';
  });
  const codeBlockProps = {
    language,
    ...filename && ({
      filename
    }),
    ...icon && ({
      icon
    }),
    ...lines !== undefined && ({
      lines
    }),
    ...wrap !== undefined && ({
      wrap
    }),
    ...expandable !== undefined && ({
      expandable
    }),
    ...highlight && ({
      highlight
    }),
    ...focus && ({
      focus
    })
  };
  return <CodeBlock {...codeBlockProps}>{code}</CodeBlock>;
};

export const CommunityButtons = () => <div style={{
  display: 'flex',
  flexWrap: 'wrap',
  justifyContent: 'center',
  gap: '1rem',
  marginTop: '2rem'
}}>
    <a href="https://community.upsun.com/" target="_blank" rel="noopener" className="community-forum-btn">
      Join the Community Forum
    </a>
  </div>;

When dealing with Javascript/Node.js stacks, the information below may help customize your configuration.
These sections provide Javascript/Node.js-specific configuration details, but you can also refer to the common Upsun documentation:

* [Configuring applications](/docs/configure-apps)
* [Setting up managed services](/docs/add-services)
* [Handling requests](/docs/routes)

## Build flavors

By default, Upsun makes assumptions about how you want to build your application.
Namely, that you are managing your dependencies with npm, and that the very first thing you'd like to run is a particular and common production flavor of `npm install`.

This is called a build `flavor`, but its assumption may prove inappropriate for your application and cause your builds to fail (such as if you'd like to use yarn or bun instead of npm).
Therefore, you can [disable this feature](/docs/languages/nodejs#dependencies).

## Available package managers

Certain package managers come pre-installed on all Upsun `nodejs` container types:

* *npm*; example: `npm install platformsh-config`
* *npx*; example: `npx create-strapi-app <APP_NAME>`
* *bun*; example: `bun install platformsh-config`

You can also use Yarn if you [install it explicitly](/docs/languages/nodejs#use-yarn-as-a-package-manager), or [nvm](/docs/languages/nodejs/node-version#use-nvm).

## Sample configuration

Below are some examples from common Node.js framework configuration:

<Tabs>
  <Tab title="Next.js">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                <APP_NAME>:
                  source:
                    root: "/"
                  type: "nodejs:{{version:nodejs:latest}}"
                  web:
                    commands:
                      start: "npx next start -p $PORT"
                  build:
                    flavor: none
                  dependencies:
                    nodejs:
                      sharp: "*"
              #services:
              #  db:
              #    type: postgresql:{{version:postgresql:latest}}
              routes:
                "https://{default}/":
                  type: upstream
                  upstream: "myapp:http"
                # A basic redirect definition
                # More information: /docs/routes#basic-redirect-definition
                "https://www.{default}/":
                  type: redirect
                  to: "https://{default}/"`
          }
    </DynamicCodeBlock>
  </Tab>

  <Tab title="Express">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                <APP_NAME>:
                  source:
                    root: "/"
                  type: "nodejs:{{version:nodejs:latest}}"
                  web:
                    commands:
                      start: "node index.js"
                  build:
                    flavor: none
                  dependencies:
                    nodejs:
                      sharp: "*"
              #services:
              #  db:
              #    type: postgresql:{{version:postgresql:latest}}
              routes:
                "https://{default}/":
                  type: upstream
                  upstream: "myapp:http"
                # A basic redirect definition
                # More information: /docs/routes#basic-redirect-definition
                "https://www.{default}/":
                  type: redirect
                  to: "https://{default}/"`
          }
    </DynamicCodeBlock>
  </Tab>

  <Tab title="Strapi">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                <APP_NAME>:
                  source:
                    root: "/"
                  type: "nodejs:{{version:nodejs:latest}}"
                  relationships:
                    postgresql:
                  mounts:
                    ...
                  web:
                    commands:
                      start: "NODE_ENV=production yarn start"
                  build:
                    flavor: none
                  dependencies:
                    nodejs:
                      yarn: "^1.22.0"
                  hooks:
                    build: |
                      set -eux
                      yarn
                      yarn build
              services:
                postgresql:
                  type: postgresql:{{version:postgresql:latest}}

              routes:
                "https://{default}/":
                  type: upstream
                  upstream: "myapp:http"
                # A basic redirect definition
                # More information: /docs/routes#basic-redirect-definition
                "https://www.{default}/":
                  type: redirect
                  to: "https://{default}/"`
          }
    </DynamicCodeBlock>
  </Tab>
</Tabs>

## Frameworks

The Upsun documentation includes a wide array of community resources to help with framework-specific configuration:

* [Express](/docs/get-started/stacks/express)
* [Next.js](/docs/get-started/stacks/nextjs)
* [Strapi](/docs/get-started/stacks/strapi)

## Get support

While there are virtually no restrictions to you deploying any kind of application on Upsun, configuration may still be unclear at this point.

Not to worry! The Upsun community is here to help.
Come and say hello, share your work, ask for help, and peek in on what others are working on.

Welcome to the Upsun community!

<CommunityButtons />
