diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..f7b1786dd5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# EditorConfig: https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[Makefile] +indent_style = tab diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b983583855..44575b8ed1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,5 +4,6 @@ # Codeowners .github/CODEOWNERS @expressjs/docs-captains -# Blog -_posts @expressjs/express-tc \ No newline at end of file +# Blog - TODO: Add once Astro blog structure is set up + +src/content/blog @expressjs/express-tc diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ec082eb3d7..f0ce1d66e5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,22 +4,54 @@ updates: directory: / schedule: interval: monthly - - - package-ecosystem: docker - directory: / - schedule: - interval: monthly - - - package-ecosystem: bundler - directory: / - schedule: - interval: monthly + groups: + github-actions: + patterns: + - '*' - package-ecosystem: npm directory: / schedule: interval: monthly open-pull-requests-limit: 10 - ignore: - - dependency-name: "*" - update-types: ["version-update:semver-major"] + groups: + astro: + patterns: + - '@astrojs/*' + - 'astro' + - 'astro-*' + - 'prettier-plugin-astro' + orama: + patterns: + - '@orama/*' + - 'gray-matter' + - 'mdast-util-from-markdown' + - 'mdast-util-to-string' + linters: + patterns: + - 'eslint' + - 'eslint-*' + - '@eslint/*' + - 'prettier' + - 'typescript-eslint' + - 'globals' + production: + dependency-type: production + exclude-patterns: + - '@astrojs/*' + - 'astro' + - 'astro-*' + - '@orama/*' + - 'gray-matter' + - 'mdast-util-from-markdown' + - 'mdast-util-to-string' + development: + dependency-type: development + exclude-patterns: + - 'eslint' + - 'eslint-*' + - '@eslint/*' + - 'prettier' + - 'prettier-plugin-astro' + - 'typescript-eslint' + - 'globals' diff --git a/.github/scripts/get-contributing.sh b/.github/scripts/get-contributing.sh index 2b046e4d1a..2ee6f77ff2 100755 --- a/.github/scripts/get-contributing.sh +++ b/.github/scripts/get-contributing.sh @@ -1,92 +1,92 @@ #!/bin/bash -# This script replaces the contents of a section with the contents from the annotated source address or local file paths inside the DEST file. +# This script replaces the contents of a section with the contents from the +# annotated source address (SRC comments) inside the DEST file. +# SRC comments use the format: {/* SRC: org/repo path/to/file */} -# read contents of file into memory -DEST="../../en/resources/contributing.md" +DEST="../../src/content/pages/en/resources/contributing.mdx" + +# Shared content transformations +transform_content() { + local content="$1" + local level="$2" + + # Keep only from first ## onward (skip title/badges) + content=$(echo "$content" | sed -En '/^##|^[^#]/,$p') + + # Downgrade headings based on parent level + content=$(echo "$content" | sed 's/^#/&'"${level:1}"'/g') + + # Convert GitHub callouts to Alert components + content=$(echo "$content" | perl -0777 -pe ' + s/> \[!(IMPORTANT|NOTE|TIP|CAUTION|WARNING)\]\s*\n((?:>.*\n)*)/ + my $type = lc($1); + my %map = (important => "info", note => "info", tip => "info", caution => "warning", warning => "warning"); + my $alert = $map{$type} || "info"; + my $body = $2; + $body =~ s|^> ?||gm; + "\n\n${body}\n<\/Alert>\n" + /ge') + + # Convert HTML comments to MDX comments + content=$(echo "$content" | sed -E 's//\{\/\*\1\*\/\}/g') + + # Convert self-closing HTML tags for MDX compatibility + content=$(echo "$content" | sed -E 's/<(br|hr|img)([^/]*[^/])?\s*>/<\1\2 \/>/gi') + + echo "$content" +} -# track the header level level='' -# tracks src for curl calls src='' -# tracks file paths for local file reads -local='' - while IFS= read -r line; do - # REMOVE PREVIOUS CONTENT SECTION - # if src or local tags are not empty - if [[ -n "$src" || -n "$local" ]]; then - # if current line not a horitzontal rule hr - if [[ "$line" != "----"* ]]; then - # if line == level -- level is num of ## - if [[ "$line" == "$level"'#'* || - # line not a header line - "$line" != '#'* ]]; then - # skip line and rewrite over old content - continue + +while IFS= read -r line; do + # Skip lines from previous SRC section (will be replaced) + if [[ -n "$src" ]]; then + if [[ "$line" != "----"* ]]; then + if [[ "$line" == "$level"'#'* || "$line" != '#'* ]]; then + continue fi fi fi - # PRINT TO PAGE SECTION src='' - local='' - # if line is a header + + # Track heading level if [[ "$line" == '#'* ]]; then - # if header has (#id-of-link) or {#id-on-page} patterns if [[ $line =~ (\(\#.*\))\. || "$line" =~ \{\#.*\} ]]; then - # isolate the matching part of line match=${BASH_REMATCH[0]} - # remove match - leaving rest rest=${line//${match}} - # remove any # symbols from start title_rest=${rest##*\#} - # slice rest of line to get only level level="${rest:0:$((${#rest} - ${#title_rest}))}" else - # any other headers -- these before SRC/LOCAL pages anchors - header=${line##*\#} + header=${line##*\#} level="${line:0:$((${#line} - ${#header}))}" fi - # if line is SRC anchor in read file - elif [[ "$line" == '/\{\/\*\1\*\/\}/g') + + # Convert self-closing HTML tags for MDX compatibility + CONTENT=$(echo "$CONTENT" | sed -E 's/<(br|hr|img)([^/]*[^/])?\s*>/<\1\2 \/>/gi') + + # Convert relative links to absolute GitHub URLs + BASEURL="https://github.com/$org/$repo/blob/HEAD" + CONTENT=$(echo "$CONTENT" | sed -E "s|\]\(([^)#/][^):]*)\)|](${BASEURL}/\1)|g") + + # Build the MDX import and component + IMPORT="import MiddlewareInfo from '@components/patterns/MiddlewareInfo/MiddlewareInfo.astro'; +import Alert from '@components/primitives/Alert/Alert.astro';" + GITHUB_URL="https://github.com/$org/$repo" + NPM_PAGE="https://www.npmjs.com/package/$repo" + COMPONENT="" + + # Write with frontmatter preserved + if [ -n "$FRONTMATTER" ]; then + printf '%s\n%s\n%s\n\n%s\n\n%s\n\n%s\n' "---" "$FRONTMATTER" "---" "$IMPORT" "$COMPONENT" "$CONTENT" > $DEST + else + printf '%s\n%s\n%s\n%s\n\n%s\n\n%s\n\n%s\n' "---" "title: $repo middleware" "description: $DESC" "---" "$IMPORT" "$COMPONENT" "$CONTENT" > $DEST fi done diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000000..4b5050b8d0 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,104 @@ +name: ci + +on: + pull_request: + branches: + - redesign + - gh-pages + pull_request_review: + types: [submitted] + push: + branches: + - redesign + - gh-pages + +# Cancel in progress workflows +# in the scenario where we already had a run going for that PR/branch/tag but then triggered a new run +concurrency: + group: '${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: true + +permissions: + contents: read + +jobs: + lint: + runs-on: ubuntu-latest + if: | + github.event.pull_request.draft != true && + (github.event_name == 'pull_request_review' && github.event.review.state == 'approved') || + github.event_name != 'pull_request_review' + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + # node-version-file: ".nvmrc" use when .nvmrc is on root of the repo + node-version: '24.13' + # cache: "npm" use when package-lock.json is on root of the repo + + - name: Install Node.js dependencies + run: npm ci + + - name: Run tests + shell: bash + run: npm run check + + build: + name: build + runs-on: ubuntu-latest + if: | + github.event.pull_request.draft != true && + (github.event_name == 'pull_request_review' && github.event.review.state == 'approved') || + github.event_name != 'pull_request_review' + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - name: Build Astro site + uses: withastro/action@b7d53628f8b666036b0238aadb0b984a2a489f26 # v6 + with: + path: . + package-manager: npm + out-dir: dist + + - name: Upload build artifact for linkChecker + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: site-dist + path: dist/ + if-no-files-found: error + + linkChecker: + needs: build + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - name: Download build artifact + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: site-dist + path: dist/ + + - name: Check links + uses: lycheeverse/lychee-action@a8c4c7cb88f0c7386610c35eb25108e448569cb0 # v2.7.0 + with: + args: | + --root-dir $PWD/dist + --exclude-path dist/llms.txt + --exclude-path dist/llms/ + --remap "https://expressjs\.com\/((?:[^\/]+\/)*[^\/\.]+)\/?$ file://$PWD/dist/\$1/index.html" + --remap "https://expressjs\.com\/(.*\.html)$ file://$PWD/dist/\$1" + dist/ + fail: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index d307223cd3..0000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: ci - -on: - pull_request: - branches: - - gh-pages - pull_request_review: - types: [submitted] - push: - branches: - - gh-pages - -permissions: - contents: read - -jobs: - test: - runs-on: ubuntu-latest - if: | - github.event.pull_request.draft != true && - (github.event_name == 'pull_request_review' && github.event.review.state == 'approved') || - github.event_name != 'pull_request_review' - - steps: - - uses: actions/checkout@v6 - with: - persist-credentials: false - - - - name: Set up Node.js - uses: actions/setup-node@v6 - - - name: Install Node.js dependencies - run: npm install - - - name: Run tests - shell: bash - run: npm test - - build: - name: Build - runs-on: ubuntu-latest - if: | - github.event.pull_request.draft != true && - (github.event_name == 'pull_request_review' && github.event.review.state == 'approved') || - github.event_name != 'pull_request_review' - - steps: - - uses: actions/checkout@v6 - with: - persist-credentials: false - - - name: Setup Ruby - uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # https://github.com/ruby/setup-ruby/releases/tag/v1.288.0 - with: - bundler-cache: true # runs 'bundle install' and caches installed gems automatically - - - name: Jekyll Build - run: bundle exec jekyll build - - - name: Check Docker support - shell: bash - run: | - docker --version - make build diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c01437a44e..7c33bd2efc 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -9,16 +9,16 @@ # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # -name: "CodeQL" +name: 'CodeQL' on: push: - branches: ["gh-pages"] + branches: ['gh-pages'] pull_request: # The branches below must be a subset of the branches above - branches: ["gh-pages"] + branches: ['gh-pages'] schedule: - - cron: "0 0 * * 1" + - cron: '0 0 * * 1' permissions: contents: read @@ -35,17 +35,17 @@ jobs: strategy: fail-fast: false matrix: - language: ["javascript", "actions"] + language: ['javascript', 'actions'] # CodeQL supports [ $supported-codeql-languages ] # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v4 + uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -55,7 +55,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v4 + uses: github/codeql-action/autobuild@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -68,6 +68,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4 + uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4 with: - category: "/language:${{matrix.language}}" + category: '/language:${{matrix.language}}' diff --git a/.github/workflows/crowdin.yml b/.github/workflows/crowdin.yml index 2c11442fb6..ec4a07c94a 100644 --- a/.github/workflows/crowdin.yml +++ b/.github/workflows/crowdin.yml @@ -2,7 +2,7 @@ name: Crowdin Upload on: push: - branches: [ gh-pages ] + branches: [gh-pages] workflow_dispatch: permissions: @@ -14,12 +14,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false - name: crowdin action - uses: crowdin/github-action@8818ff65bfc4322384f983ea37e3926948c11745 # https://github.com/crowdin/github-action/releases/tag/v2.15.0 + uses: crowdin/github-action@60debf382ee245b21794321190ad0501db89d8c1 # https://github.com/crowdin/github-action/releases/tag/v2.13.0 with: upload_sources: true upload_translations: false diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index cb955fb7cc..c3d8f65d13 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,7 +2,8 @@ name: Deploy Website on: push: - branches: ["gh-pages"] + branches: + - gh-pages workflow_dispatch: permissions: @@ -11,41 +12,32 @@ permissions: id-token: write concurrency: - group: "pages" - cancel-in-progress: false + group: 'deploy-${{ github.ref }}' + cancel-in-progress: true jobs: build: + name: build runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6 - - name: Setup Ruby - uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # https://github.com/ruby/setup-ruby/releases/tag/v1.288.0 - with: - bundler-cache: true # runs 'bundle install' and caches installed gems automatically - cache-version: 0 # Increment this number if you need to re-download cached gems - - name: Setup Pages - id: pages - uses: actions/configure-pages@v5 - - name: Build with Jekyll - # Outputs to the './_site' directory by default - run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}" - env: - JEKYLL_ENV: production - - name: Upload gh-pages artifact - uses: actions/upload-pages-artifact@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Build and upload pages artifact + uses: withastro/action@b7d53628f8b666036b0238aadb0b984a2a489f26 # v6 with: - path: _site - include-hidden-files: true # required for /.well-known/security.txt + path: . + package-manager: npm + out-dir: dist deploy: + name: deploy + needs: build + runs-on: ubuntu-latest environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: build steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000000..54c6de14b3 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,86 @@ +name: Playwright Tests + +on: + pull_request: + branches: + - redesign + - gh-pages + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + playwright: + if: github.actor != 'dependabot[bot]' + name: Playwright Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: '24.13' + cache: 'npm' + + - name: Wait for Netlify preview + id: wait-for-preview + run: | + # Calculate the Netlify preview URL based on the PR number + PREVIEW_URL="https://deploy-preview-${{ github.event.pull_request.number }}--expressjscom-preview.netlify.app" + echo "PREVIEW_URL=$PREVIEW_URL" >> "$GITHUB_ENV" + + MAX_RETRIES=10 + DELAY=20 + echo "Checking Netlify preview: $PREVIEW_URL" + + for i in $(seq 1 $MAX_RETRIES); do + # Check if the URL returns a 200 OK + if curl -s -I "$PREVIEW_URL" | grep -q "HTTP/.* 200"; then + echo "✅ Preview is live at $PREVIEW_URL" + exit 0 + fi + echo "⏳ Waiting for Netlify to deploy... ($i/$MAX_RETRIES)" + sleep $DELAY + done + + echo "❌ Preview not live after $((MAX_RETRIES*DELAY)) seconds." + exit 1 + + - name: Install dependencies + run: npm ci + + - name: Get Playwright version + id: playwright-version + run: echo "version=$(npx playwright --version | awk '{print $2}')" >> $GITHUB_OUTPUT + + - name: Cache Playwright browsers + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + with: + path: ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} + restore-keys: | + playwright-${{ runner.os }}- + + - name: Install Playwright Browsers + if: steps.playwright-cache.outputs.cache-hit != 'true' + run: npx playwright install --with-deps + + - name: Run Playwright tests + run: npm run test:e2e + env: + PLAYWRIGHT_BASE_URL: ${{ env.PREVIEW_URL }} + + - name: Upload Playwright test results + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index a7903510ee..c8306d9f41 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -12,7 +12,7 @@ on: schedule: - cron: '20 7 * * 2' push: - branches: ["gh-pages"] + branches: ['gh-pages'] # Declare default permissions as read only. permissions: read-all @@ -35,12 +35,12 @@ jobs: checks: read steps: - - name: "Checkout code" - uses: actions/checkout@v6 + - name: 'Checkout code' + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false - - name: "Run analysis" + - name: 'Run analysis' uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: results_file: results.sarif @@ -62,15 +62,15 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - - name: "Upload artifact" - uses: actions/upload-artifact@v7 + - name: 'Upload artifact' + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: SARIF file path: results.sarif retention-days: 5 # Upload the results to GitHub's code scanning dashboard. - - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@v4 + - name: 'Upload to code-scanning' + uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4 with: sarif_file: results.sarif diff --git a/.github/workflows/sync-orama.yml b/.github/workflows/sync-orama.yml new file mode 100644 index 0000000000..652df1488e --- /dev/null +++ b/.github/workflows/sync-orama.yml @@ -0,0 +1,47 @@ +name: Sync Orama Cloud + +on: + workflow_dispatch: + push: + branches: + - redesign + - gh-pages + pull_request: + branches: + - redesign + - gh-pages + +permissions: + contents: read + +concurrency: + group: '${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: ${{ github.event_name != 'push' }} + +jobs: + sync-orama-cloud: + name: Sync Orama Cloud + runs-on: ubuntu-latest + + steps: + - name: 'Checkout' + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version-file: '.nvmrc' + cache: 'npm' + + - name: Install Node.js dependencies + run: npm ci + + - name: Sync Orama Cloud + run: node ./scripts/sync-orama.mjs + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PUBLIC_ORAMA_PROJECT_ID: ${{ github.event_name == 'push' && secrets.PUBLIC_ORAMA_PRODUCTION_PROJECT_ID || secrets.PUBLIC_ORAMA_PROJECT_ID }} + PRIVATE_ORAMA_API_KEY: ${{ github.event_name == 'push' && secrets.PRIVATE_ORAMA_PRODUCTION_API_KEY || secrets.PRIVATE_ORAMA_API_KEY }} + PUBLIC_ORAMA_DATASOURCE_ID: ${{ github.event_name == 'push' && secrets.PUBLIC_ORAMA_PRODUCTION_DATASOURCE_ID || secrets.PUBLIC_ORAMA_DATASOURCE_ID }} diff --git a/.github/workflows/update-external-docs.yml b/.github/workflows/update-external-docs.yml index d96de36509..504e8e8cff 100644 --- a/.github/workflows/update-external-docs.yml +++ b/.github/workflows/update-external-docs.yml @@ -1,107 +1,142 @@ name: Update External Docs on: - workflow_dispatch: - schedule: - - cron: '0 8 * * 1' + workflow_dispatch: + schedule: + - cron: '0 8 * * 1' permissions: - pull-requests: write - contents: write + pull-requests: write + contents: write jobs: - update-security: - runs-on: ubuntu-latest - if: github.repository_owner == 'expressjs' - steps: - - uses: actions/checkout@v6 - - - name: Check security.txt expiry - id: expiry - run: | - set -eo pipefail - expires=$(grep -i '^Expires:' .well-known/security.txt | sed 's/.*: //') - if [ "$(date -d "$expires" +%s)" -le "$(date -d '+30 days' +%s)" ]; then - echo "bump=true" >> "$GITHUB_OUTPUT" - fi - - - name: Bump Expires - if: steps.expiry.outputs.bump == 'true' - run: | - sed -i "s/^Expires:.*/Expires: $(date -u -d '+180 days' +%Y-%m-%dT00:00:00Z)/I" .well-known/security.txt - - - name: Create Pull Request - if: steps.expiry.outputs.bump == 'true' - uses: gr2m/create-or-update-pull-request-action@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - commit-message: 'docs: update security.txt expiry' - title: 'docs: update security.txt expiry' - body: | - This PR extends the `Expires` date in `.well-known/security.txt`. - - Before merging, please explicitly confirm that: - - The security contact listed is still correct and actively monitored - - The linked security policy is still accurate - - The project's preferred vulnerability disclosure path has not changed - - Only merge if the above information is still valid; otherwise, update it before extending the expiration. - - cc: @expressjs/security-wg - labels: docs - branch: bot/security-expiry - - update-docs: - runs-on: ubuntu-latest - if: github.repository_owner == 'expressjs' - steps: - - uses: actions/checkout@v6 - - - name: Set up Node.js - uses: actions/setup-node@v6 - - - name: Run scripts - working-directory: .github/scripts - run: | - bash ./get-contributing.sh - bash ./get-readmes.sh - node ./get-express-version.mjs - - - name: Create Pull Request - uses: gr2m/create-or-update-pull-request-action@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - commit-message: 'docs: update external docs' - title: 'docs: update external docs' - body: > - This auto-generated PR updates external documentation to the expressjs.com repository. - - cc: @expressjs/docs-wg - labels: docs - branch: bot/update-external-docs - - synchronize-with-crowdin: - runs-on: ubuntu-latest - if: github.repository_owner == 'expressjs' - steps: - - name: Checkout - uses: actions/checkout@v6 - # see all the options at https://github.com/crowdin/github-action - - name: Crowdin PR - uses: crowdin/github-action@8818ff65bfc4322384f983ea37e3926948c11745 # https://github.com/crowdin/github-action/releases/tag/v2.15.0 - with: - upload_sources: false - upload_translations: false - download_translations: true - localization_branch_name: crowdin/translations - create_pull_request: true - pull_request_title: 'i18n: new crowdin translations' - pull_request_body: > - New Crowdin translations from the [express.js crowdin project](https://express.crowdin.com/u/projects/1). cc: @expressjs/docs-wg - pull_request_base_branch_name: 'gh-pages' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} - CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} + update-security: + runs-on: ubuntu-latest + if: github.repository_owner == 'expressjs' + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - name: Check security.txt expiry + id: expiry + run: | + set -eo pipefail + expires=$(grep -i '^Expires:' public/.well-known/security.txt | sed 's/.*: //') + if [ "$(date -d "$expires" +%s)" -le "$(date -d '+30 days' +%s)" ]; then + echo "bump=true" >> "$GITHUB_OUTPUT" + fi + + - name: Bump Expires + if: steps.expiry.outputs.bump == 'true' + run: | + sed -i "s/^Expires:.*/Expires: $(date -u -d '+180 days' +%Y-%m-%dT00:00:00Z)/I" public/.well-known/security.txt + + - name: Create Pull Request + if: steps.expiry.outputs.bump == 'true' + uses: gr2m/create-or-update-pull-request-action@b65137ca591da0b9f43bad7b24df13050ea45d1b # v1.10.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + commit-message: 'docs: update security.txt expiry' + title: 'docs: update security.txt expiry' + body: | + This PR extends the `Expires` date in `public/.well-known/security.txt`. + + Before merging, please explicitly confirm that: + - The security contact listed is still correct and actively monitored + - The linked security policy is still accurate + - The project's preferred vulnerability disclosure path has not changed + + Only merge if the above information is still valid; otherwise, update it before extending the expiration. + + cc: @expressjs/security-wg @expressjs/docs-wg + labels: docs + branch: bot/security-expiry + + update-docs: + runs-on: ubuntu-latest + if: github.repository_owner == 'expressjs' + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: lts/* + + - name: Install dependencies + run: npm ci + + - name: Run scripts + working-directory: .github/scripts + run: | + bash ./get-contributing.sh + bash ./get-readmes.sh + + - name: Fix formatting + run: npm run fix + + - name: Create Pull Request + uses: gr2m/create-or-update-pull-request-action@b65137ca591da0b9f43bad7b24df13050ea45d1b # v1.10.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + commit-message: 'docs: update external docs' + title: 'docs: update external docs' + body: > + This auto-generated PR updates external documentation to the expressjs.com repository. + + cc: @expressjs/docs-wg + labels: docs + branch: bot/update-external-docs + + synchronize-with-crowdin: + runs-on: ubuntu-latest + if: github.repository_owner == 'expressjs' + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: '24.13' + + - name: Install dependencies + run: npm ci + + # see all the options at https://github.com/crowdin/github-action + - name: Crowdin PR + uses: crowdin/github-action@60debf382ee245b21794321190ad0501db89d8c1 # https://github.com/crowdin/github-action/releases/tag/v2.13.0 + with: + upload_sources: false + upload_translations: false + download_translations: true + localization_branch_name: crowdin/translations + create_pull_request: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} + CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} + + - name: Format files + run: npm run format + + - name: Create Pull Request + uses: gr2m/create-or-update-pull-request-action@b65137ca591da0b9f43bad7b24df13050ea45d1b # v1.10.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + commit-message: 'i18n: new crowdin translations' + title: 'i18n: new crowdin translations' + body: > + New Crowdin translations from the [express.js crowdin project](https://express.crowdin.com/u/projects/1). cc: @expressjs/docs-wg + labels: docs + branch: crowdin/translations diff --git a/.gitignore b/.gitignore index 8323fd83be..c21e0d8d7a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,27 @@ -node_modules +# build output +dist/ + +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# environment variables +.env +.env.production + +# macOS-specific files .DS_Store -*.sublime* -_site -.sass-cache -*.iml -**/.idea -**/Thumbs.db - -.jekyll-metadata -vendor -.bundle + +# jetbrains setting folder +.idea/ + +playwright-report/ +test-results/ \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000000..ab18993385 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +node --run lint-staged diff --git a/.lintstagedrc.json b/.lintstagedrc.json new file mode 100644 index 0000000000..44d96dbcd1 --- /dev/null +++ b/.lintstagedrc.json @@ -0,0 +1,4 @@ +{ + "*.{js,jsx,ts,tsx,astro}": ["eslint --fix", "prettier --write"], + "*.{json,md,css,mjs}": ["prettier --write"] +} diff --git a/.lycheeignore b/.lycheeignore new file mode 100644 index 0000000000..29bed0cc5d --- /dev/null +++ b/.lycheeignore @@ -0,0 +1,28 @@ +# Prevent rate limit for npmjs links + +http://npmjs.com/.* +https://npmjs.com/.* +https://www.npmjs.com/.* +https://npmjs.org/.* +https://www.npmjs.org/.* + +# Prevent github rate limit +https://github.com/.* + +# Exclude 404 pages +dist/.*/404/index.html$ +dist/404/index.html$ + +# Exclude Open Collective links +https://opencollective.com/.* + +# Prevent netlify rate limit +https://www.netlify.com/ + +## Ignore temporaly sitemap +https://expressjs.com/sitemap-index.xml + +# Ignore Coveralls links + +https://coveralls.io/-* + diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000..889a7ccb46 --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +min-release-age=2 +ignore-scripts=true +git=false diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000000..12fd1fc277 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +24.13 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..1d644a224e --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +dist/ +.astro/ +node_modules/ +package-lock.json +pnpm-lock.yaml +LICENSE.md diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000..167200c363 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "useTabs": false, + "trailingComma": "es5", + "printWidth": 100, + "plugins": ["prettier-plugin-astro"] +} diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index a0891f563f..0000000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -3.3.4 diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000..e7b63ac2c7 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "astro-build.astro-vscode", + "esbenp.prettier-vscode", + "dbaeumer.vscode-eslint", + "unifiedjs.vscode-mdx" + ], + "unwantedRecommendations": [] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..d642209762 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "command": "./node_modules/.bin/astro dev", + "name": "Development server", + "request": "launch", + "type": "node-terminal" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..c884b6db98 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,27 @@ +{ + "prettier.enable": true, + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "files.associations": { + ".lycheeignore": "ignore" + }, + "[ignore]": { + "editor.formatOnSave": false + }, + "[astro]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[css]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } +} diff --git a/2x/applications.md b/2x/applications.md deleted file mode 100644 index 8cf4321141..0000000000 --- a/2x/applications.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sitemap: false -redirect_to: - - https://github.com/expressjs/expressjs.com/tree/2x ---- \ No newline at end of file diff --git a/2x/contrib.md b/2x/contrib.md deleted file mode 100644 index 8cf4321141..0000000000 --- a/2x/contrib.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sitemap: false -redirect_to: - - https://github.com/expressjs/expressjs.com/tree/2x ---- \ No newline at end of file diff --git a/2x/docs/applications.md b/2x/docs/applications.md deleted file mode 100644 index 8cf4321141..0000000000 --- a/2x/docs/applications.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sitemap: false -redirect_to: - - https://github.com/expressjs/expressjs.com/tree/2x ---- \ No newline at end of file diff --git a/2x/docs/contrib.md b/2x/docs/contrib.md deleted file mode 100644 index 8cf4321141..0000000000 --- a/2x/docs/contrib.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sitemap: false -redirect_to: - - https://github.com/expressjs/expressjs.com/tree/2x ---- \ No newline at end of file diff --git a/2x/docs/executable.md b/2x/docs/executable.md deleted file mode 100644 index 8cf4321141..0000000000 --- a/2x/docs/executable.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sitemap: false -redirect_to: - - https://github.com/expressjs/expressjs.com/tree/2x ---- \ No newline at end of file diff --git a/2x/docs/guide.md b/2x/docs/guide.md deleted file mode 100644 index 8cf4321141..0000000000 --- a/2x/docs/guide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sitemap: false -redirect_to: - - https://github.com/expressjs/expressjs.com/tree/2x ---- \ No newline at end of file diff --git a/2x/docs/index.md b/2x/docs/index.md deleted file mode 100644 index 8cf4321141..0000000000 --- a/2x/docs/index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sitemap: false -redirect_to: - - https://github.com/expressjs/expressjs.com/tree/2x ---- \ No newline at end of file diff --git a/2x/docs/migrate.md b/2x/docs/migrate.md deleted file mode 100644 index 8cf4321141..0000000000 --- a/2x/docs/migrate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sitemap: false -redirect_to: - - https://github.com/expressjs/expressjs.com/tree/2x ---- \ No newline at end of file diff --git a/2x/docs/screencasts.md b/2x/docs/screencasts.md deleted file mode 100644 index 8cf4321141..0000000000 --- a/2x/docs/screencasts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sitemap: false -redirect_to: - - https://github.com/expressjs/expressjs.com/tree/2x ---- \ No newline at end of file diff --git a/2x/executable.md b/2x/executable.md deleted file mode 100644 index 8cf4321141..0000000000 --- a/2x/executable.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sitemap: false -redirect_to: - - https://github.com/expressjs/expressjs.com/tree/2x ---- \ No newline at end of file diff --git a/2x/guide.md b/2x/guide.md deleted file mode 100644 index 8cf4321141..0000000000 --- a/2x/guide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sitemap: false -redirect_to: - - https://github.com/expressjs/expressjs.com/tree/2x ---- \ No newline at end of file diff --git a/2x/index.md b/2x/index.md deleted file mode 100644 index 8cf4321141..0000000000 --- a/2x/index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sitemap: false -redirect_to: - - https://github.com/expressjs/expressjs.com/tree/2x ---- \ No newline at end of file diff --git a/2x/migrate.md b/2x/migrate.md deleted file mode 100644 index 8cf4321141..0000000000 --- a/2x/migrate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sitemap: false -redirect_to: - - https://github.com/expressjs/expressjs.com/tree/2x ---- \ No newline at end of file diff --git a/2x/screencasts.md b/2x/screencasts.md deleted file mode 100644 index 8cf4321141..0000000000 --- a/2x/screencasts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sitemap: false -redirect_to: - - https://github.com/expressjs/expressjs.com/tree/2x ---- \ No newline at end of file diff --git a/404.md b/404.md deleted file mode 100644 index 5bae9ef3f0..0000000000 --- a/404.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -layout: 404 -title: Page not found -description: We couldn’t find the page you’re looking for. -lang: en ---- - -# {{ page.title }} - -We couldn’t find the page you’re looking for. Check the address or [head back home](/). - -If this is a mistake, [let us know](https://github.com/expressjs/expressjs.com/issues/new/), and we will try to fix it! \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f0b0a9ed9f..66ccf2f188 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,126 +1,141 @@ -# Contributing to Expressjs.com - -### The Official Documentation of the Express.js Framework +# Contributing to the expressjs.com website This is the contribution documentation for the [expressjs.com](https://github.com/expressjs/expressjs.com) website. ->[!NOTE] -> This is not the repo for Express.js framework. To contribute to the _[Express.js framework](https://github.com/expressjs/express)_, check out the [GitHub repo contributing page](https://github.com/expressjs/express?tab=contributing-ov-file) or the website's [Contributing to Express](https://expressjs.com/en/resources/contributing.html) page. +You can see the current [captains and committers](https://github.com/expressjs/discussions/blob/HEAD/docs/contributing/captains_and_committers.md) of this project, and learn how to join through the [governance document](https://github.com/expressjs/discussions/blob/HEAD/docs/GOVERNANCE.md). Also review the [Express Collaborator Guide](https://github.com/expressjs/.github/blob/HEAD/CONTRIBUTING.md) for general contribution guidelines across Express.js projects. +## Common Contributions -#### Need some ideas? These are some typical issues. +1. **Website Issues**: Improvements to the site's functionality, design, or accessibility. +2. **Content Issues**: Fix anything related to site content or typos. +3. **Translation Issues**: Fix translation errors or contribute new content. See the [i18n documentation](docs/i18n.md). -1. **Website issues**: If you see anything on the site that could use a tune-up, think about how to fix it. - - Display or screen sizing problems - - Mobile responsiveness issues - - Missing or broken accessibility features - - Website outages - - Broken links - - Page structure or user interface enhancements +## Working on Issues -2. **Content Issues**: Fix anything related to site content or typos. - - Spelling errors - - Incorrect/outdated Express.js documentation - - Missing content +We welcome contributions to existing bugs or enhancements. You can find these under our repo's [Issues tab](https://github.com/expressjs/expressjs.com/issues). Look for issues labeled `good first issue` or `help wanted` to get started. -3. **Translation Issues**: Fix any translation errors or contribute new content. - - Fix spelling errors - - Fix incorrect/poorly translated words - - Check out the [Contributing translations](#contributing-translations) section below for a contributing guide. +If you have found a bug, a typo, or have an idea for an enhancement: -#### Want to work on a backlog issue? +- Submit a [new issue](https://github.com/expressjs/expressjs.com/issues/new/choose) for larger proposals or to get feedback first. +- Open a [pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) directly if the work is ready to go. -We often have bugs or enhancements that need work. You can find these under our repo's [Issues tab](https://github.com/expressjs/expressjs.com/issues). Check out the tags to find something that's a good match for you. +> For significant changes, we encourage opening an issue first to discuss and align before starting work. -#### Have an idea? Found a bug? +## Getting Started -If you've found a bug or a typo, or if you have an idea for an enhancement, you can: -- Submit a [new issue](https://github.com/expressjs/expressjs.com/issues/new/choose) on our repo. Do this for larger proposals, or if you'd like to discuss or get feedback first. +### Prerequisites -- Make a [GitHub pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request). If you have already done work, and it's ready to go, feel free to send it our way. +This project uses: -## Getting Started +- **Astro** for site generation +- **TypeScript** for type safety +- **ESLint** for linting +- **Prettier** for formatting -The steps below will guide you through the Expressjs.com contribution process. +Tooling required: -#### Step 1: (OPTIONAL) Open a New Issue -So you've found a problem that you want to fix, or have a site enhancement you want to make. -1. If you want to get feedback or discuss, open a discussion [issue](https://github.com/expressjs/expressjs.com/issues/new/choose) prior to starting work. This is not required, but encouraged for larger proposals. - - While we highly encourage this step, it is only for submissions proposing significant change. It helps us to clarify and focus the work, and ensure it aligns with overall project priorities. - - For submissions proposing minor improvements or corrections, this is not needed. You can skip this step. - - When opening an issue please give it a title and fill in the description section. The more details you provide, the more feedback we can give. +- **Node.js**: v24.x or higher +- **npm**: v11.0.0 or higher (comes with Node 24) -2. After receiving your issue the Express.js documentation team will respond with feedback. We read every submission and always try to respond quickly with feedback. - - For submissions proposing significant change, we encourage you to follow the review process before starting work. +> We recommend using [nvm](https://github.com/nvm-sh/nvm) to manage Node.js versions. This project includes an `.nvmrc` file for automatic version switching. -#### Step 2: Get the Application Code Base +### Setup -Clone the repo and get the code: +1. **Clone the repository:** -```sh -git clone https://github.com/expressjs/expressjs.com.git -``` + ```bash + git clone https://github.com/expressjs/expressjs.com.git + cd expressjs.com + ``` + +2. **Install the correct Node.js version** (if using nvm): + + ```bash + nvm install + nvm use + ``` + +3. **Install dependencies:** + + ```bash + npm install + ``` -After you've got the code you're ready to start making your changes! +4. **Start the development server:** -But just in case you need a little extra explanation, this section below outlines the main sections of the code base, where most changes are likely to be made. + ```bash + npm run dev + ``` -**Markdown Page Files**: -- These files render to html and make up the individual pages of the site. Most of the site's documentation text content is written in `md` files. -- Change these to make changes to individual pages' content/text or markup. -- Each language has its own complete set of pages, located under their respective language directories - all the Spanish markdown content is found in the `es` directory, for example. + The site will be available at `http://localhost:4321` -**Includes Partials and Layout Templates** -- `_includes` are partials that are imported and reused across multiple pages. - - These are used to import text content for reuse across pages, such as the API documentation, e.g., `_includes > api > en > 5x`, which is included in every language. - - These are used to include the page components that make up site-wide user interface and periphery structure, e.g., Header, Footer, etc. -- `_layouts` are the templates used to wrap the site's individual pages. - - These are used to display the structure of the site's periphery, such as the header and footer, and for injecting and displaying individual markdown pages inside the `content` tag. +### Available Scripts -**Blog Markdown Files** -- These files make up the individual blog posts. If you want to contribute a blog post please -follow the specific instructions for [How to write a blog post.](https://expressjs.com/en/blog/write-post.html) -- Located under the `_posts` directory. +| Command | Description | +| ------------------ | ----------------------------------------- | +| `npm run dev` | Start development server with hot reload | +| `npm run build` | Build production site to `./dist` | +| `npm run preview` | Preview production build locally | +| `npm run lint` | Run ESLint to check for issues | +| `npm run check` | Run type checking and format verification | +| `npm run test:e2e` | Run Playwright E2E tests | -**CSS or Javascript** -- All css and js files are kept in `css` and `js` folders on the project root. +## Submitting a Pull Request -The Express.js website is built using [Jekyll](https://jekyllrb.com/) and is hosted on [GitHub Pages](https://pages.github.com/). +1. Create a new branch from `main`. +2. Make your changes. +3. Run `npm run check` to verify code style and types. +4. Run `npm run test:e2e` to ensure your changes don't break existing functionality. +5. Commit with a clear message. +6. Push to your fork. +7. Open a PR against `main`. -#### Step 3: Running the Application +> Ensure all checks pass and your branch is up to date with `main` before opening a PR. -Now you'll need a way to see your changes, which means you'll need a running version of the application. You have two options. +## Testing ->[!NOTE] ->If you're only making changes to the content, you most likely won't need to run the site locally. +We use **Playwright** for End-to-End (E2E) testing. All PRs are automatically tested against a Netlify Preview deployment before they can be merged. -1. __Run Locally__: This gets the local version of the application up and running on your machine. Follow our [Local Setup Guide](https://github.com/expressjs/expressjs.com?tab=readme-ov-file#build-the-website-locally) to use this option. - - This is the recommended option for moderate to complex work. +### Prerequisites -2. __Run using Deploy Preview__: Use this option if you don't want to bother with a local installation. Part of our continuous integration pipeline includes [Netlify Deploy Preview](https://docs.netlify.com/deploy/deploy-types/deploy-previews/). - 1. To use this you'll need to get your changes online - after you've made your first commit on your feature branch, make a *draft* pull request. - 2. After the build steps are complete, you'll have access to a __Deploy Preview__ tab that will run your changes on the web, rebuilding after each commit is pushed. - 3. After you are completely done your work, and it's ready for review, remove the draft status on your pull request and submit your work. - -## Contributing translations +Before running E2E tests for the first time, you need to install the browser binaries: -We use Crowdin to manage our translations in multiple languages and achieve automatic translation with artificial intelligence. Since these translations can be inefficient in some cases, we need help from the community to provide accurate and helpful translations. +```bash +npx playwright install --with-deps +``` + +### Running Tests Locally + +You can run the full test suite against your local development server: + +1. In one terminal, start the site: `npm run dev` +2. In another terminal, run the tests: `npm run test:e2e` + +### Writing Stable Tests -The documentation is translated into these languages: +When adding new tests or modifying components, please follow these stability guidelines: + +1. **Avoid CSS Classes**: Do not use CSS classes (e.g., `.hero__content`) for locators, as they are fragile and change during refactoring. +2. **Use data-testid**: Add `data-testid` attributes to components for stable targeting (e.g., `
`). +3. **User-Visible Locators**: Prefer semantic locators like `getByRole`, `getByText`, or `getByAltText` over IDs when possible. + +Example: + +```typescript +// Good: Stable and accessible +const logo = page.getByAltText('Express.js logo'); +const section = page.getByTestId('features-section'); + +// Bad: Fragile +const logo = page.locator('.hero__logo'); +``` -- Chinese Simplified (`zh-cn`) -- Chinese Traditional (`zh-tw`) -- English (`en`) -- French (`fr`) -- German (`de`) -- Italian (`it`) -- Japanese (`ja`) -- Korean (`ko`) -- Brazilian Portuguese (`pt-br`) -- Spanish (`es`) +## Further Documentation -### How to translate +For more detailed documentation about the project, see the [`docs/`](docs/) folder: -1. Request to join the Express.js Website project on [Crowdin](https://express.crowdin.com/website) -2. [Select the language you want to translate](https://support.crowdin.com/for-translators/#starting-translation) -3. [Start translating](https://support.crowdin.com/online-editor/) +- [Project Structure](docs/project-structure.md) — Architecture, folder layout, and framework policy +- [Content](docs/content.md) — Collections, frontmatter, versioning, and global pages +- [Configuration](docs/configuration.md) — Navigation menus, announcement bar, and version-specific items +- [Internationalization](docs/i18n.md) — Translations, Crowdin integration, and adding languages +- [Design System](docs/design-system.md) — Components, tokens, colors, typography, and breakpoints diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 30849ade23..0000000000 --- a/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -# Match GitHub Pages Ruby version (see https://pages.github.com/versions) -FROM ruby:3.3.4@sha256:d4233f4242ea25346f157709bb8417c615e7478468e2699c8e86a4e1f0156de8 - -# Install Jekyll and Bundler -RUN gem install jekyll bundler - -# Set the working directory -WORKDIR /usr/src/app - -# Change the permissions of the working directory -RUN chmod 777 /usr/src/app - -# Copy the Gemfile into the image -COPY Gemfile ./ -COPY Gemfile.lock ./ - -# Install the gems -RUN bundle install --no-cache - -# Copy the rest of the project into the image -COPY . . - -# Expose the port Jekyll will run on -EXPOSE 4000 - -# The default command to run Jekyll -CMD ["jekyll", "serve", "--host", "0.0.0.0", "--livereload"] diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 05b6322344..0000000000 --- a/Gemfile +++ /dev/null @@ -1,5 +0,0 @@ -source 'https://rubygems.org' - -gem 'github-pages', group: :jekyll_plugins -gem 'webrick' -gem 'jekyll-github-metadata' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index fbc4706791..0000000000 --- a/Gemfile.lock +++ /dev/null @@ -1,282 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - activesupport (7.2.1) - base64 - bigdecimal - concurrent-ruby (~> 1.0, >= 1.3.1) - connection_pool (>= 2.2.5) - drb - i18n (>= 1.6, < 2) - logger (>= 1.4.2) - minitest (>= 5.1) - securerandom (>= 0.3) - tzinfo (~> 2.0, >= 2.0.5) - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) - base64 (0.2.0) - bigdecimal (3.1.8) - coffee-script (2.4.1) - coffee-script-source - execjs - coffee-script-source (1.12.2) - colorator (1.1.0) - commonmarker (0.23.10) - concurrent-ruby (1.3.4) - connection_pool (2.4.1) - csv (3.3.0) - dnsruby (1.72.2) - simpleidn (~> 0.2.1) - drb (2.2.1) - em-websocket (0.5.3) - eventmachine (>= 0.12.9) - http_parser.rb (~> 0) - ethon (0.16.0) - ffi (>= 1.15.0) - eventmachine (1.2.7) - execjs (2.9.1) - faraday (2.14.1) - faraday-net_http (>= 2.0, < 3.5) - json - logger - faraday-net_http (3.4.2) - net-http (~> 0.5) - ffi (1.17.0-x64-mingw-ucrt) - ffi (1.17.0-x86_64-linux-gnu) - forwardable-extended (2.6.0) - gemoji (4.1.0) - github-pages (232) - github-pages-health-check (= 1.18.2) - jekyll (= 3.10.0) - jekyll-avatar (= 0.8.0) - jekyll-coffeescript (= 1.2.2) - jekyll-commonmark-ghpages (= 0.5.1) - jekyll-default-layout (= 0.1.5) - jekyll-feed (= 0.17.0) - jekyll-gist (= 1.5.0) - jekyll-github-metadata (= 2.16.1) - jekyll-include-cache (= 0.2.1) - jekyll-mentions (= 1.6.0) - jekyll-optional-front-matter (= 0.3.2) - jekyll-paginate (= 1.1.0) - jekyll-readme-index (= 0.3.0) - jekyll-redirect-from (= 0.16.0) - jekyll-relative-links (= 0.6.1) - jekyll-remote-theme (= 0.4.3) - jekyll-sass-converter (= 1.5.2) - jekyll-seo-tag (= 2.8.0) - jekyll-sitemap (= 1.4.0) - jekyll-swiss (= 1.0.0) - jekyll-theme-architect (= 0.2.0) - jekyll-theme-cayman (= 0.2.0) - jekyll-theme-dinky (= 0.2.0) - jekyll-theme-hacker (= 0.2.0) - jekyll-theme-leap-day (= 0.2.0) - jekyll-theme-merlot (= 0.2.0) - jekyll-theme-midnight (= 0.2.0) - jekyll-theme-minimal (= 0.2.0) - jekyll-theme-modernist (= 0.2.0) - jekyll-theme-primer (= 0.6.0) - jekyll-theme-slate (= 0.2.0) - jekyll-theme-tactile (= 0.2.0) - jekyll-theme-time-machine (= 0.2.0) - jekyll-titles-from-headings (= 0.5.3) - jemoji (= 0.13.0) - kramdown (= 2.4.0) - kramdown-parser-gfm (= 1.1.0) - liquid (= 4.0.4) - mercenary (~> 0.3) - minima (= 2.5.1) - nokogiri (>= 1.16.2, < 2.0) - rouge (= 3.30.0) - terminal-table (~> 1.4) - webrick (~> 1.8) - github-pages-health-check (1.18.2) - addressable (~> 2.3) - dnsruby (~> 1.60) - octokit (>= 4, < 8) - public_suffix (>= 3.0, < 6.0) - typhoeus (~> 1.3) - html-pipeline (2.14.3) - activesupport (>= 2) - nokogiri (>= 1.4) - http_parser.rb (0.8.0) - i18n (1.14.6) - concurrent-ruby (~> 1.0) - jekyll (3.10.0) - addressable (~> 2.4) - colorator (~> 1.0) - csv (~> 3.0) - em-websocket (~> 0.5) - i18n (>= 0.7, < 2) - jekyll-sass-converter (~> 1.0) - jekyll-watch (~> 2.0) - kramdown (>= 1.17, < 3) - liquid (~> 4.0) - mercenary (~> 0.3.3) - pathutil (~> 0.9) - rouge (>= 1.7, < 4) - safe_yaml (~> 1.0) - webrick (>= 1.0) - jekyll-avatar (0.8.0) - jekyll (>= 3.0, < 5.0) - jekyll-coffeescript (1.2.2) - coffee-script (~> 2.2) - coffee-script-source (~> 1.12) - jekyll-commonmark (1.4.0) - commonmarker (~> 0.22) - jekyll-commonmark-ghpages (0.5.1) - commonmarker (>= 0.23.7, < 1.1.0) - jekyll (>= 3.9, < 4.0) - jekyll-commonmark (~> 1.4.0) - rouge (>= 2.0, < 5.0) - jekyll-default-layout (0.1.5) - jekyll (>= 3.0, < 5.0) - jekyll-feed (0.17.0) - jekyll (>= 3.7, < 5.0) - jekyll-gist (1.5.0) - octokit (~> 4.2) - jekyll-github-metadata (2.16.1) - jekyll (>= 3.4, < 5.0) - octokit (>= 4, < 7, != 4.4.0) - jekyll-include-cache (0.2.1) - jekyll (>= 3.7, < 5.0) - jekyll-mentions (1.6.0) - html-pipeline (~> 2.3) - jekyll (>= 3.7, < 5.0) - jekyll-optional-front-matter (0.3.2) - jekyll (>= 3.0, < 5.0) - jekyll-paginate (1.1.0) - jekyll-readme-index (0.3.0) - jekyll (>= 3.0, < 5.0) - jekyll-redirect-from (0.16.0) - jekyll (>= 3.3, < 5.0) - jekyll-relative-links (0.6.1) - jekyll (>= 3.3, < 5.0) - jekyll-remote-theme (0.4.3) - addressable (~> 2.0) - jekyll (>= 3.5, < 5.0) - jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) - rubyzip (>= 1.3.0, < 3.0) - jekyll-sass-converter (1.5.2) - sass (~> 3.4) - jekyll-seo-tag (2.8.0) - jekyll (>= 3.8, < 5.0) - jekyll-sitemap (1.4.0) - jekyll (>= 3.7, < 5.0) - jekyll-swiss (1.0.0) - jekyll-theme-architect (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-cayman (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-dinky (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-hacker (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-leap-day (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-merlot (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-midnight (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-minimal (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-modernist (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-primer (0.6.0) - jekyll (> 3.5, < 5.0) - jekyll-github-metadata (~> 2.9) - jekyll-seo-tag (~> 2.0) - jekyll-theme-slate (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-tactile (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-time-machine (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-titles-from-headings (0.5.3) - jekyll (>= 3.3, < 5.0) - jekyll-watch (2.2.1) - listen (~> 3.0) - jemoji (0.13.0) - gemoji (>= 3, < 5) - html-pipeline (~> 2.2) - jekyll (>= 3.0, < 5.0) - json (2.19.2) - kramdown (2.4.0) - rexml - kramdown-parser-gfm (1.1.0) - kramdown (~> 2.0) - liquid (4.0.4) - listen (3.9.0) - rb-fsevent (~> 0.10, >= 0.10.3) - rb-inotify (~> 0.9, >= 0.9.10) - logger (1.7.0) - mercenary (0.3.6) - minima (2.5.1) - jekyll (>= 3.5, < 5.0) - jekyll-feed (~> 0.9) - jekyll-seo-tag (~> 2.1) - minitest (5.25.1) - net-http (0.9.1) - uri (>= 0.11.1) - nokogiri (1.19.1-x64-mingw-ucrt) - racc (~> 1.4) - nokogiri (1.19.1-x86_64-linux-gnu) - racc (~> 1.4) - octokit (4.25.1) - faraday (>= 1, < 3) - sawyer (~> 0.9) - pathutil (0.16.2) - forwardable-extended (~> 2.6) - public_suffix (5.1.1) - racc (1.8.1) - rb-fsevent (0.11.2) - rb-inotify (0.11.1) - ffi (~> 1.0) - rexml (3.4.2) - rouge (3.30.0) - rubyzip (2.3.2) - safe_yaml (1.0.5) - sass (3.7.4) - sass-listen (~> 4.0.0) - sass-listen (4.0.0) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - sawyer (0.9.2) - addressable (>= 2.3.5) - faraday (>= 0.17.3, < 3) - securerandom (0.3.1) - simpleidn (0.2.3) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) - typhoeus (1.4.1) - ethon (>= 0.9.0) - tzinfo (2.0.6) - concurrent-ruby (~> 1.0) - unicode-display_width (1.8.0) - uri (1.1.1) - webrick (1.9.2) - -PLATFORMS - x64-mingw-ucrt - x86_64-linux - -DEPENDENCIES - github-pages - jekyll-github-metadata - webrick - -BUNDLED WITH - 2.5.16 \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index ff2008ed7e..0000000000 --- a/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -GREEN := \033[1;32m -BLUE := \033[1;34m -RESET := \033[0m - -# The directory of this file -DIR := $(shell echo $(shell cd "$(shell dirname "${BASH_SOURCE[0]}" )" && pwd )) - -# This will output the help for each task -# thanks to https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html -.PHONY: help - -help: ## This help - @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "${GREEN}%-30s${RESET} %s\n", $$1, $$2}' $(MAKEFILE_LIST) - -.DEFAULT_GOAL := help - -serve: ## Local server - @echo "${BLUE}Starting expressjs.com at http://localhost:4000${RESET}" - docker run -p 4000:4000 -p 35729:35729 -v $(DIR):/usr/src/app expressjs.com bundle exec jekyll serve --host 0.0.0.0 - -build: ## Build site - @echo "${BLUE}Building site...${RESET}" - docker build -t expressjs.com . - -clean: ## Clean up - @echo "${BLUE}Clean up...${RESET}" - docker rmi expressjs.com diff --git a/README.md b/README.md index a483423508..73fdf5253b 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,42 @@ -# expressjs.com +

+ + + + Express.js + + +

-This is the repository of the website [expressjs.com](https://expressjs.com). It is hosted directly from the repository as a [GitHub Pages](https://pages.github.com/) website. +

+ The official website and documentation for Express.js, the fast, unopinionated, minimalist web framework for Node.js. +

-## Contributing - -Any person who wants to contribute to the Website is welcome! Please read [Contributors' Guide](CONTRIBUTING.md) for more information on contributing to the Express.js documentation. - -## Translations - -If you're interested in contributing to the site's translations, you can find more information [here](https://github.com/expressjs/expressjs.com/blob/gh-pages/CONTRIBUTING.md#contributing-translations). - -## Build the website locally - ->[!NOTE] ->If you're only making changes to the content, you most likely won't need to run the site locally. - -To preview the website locally, we have two options: using Docker or using Bundler. +

+ Website · + Report Issue · + Contributing Guide +

-### Using Docker +--- ->[!TIP] -> You can run `make help` to obtain detailed information on how to use our make commands. +## Getting started -1. Ensure that you have Docker and Make installed. -1. Run `make build` to build the project. -1. Run `make serve` to serve the project, this include live reloading so any change will be reflected (it can take a while, check the logs). -1. Run `make clean` to remove the docker images and resources generated. +The site is built with [Astro](https://astro.build), TypeScript, and MDX. -### Using Bundle +**Prerequisites:** Node.js >= 24.13.0 and npm >= 11.0.0 -1. Install [Ruby and Bundler](https://help.github.com/articles/setting-up-your-pages-site-locally-with-jekyll/) if you don't have them already. +```bash +npm install +npm run dev +``` -1. Install the [jekyll-redirect-from](https://github.com/jekyll/jekyll-redirect-from) gem: +The development server listens at `http://localhost:4321`. - ```sh - $ gem install jekyll-redirect-from - ``` - -1. Clone this repository by running the following command: - - ```sh - $ git clone https://github.com/expressjs/expressjs.com.git - ``` - -1. Navigate to the cloned repository directory and run the following command: - - ```sh - $ bundle install - ``` - - Bundler will look in the Gemfile for which gems to install. The `github-pages` gem includes the same version of Jekyll and other dependencies as used by GitHub Pages, so that your local setup mirrors GitHub Pages as closely as possible. - -1. Run Jekyll using the following command: +## Contributing - ```sh - $ bundle exec jekyll serve - ``` +This project welcomes contributors of all experience levels. Whether it's fixing a typo, improving documentation, translating content, or enhancing the website, every contribution matters. - Then, load in your browser. +Please read the [Contributing Guide](CONTRIBUTING.md) and the [Express Collaborator Guide](https://github.com/expressjs/.github/blob/master/CONTRIBUTING.md) before opening a pull request. ## License diff --git a/_config.yml b/_config.yml deleted file mode 100644 index 5abcc77f71..0000000000 --- a/_config.yml +++ /dev/null @@ -1,110 +0,0 @@ -url: "https://expressjs.com" -baseurl: "/" - -repository: expressjs/expressjs.com - -# Site settings - -defaults: - - - scope: - path: "_posts" - type: "posts" - values: - layout: "post" - menu: blog - lang: en - - - scope: - path: "en" - values: - lang: en - - - scope: - path: "index.md" - values: - lang: en - - - scope: - path: "de" - values: - lang: de - - - scope: - path: "es" - values: - lang: es - - - scope: - path: "fr" - values: - lang: fr - - - scope: - path: "it" - values: - lang: it - - - scope: - path: "ja" - values: - lang: ja - - - scope: - path: "ko" - values: - lang: ko - - - scope: - path: "pt-br" - values: - lang: "pt-br" - - - scope: - path: "zh-cn" - values: - lang: "zh-cn" - - - scope: - path: "zh-tw" - values: - lang: "zh-tw" - -announcement: true # Enable or disable the announcements. - -collections: - starter: - output: true - sort_by: order - guide: - output: true - sort_by: order - advanced: - output: true - sort_by: order - resources: - output: true - sort_by: order - -# Build settings - -plugins: - - jekyll-redirect-from - - jekyll-github-metadata - -highlighter: rouge -# library used for syntax highlighting - -markdown: kramdown -kramdown: - math_engine: mathjax - input: GFM - hard_wrap: false - syntax_highlighter: rouge - -exclude: - - [uk/CHANGELOG.md] - - vendor/bundle - - node_modules/ - -include: [".well-known"] \ No newline at end of file diff --git a/_data/de/footer.yml b/_data/de/footer.yml deleted file mode 100644 index f4e60d8a38..0000000000 --- a/_data/de/footer.yml +++ /dev/null @@ -1,7 +0,0 @@ -terms_of_use: Nutzungsbedingungen -privacy_policy: Datenschutzerklärung -coc: Verhaltenskodex -trademark_policy: Trademark Policy -security_policy: Security Policy -license: Lizenzvertrag -trademark_legal: Copyright OpenJS Foundation and Express contributors. All rights reserved. The OpenJS Foundation has registered trademarks and uses trademarks. For a list of trademarks of the OpenJS Foundation, please see our Trademark Policy and Trademark List. Trademarks and logos not indicated on the list of OpenJS Foundation trademarks are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them. diff --git a/_data/de/general.yml b/_data/de/general.yml deleted file mode 100644 index 16bd945615..0000000000 --- a/_data/de/general.yml +++ /dev/null @@ -1,8 +0,0 @@ -title_announcement: "Express@5.1.0: Now the Default on npm with LTS Timeline" -body_announcement: "Express 5.1.0 is now the default on npm, and we're introducing an official LTS schedule for the v4 and v5 release lines. Check out our latest blog for more information." -community-caveat-alert: "This information refers to third-party sites, products, or modules that are not maintained by the Expressjs team. Listing here does not constitute an endorsement or recommendation from the Expressjs project team." -warning: 'Warnung' -note: 'Hinweis' -caution: 'Vorsicht' -i18n_notice: "This document might be outdated relative to the documentation in English. For the latest updates, please refer to the" -i18n_notice_link_text: "documentation in english" diff --git a/_data/de/menu.yml b/_data/de/menu.yml deleted file mode 100644 index 8d331e5ea0..0000000000 --- a/_data/de/menu.yml +++ /dev/null @@ -1,50 +0,0 @@ -# Getting started -getting_started: Einführung -installing: Installation -hello_world: Hello world -generator: Express generator -basic_routing: Basisrouting -static_files: Statische Dateien -examples: Weitere Beispiele -faq: Häufig gestellte Fragen -# Guide -guide: Leitfaden -routing: Weiterleitung (Routing) -writing_middleware: Middleware schreiben -using_middleware: Middleware verwenden -overriding_express_api: Overriding the Express API -using_template_engines: Template-Engines verwenden -error_handling: Fehlerbehandlung -debugging: Debugging -behind_proxies: Express hinter Proxys -migrating_4: Wechsel zu Express 4 -migrating_5: Wechsel zu Express 5 -database_integration: Datenbankintegration -# API reference -api: API-Referenz -5x: 5.x -4x: 4.x -3x: 3.x (veraltet) -2x: 2.x (veraltet) -# Advanced topics -advanced: Themen für Fortgeschrittene -developing_template_engines: Template-Engines -security_updates: Sicherheitsupdates -best_practice_security: Sicherheitsspezifische Best Practices -best_practice_performance: Leistungsspezifische Best Practices -healthcheck_graceful_shutdown: Health checks & shutdown -# Resources -resources: Ressourcen -glossary: Glossar -middleware: Middleware -community: Community -utils: Utility modules -contributing: Contributing to Express -changelog: Release Change Log -# Support -support: Support -# Blog -blog: Blog -latest_post: Latest post -all_posts: All posts -write_post: Write a Post diff --git a/_data/docsearch.yml b/_data/docsearch.yml deleted file mode 100644 index e60470dcaa..0000000000 --- a/_data/docsearch.yml +++ /dev/null @@ -1,3 +0,0 @@ -# config: https://github.com/algolia/docsearch-configs/blob/master/configs/expressjs.json -apiKey: 7164e33055faa6ecddefd9e08fc59f5d -indexName: expressjs diff --git a/_data/en/footer.yml b/_data/en/footer.yml deleted file mode 100644 index c68d10357e..0000000000 --- a/_data/en/footer.yml +++ /dev/null @@ -1,7 +0,0 @@ -terms_of_use: Terms of Use -privacy_policy: Privacy Policy -coc: Code of Conduct -trademark_policy: Trademark Policy -security_policy: Security Policy -license: License -trademark_legal: Copyright OpenJS Foundation and Express contributors. All rights reserved. The OpenJS Foundation has registered trademarks and uses trademarks. For a list of trademarks of the OpenJS Foundation, please see our Trademark Policy and Trademark List. Trademarks and logos not indicated on the list of OpenJS Foundation trademarks are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them. diff --git a/_data/en/general.yml b/_data/en/general.yml deleted file mode 100644 index aab9e75f2a..0000000000 --- a/_data/en/general.yml +++ /dev/null @@ -1,12 +0,0 @@ -title_announcement: "Express@5.1.0: Now the Default on npm with LTS Timeline" -body_announcement: "Express 5.1.0 is now the default on npm, and we're introducing an official LTS schedule for the v4 and v5 release lines. Check out our latest blog for more information." - -community-caveat-alert: "This information refers to third-party sites, products, or modules that are not maintained by the Expressjs team. Listing here does not constitute an endorsement or recommendation from the Expressjs project team." - -warning: 'Warning' -note: 'Note' -caution: 'Caution' - -i18n_notice: "This document might be outdated relative to the documentation in English. For the latest updates, please refer to the" -i18n_notice_link_text: "documentation in english" - \ No newline at end of file diff --git a/_data/en/menu.yml b/_data/en/menu.yml deleted file mode 100644 index de6f851575..0000000000 --- a/_data/en/menu.yml +++ /dev/null @@ -1,57 +0,0 @@ -# Getting started -getting_started: Getting started -installing: Installing -hello_world: Hello world -generator: Express generator -basic_routing: Basic routing -static_files: Static files -examples: More examples -faq: FAQ - -# Guide -guide: Guide -routing: Routing -writing_middleware: Writing middleware -using_middleware: Using middleware -overriding_express_api: Overriding the Express API -using_template_engines: Using template engines -error_handling: Error handling -debugging: Debugging -behind_proxies: Express behind proxies -migrating_4: Moving to Express 4 -migrating_5: Moving to Express 5 -database_integration: Database integration - -# API reference - -api: API reference -5x: 5.x -4x: 4.x -3x: 3.x (deprecated) -2x: 2.x (deprecated) - -# Advanced topics -advanced: Advanced topics -developing_template_engines: Building template engines -security_updates: Security updates -best_practice_security: Security best practices -best_practice_performance: Performance best practices -healthcheck_graceful_shutdown: Health checks & shutdown - -# Resources -resources: Resources -glossary: Glossary -middleware: Middleware -community: Community -utils: Utility modules -contributing: Contributing to Express -changelog: Release Change Log - -# Support -support: Support - -# Blog -blog: Blog -latest_post: Latest post -all_posts: All posts -write_post: Write a Post diff --git a/_data/es/footer.yml b/_data/es/footer.yml deleted file mode 100644 index dd8ecfdd76..0000000000 --- a/_data/es/footer.yml +++ /dev/null @@ -1,7 +0,0 @@ -terms_of_use: Condiciones de uso -privacy_policy: Política de privacidad -coc: Código de conducta -trademark_policy: Política de marcas registradas -security_policy: Política de seguridad -license: Licencia -trademark_legal: Copyright OpenJS Foundation and Express contributors. All rights reserved. The OpenJS Foundation has registered trademarks and uses trademarks. For a list of trademarks of the OpenJS Foundation, please see our Trademark Policy and Trademark List. Trademarks and logos not indicated on the list of OpenJS Foundation trademarks are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them. diff --git a/_data/es/general.yml b/_data/es/general.yml deleted file mode 100644 index 73f03efc21..0000000000 --- a/_data/es/general.yml +++ /dev/null @@ -1,8 +0,0 @@ -title_announcement: "Express@5.1.0: Ahora la versión por defecto en npm con cronograma LTS" -body_announcement: "Express 5.1.0 es ahora es la versión predeterminada en npm, y estamos introduciendo un cronograma oficial de LTS para las líneas de lanzamiento v4 y v5. Mira nuestro último blog para más información." -community-caveat-alert: "Esta información se refiere a sitios, productos o módulos de terceros que no son mantenidos por el equipo de Expressjs. La lista aquí no constituye un respaldo o una recomendación del equipo del proyecto Expressjs." -warning: 'Advertencia' -note: 'Nota' -caution: 'Precaución' -i18n_notice: "Este documento puede estar desactualizado en relación con la documentación en inglés. Para las últimas actualizaciones, por favor consulte la" -i18n_notice_link_text: "documentación en inglés" diff --git a/_data/es/menu.yml b/_data/es/menu.yml deleted file mode 100644 index ab87295269..0000000000 --- a/_data/es/menu.yml +++ /dev/null @@ -1,50 +0,0 @@ -# Getting started -getting_started: Cómo empezar -installing: Instalando -hello_world: Hola mundo -generator: Generador express -basic_routing: Direccionamiento básico -static_files: Archivos estáticos -examples: Más ejemplos -faq: FAQ -# Guide -guide: Guía -routing: Direccionamiento -writing_middleware: Escritura de middleware -using_middleware: Utilización del middleware -overriding_express_api: Overriding the Express API -using_template_engines: Utilización de motores de plantilla -error_handling: Manejo de errores -debugging: Depuración -behind_proxies: Express detrás de proxies -migrating_4: Migración a Express 4 -migrating_5: Migración a Express 5 -database_integration: Integración de la base de datos -# API reference -api: Referencia de API -5x: 5.x -4x: 4.x -3x: 3.x (obsoleto) -2x: 2.x (obsoleto) -# Advanced topics -advanced: Temas avanzados -developing_template_engines: Motores de plantilla -security_updates: Actualizaciones de seguridad -best_practice_security: Mejores prácticas de seguridad -best_practice_performance: Mejores prácticas de rendimiento -healthcheck_graceful_shutdown: Health checks & shutdown -# Resources -resources: Recursos -glossary: Glosario -middleware: Middleware -community: Comunidad -utils: Utility modules -contributing: Contribuir a Express -changelog: Release Change Log -# Support -support: Soporte -# Blog -blog: Blog -latest_post: Latest post -all_posts: All posts -write_post: Escribir un post diff --git a/_data/express.yml b/_data/express.yml deleted file mode 100644 index 0a6a4d6c0b..0000000000 --- a/_data/express.yml +++ /dev/null @@ -1,2 +0,0 @@ -current_version: "5.2.1" -next_version: "undefined" \ No newline at end of file diff --git a/_data/fr/footer.yml b/_data/fr/footer.yml deleted file mode 100644 index 6b2eb659c0..0000000000 --- a/_data/fr/footer.yml +++ /dev/null @@ -1,7 +0,0 @@ -terms_of_use: Conditions d’utilisation -privacy_policy: Politique de Confidentialité -coc: Code de Conduite -trademark_policy: Politique de Marque -security_policy: Politique de Sécurité -license: Licence -trademark_legal: Copyright OpenJS Foundation and Express contributors. All rights reserved. The OpenJS Foundation has registered trademarks and uses trademarks. For a list of trademarks of the OpenJS Foundation, please see our Trademark Policy and Trademark List. Trademarks and logos not indicated on the list of OpenJS Foundation trademarks are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them. diff --git a/_data/fr/general.yml b/_data/fr/general.yml deleted file mode 100644 index e6327e6214..0000000000 --- a/_data/fr/general.yml +++ /dev/null @@ -1,8 +0,0 @@ -title_announcement: "Express@5.1.0: Now the Default on npm with LTS Timeline" -body_announcement: "Express 5.1.0 is now the default on npm, and we're introducing an official LTS schedule for the v4 and v5 release lines. Check out our latest blog for more information." -community-caveat-alert: "This information refers to third-party sites, products, or modules that are not maintained by the Expressjs team. Listing here does not constitute an endorsement or recommendation from the Expressjs project team." -warning: 'Warning' -note: 'Note' -caution: 'Caution' -i18n_notice: "This document might be outdated relative to the documentation in English. For the latest updates, please refer to the" -i18n_notice_link_text: "documentation in english" diff --git a/_data/fr/menu.yml b/_data/fr/menu.yml deleted file mode 100644 index 04c8944bcb..0000000000 --- a/_data/fr/menu.yml +++ /dev/null @@ -1,50 +0,0 @@ -# Getting started -getting_started: Mise en route -installing: Installation -hello_world: Hello world -generator: Générateur Express -basic_routing: Routage de base -static_files: Fichiers statiques -examples: More examples -faq: FAQ -# Guide -guide: Guide -routing: Routage -writing_middleware: Ecriture de middleware -using_middleware: Utilisation de middleware -overriding_express_api: Overriding the Express API -using_template_engines: Utilisation de moteurs de modèles -error_handling: Traitement d'erreurs -debugging: Débogage -behind_proxies: Express derrière Proxys -migrating_4: Migration vers Express 4 -migrating_5: Migration vers Express 5 -database_integration: Intégration de bases de données -# API reference -api: API reference -5x: 5.x -4x: 4.x -3x: 3.x (obsolète) -2x: 2.x (obsolète) -# Advanced topics -advanced: Rubriques avancées -developing_template_engines: Moteurs de modèles -security_updates: Mises à jour de sécurité -best_practice_security: Meilleures pratiques en termes de sécurité -best_practice_performance: Meilleures pratiques en termes de performances -healthcheck_graceful_shutdown: Health checks & shutdown -# Resources -resources: Ressources -glossary: Glossaire -middleware: Middleware -community: Communauté -utils: Utility modules -contributing: Contributing to Express -changelog: Release Change Log -# Support -support: Support -# Blog -blog: Blog -latest_post: Latest post -all_posts: All posts -write_post: Write a Post diff --git a/_data/it/footer.yml b/_data/it/footer.yml deleted file mode 100644 index c68d10357e..0000000000 --- a/_data/it/footer.yml +++ /dev/null @@ -1,7 +0,0 @@ -terms_of_use: Terms of Use -privacy_policy: Privacy Policy -coc: Code of Conduct -trademark_policy: Trademark Policy -security_policy: Security Policy -license: License -trademark_legal: Copyright OpenJS Foundation and Express contributors. All rights reserved. The OpenJS Foundation has registered trademarks and uses trademarks. For a list of trademarks of the OpenJS Foundation, please see our Trademark Policy and Trademark List. Trademarks and logos not indicated on the list of OpenJS Foundation trademarks are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them. diff --git a/_data/it/general.yml b/_data/it/general.yml deleted file mode 100644 index e6327e6214..0000000000 --- a/_data/it/general.yml +++ /dev/null @@ -1,8 +0,0 @@ -title_announcement: "Express@5.1.0: Now the Default on npm with LTS Timeline" -body_announcement: "Express 5.1.0 is now the default on npm, and we're introducing an official LTS schedule for the v4 and v5 release lines. Check out our latest blog for more information." -community-caveat-alert: "This information refers to third-party sites, products, or modules that are not maintained by the Expressjs team. Listing here does not constitute an endorsement or recommendation from the Expressjs project team." -warning: 'Warning' -note: 'Note' -caution: 'Caution' -i18n_notice: "This document might be outdated relative to the documentation in English. For the latest updates, please refer to the" -i18n_notice_link_text: "documentation in english" diff --git a/_data/it/menu.yml b/_data/it/menu.yml deleted file mode 100644 index a8243191f0..0000000000 --- a/_data/it/menu.yml +++ /dev/null @@ -1,50 +0,0 @@ -# Getting started -getting_started: Introduzione -installing: Installazione -hello_world: Hello world -generator: Programma di creazione Express -basic_routing: Routing di base -static_files: File statici -examples: More examples -faq: FAQ -# Guide -guide: Guide -routing: Routing -writing_middleware: Scrittura del middleware -using_middleware: Utilizzo del middleware -overriding_express_api: Overriding the Express API -using_template_engines: Utilizzo dei motori di template -error_handling: Gestione degli errori -debugging: Debugging -behind_proxies: Express con i proxy -migrating_4: Passaggio a Express 4 -migrating_5: Passaggio a Express 5 -database_integration: Integrazione database -# API reference -api: Riferimento API -5x: 5.x -4x: 4.x -3x: 3.x (deprecato) -2x: 2.x (deprecato) -# Advanced topics -advanced: Argomenti avanzati -developing_template_engines: Motori di template -security_updates: Aggiornamenti sulla sicurezza -best_practice_security: Best practice sulla sicurezza -best_practice_performance: Best practice sulle prestazioni -healthcheck_graceful_shutdown: Health checks & shutdown -# Resources -resources: Risorse -glossary: Glossario -middleware: Middleware -community: Community -utils: Utility modules -contributing: Contributing to Express -changelog: Release Change Log -# Support -support: Support -# Blog -blog: Blog -latest_post: Latest post -all_posts: All posts -write_post: Write a Post diff --git a/_data/ja/footer.yml b/_data/ja/footer.yml deleted file mode 100644 index c68d10357e..0000000000 --- a/_data/ja/footer.yml +++ /dev/null @@ -1,7 +0,0 @@ -terms_of_use: Terms of Use -privacy_policy: Privacy Policy -coc: Code of Conduct -trademark_policy: Trademark Policy -security_policy: Security Policy -license: License -trademark_legal: Copyright OpenJS Foundation and Express contributors. All rights reserved. The OpenJS Foundation has registered trademarks and uses trademarks. For a list of trademarks of the OpenJS Foundation, please see our Trademark Policy and Trademark List. Trademarks and logos not indicated on the list of OpenJS Foundation trademarks are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them. diff --git a/_data/ja/general.yml b/_data/ja/general.yml deleted file mode 100644 index 41323937a4..0000000000 --- a/_data/ja/general.yml +++ /dev/null @@ -1,8 +0,0 @@ -title_announcement: "Express@5.1.0: LTSタイムラインでnpmのデフォルトになりました" -body_announcement: "Express 5.1.0 が npm のデフォルトとなり、v4 および v5 リリース ラインの公式 LTS スケジュールが導入されます。詳細については、最新のブログをご覧ください。" -community-caveat-alert: "This information refers to third-party sites, products, or modules that are not maintained by the Expressjs team. Listing here does not constitute an endorsement or recommendation from the Expressjs project team." -warning: 'Warning' -note: 'Note' -caution: 'Caution' -i18n_notice: "このドキュメントは英語のドキュメントに比べて古くなっている可能性があります。最新情報については、" -i18n_notice_link_text: "英語のドキュメントを参照してください" diff --git a/_data/ja/menu.yml b/_data/ja/menu.yml deleted file mode 100644 index 6fe5206dd6..0000000000 --- a/_data/ja/menu.yml +++ /dev/null @@ -1,50 +0,0 @@ -# Getting started -getting_started: 概説 -installing: インストール -hello_world: Hello world -generator: Express ジェネレーター -basic_routing: 基本的なルーティング -static_files: 静的ファイル -examples: More examples -faq: FAQ -# Guide -guide: ガイド -routing: ルーティング -writing_middleware: ミドルウェアの作成 -using_middleware: ミドルウェアの使用 -overriding_express_api: Overriding the Express API -using_template_engines: テンプレート・エンジンの使用 -error_handling: エラー処理 -debugging: デバッグ -behind_proxies: プロキシーの背後の Express -migrating_4: Express 4 への移行 -migrating_5: Express 5 への移行 -database_integration: データベースの統合 -# API reference -api: API リファレンス -5x: 5.x -4x: 4.x -3x: 3.x (非推奨) -2x: 2.x (非推奨) -# Advanced topics -advanced: 高度なトピック -developing_template_engines: テンプレート・エンジン -security_updates: セキュリティー更新 -best_practice_security: セキュリティーに関するベスト・プラクティス -best_practice_performance: パフォーマンスに関するベスト・プラクティス -healthcheck_graceful_shutdown: Health checks & shutdown -# Resources -resources: リソース -glossary: 用語集 -middleware: ミドルウェア -community: コミュニティー -utils: Utility modules -contributing: Contributing to Express -changelog: Release Change Log -# Support -support: Support -# Blog -blog: Blog -latest_post: Latest post -all_posts: All posts -write_post: Write a Post diff --git a/_data/ko/footer.yml b/_data/ko/footer.yml deleted file mode 100644 index 93c6b42142..0000000000 --- a/_data/ko/footer.yml +++ /dev/null @@ -1,7 +0,0 @@ -terms_of_use: 이용 약관 -privacy_policy: 개인정보보호정책 -coc: 운영 규정 -trademark_policy: 상표 정책 -security_policy: 보안 정책 -license: 라이선스 -trademark_legal: Copyright OpenJS Foundation and Express contributors. All rights reserved. The OpenJS Foundation has registered trademarks and uses trademarks. For a list of trademarks of the OpenJS Foundation, please see our Trademark Policy and Trademark List. Trademarks and logos not indicated on the list of OpenJS Foundation trademarks are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them. diff --git a/_data/ko/general.yml b/_data/ko/general.yml deleted file mode 100644 index a1fd55a3c7..0000000000 --- a/_data/ko/general.yml +++ /dev/null @@ -1,8 +0,0 @@ -title_announcement: "Express@5.1.0: npm에서 기본 버전으로 채택, LTS 일정 함께 도입됨" -body_announcement: "이제 Express 5.1.0이 npm에서 기본 버전이 되었으며, v4와 v5 릴리스 라인에 대해 공식적인 LTS 일정도 도입했습니다.\n자세한 내용은 최신 블로그를 확인해보세요." -community-caveat-alert: "이 정보는 Expressjs 팀에서 관리하지 않는 서드파티 사이트, 제품 또는 모듈에 관한 것입니다. 여기에 나열된다고 해서 Expressjs 프로젝트 팀이 이를 보증하거나 추천한다는 의미는 아닙니다." -warning: '경고' -note: '참고' -caution: '주의' -i18n_notice: "이 문서는 영어 문서에 비해 최신 정보가 아닐 수 있습니다. 최신 내용을 확인하려면 아래의 영어 문서를 참고해 주시기 바랍니다" -i18n_notice_link_text: "영어로 된 문서" diff --git a/_data/ko/menu.yml b/_data/ko/menu.yml deleted file mode 100644 index c5469f8c10..0000000000 --- a/_data/ko/menu.yml +++ /dev/null @@ -1,50 +0,0 @@ -# Getting started -getting_started: 시작하기 -installing: 설치 -hello_world: Hello world -generator: Express 생성기 -basic_routing: 기본 라우팅 -static_files: 정적 파일 -examples: 더 많은 예시 -faq: 자주 묻는 질문(FAQ) -# Guide -guide: 안내서 -routing: 라우팅 -writing_middleware: 미들웨어 작성 -using_middleware: 미들웨어 사용 -overriding_express_api: Express API 오버라이딩 -using_template_engines: 템플리트 엔진 사용 -error_handling: 오류 처리 -debugging: 디버깅 -behind_proxies: 프록시 환경에서 Express 사용 -migrating_4: Express 4로의 이전 -migrating_5: Express 5로의 이전 -database_integration: 데이터베이스 통합 -# API reference -api: API 참조 -5x: 5.x -4x: 4.x -3x: 3.x(더 이상 사용되지 않음) -2x: 2.x(더 이상 사용되지 않음) -# Advanced topics -advanced: 고급 주제 -developing_template_engines: 템플리트 엔진 -security_updates: 보안 업데이트 -best_practice_security: 보안 우수 사례 -best_practice_performance: 성능 우수 사례 -healthcheck_graceful_shutdown: Health checks & shutdown -# Resources -resources: 자원 -glossary: 용어집 -middleware: 미들웨어 -community: 커뮤니티 -utils: 유틸리티 모듈 -contributing: Express에 기여하기 -changelog: 릴리즈 변경 로그 -# Support -support: 지원 -# Blog -blog: 블로그 -latest_post: 최신 게시물 -all_posts: 모든 게시물 -write_post: 게시물 작성 diff --git a/_data/languages.yml b/_data/languages.yml deleted file mode 100644 index 8d3055553f..0000000000 --- a/_data/languages.yml +++ /dev/null @@ -1,20 +0,0 @@ -- code: en - name: English -- code: fr - name: Français -- code: de - name: Deutsch -- code: es - name: Español -- code: it - name: Italiano -- code: ja - name: 日本語 -- code: zh-cn - name: 中文 (简体) -- code: zh-tw - name: 繁體中文 -- code: ko - name: 한국어 -- code: pt-br - name: Português \ No newline at end of file diff --git a/_data/pt-br/footer.yml b/_data/pt-br/footer.yml deleted file mode 100644 index ac75f6f544..0000000000 --- a/_data/pt-br/footer.yml +++ /dev/null @@ -1,7 +0,0 @@ -terms_of_use: Termos de Uso -privacy_policy: Política de Privacidade -coc: Código de Conduta -trademark_policy: Política de Marcas -security_policy: Política de Segurança -license: Licença -trademark_legal: Copyright OpenJS Foundation e Express contributors. Todos os direitos reservados. A OpenJS Foundation possui marcas registradas e utiliza marcas registradas. Para obter uma lista de marcas registradas da Fundação OpenJS, por favor veja nossa Politica de Marca e Lista de Marcas. Marcas e logotipos não indicados na lista de marcas registradas da OpenJS Foundation são marcas registradas ou registradas de seus respectivos titulares. O uso deles não implica em nenhuma afiliação ou endosso por eles. diff --git a/_data/pt-br/general.yml b/_data/pt-br/general.yml deleted file mode 100644 index 3544c6811b..0000000000 --- a/_data/pt-br/general.yml +++ /dev/null @@ -1,8 +0,0 @@ -title_announcement: "Express@5.1.0: Agora o Padrão no npm com LTS Timeline" -body_announcement: "Expresse 5.1.0 agora é o padrão do npm, e estamos introduzindo um cronograma oficial de LTS para as linhas de lançamento v4 e v5. Confira nosso último blog para obter mais informações." -community-caveat-alert: "Esta informação refere-se a sites de terceiros, produtos ou módulos que não são mantidos pela equipe do Expressjs. A listagem aqui não constitui um endosso ou recomendação da equipe de projeto Expressjs." -warning: 'Atenção' -note: 'Observação' -caution: 'Atenção' -i18n_notice: "Este documento pode estar desatualizado em relação à documentação em inglês. Para as últimas atualizações, por favor consulte o" -i18n_notice_link_text: "documentação em inglês" diff --git a/_data/pt-br/menu.yml b/_data/pt-br/menu.yml deleted file mode 100644 index 4b533b9206..0000000000 --- a/_data/pt-br/menu.yml +++ /dev/null @@ -1,50 +0,0 @@ -# Getting started -getting_started: Primeiros passos -installing: Instalação -hello_world: Olá mundo -generator: Gerador do Express -basic_routing: Roteamento Básico -static_files: Arquivos Estáticos -examples: Mais exemplos -faq: Perguntas mais frequentes -# Guide -guide: Guia -routing: Roteamento -writing_middleware: Escrevendo o middleware -using_middleware: Usando o middleware -overriding_express_api: Sobrescrevendo a API Express -using_template_engines: Usando template engines -error_handling: Manipulação de erros -debugging: Depuração -behind_proxies: Express por trás dos proxies -migrating_4: Migrando para o Express 4 -migrating_5: Migrando para o Express 5 -database_integration: Integração com base de dados -# API reference -api: Referência da API -5x: 5.x -4x: 4.x -3x: 3.x (descontinuada) -2x: 2.x (descontinuada) -# Advanced topics -advanced: Tópicos Avançados -developing_template_engines: Criando template engines -security_updates: Atualizações de segurança -best_practice_security: Melhores práticas de segurança -best_practice_performance: Melhores práticas de desempenho -healthcheck_graceful_shutdown: Verificações de saúde e desligamento -# Resources -resources: Recursos -glossary: Glossário -middleware: Middleware -community: Comunidade -utils: Módulos utilitários -contributing: Contribuindo para o Express -changelog: Registros de alterações -# Support -support: Suporte -# Blog -blog: Blog -latest_post: Ultimos post -all_posts: Todos posts -write_post: Escreva um Post diff --git a/_data/zh-cn/footer.yml b/_data/zh-cn/footer.yml deleted file mode 100644 index c68d10357e..0000000000 --- a/_data/zh-cn/footer.yml +++ /dev/null @@ -1,7 +0,0 @@ -terms_of_use: Terms of Use -privacy_policy: Privacy Policy -coc: Code of Conduct -trademark_policy: Trademark Policy -security_policy: Security Policy -license: License -trademark_legal: Copyright OpenJS Foundation and Express contributors. All rights reserved. The OpenJS Foundation has registered trademarks and uses trademarks. For a list of trademarks of the OpenJS Foundation, please see our Trademark Policy and Trademark List. Trademarks and logos not indicated on the list of OpenJS Foundation trademarks are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them. diff --git a/_data/zh-cn/general.yml b/_data/zh-cn/general.yml deleted file mode 100644 index e6327e6214..0000000000 --- a/_data/zh-cn/general.yml +++ /dev/null @@ -1,8 +0,0 @@ -title_announcement: "Express@5.1.0: Now the Default on npm with LTS Timeline" -body_announcement: "Express 5.1.0 is now the default on npm, and we're introducing an official LTS schedule for the v4 and v5 release lines. Check out our latest blog for more information." -community-caveat-alert: "This information refers to third-party sites, products, or modules that are not maintained by the Expressjs team. Listing here does not constitute an endorsement or recommendation from the Expressjs project team." -warning: 'Warning' -note: 'Note' -caution: 'Caution' -i18n_notice: "This document might be outdated relative to the documentation in English. For the latest updates, please refer to the" -i18n_notice_link_text: "documentation in english" diff --git a/_data/zh-cn/menu.yml b/_data/zh-cn/menu.yml deleted file mode 100644 index 2f93d3585c..0000000000 --- a/_data/zh-cn/menu.yml +++ /dev/null @@ -1,50 +0,0 @@ -# Getting started -getting_started: 入门 -installing: 安装 -hello_world: Hello world -generator: Express 生成器 -basic_routing: 基本路由 -static_files: 静态文件 -examples: More examples -faq: 常见问题及解答 -# Guide -guide: 指南 -routing: 路由 -writing_middleware: 编写中间件 -using_middleware: 使用中间件 -overriding_express_api: Overriding the Express API -using_template_engines: 使用模板引擎 -error_handling: 错误处理 -debugging: 调试 -behind_proxies: 代理背后的 Express -migrating_4: 迁移到 Express 4 -migrating_5: 迁移到 Express 5 -database_integration: 数据库集成 -# API reference -api: API 参考 -5x: 5.x -4x: 4.x -3x: 3.x (不推荐) -2x: 2.x (不推荐) -# Advanced topics -advanced: 高级主题 -developing_template_engines: 模板引擎 -security_updates: 安全更新 -best_practice_security: 安全最佳实践 -best_practice_performance: 性能最佳实践 -healthcheck_graceful_shutdown: Health checks & shutdown -# Resources -resources: 资源 -glossary: 词汇表 -middleware: 中间件 -community: 社区 -utils: Utility modules -contributing: Contributing to Express -changelog: Release Change Log -# Support -support: Support -# Blog -blog: Blog -latest_post: Latest post -all_posts: All posts -write_post: Write a Post diff --git a/_data/zh-tw/footer.yml b/_data/zh-tw/footer.yml deleted file mode 100644 index c68d10357e..0000000000 --- a/_data/zh-tw/footer.yml +++ /dev/null @@ -1,7 +0,0 @@ -terms_of_use: Terms of Use -privacy_policy: Privacy Policy -coc: Code of Conduct -trademark_policy: Trademark Policy -security_policy: Security Policy -license: License -trademark_legal: Copyright OpenJS Foundation and Express contributors. All rights reserved. The OpenJS Foundation has registered trademarks and uses trademarks. For a list of trademarks of the OpenJS Foundation, please see our Trademark Policy and Trademark List. Trademarks and logos not indicated on the list of OpenJS Foundation trademarks are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them. diff --git a/_data/zh-tw/general.yml b/_data/zh-tw/general.yml deleted file mode 100644 index e6327e6214..0000000000 --- a/_data/zh-tw/general.yml +++ /dev/null @@ -1,8 +0,0 @@ -title_announcement: "Express@5.1.0: Now the Default on npm with LTS Timeline" -body_announcement: "Express 5.1.0 is now the default on npm, and we're introducing an official LTS schedule for the v4 and v5 release lines. Check out our latest blog for more information." -community-caveat-alert: "This information refers to third-party sites, products, or modules that are not maintained by the Expressjs team. Listing here does not constitute an endorsement or recommendation from the Expressjs project team." -warning: 'Warning' -note: 'Note' -caution: 'Caution' -i18n_notice: "This document might be outdated relative to the documentation in English. For the latest updates, please refer to the" -i18n_notice_link_text: "documentation in english" diff --git a/_data/zh-tw/menu.yml b/_data/zh-tw/menu.yml deleted file mode 100644 index f8dccefefb..0000000000 --- a/_data/zh-tw/menu.yml +++ /dev/null @@ -1,50 +0,0 @@ -# Getting started -getting_started: 入門 -installing: 安裝 -hello_world: Hello world -generator: Express 產生器 -basic_routing: 基本路由 -static_files: 靜態檔案 -examples: More examples -faq: 常見問題 (FAQ) -# Guide -guide: 手冊 -routing: 路由 -writing_middleware: 撰寫中介軟體 -using_middleware: 使用中介軟體 -overriding_express_api: Overriding the Express API -using_template_engines: 使用範本引擎 -error_handling: 錯誤處理 -debugging: 除錯 -behind_proxies: 位於 Proxy 背後的 Express -migrating_4: 移至 Express 4 -migrating_5: 移至 Express 5 -database_integration: 資料庫整合 -# API reference -api: API 參照 -5x: 5.x -4x: 4.x -3x: 3.x 已淘汰 -2x: 2.x (已淘汰) -# Advanced topics -advanced: 進階主題 -developing_template_engines: 範本引擎 -security_updates: 安全更新 -best_practice_security: 安全最佳作法 -best_practice_performance: 效能最佳作法 -healthcheck_graceful_shutdown: Health checks & shutdown -# Resources -resources: 資源 -glossary: 名詞解釋 -middleware: 中介軟體 -community: 社群 -utils: Utility modules -contributing: Contributing to Express -changelog: Release Change Log -# Support -support: Support -# Blog -blog: Blog -latest_post: Latest post -all_posts: All posts -write_post: Write a Post diff --git a/_includes/admonitions/caution.html b/_includes/admonitions/caution.html deleted file mode 100644 index 0cb2624f19..0000000000 --- a/_includes/admonitions/caution.html +++ /dev/null @@ -1,4 +0,0 @@ -

{% include icons/caution.svg %} {{ site.data[page.lang].general.caution }}

-{{include.content}} -
- \ No newline at end of file diff --git a/_includes/admonitions/note.html b/_includes/admonitions/note.html deleted file mode 100644 index afed80d26b..0000000000 --- a/_includes/admonitions/note.html +++ /dev/null @@ -1,6 +0,0 @@ -
-

- {% include icons/note.svg %} {{ site.data[page.lang].general.note }} -

- {{include.content}} -
diff --git a/_includes/admonitions/warning.html b/_includes/admonitions/warning.html deleted file mode 100644 index 27672663b7..0000000000 --- a/_includes/admonitions/warning.html +++ /dev/null @@ -1,3 +0,0 @@ -

{% include icons/warning.svg %} {{ site.data[page.lang].general.warning }}

-{{include.content}} -
\ No newline at end of file diff --git a/_includes/announcement.html b/_includes/announcement.html deleted file mode 100644 index d0c213340e..0000000000 --- a/_includes/announcement.html +++ /dev/null @@ -1,9 +0,0 @@ -
    -
  • -

    {% include icons/announcement.svg %} {{ site.data[page.lang].general.title_announcement }}

    -

    - {{ site.data[page.lang].general.body_announcement }} -

    -
  • -
- \ No newline at end of file diff --git a/_includes/api/en/3x/app-VERB.md b/_includes/api/en/3x/app-VERB.md deleted file mode 100644 index 6e9b0d2759..0000000000 --- a/_includes/api/en/3x/app-VERB.md +++ /dev/null @@ -1,57 +0,0 @@ -

app.VERB(path, [callback...], callback)

- -The `app.VERB()` methods provide the routing functionality -in Express, where VERB is one of the HTTP verbs, such -as `app.post()`. Multiple callbacks may be given, all are treated -equally, and behave just like middleware, with the one exception that -these callbacks may invoke `next('route')` to bypass the -remaining route callback(s). This mechanism can be used to perform pre-conditions -on a route then pass control to subsequent routes when there is no reason to proceed -with the route matched. - -The following snippet illustrates the most simple route definition possible. Express -translates the path strings to regular expressions, used internally to match incoming requests. -Query strings are not considered when peforming these matches, for example "GET /" -would match the following route, as would "GET /?name=tobi". - -```js -app.get('/', function (req, res) { - res.send('hello world') -}) -``` - -Regular expressions may also be used, and can be useful -if you have very specific restraints, for example the following -would match "GET /commits/71dbb9c" as well as "GET /commits/71dbb9c..4c084f9". - -```js -app.get(/^\/commits\/(\w+)(?:\.\.(\w+))?$/, function (req, res) { - var from = req.params[0] - var to = req.params[1] || 'HEAD' - res.send('commit range ' + from + '..' + to) -}) -``` - -Several callbacks may also be passed, useful for re-using middleware -that load resources, perform validations, etc. - -```js -app.get('/user/:id', user.load, function () { - // ... -}) -``` - -These callbacks may be passed within arrays as well, these arrays are -simply flattened when passed: - -```js -var middleware = [loadForum, loadThread] - -app.get('/forum/:fid/thread/:tid', middleware, function () { - // ... -}) - -app.post('/forum/:fid/thread/:tid', middleware, function () { - // ... -}) -``` diff --git a/_includes/api/en/3x/app-all.md b/_includes/api/en/3x/app-all.md deleted file mode 100644 index 6b3a1e99f5..0000000000 --- a/_includes/api/en/3x/app-all.md +++ /dev/null @@ -1,32 +0,0 @@ -

app.all(path, [callback...], callback)

- -This method functions just like the `app.VERB()` methods, -however it matches all HTTP verbs. - -This method is extremely useful for -mapping "global" logic for specific path prefixes or arbitrary matches. -For example if you placed the following route at the top of all other -route definitions, it would require that all routes from that point on -would require authentication, and automatically load a user. Keep in mind -that these callbacks do not have to act as end points, `loadUser` -can perform a task, then `next()` to continue matching subsequent -routes. - -```js -app.all('*', requireAuthentication, loadUser) -``` - -Or the equivalent: - -```js -app.all('*', requireAuthentication) -app.all('*', loadUser) -``` - -Another great example of this is white-listed "global" functionality. Here -the example is much like before, however only restricting paths prefixed with -"/api": - -```js -app.all('/api/*', requireAuthentication) -``` diff --git a/_includes/api/en/3x/app-configure.md b/_includes/api/en/3x/app-configure.md deleted file mode 100644 index 19029170e0..0000000000 --- a/_includes/api/en/3x/app-configure.md +++ /dev/null @@ -1,40 +0,0 @@ -

app.configure([env], callback)

- -Conditionally invoke `callback` when `env` matches `app.get('env')`, -aka `process.env.NODE_ENV`. This method remains for legacy reasons, and is effectively -an `if` statement as illustrated in the following snippets. These functions are not -required in order to use `app.set()` and other configuration methods. - -```js -// all environments -app.configure(function () { - app.set('title', 'My Application') -}) - -// development only -app.configure('development', function () { - app.set('db uri', 'localhost/dev') -}) - -// production only -app.configure('production', function () { - app.set('db uri', 'n.n.n.n/prod') -}) -``` - -Is effectively sugar for: - -```js -// all environments -app.set('title', 'My Application') - -// development only -if (app.get('env') === 'development') { - app.set('db uri', 'localhost/dev') -} - -// production only -if (app.get('env') === 'production') { - app.set('db uri', 'n.n.n.n/prod') -} -``` diff --git a/_includes/api/en/3x/app-disable.md b/_includes/api/en/3x/app-disable.md deleted file mode 100644 index a3525d8543..0000000000 --- a/_includes/api/en/3x/app-disable.md +++ /dev/null @@ -1,9 +0,0 @@ -

app.disable(name)

- -Set setting `name` to `false`. - -```js -app.disable('trust proxy') -app.get('trust proxy') -// => false -``` diff --git a/_includes/api/en/3x/app-disabled.md b/_includes/api/en/3x/app-disabled.md deleted file mode 100644 index f2438f1c66..0000000000 --- a/_includes/api/en/3x/app-disabled.md +++ /dev/null @@ -1,12 +0,0 @@ -

app.disabled(name)

- -Check if setting `name` is disabled. - -```js -app.disabled('trust proxy') -// => true - -app.enable('trust proxy') -app.disabled('trust proxy') -// => false -``` diff --git a/_includes/api/en/3x/app-enable.md b/_includes/api/en/3x/app-enable.md deleted file mode 100644 index 926806ec1f..0000000000 --- a/_includes/api/en/3x/app-enable.md +++ /dev/null @@ -1,9 +0,0 @@ -

app.enable(name)

- -Set setting `name` to `true`. - -```js -app.enable('trust proxy') -app.get('trust proxy') -// => true -``` diff --git a/_includes/api/en/3x/app-enabled.md b/_includes/api/en/3x/app-enabled.md deleted file mode 100644 index b485780286..0000000000 --- a/_includes/api/en/3x/app-enabled.md +++ /dev/null @@ -1,12 +0,0 @@ -

app.enabled(name)

- -Check if setting `name` is enabled. - -```js -app.enabled('trust proxy') -// => false - -app.enable('trust proxy') -app.enabled('trust proxy') -// => true -``` diff --git a/_includes/api/en/3x/app-engine.md b/_includes/api/en/3x/app-engine.md deleted file mode 100644 index fcdfd7c9d9..0000000000 --- a/_includes/api/en/3x/app-engine.md +++ /dev/null @@ -1,39 +0,0 @@ -

app.engine(ext, callback)

- -Register the given template engine `callback` as `ext` - -By default will `require()` the engine based on the -file extension. For example if you try to render -a "foo.jade" file Express will invoke the following internally, -and cache the `require()` on subsequent calls to increase -performance. - -```js -app.engine('jade', require('jade').__express) -``` - -For engines that do not provide `.__express` out of the box - -or if you wish to "map" a different extension to the template engine -you may use this method. For example mapping the EJS template engine to -".html" files: - -```js -app.engine('html', require('ejs').renderFile) -``` - -In this case EJS provides a `.renderFile()` method with -the same signature that Express expects: `(path, options, callback)`, -though note that it aliases this method as `ejs.__express` internally -so if you're using ".ejs" extensions you dont need to do anything. - -Some template engines do not follow this convention, the -consolidate.js -library was created to map all of node's popular template -engines to follow this convention, thus allowing them to -work seemlessly within Express. - -```js -var engines = require('consolidate') -app.engine('haml', engines.haml) -app.engine('html', engines.hogan) -``` diff --git a/_includes/api/en/3x/app-get.md b/_includes/api/en/3x/app-get.md deleted file mode 100644 index 15b908a1cc..0000000000 --- a/_includes/api/en/3x/app-get.md +++ /dev/null @@ -1,12 +0,0 @@ -

app.get(name)

- -Get setting `name` value. - -```js -app.get('title') -// => undefined - -app.set('title', 'My Site') -app.get('title') -// => "My Site" -``` diff --git a/_includes/api/en/3x/app-listen.md b/_includes/api/en/3x/app-listen.md deleted file mode 100644 index f2349f0b54..0000000000 --- a/_includes/api/en/3x/app-listen.md +++ /dev/null @@ -1,36 +0,0 @@ -

app.listen()

- -Bind and listen for connections on the given host and port, -this method is identical to node's http.Server#listen(). - -```js -var express = require('express') -var app = express() -app.listen(3000) -``` - -The `app` returned by `express()` is in fact a JavaScript -`Function`, designed to be passed to node's http servers as a callback -to handle requests. This allows you to provide both HTTP and HTTPS versions of -your app with the same codebase easily, as the app does not inherit from these, -it is simply a callback: - -```js -var express = require('express') -var https = require('https') -var http = require('http') -var app = express() - -http.createServer(app).listen(80) -https.createServer(options, app).listen(443) -``` - -The `app.listen()` method is simply a convenience method defined as, -if you wish to use HTTPS or provide both, use the technique above. - -```js -app.listen = function () { - var server = http.createServer(this) - return server.listen.apply(server, arguments) -} -``` diff --git a/_includes/api/en/3x/app-locals.md b/_includes/api/en/3x/app-locals.md deleted file mode 100644 index d35421160e..0000000000 --- a/_includes/api/en/3x/app-locals.md +++ /dev/null @@ -1,47 +0,0 @@ -

app.locals

- -Application local variables are provided to all templates -rendered within the application. This is useful for providing -helper functions to templates, as well as app-level data. - -```js -app.locals.title = 'My App' -app.locals.strftime = require('strftime') -``` - -The `app.locals` object is a JavaScript `Function`, -which when invoked with an object will merge properties into itself, providing -a simple way to expose existing objects as local variables. - -```js -app.locals({ - title: 'My App', - phone: '1-250-858-9990', - email: 'me@myapp.com' -}) - -console.log(app.locals.title) -// => 'My App' - -console.log(app.locals.email) -// => 'me@myapp.com' -``` - -A consequence of the `app.locals` Object being ultimately a Javascript Function Object is that you must not reuse existing (native) named properties for your own variable names, such as `name, apply, bind, call, arguments, length, constructor`. - -```js -app.locals({ name: 'My App' }) - -console.log(app.locals.name) -// => return 'app.locals' in place of 'My App' (app.locals is a Function !) -// => if name's variable is used in a template, a ReferenceError will be returned. -``` - -The full list of native named properties can be found in many specifications. The JavaScript specification introduced original properties, some of which still recognized by modern engines, and the EcmaScript specification then built on it and normalized the set of properties, adding new ones and removing deprecated ones. Check out properties for Functions and Objects if interested. - -By default Express exposes only a single app-level local variable, `settings`. - -```js -app.set('title', 'My App') -// use settings.title in a view -``` diff --git a/_includes/api/en/3x/app-param.md b/_includes/api/en/3x/app-param.md deleted file mode 100644 index 72d9fd507b..0000000000 --- a/_includes/api/en/3x/app-param.md +++ /dev/null @@ -1,70 +0,0 @@ -

app.param([name], callback)

- -Map logic to route parameters. For example when `:user` -is present in a route path you may map user loading logic to automatically -provide `req.user` to the route, or perform validations -on the parameter input. - -The following snippet illustrates how the `callback` -is much like middleware, thus supporting async operations, however -providing the additional value of the parameter, here named as `id`. -An attempt to load the user is then performed, assigning `req.user`, -otherwise passing an error to `next(err)`. - -```js -app.param('user', function (req, res, next, id) { - User.find(id, function (err, user) { - if (err) { - next(err) - } else if (user) { - req.user = user - next() - } else { - next(new Error('failed to load user')) - } - }) -}) -``` - -Alternatively you may pass only a `callback`, in which -case you have the opportunity to alter the `app.param()` API. -For example the express-params -defines the following callback which allows you to restrict parameters to a given -regular expression. - -This example is a bit more advanced, checking if the second argument is a regular -expression, returning the callback which acts much like the "user" param example. - -```js -app.param(function (name, fn) { - if (fn instanceof RegExp) { - return function (req, res, next, val) { - var captures - if ((captures = fn.exec(String(val)))) { - req.params[name] = captures - next() - } else { - next('route') - } - } - } -}) -``` - -The method could now be used to effectively validate parameters, or also -parse them to provide capture groups: - -```js -app.param('id', /^\d+$/) - -app.get('/user/:id', function (req, res) { - res.send('user ' + req.params.id) -}) - -app.param('range', /^(\w+)\.\.(\w+)?$/) - -app.get('/range/:range', function (req, res) { - var range = req.params.range - res.send('from ' + range[1] + ' to ' + range[2]) -}) -``` diff --git a/_includes/api/en/3x/app-render.md b/_includes/api/en/3x/app-render.md deleted file mode 100644 index 0b1ced84c8..0000000000 --- a/_includes/api/en/3x/app-render.md +++ /dev/null @@ -1,15 +0,0 @@ -

app.render(view, [options], callback)

- -Render a `view` with a callback responding with -the rendered string. This is the app-level variant of `res.render()`, -and otherwise behaves the same way. - -```js -app.render('email', function (err, html) { - // ... -}) - -app.render('email', { name: 'Tobi' }, function (err, html) { - // ... -}) -``` diff --git a/_includes/api/en/3x/app-routes.md b/_includes/api/en/3x/app-routes.md deleted file mode 100644 index aff28f6967..0000000000 --- a/_includes/api/en/3x/app-routes.md +++ /dev/null @@ -1,29 +0,0 @@ -

app.routes

- -The `app.routes` object houses all of the routes defined mapped -by the associated HTTP verb. This object may be used for introspection capabilities, -for example Express uses this internally not only for routing but to provide default -OPTIONS behaviour unless `app.options()` is used. Your application -or framework may also remove routes by simply by removing them from this object. - -The output of `console.log(app.routes)`: - -``` -{ get: - [ { path: '/', - method: 'get', - callbacks: [Object], - keys: [], - regexp: /^\/\/?$/i }, - { path: '/user/:id', - method: 'get', - callbacks: [Object], - keys: [{ name: 'id', optional: false }], - regexp: /^\/user\/(?:([^\/]+?))\/?$/i } ], - delete: - [ { path: '/user/:id', - method: 'delete', - callbacks: [Object], - keys: [Object], - regexp: /^\/user\/(?:([^\/]+?))\/?$/i } ] } -``` diff --git a/_includes/api/en/3x/app-set.md b/_includes/api/en/3x/app-set.md deleted file mode 100644 index 8991774208..0000000000 --- a/_includes/api/en/3x/app-set.md +++ /dev/null @@ -1,9 +0,0 @@ -

app.set(name, value)

- -Assigns setting `name` to `value`. - -```js -app.set('title', 'My Site') -app.get('title') -// => "My Site" -``` diff --git a/_includes/api/en/3x/app-settings.md b/_includes/api/en/3x/app-settings.md deleted file mode 100644 index 71c7848d1e..0000000000 --- a/_includes/api/en/3x/app-settings.md +++ /dev/null @@ -1,15 +0,0 @@ -

settings

- -The following settings are provided to alter how Express will behave: - -* `env` Environment mode, defaults to process.env.NODE_ENV or "development" -* `trust proxy` Enables reverse proxy support, disabled by default -* `jsonp callback name` Changes the default callback name of ?callback= -* `json replacer` JSON replacer callback, null by default -* `json spaces` JSON response spaces for formatting, defaults to 2 in development, 0 in production -* `case sensitive routing` Enable case sensitivity, disabled by default, treating "/Foo" and "/foo" as the same -* `strict routing` Enable strict routing, by default "/foo" and "/foo/" are treated the same by the router -* `view cache` Enables view template compilation caching, enabled in production by default -* `view engine` The default engine extension to use when omitted -* `views` The view directory path, defaulting to "process.cwd() + '/views'" - diff --git a/_includes/api/en/3x/app-use.md b/_includes/api/en/3x/app-use.md deleted file mode 100644 index ceba45f66c..0000000000 --- a/_includes/api/en/3x/app-use.md +++ /dev/null @@ -1,89 +0,0 @@ -

app.use([path], function)

- -Use the given middleware `function`, with optional mount `path`, -defaulting to "/". - -```js -var express = require('express') -var app = express() - -// simple logger -app.use(function (req, res, next) { - console.log('%s %s', req.method, req.url) - next() -}) - -// respond -app.use(function (req, res, next) { - res.send('Hello World') -}) - -app.listen(3000) -``` - -The "mount" path is stripped and is not visible -to the middleware `function`. The main effect of this feature is that -mounted middleware may operate without code changes regardless of its "prefix" -pathname. - -
-A route will match any path that follows its path immediately with either a "`/`" or a "`.`". For example: `app.use('/apple', ...)` will match _/apple_, _/apple/images_, _/apple/images/news_, _/apple.html_, _/apple.html.txt_, and so on. -
- -Here's a concrete example, take the typical use-case of serving files in ./public -using the `express.static()` middleware: - -```js -// GET /javascripts/jquery.js -// GET /style.css -// GET /favicon.ico -app.use(express.static(path.join(__dirname, 'public'))) -``` - -Say for example you wanted to prefix all static files with "/static", you could -use the "mounting" feature to support this. Mounted middleware functions are _not_ -invoked unless the `req.url` contains this prefix, at which point -it is stripped when the function is invoked. This affects this function only, -subsequent middleware will see `req.url` with "/static" included -unless they are mounted as well. - -```js -// GET /static/javascripts/jquery.js -// GET /static/style.css -// GET /static/favicon.ico -app.use('/static', express.static(path.join(__dirname, 'public'))) -``` - -The order of which middleware are "defined" using `app.use()` is -very important, they are invoked sequentially, thus this defines middleware -precedence. For example usually `express.logger()` is the very -first middleware you would use, logging every request: - -```js -app.use(express.logger()) -app.use(express.static(path.join(__dirname, 'public'))) -app.use(function (req, res) { - res.send('Hello') -}) -``` - -Now suppose you wanted to ignore logging requests for static files, but to -continue logging routes and middleware defined after `logger()`, -you would simply move `static()` above: - -```js -app.use(express.static(path.join(__dirname, 'public'))) -app.use(express.logger()) -app.use(function (req, res) { - res.send('Hello') -}) -``` - -Another concrete example would be serving files from multiple directories, -giving precedence to "./public" over the others: - -```js -app.use(express.static(path.join(__dirname, 'public'))) -app.use(express.static(path.join(__dirname, 'files'))) -app.use(express.static(path.join(__dirname, 'uploads'))) -``` diff --git a/_includes/api/en/3x/app.md b/_includes/api/en/3x/app.md deleted file mode 100644 index 0d15c491a8..0000000000 --- a/_includes/api/en/3x/app.md +++ /dev/null @@ -1,69 +0,0 @@ -

Application

- -
- {% include api/en/3x/app-set.md %} -
- -
- {% include api/en/3x/app-get.md %} -
- -
- {% include api/en/3x/app-enable.md %} -
- -
- {% include api/en/3x/app-disable.md %} -
- -
- {% include api/en/3x/app-enabled.md %} -
- -
- {% include api/en/3x/app-disabled.md %} -
- -
- {% include api/en/3x/app-configure.md %} -
- -
- {% include api/en/3x/app-use.md %} -
- -
- {% include api/en/3x/app-settings.md %} -
- -
- {% include api/en/3x/app-engine.md %} -
- -
- {% include api/en/3x/app-param.md %} -
- -
- {% include api/en/3x/app-VERB.md %} -
- -
- {% include api/en/3x/app-all.md %} -
- -
- {% include api/en/3x/app-locals.md %} -
- -
- {% include api/en/3x/app-render.md %} -
- -
- {% include api/en/3x/app-routes.md %} -
- -
- {% include api/en/3x/app-listen.md %} -
diff --git a/_includes/api/en/3x/express.md b/_includes/api/en/3x/express.md deleted file mode 100644 index 195683b30d..0000000000 --- a/_includes/api/en/3x/express.md +++ /dev/null @@ -1,14 +0,0 @@ -

express()

- -Creates an Express application. The `express()` function is a top-level function exported by the _express_ module. - -```js -var express = require('express') -var app = express() - -app.get('/', function (req, res) { - res.send('hello world') -}) - -app.listen(3000) -``` diff --git a/_includes/api/en/3x/menu.md b/_includes/api/en/3x/menu.md deleted file mode 100644 index c09dbbe6f2..0000000000 --- a/_includes/api/en/3x/menu.md +++ /dev/null @@ -1,163 +0,0 @@ - - - diff --git a/_includes/api/en/3x/middleware.md b/_includes/api/en/3x/middleware.md deleted file mode 100644 index 001790acc9..0000000000 --- a/_includes/api/en/3x/middleware.md +++ /dev/null @@ -1,29 +0,0 @@ -

Middleware

- -
- {% include api/en/3x/mw-basicAuth.md %} -
- -
- {% include api/en/3x/mw-bodyParser.md %} -
- -
- {% include api/en/3x/mw-compress.md %} -
- -
- {% include api/en/3x/mw-cookieParser.md %} -
- -
- {% include api/en/3x/mw-cookieSession.md %} -
- -
- {% include api/en/3x/mw-csrf.md %} -
- -
- {% include api/en/3x/mw-directory.md %} -
diff --git a/_includes/api/en/3x/mw-basicAuth.md b/_includes/api/en/3x/mw-basicAuth.md deleted file mode 100644 index b5c2879c6a..0000000000 --- a/_includes/api/en/3x/mw-basicAuth.md +++ /dev/null @@ -1,27 +0,0 @@ -

basicAuth()

- -Basic Authentication middleware, populating `req.user` -with the username. - -Simple username and password: - -```js -app.use(express.basicAuth('username', 'password')) -``` - -Callback verification: - -```js -app.use(express.basicAuth(function (user, pass) { - return user === 'tj' && pass === 'wahoo' -})) -``` - -Async callback verification, accepting `fn(err, user)`, -in this case `req.user` will be the user object passed. - -```js -app.use(express.basicAuth(function (user, pass, fn) { - User.authenticate({ user: user, pass: pass }, fn) -})) -``` diff --git a/_includes/api/en/3x/mw-bodyParser.md b/_includes/api/en/3x/mw-bodyParser.md deleted file mode 100644 index 0052443b17..0000000000 --- a/_includes/api/en/3x/mw-bodyParser.md +++ /dev/null @@ -1,27 +0,0 @@ -

bodyParser()

- -Request body parsing middleware supporting JSON, urlencoded, -and multipart requests. This middleware is simply a wrapper -for the `json()`, `urlencoded()`, and -`multipart()` middleware. - -```js -app.use(express.bodyParser()) - -// is equivalent to: -app.use(express.json()) -app.use(express.urlencoded()) -app.use(express.multipart()) -``` - -For security sake, it's better to disable file upload if your application -doesn't need it. To do this, use only the needed middleware, i.e. don't use -the `bodyParser` and `multipart()` middleware: - -```js -app.use(express.json()) -app.use(express.urlencoded()) -``` - -If your application needs file upload you should set up -a strategy for dealing with those files. diff --git a/_includes/api/en/3x/mw-compress.md b/_includes/api/en/3x/mw-compress.md deleted file mode 100644 index 3221859c02..0000000000 --- a/_includes/api/en/3x/mw-compress.md +++ /dev/null @@ -1,12 +0,0 @@ -

compress()

- -Compress response data with gzip / deflate. This middleware -should be placed "high" within the stack to ensure all -responses may be compressed. - -```js -app.use(express.logger()) -app.use(express.compress()) -app.use(express.methodOverride()) -app.use(express.bodyParser()) -``` diff --git a/_includes/api/en/3x/mw-cookieParser.md b/_includes/api/en/3x/mw-cookieParser.md deleted file mode 100644 index 32b5c28e2e..0000000000 --- a/_includes/api/en/3x/mw-cookieParser.md +++ /dev/null @@ -1,10 +0,0 @@ -

cookieParser()

- -Parses the Cookie header field and populates `req.cookies` -with an object keyed by the cookie names. Optionally you may enabled -signed cookie support by passing a `secret` string. - -```js -app.use(express.cookieParser()) -app.use(express.cookieParser('some secret')) -``` diff --git a/_includes/api/en/3x/mw-cookieSession.md b/_includes/api/en/3x/mw-cookieSession.md deleted file mode 100644 index 5daecf0bfe..0000000000 --- a/_includes/api/en/3x/mw-cookieSession.md +++ /dev/null @@ -1,19 +0,0 @@ -

cookieSession()

- -Provides cookie-based sessions, and populates `req.session`. -This middleware takes the following options: - -* `key` cookie name defaulting to `connect.sess` -* `secret` prevents cookie tampering -* `cookie` session cookie settings, defaulting to `{ path: '/', httpOnly: true, maxAge: null }` -* `proxy` trust the reverse proxy when setting secure cookies (via "x-forwarded-proto") - -```js -app.use(express.cookieSession()) -``` - -To clear a cookie simply assign the session to null before responding: - -```js -req.session = null -``` diff --git a/_includes/api/en/3x/mw-csrf.md b/_includes/api/en/3x/mw-csrf.md deleted file mode 100644 index beacfe56b2..0000000000 --- a/_includes/api/en/3x/mw-csrf.md +++ /dev/null @@ -1,15 +0,0 @@ -

csrf()

- -CSRF protection middleware. - -By default this middleware generates a token named "_csrf" -which should be added to requests which mutate -state, within a hidden form field, query-string etc. This -token is validated against `req.csrfToken()`. - -The default `value` function checks `req.body` generated -by the `bodyParser()` middleware, `req.query` generated -by `query()`, and the "X-CSRF-Token" header field. - -This middleware requires session support, thus should be added -somewhere below `session()`. diff --git a/_includes/api/en/3x/mw-directory.md b/_includes/api/en/3x/mw-directory.md deleted file mode 100644 index b2e785489d..0000000000 --- a/_includes/api/en/3x/mw-directory.md +++ /dev/null @@ -1,16 +0,0 @@ -

directory()

- -Directory serving middleware, serves the given `path`. -This middleware may be paired with `static()` to serve -files, providing a full-featured file browser. - -```js -app.use(express.directory('public')) -app.use(express.static('public')) -``` - -This middleware accepts the following options: - -* `hidden` display hidden (dot) files. Defaults to false. -* `icons` display icons. Defaults to false. -* `filter` Apply this filter function to files. Defaults to false. diff --git a/_includes/api/en/3x/req-accepted.md b/_includes/api/en/3x/req-accepted.md deleted file mode 100644 index 04d664dafd..0000000000 --- a/_includes/api/en/3x/req-accepted.md +++ /dev/null @@ -1,14 +0,0 @@ -

req.accepted

- -Return an array of Accepted media types ordered from highest quality to lowest. - -``` -[ { value: 'application/json', - quality: 1, - type: 'application', - subtype: 'json' }, - { value: 'text/html', - quality: 0.5, - type: 'text', - subtype: 'html' } ] -``` diff --git a/_includes/api/en/3x/req-acceptedCharsets.md b/_includes/api/en/3x/req-acceptedCharsets.md deleted file mode 100644 index fb4cca0c74..0000000000 --- a/_includes/api/en/3x/req-acceptedCharsets.md +++ /dev/null @@ -1,8 +0,0 @@ -

req.acceptedCharsets

- -Return an array of Accepted charsets ordered from highest quality to lowest. - -``` -Accept-Charset: iso-8859-5;q=.2, unicode-1-1;q=0.8 -// => ['unicode-1-1', 'iso-8859-5'] -``` diff --git a/_includes/api/en/3x/req-acceptedLanguages.md b/_includes/api/en/3x/req-acceptedLanguages.md deleted file mode 100644 index 42c5494e89..0000000000 --- a/_includes/api/en/3x/req-acceptedLanguages.md +++ /dev/null @@ -1,8 +0,0 @@ -

req.acceptedLanguages

- -Return an array of Accepted languages ordered from highest quality to lowest. - -``` -Accept-Language: en;q=.5, en-us -// => ['en-us', 'en'] -``` diff --git a/_includes/api/en/3x/req-accepts.md b/_includes/api/en/3x/req-accepts.md deleted file mode 100644 index aac9440ad6..0000000000 --- a/_includes/api/en/3x/req-accepts.md +++ /dev/null @@ -1,36 +0,0 @@ -

req.accepts(types)

- -Check if the given `types` are acceptable, returning -the best match when true, otherwise `undefined` - in which -case you should respond with 406 "Not Acceptable". - -The `type` value may be a single mime type string -such as "application/json", the extension name -such as "json", a comma-delimited list or an array. When a list -or array is given the best match, if any is returned. - -```js -// Accept: text/html -req.accepts('html') -// => "html" - -// Accept: text/*, application/json -req.accepts('html') -// => "html" -req.accepts('text/html') -// => "text/html" -req.accepts('json, text') -// => "json" -req.accepts('application/json') -// => "application/json" - -// Accept: text/*, application/json -req.accepts('image/png') -req.accepts('png') -// => undefined - -// Accept: text/*;q=.5, application/json -req.accepts(['html', 'json']) -req.accepts('html, json') -// => "json" -``` diff --git a/_includes/api/en/3x/req-acceptsCharset.md b/_includes/api/en/3x/req-acceptsCharset.md deleted file mode 100644 index 99e8e16a43..0000000000 --- a/_includes/api/en/3x/req-acceptsCharset.md +++ /dev/null @@ -1,3 +0,0 @@ -

req.acceptsCharset(charset)

- -Check if the given `charset` are acceptable. diff --git a/_includes/api/en/3x/req-acceptsLanguage.md b/_includes/api/en/3x/req-acceptsLanguage.md deleted file mode 100644 index a640dd308e..0000000000 --- a/_includes/api/en/3x/req-acceptsLanguage.md +++ /dev/null @@ -1,3 +0,0 @@ -

req.acceptsLanguage(lang)

- -Check if the given `lang` are acceptable. diff --git a/_includes/api/en/3x/req-body.md b/_includes/api/en/3x/req-body.md deleted file mode 100644 index dfed5c3eff..0000000000 --- a/_includes/api/en/3x/req-body.md +++ /dev/null @@ -1,19 +0,0 @@ -

req.body

- -This property is an object containing the parsed request body. This feature -is provided by the `bodyParser()` middleware, though other body -parsing middleware may follow this convention as well. This property -defaults to `{}` when `bodyParser()` is used. - -```js -// POST user[name]=tobi&user[email]=tobi@learnboost.com -console.log(req.body.user.name) -// => "tobi" - -console.log(req.body.user.email) -// => "tobi@learnboost.com" - -// POST { "name": "tobi" } -console.log(req.body.name) -// => "tobi" -``` diff --git a/_includes/api/en/3x/req-cookies.md b/_includes/api/en/3x/req-cookies.md deleted file mode 100644 index 66ad363ace..0000000000 --- a/_includes/api/en/3x/req-cookies.md +++ /dev/null @@ -1,11 +0,0 @@ -

req.cookies

- -This object requires the `cookieParser()` middleware for use. -It contains cookies sent by the user-agent. If no cookies are sent, it -defaults to `{}`. - -```js -// Cookie: name=tj -console.log(req.cookies.name) -// => "tj" -``` diff --git a/_includes/api/en/3x/req-files.md b/_includes/api/en/3x/req-files.md deleted file mode 100644 index 98331fbd48..0000000000 --- a/_includes/api/en/3x/req-files.md +++ /dev/null @@ -1,45 +0,0 @@ -

req.files

- -This property is an object of the files uploaded. This feature -is provided by the `bodyParser()` middleware, though other body -parsing middleware may follow this convention as well. This property -defaults to `{}` when `bodyParser()` is used. - -For example if a file field was named "image", -and a file was uploaded, `req.files.image` would contain -the following `File` object: - -``` -{ size: 74643, - path: '/tmp/8ef9c52abe857867fd0a4e9a819d1876', - name: 'edge.png', - type: 'image/png', - hash: false, - lastModifiedDate: Thu Aug 09 2012 20:07:51 GMT-0700 (PDT), - _writeStream: - { path: '/tmp/8ef9c52abe857867fd0a4e9a819d1876', - fd: 13, - writable: false, - flags: 'w', - encoding: 'binary', - mode: 438, - bytesWritten: 74643, - busy: false, - _queue: [], - _open: [Function], - drainable: true }, - length: [Getter], - filename: [Getter], - mime: [Getter] } -``` - -The `bodyParser()` middleware utilizes the -node-formidable -module internally, and accepts the same options. An example of this -is the `keepExtensions` formidable option, defaulting to false -which in this case gives you the filename "/tmp/8ef9c52abe857867fd0a4e9a819d1876" void of -the ".png" extension. To enable this, and others you may pass them to `bodyParser()`: - -```js -app.use(express.bodyParser({ keepExtensions: true, uploadDir: '/my/files' })) -``` diff --git a/_includes/api/en/3x/req-fresh.md b/_includes/api/en/3x/req-fresh.md deleted file mode 100644 index 6b4e6e5832..0000000000 --- a/_includes/api/en/3x/req-fresh.md +++ /dev/null @@ -1,9 +0,0 @@ -

req.fresh

- -Check if the request is fresh - aka Last-Modified and/or the ETag still match, -indicating that the resource is "fresh". - -```js -console.dir(req.fresh) -// => true -``` diff --git a/_includes/api/en/3x/req-header.md b/_includes/api/en/3x/req-header.md deleted file mode 100644 index e186dbdcd9..0000000000 --- a/_includes/api/en/3x/req-header.md +++ /dev/null @@ -1,16 +0,0 @@ -

req.get(field)

- -Get the case-insensitive request header `field`. The "Referrer" and "Referer" fields are interchangeable. - -```js -req.get('Content-Type') -// => "text/plain" - -req.get('content-type') -// => "text/plain" - -req.get('Something') -// => undefined -``` - -p Aliased as `req.header(field)`. diff --git a/_includes/api/en/3x/req-host.md b/_includes/api/en/3x/req-host.md deleted file mode 100644 index d24fdfe1c4..0000000000 --- a/_includes/api/en/3x/req-host.md +++ /dev/null @@ -1,9 +0,0 @@ -

req.host

- -Returns the hostname from the "Host" header field (void of portno). - -```js -// Host: "example.com:3000" -console.dir(req.host) -// => 'example.com' -``` diff --git a/_includes/api/en/3x/req-ip.md b/_includes/api/en/3x/req-ip.md deleted file mode 100644 index 1de60fabf3..0000000000 --- a/_includes/api/en/3x/req-ip.md +++ /dev/null @@ -1,9 +0,0 @@ -

req.ip

- -Return the remote address, or when "trust proxy" -is enabled - the upstream address. - -```js -console.dir(req.ip) -// => '127.0.0.1' -``` diff --git a/_includes/api/en/3x/req-ips.md b/_includes/api/en/3x/req-ips.md deleted file mode 100644 index 7b139a29a6..0000000000 --- a/_includes/api/en/3x/req-ips.md +++ /dev/null @@ -1,10 +0,0 @@ -

req.ips

- -When "trust proxy" is `true`, parse -the "X-Forwarded-For" ip address list -and return an array, otherwise an empty -array is returned. - -For example if the value were "client, proxy1, proxy2" -you would receive the array `["client", "proxy1", "proxy2"]` -where "proxy2" is the furthest down-stream. diff --git a/_includes/api/en/3x/req-is.md b/_includes/api/en/3x/req-is.md deleted file mode 100644 index aa93c425cb..0000000000 --- a/_includes/api/en/3x/req-is.md +++ /dev/null @@ -1,21 +0,0 @@ -

req.is(type)

- -Check if the incoming request contains the "Content-Type" -header field, and it matches the give mime `type`. - -```js -// With Content-Type: text/html; charset=utf-8 -req.is('html') -req.is('text/html') -req.is('text/*') -// => true - -// When Content-Type is application/json -req.is('json') -req.is('application/json') -req.is('application/*') -// => true - -req.is('html') -// => false -``` diff --git a/_includes/api/en/3x/req-originalUrl.md b/_includes/api/en/3x/req-originalUrl.md deleted file mode 100644 index d209148d87..0000000000 --- a/_includes/api/en/3x/req-originalUrl.md +++ /dev/null @@ -1,13 +0,0 @@ -

req.originalUrl

- -This property is much like `req.url`, however it retains -the original request url, allowing you to rewrite `req.url` -freely for internal routing purposes. For example the "mounting" feature -of app.use() will rewrite `req.url` to -strip the mount point. - -```js -// GET /search?q=something -console.log(req.originalUrl) -// => "/search?q=something" -``` diff --git a/_includes/api/en/3x/req-param.md b/_includes/api/en/3x/req-param.md deleted file mode 100644 index 5aaa22f80a..0000000000 --- a/_includes/api/en/3x/req-param.md +++ /dev/null @@ -1,27 +0,0 @@ -

req.param(name)

- -Return the value of param `name` when present. - -```js -// ?name=tobi -req.param('name') -// => "tobi" - -// POST name=tobi -req.param('name') -// => "tobi" - -// /user/tobi for /user/:name -req.param('name') -// => "tobi" -``` - -Lookup is performed in the following order: - -* `req.params` -* `req.body` -* `req.query` - -Direct access to `req.body`, `req.params`, -and `req.query` should be favoured for clarity - unless -you truly accept input from each object. diff --git a/_includes/api/en/3x/req-params.md b/_includes/api/en/3x/req-params.md deleted file mode 100644 index 408f8ef8b6..0000000000 --- a/_includes/api/en/3x/req-params.md +++ /dev/null @@ -1,22 +0,0 @@ -

req.params

- -This property is an array containing properties mapped to the named route "parameters". -For example if you have the route `/user/:name`, then the "name" property -is available to you as `req.params.name`. This object defaults to `{}`. - -```js -// GET /user/tj -console.dir(req.params.name) -// => 'tj' -``` - -When a regular expression is used for the route definition, capture groups -are provided in the array using `req.params[N]`, where `N` -is the nth capture group. This rule is applied to unnamed wild-card matches -with string routes such as `/file/*`: - -```js -// GET /file/javascripts/jquery.js -console.dir(req.params[0]) -// => 'javascripts/jquery.js' -``` diff --git a/_includes/api/en/3x/req-path.md b/_includes/api/en/3x/req-path.md deleted file mode 100644 index 52f7ace8e4..0000000000 --- a/_includes/api/en/3x/req-path.md +++ /dev/null @@ -1,9 +0,0 @@ -

req.path

- -Returns the request URL pathname. - -```js -// example.com/users?sort=desc -console.dir(req.path) -// => '/users' -``` diff --git a/_includes/api/en/3x/req-protocol.md b/_includes/api/en/3x/req-protocol.md deleted file mode 100644 index 1090ab51cc..0000000000 --- a/_includes/api/en/3x/req-protocol.md +++ /dev/null @@ -1,13 +0,0 @@ -

req.protocol

- -Return the protocol string "http" or "https" -when requested with TLS. When the "trust proxy" -setting is enabled the "X-Forwarded-Proto" header -field will be trusted. If you're running behind -a reverse proxy that supplies https for you this -may be enabled. - -```js -console.dir(req.protocol) -// => 'http' -``` diff --git a/_includes/api/en/3x/req-query.md b/_includes/api/en/3x/req-query.md deleted file mode 100644 index 17c8a1dda8..0000000000 --- a/_includes/api/en/3x/req-query.md +++ /dev/null @@ -1,20 +0,0 @@ -

req.query

- -This property is an object containing the parsed query-string, -defaulting to `{}`. - -```js -// GET /search?q=tobi+ferret -console.dir(req.query.q) -// => 'tobi ferret' - -// GET /shoes?order=desc&shoe[color]=blue&shoe[type]=converse -console.dir(req.query.order) -// => 'desc' - -console.dir(req.query.shoe.color) -// => 'blue' - -console.dir(req.query.shoe.type) -// => 'converse' -``` diff --git a/_includes/api/en/3x/req-res.md b/_includes/api/en/3x/req-res.md deleted file mode 100644 index b6f30a3aa0..0000000000 --- a/_includes/api/en/3x/req-res.md +++ /dev/null @@ -1,4 +0,0 @@ -

req.res

- -This property holds a reference to the response object -that relates to this request object. diff --git a/_includes/api/en/3x/req-route.md b/_includes/api/en/3x/req-route.md deleted file mode 100644 index bbb59f2de8..0000000000 --- a/_includes/api/en/3x/req-route.md +++ /dev/null @@ -1,22 +0,0 @@ -

req.route

- -The currently matched `Route` containing -several properties such as the route's original path -string, the regexp generated, and so on. - -```js -app.get('/user/:id?', function (req, res) { - console.dir(req.route) -}) -``` - -Example output from the previous snippet: - -``` -{ path: '/user/:id?', - method: 'get', - callbacks: [ [Function] ], - keys: [ { name: 'id', optional: true } ], - regexp: /^\/user(?:\/([^\/]+?))?\/?$/i, - params: [ id: '12' ] } -``` diff --git a/_includes/api/en/3x/req-secure.md b/_includes/api/en/3x/req-secure.md deleted file mode 100644 index d78bc954a0..0000000000 --- a/_includes/api/en/3x/req-secure.md +++ /dev/null @@ -1,8 +0,0 @@ -

req.secure

- -Check if a TLS connection is established. This is a short-hand for: - -```js -console.dir(req.protocol === 'https') -// => true -``` diff --git a/_includes/api/en/3x/req-signedCookies.md b/_includes/api/en/3x/req-signedCookies.md deleted file mode 100644 index a60f16061a..0000000000 --- a/_includes/api/en/3x/req-signedCookies.md +++ /dev/null @@ -1,15 +0,0 @@ -

req.signedCookies

- -This object requires the `cookieParser(secret)` middleware for use. -It contains signed cookies sent by the user-agent, unsigned and ready for use. -Signed cookies reside in a different object to show developer intent; otherwise, -a malicious attack could be placed on `req.cookie` values (which are easy to spoof). -Note that signing a cookie does not make it "hidden" or encrypted; this simply -prevents tampering (because the secret used to sign is private). If no signed -cookies are sent, it defaults to `{}`. - -```js -// Cookie: user=tobi.CP7AWaXDfAKIRfH49dQzKJx7sKzzSoPq7/AcBBRVwlI3 -console.dir(req.signedCookies.user) -// => 'tobi' -``` diff --git a/_includes/api/en/3x/req-stale.md b/_includes/api/en/3x/req-stale.md deleted file mode 100644 index c41d3df8c7..0000000000 --- a/_includes/api/en/3x/req-stale.md +++ /dev/null @@ -1,9 +0,0 @@ -

req.stale

- -Check if the request is stale - aka Last-Modified and/or the ETag do not match, -indicating that the resource is "stale". - -```js -console.dir(req.stale) -// => true -``` diff --git a/_includes/api/en/3x/req-subdomains.md b/_includes/api/en/3x/req-subdomains.md deleted file mode 100644 index 5b405ac6e2..0000000000 --- a/_includes/api/en/3x/req-subdomains.md +++ /dev/null @@ -1,9 +0,0 @@ -

req.subdomains

- -Return subdomains as an array. - -```js -// Host: "tobi.ferrets.example.com" -console.dir(req.subdomains) -// => ['ferrets', 'tobi'] -``` diff --git a/_includes/api/en/3x/req-xhr.md b/_includes/api/en/3x/req-xhr.md deleted file mode 100644 index 05c8a4b30f..0000000000 --- a/_includes/api/en/3x/req-xhr.md +++ /dev/null @@ -1,9 +0,0 @@ -

req.xhr

- -Check if the request was issued with the "X-Requested-With" -header field set to "XMLHttpRequest" (jQuery etc). - -```js -console.dir(req.xhr) -// => true -``` diff --git a/_includes/api/en/3x/req.md b/_includes/api/en/3x/req.md deleted file mode 100644 index 115d5dd721..0000000000 --- a/_includes/api/en/3x/req.md +++ /dev/null @@ -1,116 +0,0 @@ -

Request

- -The `req` object is an enhanced version of Node's own request object -and supports all [built-in fields and methods](https://nodejs.org/api/http.html#http_class_http_incomingmessage). - -
- {% include api/en/3x/req-params.md %} -
- -
- {% include api/en/3x/req-query.md %} -
- -
- {% include api/en/3x/req-body.md %} -
- -
- {% include api/en/3x/req-files.md %} -
- -
- {% include api/en/3x/req-param.md %} -
- -
- {% include api/en/3x/req-route.md %} -
- -
- {% include api/en/3x/req-cookies.md %} -
- -
- {% include api/en/3x/req-signedCookies.md %} -
- -
- {% include api/en/3x/req-header.md %} -
- -
- {% include api/en/3x/req-accepts.md %} -
- -
- {% include api/en/3x/req-accepted.md %} -
- -
- {% include api/en/3x/req-is.md %} -
- -
- {% include api/en/3x/req-ip.md %} -
- -
- {% include api/en/3x/req-ips.md %} -
- -
- {% include api/en/3x/req-path.md %} -
- -
- {% include api/en/3x/req-host.md %} -
- -
- {% include api/en/3x/req-fresh.md %} -
- -
- {% include api/en/3x/req-stale.md %} -
- -
- {% include api/en/3x/req-xhr.md %} -
- -
- {% include api/en/3x/req-protocol.md %} -
- -
- {% include api/en/3x/req-secure.md %} -
- -
- {% include api/en/3x/req-subdomains.md %} -
- -
- {% include api/en/3x/req-originalUrl.md %} -
- -
- {% include api/en/3x/req-acceptedLanguages.md %} -
- -
- {% include api/en/3x/req-acceptedCharsets.md %} -
- -
- {% include api/en/3x/req-acceptsCharset.md %} -
- -
- {% include api/en/3x/req-acceptsLanguage.md %} -
- -
- {% include api/en/3x/req-res.md %} -
diff --git a/_includes/api/en/3x/res-attachment.md b/_includes/api/en/3x/res-attachment.md deleted file mode 100644 index d52e3bba6f..0000000000 --- a/_includes/api/en/3x/res-attachment.md +++ /dev/null @@ -1,15 +0,0 @@ -

res.attachment([filename])

- -Sets the Content-Disposition header field to "attachment". If -a `filename` is given then the Content-Type will be -automatically set based on the extname via `res.type()`, -and the Content-Disposition's "filename=" parameter will be set. - -```js -res.attachment() -// Content-Disposition: attachment - -res.attachment('path/to/logo.png') -// Content-Disposition: attachment; filename="logo.png" -// Content-Type: image/png -``` diff --git a/_includes/api/en/3x/res-charset.md b/_includes/api/en/3x/res-charset.md deleted file mode 100644 index 07d0a35e95..0000000000 --- a/_includes/api/en/3x/res-charset.md +++ /dev/null @@ -1,9 +0,0 @@ -

res.charset

- -Assign the charset. Defaults to "utf-8". - -```js -res.charset = 'value' -res.send('

some html

') -// => Content-Type: text/html; charset=value -``` diff --git a/_includes/api/en/3x/res-clearCookie.md b/_includes/api/en/3x/res-clearCookie.md deleted file mode 100644 index afb139628d..0000000000 --- a/_includes/api/en/3x/res-clearCookie.md +++ /dev/null @@ -1,8 +0,0 @@ -

res.clearCookie(name, [options])

- -Clear cookie `name`. The `path` option defaults to "/". - -```js -res.cookie('name', 'tobi', { path: '/admin' }) -res.clearCookie('name', { path: '/admin' }) -``` diff --git a/_includes/api/en/3x/res-cookie.md b/_includes/api/en/3x/res-cookie.md deleted file mode 100644 index 5f6eb0186c..0000000000 --- a/_includes/api/en/3x/res-cookie.md +++ /dev/null @@ -1,37 +0,0 @@ -

res.cookie(name, value, [options])

- -Set cookie `name` to `value`, which may be a string or object converted to JSON. The `path` -option defaults to "/". - -```js -res.cookie('name', 'tobi', { domain: '.example.com', path: '/admin', secure: true }) -res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true }) -``` - -The `maxAge` option is a convenience option for setting "expires" -relative to the current time in milliseconds. The following is equivalent to -the previous example. - -```js -res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true }) -``` - -An object may be passed which is then serialized as JSON, which is -automatically parsed by the `bodyParser()` middleware. - -```js -res.cookie('cart', { items: [1, 2, 3] }) -res.cookie('cart', { items: [1, 2, 3] }, { maxAge: 900000 }) -``` - -Signed cookies are also supported through this method. Simply -pass the `signed` option. When given `res.cookie()` -will use the secret passed to `express.cookieParser(secret)` -to sign the value. - -```js -res.cookie('name', 'tobi', { signed: true }) -``` - -Later you may access this value through the req.signedCookie -object. diff --git a/_includes/api/en/3x/res-download.md b/_includes/api/en/3x/res-download.md deleted file mode 100644 index 43d48f769d..0000000000 --- a/_includes/api/en/3x/res-download.md +++ /dev/null @@ -1,26 +0,0 @@ -

res.download(path, [filename], [fn])

- -Transfer the file at `path` as an "attachment", -typically browsers will prompt the user for download. The -Content-Disposition "filename=" parameter, aka the one -that will appear in the brower dialog is set to `path` -by default, however you may provide an override `filename`. - -When an error has ocurred or transfer is complete the optional -callback `fn` is invoked. This method uses res.sendfile() -to transfer the file. - -```js -res.download('/report-12345.pdf') - -res.download('/report-12345.pdf', 'report.pdf') - -res.download('/report-12345.pdf', 'report.pdf', function (err) { - if (err) { - // handle error, keep in mind the response may be partially-sent - // so check res.headerSent - } else { - // decrement a download credit etc - } -}) -``` diff --git a/_includes/api/en/3x/res-format.md b/_includes/api/en/3x/res-format.md deleted file mode 100644 index 5092c626a1..0000000000 --- a/_includes/api/en/3x/res-format.md +++ /dev/null @@ -1,52 +0,0 @@ -

res.format(object)

- -Performs content-negotiation on the request Accept header -field when present. This method uses `req.accepted`, an array of -acceptable types ordered by their quality values, otherwise the -first callback is invoked. When no match is performed the server -responds with 406 "Not Acceptable", or invokes the `default` -callback. - -The Content-Type is set for you when a callback is selected, -however you may alter this within the callback using `res.set()` -or `res.type()` etcetera. - -The following example would respond with `{ "message": "hey" }` -when the Accept header field is set to "application/json" or "*/json", -however if "*/*" is given then "hey" will be the response. - -```js -res.format({ - 'text/plain': function () { - res.send('hey') - }, - - 'text/html': function () { - res.send('

hey

') - }, - - 'application/json': function () { - res.send({ message: 'hey' }) - } -}) -``` - -In addition to canonicalized MIME types you may also -use extnames mapped to these types, providing a slightly -less verbose implementation: - -```js -res.format({ - text: function () { - res.send('hey') - }, - - html: function () { - res.send('

hey

') - }, - - json: function () { - res.send({ message: 'hey' }) - } -}) -``` diff --git a/_includes/api/en/3x/res-get.md b/_includes/api/en/3x/res-get.md deleted file mode 100644 index 2dbf992020..0000000000 --- a/_includes/api/en/3x/res-get.md +++ /dev/null @@ -1,8 +0,0 @@ -

res.get(field)

- -Get the case-insensitive response header `field`. - -```js -res.get('Content-Type') -// => "text/plain" -``` diff --git a/_includes/api/en/3x/res-json.md b/_includes/api/en/3x/res-json.md deleted file mode 100644 index d13bb2ab0b..0000000000 --- a/_includes/api/en/3x/res-json.md +++ /dev/null @@ -1,13 +0,0 @@ -

res.json([status|body], [body])

- -Send a JSON response. This method is identical -to `res.send()` when an object or -array is passed, however it may be used for -explicit JSON conversion of non-objects (null, undefined, etc), -though these are technically not valid JSON. - -```js -res.json(null) -res.json({ user: 'tobi' }) -res.json(500, { error: 'message' }) -``` diff --git a/_includes/api/en/3x/res-jsonp.md b/_includes/api/en/3x/res-jsonp.md deleted file mode 100644 index 5340c4487d..0000000000 --- a/_includes/api/en/3x/res-jsonp.md +++ /dev/null @@ -1,33 +0,0 @@ -

res.jsonp([status|body], [body])

- -Send a JSON response with JSONP support. This method is identical -to `res.json()` however opts-in to JSONP callback -support. - -```js -res.jsonp(null) -// => null - -res.jsonp({ user: 'tobi' }) -// => { "user": "tobi" } - -res.jsonp(500, { error: 'message' }) -// => { "error": "message" } -``` - -By default the JSONP callback name is simply `callback`, -however you may alter this with the jsonp callback name -setting. The following are some examples of JSONP responses using the same -code: - -```js -// ?callback=foo -res.jsonp({ user: 'tobi' }) -// => foo({ "user": "tobi" }) - -app.set('jsonp callback name', 'cb') - -// ?cb=foo -res.jsonp(500, { error: 'message' }) -// => foo({ "error": "message" }) -``` diff --git a/_includes/api/en/3x/res-links.md b/_includes/api/en/3x/res-links.md deleted file mode 100644 index 2b8c734b24..0000000000 --- a/_includes/api/en/3x/res-links.md +++ /dev/null @@ -1,17 +0,0 @@ - - -Join the given `links` to populate the "Link" response header field. - -```js -res.links({ - next: 'http://api.example.com/users?page=2', - last: 'http://api.example.com/users?page=5' -}) -``` - -p yields: - -``` -Link: rel="next", - rel="last" -``` diff --git a/_includes/api/en/3x/res-locals.md b/_includes/api/en/3x/res-locals.md deleted file mode 100644 index 5c6dbfbf95..0000000000 --- a/_includes/api/en/3x/res-locals.md +++ /dev/null @@ -1,16 +0,0 @@ -

res.locals

- -Response local variables are scoped to the request, thus only -available to the view(s) rendered during that request / response -cycle, if any. Otherwise this API is identical to app.locals. - -This object is useful for exposing request-level information such as the -request pathname, authenticated user, user settings etcetera. - -```js -app.use(function (req, res, next) { - res.locals.user = req.user - res.locals.authenticated = !req.user.anonymous - next() -}) -``` diff --git a/_includes/api/en/3x/res-location.md b/_includes/api/en/3x/res-location.md deleted file mode 100644 index 60b59db930..0000000000 --- a/_includes/api/en/3x/res-location.md +++ /dev/null @@ -1,21 +0,0 @@ -

res.location

- -Set the location header. - -```js -res.location('/foo/bar') -res.location('foo/bar') -res.location('http://example.com') -res.location('../login') -res.location('back') -``` - -You can use the same kind of `urls` as in `res.redirect()`. - -For example, if your application is mounted at `/blog`, -the following would set the `location` header to -`/blog/admin`: - -```js -res.location('admin') -``` diff --git a/_includes/api/en/3x/res-redirect.md b/_includes/api/en/3x/res-redirect.md deleted file mode 100644 index dc87d689c2..0000000000 --- a/_includes/api/en/3x/res-redirect.md +++ /dev/null @@ -1,51 +0,0 @@ -

res.redirect([status], url)

- -Redirect to the given `url` with optional `status` code -defaulting to 302 "Found". - -```js -res.redirect('/foo/bar') -res.redirect('http://example.com') -res.redirect(301, 'http://example.com') -res.redirect('../login') -``` - -Express supports a few forms of redirection, first being -a fully qualified URI for redirecting to a different site: - -```js -res.redirect('http://google.com') -``` - -The second form is the pathname-relative redirect, for example -if you were on `http://example.com/admin/post/new`, the -following redirect to `/admin` would land you at `http://example.com/admin`: - -```js -res.redirect('/admin') -``` - -This next redirect is relative to the `mount` point of the application. For example -if you have a blog application mounted at `/blog`, ideally it has no knowledge of -where it was mounted, so where a redirect of `/admin/post/new` would simply give you -`http://example.com/admin/post/new`, the following mount-relative redirect would give -you `http://example.com/blog/admin/post/new`: - -```js -res.redirect('admin/post/new') -``` - -Pathname relative redirects are also possible. If you were -on `http://example.com/admin/post/new`, the following redirect -would land you at `http//example.com/admin/post`: - -```js -res.redirect('..') -``` - -The final special-case is a `back` redirect, redirecting back to -the Referer (or Referrer), defaulting to `/` when missing. - -```js -res.redirect('back') -``` diff --git a/_includes/api/en/3x/res-render.md b/_includes/api/en/3x/res-render.md deleted file mode 100644 index e87cba9f3e..0000000000 --- a/_includes/api/en/3x/res-render.md +++ /dev/null @@ -1,16 +0,0 @@ -

res.render(view, [locals], callback)

- -Render a `view` with a callback responding with -the rendered string. When an error occurs `next(err)` -is invoked internally. When a callback is provided both the possible error -and rendered string are passed, and no automated response is performed. - -```js -res.render('index', function (err, html) { - // ... -}) - -res.render('user', { name: 'Tobi' }, function (err, html) { - // ... -}) -``` diff --git a/_includes/api/en/3x/res-req.md b/_includes/api/en/3x/res-req.md deleted file mode 100644 index 6c0236cdc1..0000000000 --- a/_includes/api/en/3x/res-req.md +++ /dev/null @@ -1,4 +0,0 @@ -

res.req

- -This property holds a reference to the request object -that relates to this response object. diff --git a/_includes/api/en/3x/res-send.md b/_includes/api/en/3x/res-send.md deleted file mode 100644 index 75903b13cb..0000000000 --- a/_includes/api/en/3x/res-send.md +++ /dev/null @@ -1,53 +0,0 @@ -

res.send([body|status], [body])

- -Send a response. - -```js -res.send(Buffer.from('whoop')) -res.send({ some: 'json' }) -res.send('

some html

') -res.send(404, 'Sorry, we cannot find that!') -res.send(500, { error: 'something blew up' }) -res.send(200) -``` - -This method performs a myriad of -useful tasks for simple non-streaming responses such -as automatically assigning the Content-Length unless -previously defined and providing automatic HEAD and -HTTP cache freshness support. - -When a `Buffer` is given -the Content-Type is set to "application/octet-stream" -unless previously defined as shown below: - -```js -res.set('Content-Type', 'text/html') -res.send(Buffer.from('

some html

')) -``` - -When a `String` is given the -Content-Type is set defaulted to "text/html": - -```js -res.send('

some html

') -``` - -When an `Array` or `Object` is -given Express will respond with the JSON representation: - -```js -res.send({ user: 'tobi' }) -res.send([1, 2, 3]) -``` - -Finally when a `Number` is given without -any of the previously mentioned bodies, then a response -body string is assigned for you. For example 200 will -respond will the text "OK", and 404 "Not Found" and so on. - -```js -res.send(200) -res.send(404) -res.send(500) -``` diff --git a/_includes/api/en/3x/res-sendfile.md b/_includes/api/en/3x/res-sendfile.md deleted file mode 100644 index 0580a98b48..0000000000 --- a/_includes/api/en/3x/res-sendfile.md +++ /dev/null @@ -1,30 +0,0 @@ -

res.sendfile(path, [options], [fn]])

- -Transfer the file at the given `path`. - -Automatically defaults the Content-Type response header field based -on the filename's extension. The callback `fn(err)` is -invoked when the transfer is complete or when an error occurs. - -Options: - -* `maxAge` in milliseconds defaulting to 0 -* `root` root directory for relative filenames - -This method provides fine-grained support for file serving -as illustrated in the following example: - -```js -app.get('/user/:uid/photos/:file', function (req, res) { - var uid = req.params.uid - var file = req.params.file - - req.user.mayViewFilesFrom(uid, function (yes) { - if (yes) { - res.sendfile('/uploads/' + uid + '/' + file) - } else { - res.send(403, 'Sorry! you cant see that.') - } - }) -}) -``` diff --git a/_includes/api/en/3x/res-set.md b/_includes/api/en/3x/res-set.md deleted file mode 100644 index 76ca5086b1..0000000000 --- a/_includes/api/en/3x/res-set.md +++ /dev/null @@ -1,15 +0,0 @@ -

res.set(field, [value])

- -Set header `field` to `value`, or pass an object to set multiple fields at once. - -```js -res.set('Content-Type', 'text/plain') - -res.set({ - 'Content-Type': 'text/plain', - 'Content-Length': '123', - ETag: '12345' -}) -``` - -Aliased as `res.header(field, [value])`. diff --git a/_includes/api/en/3x/res-status.md b/_includes/api/en/3x/res-status.md deleted file mode 100644 index 3be23e5138..0000000000 --- a/_includes/api/en/3x/res-status.md +++ /dev/null @@ -1,7 +0,0 @@ -

res.status(code)

- -Chainable alias of node's `res.statusCode=`. - -```js -res.status(404).sendfile('path/to/404.png') -``` diff --git a/_includes/api/en/3x/res-type.md b/_includes/api/en/3x/res-type.md deleted file mode 100644 index d686d65105..0000000000 --- a/_includes/api/en/3x/res-type.md +++ /dev/null @@ -1,15 +0,0 @@ -

res.type(type)

- -Sets the Content-Type to the mime lookup of `type`, -or when "/" is present the Content-Type is simply set to this -literal value. - -```js -res.type('.html') -res.type('html') -res.type('json') -res.type('application/json') -res.type('png') -``` - -p Aliased as `res.contentType(type)`. diff --git a/_includes/api/en/3x/res.md b/_includes/api/en/3x/res.md deleted file mode 100644 index b56f9fb7d6..0000000000 --- a/_includes/api/en/3x/res.md +++ /dev/null @@ -1,84 +0,0 @@ -

Response

- -The `res` object is an enhanced version of Node's own response object -and supports all [built-in fields and methods](https://nodejs.org/api/http.html#http_class_http_serverresponse). - -
- {% include api/en/3x/res-status.md %} -
- -
- {% include api/en/3x/res-set.md %} -
- -
- {% include api/en/3x/res-get.md %} -
- -
- {% include api/en/3x/res-cookie.md %} -
- -
- {% include api/en/3x/res-clearCookie.md %} -
- -
- {% include api/en/3x/res-redirect.md %} -
- -
- {% include api/en/3x/res-location.md %} -
- -
- {% include api/en/3x/res-charset.md %} -
- -
- {% include api/en/3x/res-send.md %} -
- -
- {% include api/en/3x/res-json.md %} -
- -
- {% include api/en/3x/res-jsonp.md %} -
- -
- {% include api/en/3x/res-type.md %} -
- -
- {% include api/en/3x/res-format.md %} -
- -
- {% include api/en/3x/res-attachment.md %} -
- -
- {% include api/en/3x/res-sendfile.md %} -
- -
- {% include api/en/3x/res-download.md %} -
- -
- {% include api/en/3x/res-links.md %} -
- -
- {% include api/en/3x/res-locals.md %} -
- -
- {% include api/en/3x/res-render.md %} -
- -
- {% include api/en/3x/res-req.md %} -
diff --git a/_includes/api/en/4x/app-METHOD.md b/_includes/api/en/4x/app-METHOD.md deleted file mode 100644 index c526a04d11..0000000000 --- a/_includes/api/en/4x/app-METHOD.md +++ /dev/null @@ -1,60 +0,0 @@ -

app.METHOD(path, callback [, callback ...])

- -Routes an HTTP request, where METHOD is the HTTP method of the request, such as GET, -PUT, POST, and so on, in lowercase. Thus, the actual methods are `app.get()`, -`app.post()`, `app.put()`, and so on. See [Routing methods](#routing-methods) below for the complete list. - -{% include api/en/4x/routing-args.html %} - -#### Routing methods - -Express supports the following routing methods corresponding to the HTTP methods of the same names: - -
-
    -
  • checkout
  • -
  • copy
  • -
  • delete
  • -
  • get
  • -
  • head
  • -
  • lock
  • -
  • merge
  • -
  • mkactivity
  • -
-
    -
  • mkcol
  • -
  • move
  • -
  • m-search
  • -
  • notify
  • -
  • options
  • -
  • patch
  • -
  • post
  • -
-
    -
  • purge
  • -
  • put
  • -
  • report
  • -
  • search
  • -
  • subscribe
  • -
  • trace
  • -
  • unlock
  • -
  • unsubscribe
  • -
-
- -The API documentation has explicit entries only for the most popular HTTP methods `app.get()`, -`app.post()`, `app.put()`, and `app.delete()`. -However, the other methods listed above work in exactly the same way. - -To route methods that translate to invalid JavaScript variable names, use the bracket notation. For example, `app['m-search']('/', function ...`. - -
- The `app.get()` function is automatically called for the HTTP `HEAD` method in addition to the `GET` - method if `app.head()` was not called for the path before `app.get()`. -
- -The method, `app.all()`, is not derived from any HTTP method and loads middleware at -the specified path for _all_ HTTP request methods. -For more information, see [app.all](#app.all). - -For more information on routing, see the [routing guide](/{{page.lang}}/guide/routing.html). diff --git a/_includes/api/en/4x/app-all.md b/_includes/api/en/4x/app-all.md deleted file mode 100644 index 2939bd6f07..0000000000 --- a/_includes/api/en/4x/app-all.md +++ /dev/null @@ -1,44 +0,0 @@ -

app.all(path, callback [, callback ...])

- -This method is like the standard [app.METHOD()](#app.METHOD) methods, -except it matches all HTTP verbs. - -{% include api/en/4x/routing-args.html %} - -#### Examples - -The following callback is executed for requests to `/secret` whether using -GET, POST, PUT, DELETE, or any other HTTP request method: - -```js -app.all('/secret', function (req, res, next) { - console.log('Accessing the secret section ...') - next() // pass control to the next handler -}) -``` - -The `app.all()` method is useful for mapping "global" logic for specific path prefixes or arbitrary matches. For example, if you put the following at the top of all other -route definitions, it requires that all routes from that point on -require authentication, and automatically load a user. Keep in mind -that these callbacks do not have to act as end-points: `loadUser` -can perform a task, then call `next()` to continue matching subsequent -routes. - -```js -app.all('*', requireAuthentication, loadUser) -``` - -Or the equivalent: - -```js -app.all('*', requireAuthentication) -app.all('*', loadUser) -``` - -Another example is white-listed "global" functionality. -The example is similar to the ones above, but it only restricts paths that start with -"/api": - -```js -app.all('/api/*', requireAuthentication) -``` diff --git a/_includes/api/en/4x/app-delete-method.md b/_includes/api/en/4x/app-delete-method.md deleted file mode 100644 index 0d9e5cbcc0..0000000000 --- a/_includes/api/en/4x/app-delete-method.md +++ /dev/null @@ -1,14 +0,0 @@ -

app.delete(path, callback [, callback ...])

- -Routes HTTP DELETE requests to the specified path with the specified callback functions. -For more information, see the [routing guide](/{{page.lang}}/guide/routing.html). - -{% include api/en/4x/routing-args.html %} - -#### Example - -```js -app.delete('/', function (req, res) { - res.send('DELETE request to homepage') -}) -``` diff --git a/_includes/api/en/4x/app-disable.md b/_includes/api/en/4x/app-disable.md deleted file mode 100644 index 08bbcf11ba..0000000000 --- a/_includes/api/en/4x/app-disable.md +++ /dev/null @@ -1,12 +0,0 @@ -

app.disable(name)

- -Sets the Boolean setting `name` to `false`, where `name` is one of the properties from the [app settings table](#app.settings.table). -Calling `app.set('foo', false)` for a Boolean property is the same as calling `app.disable('foo')`. - -For example: - -```js -app.disable('trust proxy') -app.get('trust proxy') -// => false -``` diff --git a/_includes/api/en/4x/app-disabled.md b/_includes/api/en/4x/app-disabled.md deleted file mode 100644 index 370048fde6..0000000000 --- a/_includes/api/en/4x/app-disabled.md +++ /dev/null @@ -1,13 +0,0 @@ -

app.disabled(name)

- -Returns `true` if the Boolean setting `name` is disabled (`false`), where `name` is one of the properties from -the [app settings table](#app.settings.table). - -```js -app.disabled('trust proxy') -// => true - -app.enable('trust proxy') -app.disabled('trust proxy') -// => false -``` diff --git a/_includes/api/en/4x/app-enable.md b/_includes/api/en/4x/app-enable.md deleted file mode 100644 index 1152335a4c..0000000000 --- a/_includes/api/en/4x/app-enable.md +++ /dev/null @@ -1,10 +0,0 @@ -

app.enable(name)

- -Sets the Boolean setting `name` to `true`, where `name` is one of the properties from the [app settings table](#app.settings.table). -Calling `app.set('foo', true)` for a Boolean property is the same as calling `app.enable('foo')`. - -```js -app.enable('trust proxy') -app.get('trust proxy') -// => true -``` diff --git a/_includes/api/en/4x/app-enabled.md b/_includes/api/en/4x/app-enabled.md deleted file mode 100644 index ad9b17aa89..0000000000 --- a/_includes/api/en/4x/app-enabled.md +++ /dev/null @@ -1,13 +0,0 @@ -

app.enabled(name)

- -Returns `true` if the setting `name` is enabled (`true`), where `name` is one of the -properties from the [app settings table](#app.settings.table). - -```js -app.enabled('trust proxy') -// => false - -app.enable('trust proxy') -app.enabled('trust proxy') -// => true -``` diff --git a/_includes/api/en/4x/app-engine.md b/_includes/api/en/4x/app-engine.md deleted file mode 100644 index 4906a254f1..0000000000 --- a/_includes/api/en/4x/app-engine.md +++ /dev/null @@ -1,36 +0,0 @@ -

app.engine(ext, callback)

- -Registers the given template engine `callback` as `ext`. - -By default, Express will `require()` the engine based on the file extension. -For example, if you try to render a "foo.pug" file, Express invokes the -following internally, and caches the `require()` on subsequent calls to increase -performance. - -```js -app.engine('pug', require('pug').__express) -``` - -Use this method for engines that do not provide `.__express` out of the box, -or if you wish to "map" a different extension to the template engine. - -For example, to map the EJS template engine to ".html" files: - -```js -app.engine('html', require('ejs').renderFile) -``` - -In this case, EJS provides a `.renderFile()` method with -the same signature that Express expects: `(path, options, callback)`, -though note that it aliases this method as `ejs.__express` internally -so if you're using ".ejs" extensions you don't need to do anything. - -Some template engines do not follow this convention. The -[consolidate.js](https://github.com/tj/consolidate.js) library maps Node template engines to follow this convention, -so they work seamlessly with Express. - -```js -var engines = require('consolidate') -app.engine('haml', engines.haml) -app.engine('html', engines.hogan) -``` diff --git a/_includes/api/en/4x/app-get-method.md b/_includes/api/en/4x/app-get-method.md deleted file mode 100644 index 37c61c342c..0000000000 --- a/_includes/api/en/4x/app-get-method.md +++ /dev/null @@ -1,15 +0,0 @@ -

app.get(path, callback [, callback ...])

- -Routes HTTP GET requests to the specified path with the specified callback functions. - -{% include api/en/4x/routing-args.html %} - -For more information, see the [routing guide](/{{page.lang}}/guide/routing.html). - -#### Example - -```js -app.get('/', function (req, res) { - res.send('GET request to homepage') -}) -``` diff --git a/_includes/api/en/4x/app-get.md b/_includes/api/en/4x/app-get.md deleted file mode 100644 index c93ef34d70..0000000000 --- a/_includes/api/en/4x/app-get.md +++ /dev/null @@ -1,13 +0,0 @@ -

app.get(name)

- -Returns the value of `name` app setting, where `name` is one of the strings in the -[app settings table](#app.settings.table). For example: - -```js -app.get('title') -// => undefined - -app.set('title', 'My Site') -app.get('title') -// => "My Site" -``` diff --git a/_includes/api/en/4x/app-listen.md b/_includes/api/en/4x/app-listen.md deleted file mode 100644 index 26bde4121b..0000000000 --- a/_includes/api/en/4x/app-listen.md +++ /dev/null @@ -1,53 +0,0 @@ -

app.listen(path, [callback])

- -Starts a UNIX socket and listens for connections on the given path. -This method is identical to Node's [http.Server.listen()](https://nodejs.org/api/http.html#http_server_listen). - -```js -var express = require('express') -var app = express() -app.listen('/tmp/sock') -``` - -

app.listen([port[, host[, backlog]]][, callback])

- -Binds and listens for connections on the specified host and port. -This method is identical to Node's [http.Server.listen()](https://nodejs.org/api/http.html#http_server_listen). - -If port is omitted or is 0, the operating system will assign an arbitrary unused -port, which is useful for cases like automated tasks (tests, etc.). - -```js -var express = require('express') -var app = express() -app.listen(3000) -``` - -The `app` returned by `express()` is in fact a JavaScript -`Function`, designed to be passed to Node's HTTP servers as a callback -to handle requests. This makes it easy to provide both HTTP and HTTPS versions of -your app with the same code base, as the app does not inherit from these -(it is simply a callback): - -```js -var express = require('express') -var https = require('https') -var http = require('http') -var app = express() - -http.createServer(app).listen(80) -https.createServer(options, app).listen(443) -``` - -The `app.listen()` method returns an [http.Server](https://nodejs.org/api/http.html#http_class_http_server) object and (for HTTP) is a convenience method for the following: - -```js -app.listen = function () { - var server = http.createServer(this) - return server.listen.apply(server, arguments) -} -``` - -{% include admonitions/note.html content="All the forms of Node's -[http.Server.listen()](https://nodejs.org/api/http.html#http_server_listen) -method are in fact actually supported." %} diff --git a/_includes/api/en/4x/app-locals.md b/_includes/api/en/4x/app-locals.md deleted file mode 100644 index 178d640054..0000000000 --- a/_includes/api/en/4x/app-locals.md +++ /dev/null @@ -1,34 +0,0 @@ -

app.locals

- -The `app.locals` object has properties that are local variables within the application, -and will be available in templates rendered with [res.render](#res.render). - -
-The `locals` object is used by view engines to render a response. The object -keys may be particularly sensitive and should not contain user-controlled -input, as it may affect the operation of the view engine or provide a path to -cross-site scripting. Consult the documentation for the used view engine for -additional considerations. -
- -```js -console.dir(app.locals.title) -// => 'My App' - -console.dir(app.locals.email) -// => 'me@myapp.com' -``` - -Once set, the value of `app.locals` properties persist throughout the life of the application, -in contrast with [res.locals](#res.locals) properties that -are valid only for the lifetime of the request. - -You can access local variables in templates rendered within the application. -This is useful for providing helper functions to templates, as well as application-level data. -Local variables are available in middleware via `req.app.locals` (see [req.app](#req.app)) - -```js -app.locals.title = 'My App' -app.locals.strftime = require('strftime') -app.locals.email = 'me@myapp.com' -``` diff --git a/_includes/api/en/4x/app-mountpath.md b/_includes/api/en/4x/app-mountpath.md deleted file mode 100644 index 2ccdd57202..0000000000 --- a/_includes/api/en/4x/app-mountpath.md +++ /dev/null @@ -1,45 +0,0 @@ -

app.mountpath

- -The `app.mountpath` property contains one or more path patterns on which a sub-app was mounted. - -
- A sub-app is an instance of `express` that may be used for handling the request to a route. -
- -```js -var express = require('express') - -var app = express() // the main app -var admin = express() // the sub app - -admin.get('/', function (req, res) { - console.log(admin.mountpath) // /admin - res.send('Admin Homepage') -}) - -app.use('/admin', admin) // mount the sub app -``` - -It is similar to the [baseUrl](#req.baseUrl) property of the `req` object, except `req.baseUrl` -returns the matched URL path, instead of the matched patterns. - -If a sub-app is mounted on multiple path patterns, `app.mountpath` returns the list of -patterns it is mounted on, as shown in the following example. - -```js -var admin = express() - -admin.get('/', function (req, res) { - console.dir(admin.mountpath) // [ '/adm*n', '/manager' ] - res.send('Admin Homepage') -}) - -var secret = express() -secret.get('/', function (req, res) { - console.log(secret.mountpath) // /secr*t - res.send('Admin Secret') -}) - -admin.use('/secr*t', secret) // load the 'secret' router on '/secr*t', on the 'admin' sub app -app.use(['/adm*n', '/manager'], admin) // load the 'admin' router on '/adm*n' and '/manager', on the parent app -``` diff --git a/_includes/api/en/4x/app-onmount.md b/_includes/api/en/4x/app-onmount.md deleted file mode 100644 index 8275cf19b3..0000000000 --- a/_includes/api/en/4x/app-onmount.md +++ /dev/null @@ -1,29 +0,0 @@ -

app.on('mount', callback(parent))

- -The `mount` event is fired on a sub-app, when it is mounted on a parent app. The parent app is passed to the callback function. - -
-**NOTE** - -Sub-apps will: - -* Not inherit the value of settings that have a default value. You must set the value in the sub-app. -* Inherit the value of settings with no default value. - -For details, see [Application settings](/en/4x/api.html#app.settings.table). -
- -```js -var admin = express() - -admin.on('mount', function (parent) { - console.log('Admin Mounted') - console.log(parent) // refers to the parent app -}) - -admin.get('/', function (req, res) { - res.send('Admin Homepage') -}) - -app.use('/admin', admin) -``` diff --git a/_includes/api/en/4x/app-param.md b/_includes/api/en/4x/app-param.md deleted file mode 100644 index 2b0d8bf2aa..0000000000 --- a/_includes/api/en/4x/app-param.md +++ /dev/null @@ -1,154 +0,0 @@ -

app.param([name], callback)

- -Add callback triggers to [route parameters](/{{ page.lang }}/guide/routing.html#route-parameters), where `name` is the name of the parameter or an array of them, and `callback` is the callback function. The parameters of the callback function are the request object, the response object, the next middleware, the value of the parameter and the name of the parameter, in that order. - -If `name` is an array, the `callback` trigger is registered for each parameter declared in it, in the order in which they are declared. Furthermore, for each declared parameter except the last one, a call to `next` inside the callback will call the callback for the next declared parameter. For the last parameter, a call to `next` will call the next middleware in place for the route currently being processed, just like it would if `name` were just a string. - -For example, when `:user` is present in a route path, you may map user loading logic to automatically provide `req.user` to the route, or perform validations on the parameter input. - -```js -app.param('user', function (req, res, next, id) { - // try to get the user details from the User model and attach it to the request object - User.find(id, function (err, user) { - if (err) { - next(err) - } else if (user) { - req.user = user - next() - } else { - next(new Error('failed to load user')) - } - }) -}) -``` - -Param callback functions are local to the router on which they are defined. They are not inherited by mounted apps or routers, nor are they triggered for route parameters inherited from parent routers. Hence, param callbacks defined on `app` will be triggered only by route parameters defined on `app` routes. - -All param callbacks will be called before any handler of any route in which the param occurs, and they will each be called only once in a request-response cycle, even if the parameter is matched in multiple routes, as shown in the following examples. - -```js -app.param('id', function (req, res, next, id) { - console.log('CALLED ONLY ONCE') - next() -}) - -app.get('/user/:id', function (req, res, next) { - console.log('although this matches') - next() -}) - -app.get('/user/:id', function (req, res) { - console.log('and this matches too') - res.end() -}) -``` - -On `GET /user/42`, the following is printed: - -``` -CALLED ONLY ONCE -although this matches -and this matches too -``` - -```js -app.param(['id', 'page'], function (req, res, next, value) { - console.log('CALLED ONLY ONCE with', value) - next() -}) - -app.get('/user/:id/:page', function (req, res, next) { - console.log('although this matches') - next() -}) - -app.get('/user/:id/:page', function (req, res) { - console.log('and this matches too') - res.end() -}) -``` - -On `GET /user/42/3`, the following is printed: - -``` -CALLED ONLY ONCE with 42 -CALLED ONLY ONCE with 3 -although this matches -and this matches too -``` - -
-The following section describes `app.param(callback)`, which is deprecated as of v4.11.0. -
- -The behavior of the `app.param(name, callback)` method can be altered entirely by passing only a function to `app.param()`. This function is a custom implementation of how `app.param(name, callback)` should behave - it accepts two parameters and must return a middleware. - -The first parameter of this function is the name of the URL parameter that should be captured, the second parameter can be any JavaScript object which might be used for returning the middleware implementation. - -The middleware returned by the function decides the behavior of what happens when a URL parameter is captured. - -In this example, the `app.param(name, callback)` signature is modified to `app.param(name, accessId)`. Instead of accepting a name and a callback, `app.param()` will now accept a name and a number. - -```js -var express = require('express') -var app = express() - -// customizing the behavior of app.param() -app.param(function (param, option) { - return function (req, res, next, val) { - if (val === option) { - next() - } else { - next('route') - } - } -}) - -// using the customized app.param() -app.param('id', 1337) - -// route to trigger the capture -app.get('/user/:id', function (req, res) { - res.send('OK') -}) - -app.listen(3000, function () { - console.log('Ready') -}) -``` - -In this example, the `app.param(name, callback)` signature remains the same, but instead of a middleware callback, a custom data type checking function has been defined to validate the data type of the user id. - -```js -app.param(function (param, validator) { - return function (req, res, next, val) { - if (validator(val)) { - next() - } else { - next('route') - } - } -}) - -app.param('id', function (candidate) { - return !isNaN(parseFloat(candidate)) && isFinite(candidate) -}) -``` - -
-The '`.`' character can't be used to capture a character in your capturing regexp. For example you can't use `'/user-.+/'` to capture `'users-gami'`, use `[\\s\\S]` or `[\\w\\W]` instead (as in `'/user-[\\s\\S]+/'`. - -Examples: - -```js -// captures '1-a_6' but not '543-azser-sder' -router.get('/[0-9]+-[[\\w]]*', function (req, res, next) { next() }) - -// captures '1-a_6' and '543-az(ser"-sder' but not '5-a s' -router.get('/[0-9]+-[[\\S]]*', function (req, res, next) { next() }) - -// captures all (equivalent to '.*') -router.get('[[\\s\\S]]*', function (req, res, next) { next() }) -``` - -
diff --git a/_includes/api/en/4x/app-path.md b/_includes/api/en/4x/app-path.md deleted file mode 100644 index 6dd12d3d2d..0000000000 --- a/_includes/api/en/4x/app-path.md +++ /dev/null @@ -1,19 +0,0 @@ -

app.path()

- -Returns the canonical path of the app, a string. - -```js -var app = express() -var blog = express() -var blogAdmin = express() - -app.use('/blog', blog) -blog.use('/admin', blogAdmin) - -console.dir(app.path()) // '' -console.dir(blog.path()) // '/blog' -console.dir(blogAdmin.path()) // '/blog/admin' -``` - -The behavior of this method can become very complicated in complex cases of mounted apps: -it is usually better to use [req.baseUrl](#req.baseUrl) to get the canonical path of the app. diff --git a/_includes/api/en/4x/app-post-method.md b/_includes/api/en/4x/app-post-method.md deleted file mode 100644 index 2ccb9ce81a..0000000000 --- a/_includes/api/en/4x/app-post-method.md +++ /dev/null @@ -1,14 +0,0 @@ -

app.post(path, callback [, callback ...])

- -Routes HTTP POST requests to the specified path with the specified callback functions. -For more information, see the [routing guide](/{{page.lang}}/guide/routing.html). - -{% include api/en/4x/routing-args.html %} - -#### Example - -```js -app.post('/', function (req, res) { - res.send('POST request to homepage') -}) -``` diff --git a/_includes/api/en/4x/app-put-method.md b/_includes/api/en/4x/app-put-method.md deleted file mode 100644 index 3d4d62c9fc..0000000000 --- a/_includes/api/en/4x/app-put-method.md +++ /dev/null @@ -1,13 +0,0 @@ -

app.put(path, callback [, callback ...])

- -Routes HTTP PUT requests to the specified path with the specified callback functions. - -{% include api/en/4x/routing-args.html %} - -#### Example - -```js -app.put('/', function (req, res) { - res.send('PUT request to homepage') -}) -``` diff --git a/_includes/api/en/4x/app-render.md b/_includes/api/en/4x/app-render.md deleted file mode 100644 index 8e35207ebd..0000000000 --- a/_includes/api/en/4x/app-render.md +++ /dev/null @@ -1,39 +0,0 @@ -

app.render(view, [locals], callback)

- -Returns the rendered HTML of a view via the `callback` function. It accepts an optional parameter -that is an object containing local variables for the view. It is like [res.render()](#res.render), -except it cannot send the rendered view to the client on its own. - -
-Think of `app.render()` as a utility function for generating rendered view strings. -Internally `res.render()` uses `app.render()` to render views. -
- -
-The `view` argument performs file system operations like reading a file from -disk and evaluating Node.js modules, and as so for security reasons should not -contain input from the end-user. -
- -
-The `locals` object is used by view engines to render a response. The object -keys may be particularly sensitive and should not contain user-controlled -input, as it may affect the operation of the view engine or provide a path to -cross-site scripting. Consult the documentation for the used view engine for -additional considerations. -
- -
-The local variable `cache` is reserved for enabling view cache. Set it to `true`, if you want to -cache view during development; view caching is enabled in production by default. -
- -```js -app.render('email', function (err, html) { - // ... -}) - -app.render('email', { name: 'Tobi' }, function (err, html) { - // ... -}) -``` diff --git a/_includes/api/en/4x/app-route.md b/_includes/api/en/4x/app-route.md deleted file mode 100644 index 60a339b2e9..0000000000 --- a/_includes/api/en/4x/app-route.md +++ /dev/null @@ -1,20 +0,0 @@ -

app.route(path)

- -Returns an instance of a single route, which you can then use to handle HTTP verbs with optional middleware. -Use `app.route()` to avoid duplicate route names (and thus typo errors). - -```js -var app = express() - -app.route('/events') - .all(function (req, res, next) { - // runs for all HTTP verbs first - // think of it as route specific middleware! - }) - .get(function (req, res, next) { - res.json({}) - }) - .post(function (req, res, next) { - // maybe add a new event... - }) -``` diff --git a/_includes/api/en/4x/app-set.md b/_includes/api/en/4x/app-set.md deleted file mode 100644 index 4215749fe8..0000000000 --- a/_includes/api/en/4x/app-set.md +++ /dev/null @@ -1,20 +0,0 @@ -

app.set(name, value)

- -Assigns setting `name` to `value`. You may store any value that you want, -but certain names can be used to configure the behavior of the server. These -special names are listed in the [app settings table](#app.settings.table). - -Calling `app.set('foo', true)` for a Boolean property is the same as calling -`app.enable('foo')`. Similarly, calling `app.set('foo', false)` for a Boolean -property is the same as calling `app.disable('foo')`. - -Retrieve the value of a setting with [`app.get()`](#app.get). - -```js -app.set('title', 'My Site') -app.get('title') // "My Site" -``` - -

Application Settings

- -{% include api/en/4x/app-settings.md %} diff --git a/_includes/api/en/4x/app-settings.md b/_includes/api/en/4x/app-settings.md deleted file mode 100644 index 1b5a2610fe..0000000000 --- a/_includes/api/en/4x/app-settings.md +++ /dev/null @@ -1,329 +0,0 @@ -The following table lists application settings. - -Note that sub-apps will: - -* Not inherit the value of settings that have a default value. You must set the value in the sub-app. -* Inherit the value of settings with no default value; these are explicitly noted in the table below. - -Exceptions: Sub-apps will inherit the value of `trust proxy` even though it has a default value (for backward-compatibility); -Sub-apps will not inherit the value of `view cache` in production (when `NODE_ENV` is "production"). - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyTypeDescriptionDefault
- `case sensitive routing` - Boolean

Enable case sensitivity. - When enabled, "/Foo" and "/foo" are different routes. - When disabled, "/Foo" and "/foo" are treated the same.

-

NOTE: Sub-apps will inherit the value of this setting.

-
N/A (undefined) -
- `env` - String - Environment mode. Be sure to set to "production" in a production environment; see [Production best practices: performance and reliability](/{{page.lang}}/advanced/best-practice-performance.html#env). - - `process.env.NODE_ENV` (`NODE_ENV` environment variable) or "development" if `NODE_ENV` is not set. -
- `etag` - Varied - Set the ETag response header. For possible values, see the [`etag` options table](#etag.options.table). - - [More about the HTTP ETag header](http://en.wikipedia.org/wiki/HTTP_ETag). - - `weak` -
- `jsonp callback name` - StringSpecifies the default JSONP callback name. - "callback" -
- `json escape` - Boolean - Enable escaping JSON responses from the `res.json`, `res.jsonp`, and `res.send` APIs. This will escape the characters `<`, `>`, and `&` as Unicode escape sequences in JSON. The purpose of this it to assist with [mitigating certain types of persistent XSS attacks](https://blog.mozilla.org/security/2017/07/18/web-service-audits-firefox-accounts/) when clients sniff responses for HTML. -

NOTE: Sub-apps will inherit the value of this setting.

-
N/A (undefined)
- `json replacer` - VariedThe 'replacer' argument used by `JSON.stringify`. -

NOTE: Sub-apps will inherit the value of this setting.

-
N/A (undefined) -
- `json spaces` - VariedThe 'space' argument used by `JSON.stringify`. -This is typically set to the number of spaces to use to indent prettified JSON. -

NOTE: Sub-apps will inherit the value of this setting.

-
N/A (undefined)
- `query parser` - Varied -Disable query parsing by setting the value to `false`, or set the query parser to use either "simple" or "extended" or a custom query string parsing function. - -The simple query parser is based on Node's native query parser, [querystring](http://nodejs.org/api/querystring.html). - -The extended query parser is based on [qs](https://www.npmjs.org/package/qs). - -A custom query string parsing function will receive the complete query string, and must return an object of query keys and their values. - "extended"
- `strict routing` - Boolean

Enable strict routing. - When enabled, the router treats "/foo" and "/foo/" as different. - Otherwise, the router treats "/foo" and "/foo/" as the same.

-

NOTE: Sub-apps will inherit the value of this setting.

-
N/A (undefined)
- `subdomain offset` - NumberThe number of dot-separated parts of the host to remove to access subdomain.2
- `trust proxy` - Varied - Indicates the app is behind a front-facing proxy, and to use the `X-Forwarded-*` headers to determine the connection and the IP address of the client. NOTE: `X-Forwarded-*` headers are easily spoofed and the detected IP addresses are unreliable. -

- When enabled, Express attempts to determine the IP address of the client connected through the front-facing proxy, or series of proxies. The `req.ips` property, then contains an array of IP addresses the client is connected through. To enable it, use the values described in the trust proxy options table. -

- The `trust proxy` setting is implemented using the proxy-addr package. For more information, see its documentation. -

-NOTE: Sub-apps will inherit the value of this setting, even though it has a default value. -

-
- `false` (disabled) -
- `views` - String or ArrayA directory or an array of directories for the application's views. If an array, the views are looked up in the order they occur in the array. - `process.cwd() + '/views'` -
- `view cache` - Boolean

Enables view template compilation caching.

-

NOTE: Sub-apps will not inherit the value of this setting in production (when `NODE_ENV` is "production").

-
- `true` in production, otherwise undefined. -
- `view engine` - StringThe default engine extension to use when omitted. -

NOTE: Sub-apps will inherit the value of this setting.

-
N/A (undefined)
- `x-powered-by` - BooleanEnables the "X-Powered-By: Express" HTTP header. - `true` -
-
- -
Options for `trust proxy` setting
- -

- Read [Express behind proxies](/{{page.lang}}/guide/behind-proxies.html) for more - information. -

- -
- - - - - - - - - - - - - - - - - - - - -
TypeValue
Boolean - If `true`, the client's IP address is understood as the left-most entry in the `X-Forwarded-*` header. - - If `false`, the app is understood as directly facing the Internet and the client's IP address is derived from `req.connection.remoteAddress`. This is the default setting. -
String
String containing comma-separated values
Array of strings
- An IP address, subnet, or an array of IP addresses, and subnets to trust. Pre-configured subnet names are: - - * loopback - `127.0.0.1/8`, `::1/128` - * linklocal - `169.254.0.0/16`, `fe80::/10` - * uniquelocal - `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `fc00::/7` - - Set IP addresses in any of the following ways: - -Specify a single subnet: - -```js -app.set('trust proxy', 'loopback') -``` - -Specify a subnet and an address: - -```js -app.set('trust proxy', 'loopback, 123.123.123.123') -``` - -Specify multiple subnets as CSV: - -```js -app.set('trust proxy', 'loopback, linklocal, uniquelocal') -``` - -Specify multiple subnets as an array: - -```js -app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']) -``` - - When specified, the IP addresses or the subnets are excluded from the address determination process, and the untrusted IP address nearest to the application server is determined as the client's IP address. -
Number - Trust the nth hop from the front-facing proxy server as the client. -
Function - Custom trust implementation. Use this only if you know what you are doing. - -```js -app.set('trust proxy', function (ip) { - if (ip === '127.0.0.1' || ip === '123.123.123.123') return true // trusted IPs - else return false -}) -``` -
-
- -
Options for `etag` setting
- -

-**NOTE**: These settings apply only to dynamic files, not static files. -The [express.static](#express.static) middleware ignores these settings. -

- -

- The ETag functionality is implemented using the - [etag](https://www.npmjs.org/package/etag) package. - For more information, see its documentation. -

- -
- - - - - - - - - - - - - - - - -
TypeValue
Boolean - `true` enables weak ETag. This is the default setting.
- `false` disables ETag altogether. -
String - If "strong", enables strong ETag.
- If "weak", enables weak ETag. -
FunctionCustom ETag function implementation. Use this only if you know what you are doing. - -```js -app.set('etag', function (body, encoding) { - return generateHash(body, encoding) // consider the function is defined -}) -``` -
-
diff --git a/_includes/api/en/4x/app-use.md b/_includes/api/en/4x/app-use.md deleted file mode 100644 index c5e9dbf90c..0000000000 --- a/_includes/api/en/4x/app-use.md +++ /dev/null @@ -1,318 +0,0 @@ -

app.use([path,] callback [, callback...])

- -Mounts the specified [middleware](/{{page.lang}}/guide/using-middleware.html) function or functions -at the specified path: -the middleware function is executed when the base of the requested path matches `path`. - -{% include api/en/4x/routing-args.html %} - -#### Description - -A route will match any path that follows its path immediately with a "`/`". -For example: `app.use('/apple', ...)` will match "/apple", "/apple/images", -"/apple/images/news", and so on. - -Since `path` defaults to "/", middleware mounted without a path will be executed for every request to the app. -For example, this middleware function will be executed for _every_ request to the app: - -```js -app.use(function (req, res, next) { - console.log('Time: %d', Date.now()) - next() -}) -``` - -
-**NOTE** - -Sub-apps will: - -* Not inherit the value of settings that have a default value. You must set the value in the sub-app. -* Inherit the value of settings with no default value. - -For details, see [Application settings](/en/4x/api.html#app.settings.table). -
- -Middleware functions are executed sequentially, therefore the order of middleware inclusion is important. - -```js -// this middleware will not allow the request to go beyond it -app.use(function (req, res, next) { - res.send('Hello World') -}) - -// requests will never reach this route -app.get('/', function (req, res) { - res.send('Welcome') -}) -``` - -**Error-handling middleware** - -Error-handling middleware always takes _four_ arguments. You must provide four arguments to identify it as an error-handling middleware function. Even if you don't need to use the `next` object, you must specify it to maintain the signature. Otherwise, the `next` object will be interpreted as regular middleware and will fail to handle errors. For details about error-handling middleware, see: [Error handling](/{{ page.lang }}/guide/error-handling.html). - -Define error-handling middleware functions in the same way as other middleware functions, except with four arguments instead of three, specifically with the signature `(err, req, res, next)`): - -```js -app.use(function (err, req, res, next) { - console.error(err.stack) - res.status(500).send('Something broke!') -}) -``` - -#### Path examples - -The following table provides some simple examples of valid `path` values for -mounting middleware. - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeExample
Path -Matches the exact path `/abcd` and any sub-paths starting with `/abcd/` (for example, `/abcd/foo`): - - -```js -app.use('/abcd', function (req, res, next) { - next() -}) -``` - -
Path Pattern -This will match paths starting with `/abcd` and `/abd`: - -```js -app.use('/abc?d', function (req, res, next) { - next() -}) -``` - -This will match paths starting with `/abcd`, `/abbcd`, `/abbbbbcd`, and so on: - -```js -app.use('/ab+cd', function (req, res, next) { - next() -}) -``` - -This will match paths starting with `/abcd`, `/abxcd`, `/abFOOcd`, `/abbArcd`, and so on: - -```js -app.use('/ab*cd', function (req, res, next) { - next() -}) -``` - -This will match paths starting with `/ad` and `/abcd`: - -```js -app.use('/a(bc)?d', function (req, res, next) { - next() -}) -``` - -
Regular Expression -This will match paths starting with `/abc` and `/xyz`: - -```js -app.use(/\/abc|\/xyz/, function (req, res, next) { - next() -}) -``` - -
Array -This will match paths starting with `/abcd`, `/xyza`, `/lmn`, and `/pqr`: - -```js -app.use(['/abcd', '/xyza', /\/lmn|\/pqr/], function (req, res, next) { - next() -}) -``` - -
-
- -#### Middleware callback function examples - -The following table provides some simple examples of middleware functions that -can be used as the `callback` argument to `app.use()`, `app.METHOD()`, and `app.all()`. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
UsageExample
Single Middleware -You can define and mount a middleware function locally. - -```js -app.use(function (req, res, next) { - next() -}) -``` - -A router is valid middleware. - -```js -var router = express.Router() -router.get('/', function (req, res, next) { - next() -}) -app.use(router) -``` - -An Express app is valid middleware. - -```js -var subApp = express() -subApp.get('/', function (req, res, next) { - next() -}) -app.use(subApp) -``` - -
Series of Middleware -You can specify more than one middleware function at the same mount path. - -```js -var r1 = express.Router() -r1.get('/', function (req, res, next) { - next() -}) - -var r2 = express.Router() -r2.get('/', function (req, res, next) { - next() -}) - -app.use(r1, r2) -``` - -
Array -Use an array to group middleware logically. - -```js -var r1 = express.Router() -r1.get('/', function (req, res, next) { - next() -}) - -var r2 = express.Router() -r2.get('/', function (req, res, next) { - next() -}) - -app.use([r1, r2]) -``` - -
Combination -You can combine all the above ways of mounting middleware. - -```js -function mw1 (req, res, next) { next() } -function mw2 (req, res, next) { next() } - -var r1 = express.Router() -r1.get('/', function (req, res, next) { next() }) - -var r2 = express.Router() -r2.get('/', function (req, res, next) { next() }) - -var subApp = express() -subApp.get('/', function (req, res, next) { next() }) - -app.use(mw1, [mw2, r1, r2], subApp) -``` - -
-
- -Following are some examples of using the [express.static](/{{page.lang}}/guide/using-middleware.html#middleware.built-in) -middleware in an Express app. - -Serve static content for the app from the "public" directory in the application directory: - -```js -// GET /style.css etc -app.use(express.static(path.join(__dirname, 'public'))) -``` - -Mount the middleware at "/static" to serve static content only when their request path is prefixed with "/static": - -```js -// GET /static/style.css etc. -app.use('/static', express.static(path.join(__dirname, 'public'))) -``` - -Disable logging for static content requests by loading the logger middleware after the static middleware: - -```js -app.use(express.static(path.join(__dirname, 'public'))) -app.use(logger()) -``` - -Serve static files from multiple directories, but give precedence to "./public" over the others: - -```js -app.use(express.static(path.join(__dirname, 'public'))) -app.use(express.static(path.join(__dirname, 'files'))) -app.use(express.static(path.join(__dirname, 'uploads'))) -``` diff --git a/_includes/api/en/4x/app.md b/_includes/api/en/4x/app.md deleted file mode 100644 index 518ec66efd..0000000000 --- a/_includes/api/en/4x/app.md +++ /dev/null @@ -1,123 +0,0 @@ -

Application

- -The `app` object conventionally denotes the Express application. -Create it by calling the top-level `express()` function exported by the Express module: - -```js -var express = require('express') -var app = express() - -app.get('/', function (req, res) { - res.send('hello world') -}) - -app.listen(3000) -``` - -The `app` object has methods for - -* Routing HTTP requests; see for example, [app.METHOD](#app.METHOD) and [app.param](#app.param). -* Configuring middleware; see [app.route](#app.route). -* Rendering HTML views; see [app.render](#app.render). -* Registering a template engine; see [app.engine](#app.engine). - -It also has settings (properties) that affect how the application behaves; -for more information, see [Application settings](#app.settings.table). - -
-The Express application object can be referred from the [request object](#req) and the [response object](#res) as `req.app`, and `res.app`, respectively. -
- -

Properties

- -
- {% include api/en/4x/app-locals.md %} -
- -
- {% include api/en/4x/app-mountpath.md %} -
- -

Events

- -
- {% include api/en/4x/app-onmount.md %} -
- -

Methods

- -
- {% include api/en/4x/app-all.md %} -
- -
- {% include api/en/4x/app-delete-method.md %} -
- -
- {% include api/en/4x/app-disable.md %} -
- -
- {% include api/en/4x/app-disabled.md %} -
- -
- {% include api/en/4x/app-enable.md %} -
- -
- {% include api/en/4x/app-enabled.md %} -
- -
- {% include api/en/4x/app-engine.md %} -
- -
- {% include api/en/4x/app-get.md %} -
- -
- {% include api/en/4x/app-get-method.md %} -
- -
- {% include api/en/4x/app-listen.md %} -
- -
- {% include api/en/4x/app-METHOD.md %} -
- -
- {% include api/en/4x/app-param.md %} -
- -
- {% include api/en/4x/app-path.md %} -
- -
- {% include api/en/4x/app-post-method.md %} -
- -
- {% include api/en/4x/app-put-method.md %} -
- -
- {% include api/en/4x/app-render.md %} -
- -
- {% include api/en/4x/app-route.md %} -
- -
- {% include api/en/4x/app-set.md %} -
- -
- {% include api/en/4x/app-use.md %} -
diff --git a/_includes/api/en/4x/express.json.md b/_includes/api/en/4x/express.json.md deleted file mode 100644 index ffc460193d..0000000000 --- a/_includes/api/en/4x/express.json.md +++ /dev/null @@ -1,42 +0,0 @@ -

express.json([options])

- -
-This middleware is available in Express v4.16.0 onwards. -
- -This is a built-in middleware function in Express. It parses incoming requests -with JSON payloads and is based on -[body-parser](/resources/middleware/body-parser.html). - -Returns middleware that only parses JSON and only looks at requests where -the `Content-Type` header matches the `type` option. This parser accepts any -Unicode encoding of the body and supports automatic inflation of `gzip` and -`deflate` encodings. - -A new `body` object containing the parsed data is populated on the `request` -object after the middleware (i.e. `req.body`), or an empty object (`{}`) if -there was no body to parse, the `Content-Type` was not matched, or an error -occurred. - -
-As `req.body`'s shape is based on user-controlled input, all properties and -values in this object are untrusted and should be validated before trusting. -For example, `req.body.foo.toString()` may fail in multiple ways, for example -`foo` may not be there or may not be a string, and `toString` may not be a -function and instead a string or other user-input. -
- -
- -The following table describes the properties of the optional `options` object. - -| Property | Description | Type | Default | -|---------------|-----------------------------------------------------------------------|-------------|-----------------| -| `inflate` | Enables or disables handling deflated (compressed) bodies; when disabled, deflated bodies are rejected. | Boolean | `true` | -| `limit` | Controls the maximum request body size. If this is a number, then the value specifies the number of bytes; if it is a string, the value is passed to the [bytes](https://www.npmjs.com/package/bytes) library for parsing. | Mixed | `"100kb"` | -| `reviver` | The `reviver` option is passed directly to `JSON.parse` as the second argument. You can find more information on this argument [in the MDN documentation about JSON.parse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Example.3A_Using_the_reviver_parameter). | Function | `null` | -| `strict` | Enables or disables only accepting arrays and objects; when disabled will accept anything `JSON.parse` accepts. | Boolean | `true` | -| `type` | This is used to determine what media type the middleware will parse. This option can be a string, array of strings, or a function. If not a function, `type` option is passed directly to the [type-is](https://www.npmjs.org/package/type-is#readme) library and this can be an extension name (like `json`), a mime type (like `application/json`), or a mime type with a wildcard (like `*/*` or `*/json`). If a function, the `type` option is called as `fn(req)` and the request is parsed if it returns a truthy value. | Mixed | `"application/json"` | -| `verify` | This option, if supplied, is called as `verify(req, res, buf, encoding)`, where `buf` is a `Buffer` of the raw request body and `encoding` is the encoding of the request. The parsing can be aborted by throwing an error. | Function | `undefined` | - -
\ No newline at end of file diff --git a/_includes/api/en/4x/express.md b/_includes/api/en/4x/express.md deleted file mode 100644 index 6ba0094cc7..0000000000 --- a/_includes/api/en/4x/express.md +++ /dev/null @@ -1,34 +0,0 @@ -

express()

- -Creates an Express application. The `express()` function is a top-level function exported by the `express` module. - -```js -var express = require('express') -var app = express() -``` - -

Methods

- -
- {% include api/en/4x/express.json.md %} -
- -
- {% include api/en/4x/express.raw.md %} -
- -
- {% include api/en/4x/express.router.md %} -
- -
- {% include api/en/4x/express.static.md %} -
- -
- {% include api/en/4x/express.text.md %} -
- -
- {% include api/en/4x/express.urlencoded.md %} -
diff --git a/_includes/api/en/4x/express.raw.md b/_includes/api/en/4x/express.raw.md deleted file mode 100644 index cac172fb95..0000000000 --- a/_includes/api/en/4x/express.raw.md +++ /dev/null @@ -1,40 +0,0 @@ -

express.raw([options])

- -
-This middleware is available in Express v4.17.0 onwards. -
- -This is a built-in middleware function in Express. It parses incoming request -payloads into a `Buffer` and is based on -[body-parser](/resources/middleware/body-parser.html). - -Returns middleware that parses all bodies as a `Buffer` and only looks at requests -where the `Content-Type` header matches the `type` option. This parser accepts -any Unicode encoding of the body and supports automatic inflation of `gzip` and -`deflate` encodings. - -A new `body` `Buffer` containing the parsed data is populated on the `request` -object after the middleware (i.e. `req.body`), or an empty object (`{}`) if -there was no body to parse, the `Content-Type` was not matched, or an error -occurred. - -
-As `req.body`'s shape is based on user-controlled input, all properties and -values in this object are untrusted and should be validated before trusting. -For example, `req.body.toString()` may fail in multiple ways, for example -stacking multiple parsers `req.body` may be from a different parser. Testing -that `req.body` is a `Buffer` before calling buffer methods is recommended. -
- -The following table describes the properties of the optional `options` object. - -
- -| Property | Description | Type | Default | -|-----------|-----------------------------------------------------------------------|-------------|-----------------| -| `inflate` | Enables or disables handling deflated (compressed) bodies; when disabled, deflated bodies are rejected. | Boolean | `true` | -| `limit` | Controls the maximum request body size. If this is a number, then the value specifies the number of bytes; if it is a string, the value is passed to the [bytes](https://www.npmjs.com/package/bytes) library for parsing. | Mixed | `"100kb"` | -| `type` | This is used to determine what media type the middleware will parse. This option can be a string, array of strings, or a function. If not a function, `type` option is passed directly to the [type-is](https://www.npmjs.org/package/type-is#readme) library and this can be an extension name (like `bin`), a mime type (like `application/octet-stream`), or a mime type with a wildcard (like `*/*` or `application/*`). If a function, the `type` option is called as `fn(req)` and the request is parsed if it returns a truthy value. | Mixed | `"application/octet-stream"` | -| `verify` | This option, if supplied, is called as `verify(req, res, buf, encoding)`, where `buf` is a `Buffer` of the raw request body and `encoding` is the encoding of the request. The parsing can be aborted by throwing an error. | Function | `undefined` | - -
\ No newline at end of file diff --git a/_includes/api/en/4x/express.router.md b/_includes/api/en/4x/express.router.md deleted file mode 100644 index 10dc92c94a..0000000000 --- a/_includes/api/en/4x/express.router.md +++ /dev/null @@ -1,24 +0,0 @@ -

express.Router([options])

- -Creates a new [router](#router) object. - -```js -var router = express.Router([options]) -``` - -The optional `options` parameter specifies the behavior of the router. - -
- -| Property | Description | Default | Availability | -|-----------------|-------------------------------------------------|-------------|---------------| -| `caseSensitive` | Enable case sensitivity. | Disabled by default, treating "/Foo" and "/foo" as the same.| | -| `mergeParams` | Preserve the `req.params` values from the parent router. If the parent and the child have conflicting param names, the child's value take precedence.| `false` | 4.5.0+ | -| `strict` | Enable strict routing. | Disabled by default, "/foo" and "/foo/" are treated the same by the router.|   | - -
- -You can add middleware and HTTP method routes (such as `get`, `put`, `post`, and -so on) to `router` just like an application. - -For more information, see [Router](#router). diff --git a/_includes/api/en/4x/express.static.md b/_includes/api/en/4x/express.static.md deleted file mode 100644 index d7e899cd58..0000000000 --- a/_includes/api/en/4x/express.static.md +++ /dev/null @@ -1,97 +0,0 @@ -

express.static(root, [options])

- -This is a built-in middleware function in Express. -It serves static files and is based on [serve-static](/resources/middleware/serve-static.html). - -{% capture alert_content %} -For best results, [use a reverse proxy](/{{page.lang}}/advanced/best-practice-performance.html#use-a-reverse-proxy) cache to improve performance of serving static assets. -{% endcapture %} -{% include admonitions/note.html content=alert_content %} - -The `root` argument specifies the root directory from which to serve static assets. -The function determines the file to serve by combining `req.url` with the provided `root` directory. -When a file is not found, instead of sending a 404 response, it calls `next()` -to move on to the next middleware, allowing for stacking and fall-backs. - -The following table describes the properties of the `options` object. -See also the [example below](#example.of.express.static). - -
- -| Property | Description | Type | Default | -|---------------|-----------------------------------------------------------------------|-------------|-----------------| -| `dotfiles` | Determines how dotfiles (files or directories that begin with a dot ".") are treated.

See [dotfiles](#dotfiles) below. | String | `undefined` | -| `etag` | Enable or disable etag generation

NOTE: `express.static` always sends weak ETags. | Boolean | `true` | -| `extensions` | Sets file extension fallbacks: If a file is not found, search for files with the specified extensions and serve the first one found. Example: `['html', 'htm']`.| Mixed | `false` | -| `fallthrough` | Let client errors fall-through as unhandled requests, otherwise forward a client error.

See [fallthrough](#fallthrough) below.| Boolean | `true` | -| `immutable` | Enable or disable the `immutable` directive in the `Cache-Control` response header. If enabled, the `maxAge` option should also be specified to enable caching. The `immutable` directive will prevent supported clients from making conditional requests during the life of the `maxAge` option to check if the file has changed. | Boolean | `false` | -| `index` | Sends the specified directory index file. Set to `false` to disable directory indexing. | Mixed | "index.html" | -| `lastModified` | Set the `Last-Modified` header to the last modified date of the file on the OS. | Boolean | `true` | -| `maxAge` | Set the max-age property of the Cache-Control header in milliseconds or a string in [ms format](https://www.npmjs.org/package/ms). | Number | 0 | -| `redirect` | Redirect to trailing "/" when the pathname is a directory. | Boolean | `true` | -| `setHeaders` | Function for setting HTTP headers to serve with the file.

See [setHeaders](#setHeaders) below. | Function | | -| `acceptRanges` | Enable or disable accepting ranged requests. Disabling this will not send the `Accept-Ranges` header and will ignore the contents of the Range request header.| Boolean | true | -| `cacheControl` | Enable or disable setting the `Cache-Control` response header. Disabling this will ignore the immutable and maxAge options. | Boolean | true | - -
- -For more information, see [Serving static files in Express](/starter/static-files.html). -and [Using middleware - Built-in middleware](/{{page.lang}}/guide/using-middleware.html#middleware.built-in). - -
dotfiles
- -Possible values for this option are: - -- "allow" - No special treatment for dotfiles. -- "deny" - Deny a request for a dotfile, respond with `403`, then call `next()`. -- "ignore" - Act as if the dotfile does not exist, respond with `404`, then call `next()`. -- `undefined` - Act as ignore, except that files in a directory that begins with a dot are **NOT** ignored. - -
fallthrough
- -When this option is `true`, client errors such as a bad request or a request to a non-existent -file will cause this middleware to simply call `next()` to invoke the next middleware in the stack. -When false, these errors (even 404s), will invoke `next(err)`. - -Set this option to `true` so you can map multiple physical directories -to the same web address or for routes to fill in non-existent files. - -Use `false` if you have mounted this middleware at a path designed -to be strictly a single file system directory, which allows for short-circuiting 404s -for less overhead. This middleware will also reply to all methods. - -
setHeaders
- -For this option, specify a function to set custom response headers. Alterations to the headers must occur synchronously. - -The signature of the function is: - -```js -fn(res, path, stat) -``` - -Arguments: - -- `res`, the [response object](#res). -- `path`, the file path that is being sent. -- `stat`, the `stat` object of the file that is being sent. - -

Example of express.static

- -Here is an example of using the `express.static` middleware function with an elaborate options object: - -```js -var options = { - dotfiles: 'ignore', - etag: false, - extensions: ['htm', 'html'], - index: false, - maxAge: '1d', - redirect: false, - setHeaders: function (res, path, stat) { - res.set('x-timestamp', Date.now()) - } -} - -app.use(express.static('public', options)) -``` diff --git a/_includes/api/en/4x/express.text.md b/_includes/api/en/4x/express.text.md deleted file mode 100644 index 4d762427e5..0000000000 --- a/_includes/api/en/4x/express.text.md +++ /dev/null @@ -1,41 +0,0 @@ -

express.text([options])

- -
-This middleware is available in Express v4.17.0 onwards. -
- -This is a built-in middleware function in Express. It parses incoming request -payloads into a string and is based on -[body-parser](/resources/middleware/body-parser.html). - -Returns middleware that parses all bodies as a string and only looks at requests -where the `Content-Type` header matches the `type` option. This parser accepts -any Unicode encoding of the body and supports automatic inflation of `gzip` and -`deflate` encodings. - -A new `body` string containing the parsed data is populated on the `request` -object after the middleware (i.e. `req.body`), or an empty object (`{}`) if -there was no body to parse, the `Content-Type` was not matched, or an error -occurred. - -
-As `req.body`'s shape is based on user-controlled input, all properties and -values in this object are untrusted and should be validated before trusting. -For example, `req.body.trim()` may fail in multiple ways, for example -stacking multiple parsers `req.body` may be from a different parser. Testing -that `req.body` is a string before calling string methods is recommended. -
- -The following table describes the properties of the optional `options` object. - -
- -| Property | Description | Type | Default | -|------------------|-----------------------------------------------------------------------|-------------|-----------------| -| `defaultCharset` | Specify the default character set for the text content if the charset is not specified in the `Content-Type` header of the request. | String | `"utf-8"` | -| `inflate` | Enables or disables handling deflated (compressed) bodies; when disabled, deflated bodies are rejected. | Boolean | `true` | -| `limit` | Controls the maximum request body size. If this is a number, then the value specifies the number of bytes; if it is a string, the value is passed to the [bytes](https://www.npmjs.com/package/bytes) library for parsing. | Mixed | `"100kb"` | -| `type` | This is used to determine what media type the middleware will parse. This option can be a string, array of strings, or a function. If not a function, `type` option is passed directly to the [type-is](https://www.npmjs.org/package/type-is#readme) library and this can be an extension name (like `txt`), a mime type (like `text/plain`), or a mime type with a wildcard (like `*/*` or `text/*`). If a function, the `type` option is called as `fn(req)` and the request is parsed if it returns a truthy value. | Mixed | `"text/plain"` | -| `verify` | This option, if supplied, is called as `verify(req, res, buf, encoding)`, where `buf` is a `Buffer` of the raw request body and `encoding` is the encoding of the request. The parsing can be aborted by throwing an error. | Function | `undefined` | - -
\ No newline at end of file diff --git a/_includes/api/en/4x/express.urlencoded.md b/_includes/api/en/4x/express.urlencoded.md deleted file mode 100644 index f6bcc46325..0000000000 --- a/_includes/api/en/4x/express.urlencoded.md +++ /dev/null @@ -1,48 +0,0 @@ -

express.urlencoded([options])

- -
-This middleware is available in Express v4.16.0 onwards. -
- -This is a built-in middleware function in Express. It parses incoming requests -with urlencoded payloads and is based on [body-parser](/resources/middleware/body-parser.html). - -Returns middleware that only parses urlencoded bodies and only looks at -requests where the `Content-Type` header matches the `type` option. This -parser accepts only UTF-8 encoding of the body and supports automatic -inflation of `gzip` and `deflate` encodings. - -A new `body` object containing the parsed data is populated on the `request` -object after the middleware (i.e. `req.body`), or an empty object (`{}`) if -there was no body to parse, the `Content-Type` was not matched, or an error -occurred. This object will contain key-value pairs, where the value can be -a string or array (when `extended` is `false`), or any type (when `extended` -is `true`). - -
-As `req.body`'s shape is based on user-controlled input, all properties and -values in this object are untrusted and should be validated before trusting. -For example, `req.body.foo.toString()` may fail in multiple ways, for example -`foo` may not be there or may not be a string, and `toString` may not be a -function and instead a string or other user-input. -
- -The following table describes the properties of the optional `options` object. - -
- -| Property | Description | Type | Default | -|------------------|-----------------------------------------------------------------------|-------------|-----------------| -| `extended` | This option allows to choose between parsing the URL-encoded data with the `querystring` library (when `false`) or the `qs` library (when `true`). The "extended" syntax allows for rich objects and arrays to be encoded into the URL-encoded format, allowing for a JSON-like experience with URL-encoded. For more information, please [see the qs library](https://www.npmjs.org/package/qs#readme). | Boolean | `true` | -| `inflate` | Enables or disables handling deflated (compressed) bodies; when disabled, deflated bodies are rejected. | Boolean | `true` | -| `limit` | Controls the maximum request body size. If this is a number, then the value specifies the number of bytes; if it is a string, the value is passed to the [bytes](https://www.npmjs.com/package/bytes) library for parsing. | Mixed | `"100kb"` | -| `parameterLimit` | This option controls the maximum number of parameters that are allowed in the URL-encoded data. If a request contains more parameters than this value, an error will be raised. | Number | `1000` | -| `type` | This is used to determine what media type the middleware will parse. This option can be a string, array of strings, or a function. If not a function, `type` option is passed directly to the [type-is](https://www.npmjs.org/package/type-is#readme) library and this can be an extension name (like `urlencoded`), a mime type (like `application/x-www-form-urlencoded`), or a mime type with a wildcard (like `*/x-www-form-urlencoded`). If a function, the `type` option is called as `fn(req)` and the request is parsed if it returns a truthy value. | Mixed | `"application/x-www-form-urlencoded"` | -| `verify` | This option, if supplied, is called as `verify(req, res, buf, encoding)`, where `buf` is a `Buffer` of the raw request body and `encoding` is the encoding of the request. The parsing can be aborted by throwing an error. | Function | `undefined` | -| `depth` | Configure the maximum depth of the `qs` library when `extended` is `true`. This allows you to limit the amount of keys that are parsed and can be useful to prevent certain types of abuse. Defaults to `32`. It is recommended to keep this value as low as possible. | Number | `32` | - -
- -
-The `depth` option was added in Express v4.20.0. If you are using an earlier version, this option will not be available. -
diff --git a/_includes/api/en/4x/menu.md b/_includes/api/en/4x/menu.md deleted file mode 100644 index 81584103c3..0000000000 --- a/_includes/api/en/4x/menu.md +++ /dev/null @@ -1,208 +0,0 @@ - - - diff --git a/_includes/api/en/4x/req-accepts.md b/_includes/api/en/4x/req-accepts.md deleted file mode 100644 index 7258c71b4c..0000000000 --- a/_includes/api/en/4x/req-accepts.md +++ /dev/null @@ -1,36 +0,0 @@ -

req.accepts(types)

- -Checks if the specified content types are acceptable, based on the request's `Accept` HTTP header field. -The method returns the best match, or if none of the specified content types is acceptable, returns -`false` (in which case, the application should respond with `406 "Not Acceptable"`). - -The `type` value may be a single MIME type string (such as "application/json"), -an extension name such as "json", a comma-delimited list, or an array. For a -list or array, the method returns the *best* match (if any). - -```js -// Accept: text/html -req.accepts('html') -// => "html" - -// Accept: text/*, application/json -req.accepts('html') -// => "html" -req.accepts('text/html') -// => "text/html" -req.accepts(['json', 'text']) -// => "json" -req.accepts('application/json') -// => "application/json" - -// Accept: text/*, application/json -req.accepts('image/png') -req.accepts('png') -// => false - -// Accept: text/*;q=.5, application/json -req.accepts(['html', 'json']) -// => "json" -``` - -For more information, or if you have issues or concerns, see [accepts](https://github.com/expressjs/accepts). diff --git a/_includes/api/en/4x/req-acceptsCharsets.md b/_includes/api/en/4x/req-acceptsCharsets.md deleted file mode 100644 index d47d0480d3..0000000000 --- a/_includes/api/en/4x/req-acceptsCharsets.md +++ /dev/null @@ -1,7 +0,0 @@ -

req.acceptsCharsets(charset [, ...])

- -Returns the first accepted charset of the specified character sets, -based on the request's `Accept-Charset` HTTP header field. -If none of the specified charsets is accepted, returns `false`. - -For more information, or if you have issues or concerns, see [accepts](https://github.com/expressjs/accepts). diff --git a/_includes/api/en/4x/req-acceptsEncodings.md b/_includes/api/en/4x/req-acceptsEncodings.md deleted file mode 100644 index 2c6a3f236f..0000000000 --- a/_includes/api/en/4x/req-acceptsEncodings.md +++ /dev/null @@ -1,7 +0,0 @@ -

req.acceptsEncodings(encoding [, ...])

- -Returns the first accepted encoding of the specified encodings, -based on the request's `Accept-Encoding` HTTP header field. -If none of the specified encodings is accepted, returns `false`. - -For more information, or if you have issues or concerns, see [accepts](https://github.com/expressjs/accepts). diff --git a/_includes/api/en/4x/req-acceptsLanguages.md b/_includes/api/en/4x/req-acceptsLanguages.md deleted file mode 100644 index 0ea278b1da..0000000000 --- a/_includes/api/en/4x/req-acceptsLanguages.md +++ /dev/null @@ -1,15 +0,0 @@ -

req.acceptsLanguages([lang, ...])

- -Returns the first accepted language of the specified languages, -based on the request's `Accept-Language` HTTP header field. -If none of the specified languages is accepted, returns `false`. - -If no `lang` argument is given, then `req.acceptsLanguages()` -returns all languages from the HTTP `Accept-Language` header -as an `Array`. - -For more information, or if you have issues or concerns, see [accepts](https://github.com/expressjs/accepts). - -Express (4.x) source: [request.js line 179](https://github.com/expressjs/express/blob/4.x/lib/request.js#L179) - -Accepts (1.3) source: [index.js line 195](https://github.com/jshttp/accepts/blob/f69c19e459bd501e59fb0b1a40b7471bb578113a/index.js#L195) diff --git a/_includes/api/en/4x/req-app.md b/_includes/api/en/4x/req-app.md deleted file mode 100644 index 8fde7da97e..0000000000 --- a/_includes/api/en/4x/req-app.md +++ /dev/null @@ -1,20 +0,0 @@ -

req.app

- -This property holds a reference to the instance of the Express application that is using the middleware. - -If you follow the pattern in which you create a module that just exports a middleware function -and `require()` it in your main file, then the middleware can access the Express instance via `req.app` - -For example: - -```js -// index.js -app.get('/viewdirectory', require('./mymiddleware.js')) -``` - -```js -// mymiddleware.js -module.exports = function (req, res) { - res.send('The views directory is ' + req.app.get('views')) -} -``` diff --git a/_includes/api/en/4x/req-baseUrl.md b/_includes/api/en/4x/req-baseUrl.md deleted file mode 100644 index 039c636f93..0000000000 --- a/_includes/api/en/4x/req-baseUrl.md +++ /dev/null @@ -1,30 +0,0 @@ -

req.baseUrl

- -The URL path on which a router instance was mounted. - -The `req.baseUrl` property is similar to the [mountpath](#app.mountpath) property of the `app` object, -except `app.mountpath` returns the matched path pattern(s). - -For example: - -```js -var greet = express.Router() - -greet.get('/jp', function (req, res) { - console.log(req.baseUrl) // /greet - res.send('Konnichiwa!') -}) - -app.use('/greet', greet) // load the router on '/greet' -``` - -Even if you use a path pattern or a set of path patterns to load the router, -the `baseUrl` property returns the matched string, not the pattern(s). In the -following example, the `greet` router is loaded on two path patterns. - -```js -app.use(['/gre+t', '/hel{2}o'], greet) // load the router on '/gre+t' and '/hel{2}o' -``` - -When a request is made to `/greet/jp`, `req.baseUrl` is "/greet". When a request is -made to `/hello/jp`, `req.baseUrl` is "/hello". diff --git a/_includes/api/en/4x/req-body.md b/_includes/api/en/4x/req-body.md deleted file mode 100644 index a0caa50067..0000000000 --- a/_includes/api/en/4x/req-body.md +++ /dev/null @@ -1,25 +0,0 @@ -

req.body

- -Contains key-value pairs of data submitted in the request body. -By default, it is `undefined`, and is populated when you use body-parsing middleware such -as [`express.json()`](#express.json) or [`express.urlencoded()`](#express.urlencoded). - -
-As `req.body`'s shape is based on user-controlled input, all properties and values in this object are untrusted and should be validated before trusting. For example, `req.body.foo.toString()` may fail in multiple ways, for example `foo` may not be there or may not be a string, and `toString` may not be a function and instead a string or other user-input. -
- -The following example shows how to use body-parsing middleware to populate `req.body`. - -```js -var express = require('express') - -var app = express() - -app.use(express.json()) // for parsing application/json -app.use(express.urlencoded({ extended: true })) // for parsing application/x-www-form-urlencoded - -app.post('/profile', function (req, res, next) { - console.log(req.body) - res.json(req.body) -}) -``` diff --git a/_includes/api/en/4x/req-cookies.md b/_includes/api/en/4x/req-cookies.md deleted file mode 100644 index 244276fffe..0000000000 --- a/_includes/api/en/4x/req-cookies.md +++ /dev/null @@ -1,14 +0,0 @@ -

req.cookies

- -When using [cookie-parser](https://www.npmjs.com/package/cookie-parser) middleware, this property is an object that -contains cookies sent by the request. If the request contains no cookies, it defaults to `{}`. - -```js -// Cookie: name=tj -console.dir(req.cookies.name) -// => 'tj' -``` - -If the cookie has been signed, you have to use [req.signedCookies](#req.signedCookies). - -For more information, issues, or concerns, see [cookie-parser](https://github.com/expressjs/cookie-parser). diff --git a/_includes/api/en/4x/req-fresh.md b/_includes/api/en/4x/req-fresh.md deleted file mode 100644 index 1b6ec61c59..0000000000 --- a/_includes/api/en/4x/req-fresh.md +++ /dev/null @@ -1,13 +0,0 @@ -

req.fresh

- -When the response is still "fresh" in the client's cache `true` is returned, otherwise `false` is returned to indicate that the client cache is now stale and the full response should be sent. - -When a client sends the `Cache-Control: no-cache` request header to indicate an end-to-end reload request, this module will return `false` to make handling these requests transparent. - -Further details for how cache validation works can be found in the -[HTTP/1.1 Caching Specification](https://tools.ietf.org/html/rfc7234). - -```js -console.dir(req.fresh) -// => true -``` diff --git a/_includes/api/en/4x/req-get.md b/_includes/api/en/4x/req-get.md deleted file mode 100644 index 7cd4250b22..0000000000 --- a/_includes/api/en/4x/req-get.md +++ /dev/null @@ -1,17 +0,0 @@ -

req.get(field)

- -Returns the specified HTTP request header field (case-insensitive match). -The `Referrer` and `Referer` fields are interchangeable. - -```js -req.get('Content-Type') -// => "text/plain" - -req.get('content-type') -// => "text/plain" - -req.get('Something') -// => undefined -``` - -Aliased as `req.header(field)`. diff --git a/_includes/api/en/4x/req-hostname.md b/_includes/api/en/4x/req-hostname.md deleted file mode 100644 index fbb4731440..0000000000 --- a/_includes/api/en/4x/req-hostname.md +++ /dev/null @@ -1,23 +0,0 @@ -

req.hostname

- -Contains the hostname derived from the `Host` HTTP header. - -When the [`trust proxy` setting](/4x/api.html#trust.proxy.options.table) -does not evaluate to `false`, this property will instead get the value -from the `X-Forwarded-Host` header field. This header can be set by -the client or by the proxy. - -If there is more than one `X-Forwarded-Host` header in the request, the -value of the first header is used. This includes a single header with -comma-separated values, in which the first value is used. - -
-Prior to Express v4.17.0, the `X-Forwarded-Host` could not contain multiple -values or be present more than once. -
- -```js -// Host: "example.com:3000" -console.dir(req.hostname) -// => 'example.com' -``` diff --git a/_includes/api/en/4x/req-ip.md b/_includes/api/en/4x/req-ip.md deleted file mode 100644 index fea8639d52..0000000000 --- a/_includes/api/en/4x/req-ip.md +++ /dev/null @@ -1,12 +0,0 @@ -

req.ip

- -Contains the remote IP address of the request. - -When the [`trust proxy` setting](/4x/api.html#trust.proxy.options.table) does not evaluate to `false`, -the value of this property is derived from the left-most entry in the -`X-Forwarded-For` header. This header can be set by the client or by the proxy. - -```js -console.dir(req.ip) -// => '127.0.0.1' -``` diff --git a/_includes/api/en/4x/req-ips.md b/_includes/api/en/4x/req-ips.md deleted file mode 100644 index 7fa7b2653e..0000000000 --- a/_includes/api/en/4x/req-ips.md +++ /dev/null @@ -1,9 +0,0 @@ -

req.ips

- -When the [`trust proxy` setting](/4x/api.html#trust.proxy.options.table) does not evaluate to `false`, -this property contains an array of IP addresses -specified in the `X-Forwarded-For` request header. Otherwise, it contains an -empty array. This header can be set by the client or by the proxy. - -For example, if `X-Forwarded-For` is `client, proxy1, proxy2`, `req.ips` would be -`["client", "proxy1", "proxy2"]`, where `proxy2` is the furthest downstream. diff --git a/_includes/api/en/4x/req-is.md b/_includes/api/en/4x/req-is.md deleted file mode 100644 index 93d8fd40f4..0000000000 --- a/_includes/api/en/4x/req-is.md +++ /dev/null @@ -1,42 +0,0 @@ -

req.is(type)

- -Returns the matching content type if the incoming request's "Content-Type" HTTP header field -matches the MIME type specified by the `type` parameter. If the request has no body, returns `null`. -Returns `false` otherwise. - -```js -// With Content-Type: text/html; charset=utf-8 -req.is('html') -// => 'html' -req.is('text/html') -// => 'text/html' -req.is('text/*') -// => 'text/*' - -// When Content-Type is application/json -req.is('json') -// => 'json' -req.is('application/json') -// => 'application/json' -req.is('application/*') -// => 'application/*' - -// Using arrays -// When Content-Type is application/json -req.is(['json', 'html']) -// => 'json' - -// Using multiple arguments -// When Content-Type is application/json -req.is('json', 'html') -// => 'json' - -req.is('html') -// => false -req.is(['xml', 'yaml']) -// => false -req.is('xml', 'yaml') -// => false -``` - -For more information, or if you have issues or concerns, see [type-is](https://github.com/expressjs/type-is). \ No newline at end of file diff --git a/_includes/api/en/4x/req-method.md b/_includes/api/en/4x/req-method.md deleted file mode 100644 index 3d2b886d7d..0000000000 --- a/_includes/api/en/4x/req-method.md +++ /dev/null @@ -1,4 +0,0 @@ -

req.method

- -Contains a string corresponding to the HTTP method of the request: -`GET`, `POST`, `PUT`, and so on. diff --git a/_includes/api/en/4x/req-originalUrl.md b/_includes/api/en/4x/req-originalUrl.md deleted file mode 100644 index 4babd80d04..0000000000 --- a/_includes/api/en/4x/req-originalUrl.md +++ /dev/null @@ -1,27 +0,0 @@ -

req.originalUrl

- -
-`req.url` is not a native Express property, it is inherited from Node's [http module](https://nodejs.org/api/http.html#http_message_url). -
- -This property is much like `req.url`; however, it retains the original request URL, -allowing you to rewrite `req.url` freely for internal routing purposes. For example, -the "mounting" feature of [app.use()](#app.use) will rewrite `req.url` to strip the mount point. - -```js -// GET /search?q=something -console.dir(req.originalUrl) -// => '/search?q=something' -``` - -`req.originalUrl` is available both in middleware and router objects, and is a -combination of `req.baseUrl` and `req.url`. Consider following example: - -```js -app.use('/admin', function (req, res, next) { // GET 'http://www.example.com/admin/new?sort=desc' - console.dir(req.originalUrl) // '/admin/new?sort=desc' - console.dir(req.baseUrl) // '/admin' - console.dir(req.path) // '/new' - next() -}) -``` diff --git a/_includes/api/en/4x/req-param.md b/_includes/api/en/4x/req-param.md deleted file mode 100644 index c0a31602be..0000000000 --- a/_includes/api/en/4x/req-param.md +++ /dev/null @@ -1,35 +0,0 @@ -

req.param(name [, defaultValue])

- -
-Deprecated. Use either `req.params`, `req.body` or `req.query`, as applicable. -
- -Returns the value of param `name` when present. - -```js -// ?name=tobi -req.param('name') -// => "tobi" - -// POST name=tobi -req.param('name') -// => "tobi" - -// /user/tobi for /user/:name -req.param('name') -// => "tobi" -``` - -Lookup is performed in the following order: - -* `req.params` -* `req.body` -* `req.query` - -Optionally, you can specify `defaultValue` to set a default value if the parameter is not found in any of the request objects. - -
-Direct access to `req.body`, `req.params`, and `req.query` should be favoured for clarity - unless you truly accept input from each object. - -Body-parsing middleware must be loaded for `req.param()` to work predictably. Refer [req.body](#req.body) for details. -
diff --git a/_includes/api/en/4x/req-params.md b/_includes/api/en/4x/req-params.md deleted file mode 100644 index a92d640508..0000000000 --- a/_includes/api/en/4x/req-params.md +++ /dev/null @@ -1,25 +0,0 @@ -

req.params

- -This property is an object containing properties mapped to the [named route "parameters"](/{{ page.lang }}/guide/routing.html#route-parameters). For example, if you have the route `/user/:name`, then the "name" property is available as `req.params.name`. This object defaults to `{}`. - -```js -// GET /user/tj -console.dir(req.params.name) -// => 'tj' -``` - -When you use a regular expression for the route definition, capture groups are provided as integer keys using `req.params[n]`, where `n` is the nth capture group. This rule is applied to unnamed wild card matches with string routes such as `/file/*`: - -```js -// GET /file/javascripts/jquery.js -console.dir(req.params[0]) -// => 'javascripts/jquery.js' -``` - -Named capturing groups in regular expressions behave like named route parameters. For example the group from `/^\/file\/(?.*)$/` expression is available as `req.params.path`. - -If you need to make changes to a key in `req.params`, use the [app.param](/{{ page.lang }}/4x/api.html#app.param) handler. Changes are applicable only to [parameters](/{{ page.lang }}/guide/routing.html#route-parameters) already defined in the route path. - -Any changes made to the `req.params` object in a middleware or route handler will be reset. - -{% include admonitions/note.html content="Express automatically decodes the values in `req.params` (using `decodeURIComponent`)." %} diff --git a/_includes/api/en/4x/req-path.md b/_includes/api/en/4x/req-path.md deleted file mode 100644 index 0b71c71bf7..0000000000 --- a/_includes/api/en/4x/req-path.md +++ /dev/null @@ -1,13 +0,0 @@ -

req.path

- -Contains the path part of the request URL. - -```js -// example.com/users?sort=desc -console.dir(req.path) -// => '/users' -``` - -
-When called from a middleware, the mount point is not included in `req.path`. See [app.use()](/4x/api.html#app.use) for more details. -
diff --git a/_includes/api/en/4x/req-protocol.md b/_includes/api/en/4x/req-protocol.md deleted file mode 100644 index 6d38cc1f19..0000000000 --- a/_includes/api/en/4x/req-protocol.md +++ /dev/null @@ -1,12 +0,0 @@ -

req.protocol

- -Contains the request protocol string: either `http` or (for TLS requests) `https`. - -When the [`trust proxy` setting](#trust.proxy.options.table) does not evaluate to `false`, -this property will use the value of the `X-Forwarded-Proto` header field if present. -This header can be set by the client or by the proxy. - -```js -console.dir(req.protocol) -// => 'http' -``` diff --git a/_includes/api/en/4x/req-query.md b/_includes/api/en/4x/req-query.md deleted file mode 100644 index 019779b8f3..0000000000 --- a/_includes/api/en/4x/req-query.md +++ /dev/null @@ -1,19 +0,0 @@ -

req.query

- -This property is an object containing a property for each query string parameter in the route. -When [query parser](#app.settings.table) is set to disabled, it is an empty object `{}`, otherwise it is the result of the configured query parser. - -
-As `req.query`'s shape is based on user-controlled input, all properties and values in this object are untrusted and should be validated before trusting. For example, `req.query.foo.toString()` may fail in multiple ways, for example `foo` may not be there or may not be a string, and `toString` may not be a function and instead a string or other user-input. -
- -The value of this property can be configured with the [query parser application setting](#app.settings.table) to work how your application needs it. A very popular query string parser is the [`qs` module](https://www.npmjs.org/package/qs), and this is used by default. The `qs` module is very configurable with many settings, and it may be desirable to use different settings than the default to populate `req.query`: - -```js -var qs = require('qs') -app.set('query parser', function (str) { - return qs.parse(str, { /* custom options */ }) -}) -``` - -Check out the [query parser application setting](#app.settings.table) documentation for other customization options. diff --git a/_includes/api/en/4x/req-range.md b/_includes/api/en/4x/req-range.md deleted file mode 100644 index dbf2afeb5e..0000000000 --- a/_includes/api/en/4x/req-range.md +++ /dev/null @@ -1,33 +0,0 @@ -

req.range(size[, options])

- -`Range` header parser. - -The `size` parameter is the maximum size of the resource. - -The `options` parameter is an object that can have the following properties. - -
- -| Property | Type | Description | -|-------------|-------------------------------------------------------------------------| -| `combine` | Boolean | Specify if overlapping & adjacent ranges should be combined, defaults to `false`. When `true`, ranges will be combined and returned as if they were specified that way in the header.| - -
- -An array of ranges will be returned or negative numbers indicating an error parsing. - -* `-2` signals a malformed header string -* `-1` signals an unsatisfiable range - -```js -// parse header from request -var range = req.range(1000) - -// the type of the range -if (range.type === 'bytes') { - // the ranges - range.forEach(function (r) { - // do something with r.start and r.end - }) -} -``` diff --git a/_includes/api/en/4x/req-res.md b/_includes/api/en/4x/req-res.md deleted file mode 100644 index 772ddb43b4..0000000000 --- a/_includes/api/en/4x/req-res.md +++ /dev/null @@ -1,4 +0,0 @@ -

req.res

- -This property holds a reference to the response object -that relates to this request object. diff --git a/_includes/api/en/4x/req-route.md b/_includes/api/en/4x/req-route.md deleted file mode 100644 index bc5bc31a0b..0000000000 --- a/_includes/api/en/4x/req-route.md +++ /dev/null @@ -1,25 +0,0 @@ -

req.route

- -Contains the currently-matched route, a string. For example: - -```js -app.get('/user/:id?', function userIdHandler (req, res) { - console.log(req.route) - res.send('GET') -}) -``` - -Example output from the previous snippet: - -``` -{ path: '/user/:id?', - stack: - [ { handle: [Function: userIdHandler], - name: 'userIdHandler', - params: undefined, - path: undefined, - keys: [], - regexp: /^\/?$/i, - method: 'get' } ], - methods: { get: true } } -``` diff --git a/_includes/api/en/4x/req-secure.md b/_includes/api/en/4x/req-secure.md deleted file mode 100644 index 5519638a47..0000000000 --- a/_includes/api/en/4x/req-secure.md +++ /dev/null @@ -1,8 +0,0 @@ -

req.secure

- -A Boolean property that is true if a TLS connection is established. Equivalent to: - -```js -console.dir(req.protocol === 'https') -// => true -``` diff --git a/_includes/api/en/4x/req-signedCookies.md b/_includes/api/en/4x/req-signedCookies.md deleted file mode 100644 index eb2cba8343..0000000000 --- a/_includes/api/en/4x/req-signedCookies.md +++ /dev/null @@ -1,17 +0,0 @@ -

req.signedCookies

- -When using [cookie-parser](https://www.npmjs.com/package/cookie-parser) middleware, this property -contains signed cookies sent by the request, unsigned and ready for use. Signed cookies reside -in a different object to show developer intent; otherwise, a malicious attack could be placed on -`req.cookie` values (which are easy to spoof). Note that signing a cookie does not make it "hidden" -or encrypted; but simply prevents tampering (because the secret used to sign is private). - -If no signed cookies are sent, the property defaults to `{}`. - -```js -// Cookie: user=tobi.CP7AWaXDfAKIRfH49dQzKJx7sKzzSoPq7/AcBBRVwlI3 -console.dir(req.signedCookies.user) -// => 'tobi' -``` - -For more information, issues, or concerns, see [cookie-parser](https://github.com/expressjs/cookie-parser). diff --git a/_includes/api/en/4x/req-stale.md b/_includes/api/en/4x/req-stale.md deleted file mode 100644 index ca8b479f4c..0000000000 --- a/_includes/api/en/4x/req-stale.md +++ /dev/null @@ -1,9 +0,0 @@ -

req.stale

- -Indicates whether the request is "stale," and is the opposite of `req.fresh`. -For more information, see [req.fresh](#req.fresh). - -```js -console.dir(req.stale) -// => true -``` diff --git a/_includes/api/en/4x/req-subdomains.md b/_includes/api/en/4x/req-subdomains.md deleted file mode 100644 index 0f8e27a840..0000000000 --- a/_includes/api/en/4x/req-subdomains.md +++ /dev/null @@ -1,13 +0,0 @@ -

req.subdomains

- -An array of subdomains in the domain name of the request. - -```js -// Host: "tobi.ferrets.example.com" -console.dir(req.subdomains) -// => ['ferrets', 'tobi'] -``` - -The application property `subdomain offset`, which defaults to 2, is used for determining the -beginning of the subdomain segments. To change this behavior, change its value -using [app.set](/{{ page.lang }}/4x/api.html#app.set). diff --git a/_includes/api/en/4x/req-xhr.md b/_includes/api/en/4x/req-xhr.md deleted file mode 100644 index 5c1da1c704..0000000000 --- a/_includes/api/en/4x/req-xhr.md +++ /dev/null @@ -1,9 +0,0 @@ -

req.xhr

- -A Boolean property that is `true` if the request's `X-Requested-With` header field is -"XMLHttpRequest", indicating that the request was issued by a client library such as jQuery. - -```js -console.dir(req.xhr) -// => true -``` diff --git a/_includes/api/en/4x/req.md b/_includes/api/en/4x/req.md deleted file mode 100644 index 8e217a6f5b..0000000000 --- a/_includes/api/en/4x/req.md +++ /dev/null @@ -1,156 +0,0 @@ -

Request

- -The `req` object represents the HTTP request and has properties for the -request query string, parameters, body, HTTP headers, and so on. In this documentation and by convention, -the object is always referred to as `req` (and the HTTP response is `res`) but its actual name is determined -by the parameters to the callback function in which you're working. - -For example: - -```js -app.get('/user/:id', function (req, res) { - res.send('user ' + req.params.id) -}) -``` - -But you could just as well have: - -```js -app.get('/user/:id', function (request, response) { - response.send('user ' + request.params.id) -}) -``` - -The `req` object is an enhanced version of Node's own request object -and supports all [built-in fields and methods](https://nodejs.org/api/http.html#http_class_http_incomingmessage). - -

Properties

- -
-In Express 4, `req.files` is no longer available on the `req` object by default. To access uploaded files -on the `req.files` object, use multipart-handling middleware like [busboy](https://www.npmjs. -com/package/busboy), [multer](https://www.npmjs.com/package/multer), -[formidable](https://www.npmjs.com/package/formidable), -[multiparty](https://www.npmjs.com/package/multiparty), -[connect-multiparty](https://www.npmjs.com/package/connect-multiparty), -or [pez](https://www.npmjs.com/package/pez). -
- -
- {% include api/en/4x/req-app.md %} -
- -
- {% include api/en/4x/req-baseUrl.md %} -
- -
- {% include api/en/4x/req-body.md %} -
- -
- {% include api/en/4x/req-cookies.md %} -
- -
- {% include api/en/4x/req-fresh.md %} -
- -
- {% include api/en/4x/req-hostname.md %} -
- -
- {% include api/en/4x/req-ip.md %} -
- -
- {% include api/en/4x/req-ips.md %} -
- -
- {% include api/en/4x/req-method.md %} -
- -
- {% include api/en/4x/req-originalUrl.md %} -
- -
- {% include api/en/4x/req-params.md %} -
- -
- {% include api/en/4x/req-path.md %} -
- -
- {% include api/en/4x/req-protocol.md %} -
- -
- {% include api/en/4x/req-query.md %} -
- -
- {% include api/en/4x/req-res.md %} -
- -
- {% include api/en/4x/req-route.md %} -
- -
- {% include api/en/4x/req-secure.md %} -
- -
- {% include api/en/4x/req-signedCookies.md %} -
- -
- {% include api/en/4x/req-stale.md %} -
- -
- {% include api/en/4x/req-subdomains.md %} -
- -
- {% include api/en/4x/req-xhr.md %} -
- -

Methods

- -
- {% include api/en/4x/req-accepts.md %} -
- -
- {% include api/en/4x/req-acceptsCharsets.md %} -
- -
- {% include api/en/4x/req-acceptsEncodings.md %} -
- -
- {% include api/en/4x/req-acceptsLanguages.md %} -
- -
- {% include api/en/4x/req-get.md %} -
- -
- {% include api/en/4x/req-is.md %} -
- -
- {% include api/en/4x/req-param.md %} -
- -
- {% include api/en/4x/req-range.md %} -
- diff --git a/_includes/api/en/4x/res-app.md b/_includes/api/en/4x/res-app.md deleted file mode 100644 index 19d8681d23..0000000000 --- a/_includes/api/en/4x/res-app.md +++ /dev/null @@ -1,5 +0,0 @@ -

res.app

- -This property holds a reference to the instance of the Express application that is using the middleware. - -`res.app` is identical to the [req.app](#req.app) property in the request object. diff --git a/_includes/api/en/4x/res-append.md b/_includes/api/en/4x/res-append.md deleted file mode 100644 index c74e2fd3ef..0000000000 --- a/_includes/api/en/4x/res-append.md +++ /dev/null @@ -1,13 +0,0 @@ -

res.append(field [, value])

- -{% include admonitions/note.html content="`res.append()` is supported by Express v4.11.0+" %} -Appends the specified `value` to the HTTP response header `field`. If the header is not already set, -it creates the header with the specified value. The `value` parameter can be a string or an array. - -{% include admonitions/note.html content="calling `res.set()` after `res.append()` will reset the previously-set header value." %} - -```js -res.append('Link', ['', '']) -res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly') -res.append('Warning', '199 Miscellaneous warning') -``` diff --git a/_includes/api/en/4x/res-attachment.md b/_includes/api/en/4x/res-attachment.md deleted file mode 100644 index 73932d14fc..0000000000 --- a/_includes/api/en/4x/res-attachment.md +++ /dev/null @@ -1,14 +0,0 @@ -

res.attachment([filename])

- -Sets the HTTP response `Content-Disposition` header field to "attachment". If a `filename` is given, -then it sets the Content-Type based on the extension name via `res.type()`, -and sets the `Content-Disposition` "filename=" parameter. - -```js -res.attachment() -// Content-Disposition: attachment - -res.attachment('path/to/logo.png') -// Content-Disposition: attachment; filename="logo.png" -// Content-Type: image/png -``` diff --git a/_includes/api/en/4x/res-clearCookie.md b/_includes/api/en/4x/res-clearCookie.md deleted file mode 100644 index 5b6ea8517a..0000000000 --- a/_includes/api/en/4x/res-clearCookie.md +++ /dev/null @@ -1,22 +0,0 @@ -

res.clearCookie(name [, options])

- -Clears the cookie with the specified `name` by sending a `Set-Cookie` header that sets its expiration date in the past. -This instructs the client that the cookie has expired and is no longer valid. For more information -about available `options`, see [res.cookie()](#res.cookie). - -
-If the `maxAge` or `expires` options are set, the cookie may not be cleared depending on the time values provided, -as Express does not ignore these options. It is therefore recommended to omit these options when calling this -method. Passing these two options has been deprecated since Express v4.20.0. -
- -
-Web browsers and other compliant clients will only clear the cookie if the given -`options` is identical to those given to [res.cookie()](#res.cookie), excluding -`expires` and `maxAge`. -
- -```js -res.cookie('name', 'tobi', { path: '/admin' }) -res.clearCookie('name', { path: '/admin' }) -``` diff --git a/_includes/api/en/4x/res-cookie.md b/_includes/api/en/4x/res-cookie.md deleted file mode 100644 index 814779340c..0000000000 --- a/_includes/api/en/4x/res-cookie.md +++ /dev/null @@ -1,87 +0,0 @@ -

res.cookie(name, value [, options])

- -Sets cookie `name` to `value`. The `value` parameter may be a string or object converted to JSON. - -The `options` parameter is an object that can have the following properties. - -
- -| Property | Type | Description | -|---------------|-------------------------------------------------------------------------| -| `domain` | String | Domain name for the cookie. Defaults to the domain name of the app.| -| `encode` | Function | A synchronous function used for cookie value encoding. Defaults to `encodeURIComponent`.| -| `expires` | Date | Expiry date of the cookie in GMT. If not specified or set to 0, creates a session cookie.| -| `httpOnly` | Boolean | Flags the cookie to be accessible only by the web server.| -| `maxAge` | Number | Convenient option for setting the expiry time relative to the current time in milliseconds.| -| `path` | String | Path for the cookie. Defaults to "/".| -| `partitioned` | Boolean | Indicates that the cookie should be stored using partitioned storage. See [Cookies Having Independent Partitioned State (CHIPS)](https://developer.mozilla.org/en-US/docs/Web/Privacy/Partitioned_cookies) for more details.| -| `priority` | String | Value of the "Priority" **Set-Cookie** attribute.| -| `secure` | Boolean | Marks the cookie to be used with HTTPS only.| -| `signed` | Boolean | Indicates if the cookie should be signed.| -| `sameSite` | Boolean or String | Value of the "SameSite" **Set-Cookie** attribute. More information at [https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00#section-4.1.1](https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00#section-4.1.1).| - -
- -
-All `res.cookie()` does is set the HTTP `Set-Cookie` header with the options provided. -Any option not specified defaults to the value stated in [RFC 6265](http://tools.ietf.org/html/rfc6265). -
- -For example: - -```js -res.cookie('name', 'tobi', { domain: '.example.com', path: '/admin', secure: true }) -res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true }) -``` - -You can set multiple cookies in a single response by calling `res.cookie` multiple times, for example: - -```js -res - .status(201) - .cookie('access_token', 'Bearer ' + token, { - expires: new Date(Date.now() + 8 * 3600000) // cookie will be removed after 8 hours - }) - .cookie('test', 'test') - .redirect(301, '/admin') -``` - -The `encode` option allows you to choose the function used for cookie value encoding. -Does not support asynchronous functions. - -Example use case: You need to set a domain-wide cookie for another site in your organization. -This other site (not under your administrative control) does not use URI-encoded cookie values. - -```js -// Default encoding -res.cookie('some_cross_domain_cookie', 'http://mysubdomain.example.com', { domain: 'example.com' }) -// Result: 'some_cross_domain_cookie=http%3A%2F%2Fmysubdomain.example.com; Domain=example.com; Path=/' - -// Custom encoding -res.cookie('some_cross_domain_cookie', 'http://mysubdomain.example.com', { domain: 'example.com', encode: String }) -// Result: 'some_cross_domain_cookie=http://mysubdomain.example.com; Domain=example.com; Path=/;' -``` - -The `maxAge` option is a convenience option for setting "expires" relative to the current time in milliseconds. -The following is equivalent to the second example above. - -```js -res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true }) -``` - -You can pass an object as the `value` parameter; it is then serialized as JSON and parsed by `bodyParser()` middleware. - -```js -res.cookie('cart', { items: [1, 2, 3] }) -res.cookie('cart', { items: [1, 2, 3] }, { maxAge: 900000 }) -``` - -When using [cookie-parser](https://www.npmjs.com/package/cookie-parser) middleware, this method also -supports signed cookies. Simply include the `signed` option set to `true`. -Then `res.cookie()` will use the secret passed to `cookieParser(secret)` to sign the value. - -```js -res.cookie('name', 'tobi', { signed: true }) -``` - -Later you may access this value through the [req.signedCookie](#req.signedCookies) object. diff --git a/_includes/api/en/4x/res-download.md b/_includes/api/en/4x/res-download.md deleted file mode 100644 index 6851716f16..0000000000 --- a/_includes/api/en/4x/res-download.md +++ /dev/null @@ -1,56 +0,0 @@ -

res.download(path [, filename] [, options] [, fn])

- -Transfers the file at `path` as an "attachment". Typically, browsers will prompt the user for download. -By default, the `Content-Disposition` header "filename=" parameter is derived from the `path` argument, but can be overridden with the `filename` parameter. -If `path` is relative, then it will be based on the current working directory of the process or -the `root` option, if provided. - -
-This API provides access to data on the running file system. Ensure that either (a) the way in -which the `path` argument was constructed is secure if it contains user input or (b) set the `root` -option to the absolute path of a directory to contain access within. - -When the `root` option is provided, Express will validate that the relative path provided as -`path` will resolve within the given `root` option. -
- -The following table provides details on the `options` parameter. - -
-The optional `options` argument is supported by Express v4.16.0 onwards. -
- -
- -| Property | Description | Default | Availability | -|-----------------|-------------------------------------------------|-------------|--------------| -| `maxAge` | Sets the max-age property of the `Cache-Control` header in milliseconds or a string in [ms format](https://www.npmjs.org/package/ms)| 0 | 4.16+ | -| `root` | Root directory for relative filenames.| | 4.18+ | -| `lastModified` | Sets the `Last-Modified` header to the last modified date of the file on the OS. Set `false` to disable it.| Enabled | 4.16+ | -| `headers` | Object containing HTTP headers to serve with the file. The header `Content-Disposition` will be overridden by the `filename` argument.| | 4.16+ | -| `dotfiles` | Option for serving dotfiles. Possible values are "allow", "deny", "ignore".| "ignore" | 4.16+ | -| `acceptRanges` | Enable or disable accepting ranged requests. | `true` | 4.16+ | -| `cacheControl` | Enable or disable setting `Cache-Control` response header.| `true` | 4.16+ | -| `immutable` | Enable or disable the `immutable` directive in the `Cache-Control` response header. If enabled, the `maxAge` option should also be specified to enable caching. The `immutable` directive will prevent supported clients from making conditional requests during the life of the `maxAge` option to check if the file has changed. | `false` | 4.16+ | - -
- -The method invokes the callback function `fn(err)` when the transfer is complete -or when an error occurs. If the callback function is specified and an error occurs, -the callback function must explicitly handle the response process either by -ending the request-response cycle, or by passing control to the next route. - -```js -res.download('/report-12345.pdf') - -res.download('/report-12345.pdf', 'report.pdf') - -res.download('/report-12345.pdf', 'report.pdf', function (err) { - if (err) { - // Handle error, but keep in mind the response may be partially-sent - // so check res.headersSent - } else { - // decrement a download credit, etc. - } -}) -``` diff --git a/_includes/api/en/4x/res-end.md b/_includes/api/en/4x/res-end.md deleted file mode 100644 index 2906805fea..0000000000 --- a/_includes/api/en/4x/res-end.md +++ /dev/null @@ -1,10 +0,0 @@ -

res.end([data[, encoding]][, callback])

- -Ends the response process. This method actually comes from Node core, specifically the [response.end() method of http.ServerResponse](https://nodejs.org/api/http.html#responseenddata-encoding-callback). - -Use to quickly end the response without any data. If you need to respond with data, instead use methods such as [res.send()](#res.send) and [res.json()](#res.json). - -```js -res.end() -res.status(404).end() -``` diff --git a/_includes/api/en/4x/res-format.md b/_includes/api/en/4x/res-format.md deleted file mode 100644 index 476a2caea7..0000000000 --- a/_includes/api/en/4x/res-format.md +++ /dev/null @@ -1,52 +0,0 @@ -

res.format(object)

- -Performs content-negotiation on the `Accept` HTTP header on the request object, when present. -It uses [req.accepts()](#req.accepts) to select a handler for the request, based on the acceptable -types ordered by their quality values. If the header is not specified, the first callback is invoked. -When no match is found, the server responds with 406 "Not Acceptable", or invokes the `default` callback. - -The `Content-Type` response header is set when a callback is selected. However, you may alter -this within the callback using methods such as `res.set()` or `res.type()`. - -The following example would respond with `{ "message": "hey" }` when the `Accept` header field is set -to "application/json" or "\*/json" (however if it is "\*/\*", then the response will be "hey"). - -```js -res.format({ - 'text/plain': function () { - res.send('hey') - }, - - 'text/html': function () { - res.send('

hey

') - }, - - 'application/json': function () { - res.send({ message: 'hey' }) - }, - - default: function () { - // log the request and respond with 406 - res.status(406).send('Not Acceptable') - } -}) -``` - -In addition to canonicalized MIME types, you may also use extension names mapped -to these types for a slightly less verbose implementation: - -```js -res.format({ - text: function () { - res.send('hey') - }, - - html: function () { - res.send('

hey

') - }, - - json: function () { - res.send({ message: 'hey' }) - } -}) -``` diff --git a/_includes/api/en/4x/res-get.md b/_includes/api/en/4x/res-get.md deleted file mode 100644 index 8aefb205ef..0000000000 --- a/_includes/api/en/4x/res-get.md +++ /dev/null @@ -1,9 +0,0 @@ -

res.get(field)

- -Returns the HTTP response header specified by `field`. -The match is case-insensitive. - -```js -res.get('Content-Type') -// => "text/plain" -``` diff --git a/_includes/api/en/4x/res-headersSent.md b/_includes/api/en/4x/res-headersSent.md deleted file mode 100644 index 3f7923ee7d..0000000000 --- a/_includes/api/en/4x/res-headersSent.md +++ /dev/null @@ -1,11 +0,0 @@ -

res.headersSent

- -Boolean property that indicates if the app sent HTTP headers for the response. - -```js -app.get('/', function (req, res) { - console.dir(res.headersSent) // false - res.send('OK') - console.dir(res.headersSent) // true -}) -``` diff --git a/_includes/api/en/4x/res-json.md b/_includes/api/en/4x/res-json.md deleted file mode 100644 index 8e699a7ed5..0000000000 --- a/_includes/api/en/4x/res-json.md +++ /dev/null @@ -1,13 +0,0 @@ -

res.json([body])

- -Sends a JSON response. This method sends a response (with the correct content-type) that is the parameter converted to a -JSON string using [JSON.stringify()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). - -The parameter can be any JSON type, including object, array, string, Boolean, number, or null, -and you can also use it to convert other values to JSON. - -```js -res.json(null) -res.json({ user: 'tobi' }) -res.status(500).json({ error: 'message' }) -``` diff --git a/_includes/api/en/4x/res-jsonp.md b/_includes/api/en/4x/res-jsonp.md deleted file mode 100644 index edb7e45466..0000000000 --- a/_includes/api/en/4x/res-jsonp.md +++ /dev/null @@ -1,32 +0,0 @@ -

res.jsonp([body])

- -Sends a JSON response with JSONP support. This method is identical to `res.json()`, -except that it opts-in to JSONP callback support. - -```js -res.jsonp(null) -// => callback(null) - -res.jsonp({ user: 'tobi' }) -// => callback({ "user": "tobi" }) - -res.status(500).jsonp({ error: 'message' }) -// => callback({ "error": "message" }) -``` - -By default, the JSONP callback name is simply `callback`. Override this with the -jsonp callback name setting. - -The following are some examples of JSONP responses using the same code: - -```js -// ?callback=foo -res.jsonp({ user: 'tobi' }) -// => foo({ "user": "tobi" }) - -app.set('jsonp callback name', 'cb') - -// ?cb=foo -res.status(500).jsonp({ error: 'message' }) -// => foo({ "error": "message" }) -``` diff --git a/_includes/api/en/4x/res-links.md b/_includes/api/en/4x/res-links.md deleted file mode 100644 index 17164155d4..0000000000 --- a/_includes/api/en/4x/res-links.md +++ /dev/null @@ -1,20 +0,0 @@ - - -Joins the `links` provided as properties of the parameter to populate the response's -`Link` HTTP header field. - -For example, the following call: - -```js -res.links({ - next: 'http://api.example.com/users?page=2', - last: 'http://api.example.com/users?page=5' -}) -``` - -Yields the following results: - -``` -Link: ; rel="next", - ; rel="last" -``` diff --git a/_includes/api/en/4x/res-locals.md b/_includes/api/en/4x/res-locals.md deleted file mode 100644 index 38949ea971..0000000000 --- a/_includes/api/en/4x/res-locals.md +++ /dev/null @@ -1,28 +0,0 @@ -

res.locals

- -Use this property to set variables accessible in templates rendered with [res.render](#res.render). -The variables set on `res.locals` are available within a single request-response cycle, and will not -be shared between requests. - -
-The `locals` object is used by view engines to render a response. The object -keys may be particularly sensitive and should not contain user-controlled -input, as it may affect the operation of the view engine or provide a path to -cross-site scripting. Consult the documentation for the used view engine for -additional considerations. -
- -In order to keep local variables for use in template rendering between requests, use -[app.locals](#app.locals) instead. - -This property is useful for exposing request-level information such as the request path name, -authenticated user, user settings, and so on to templates rendered within the application. - -```js -app.use(function (req, res, next) { - // Make `user` and `authenticated` available in templates - res.locals.user = req.user - res.locals.authenticated = !req.user.anonymous - next() -}) -``` diff --git a/_includes/api/en/4x/res-location.md b/_includes/api/en/4x/res-location.md deleted file mode 100644 index c093ef3966..0000000000 --- a/_includes/api/en/4x/res-location.md +++ /dev/null @@ -1,24 +0,0 @@ -

res.location(path)

- -Sets the response `Location` HTTP header to the specified `path` parameter. - -```js -res.location('/foo/bar') -res.location('http://example.com') -res.location('back') -``` - -{% include admonitions/note.html content="`'back'` was deprecated in 4.21.0, use `req.get('Referrer') || '/'` as an argument instead." %} - -A `path` value of "back" has a special meaning, it refers to the URL specified in the `Referer` header of the request. If the `Referer` header was not specified, it refers to "/". - -See also [Security best practices: Prevent open redirect -vulnerabilities](http://expressjs.com/en/advanced/best-practice-security.html#prevent-open-redirects). - -
-After encoding the URL, if not encoded already, Express passes the specified URL to the browser in the `Location` header, -without any validation. - -Browsers take the responsibility of deriving the intended URL from the current URL -or the referring URL, and the URL specified in the `Location` header; and redirect the user accordingly. -
diff --git a/_includes/api/en/4x/res-redirect.md b/_includes/api/en/4x/res-redirect.md deleted file mode 100644 index 0ff536ef32..0000000000 --- a/_includes/api/en/4x/res-redirect.md +++ /dev/null @@ -1,58 +0,0 @@ -

res.redirect([status,] path)

- -Redirects to the URL derived from the specified `path`, with specified `status`, a positive integer -that corresponds to an [HTTP status code](https://www.rfc-editor.org/rfc/rfc9110.html#name-status-codes) . -If not specified, `status` defaults to "302 "Found". - -```js -res.redirect('/foo/bar') -res.redirect('http://example.com') -res.redirect(301, 'http://example.com') -res.redirect('../login') -``` -Redirects can be a fully-qualified URL for redirecting to a different site: - -```js -res.redirect('http://google.com') -``` -Redirects can be relative to the root of the host name. For example, if the -application is on `http://example.com/admin/post/new`, the following -would redirect to the URL `http://example.com/admin`: - -```js -res.redirect('/admin') -``` - -Redirects can be relative to the current URL. For example, -from `http://example.com/blog/admin/` (notice the trailing slash), the following -would redirect to the URL `http://example.com/blog/admin/post/new`. - -```js -res.redirect('post/new') -``` - -Redirecting to `post/new` from `http://example.com/blog/admin` (no trailing slash), -will redirect to `http://example.com/blog/post/new`. - -If you found the above behavior confusing, think of path segments as directories -(with trailing slashes) and files, it will start to make sense. - -Path-relative redirects are also possible. If you were on -`http://example.com/admin/post/new`, the following would redirect to -`http://example.com/admin/post`: - -```js -res.redirect('..') -``` - -A `back` redirection redirects the request back to the [referer](http://en.wikipedia.org/wiki/HTTP_referer), -defaulting to `/` when the referer is missing. - -```js -res.redirect('back') -``` - -{% include admonitions/note.html content="`back` redirect was deprecated in 4.21.0, use `req.get('Referrer') || '/'` as an argument instead." %} - -See also [Security best practices: Prevent open redirect -vulnerabilities](http://expressjs.com/en/advanced/best-practice-security.html#prevent-open-redirects). diff --git a/_includes/api/en/4x/res-render.md b/_includes/api/en/4x/res-render.md deleted file mode 100644 index 2bcb4e60cb..0000000000 --- a/_includes/api/en/4x/res-render.md +++ /dev/null @@ -1,45 +0,0 @@ -

res.render(view [, locals] [, callback])

- -Renders a `view` and sends the rendered HTML string to the client. -Optional parameters: - -- `locals`, an object whose properties define local variables for the view. -- `callback`, a callback function. If provided, the method returns both the possible error and rendered string, but does not perform an automated response. When an error occurs, the method invokes `next(err)` internally. - -The `view` argument is a string that is the file path of the view file to render. This can be an absolute path, or a path relative to the `views` setting. If the path does not contain a file extension, then the `view engine` setting determines the file extension. If the path does contain a file extension, then Express will load the module for the specified template engine (via `require()`) and render it using the loaded module's `__express` function. - -For more information, see [Using template engines with Express](/{{page.lang}}/guide/using-template-engines.html). - -
-The `view` argument performs file system operations like reading a file from -disk and evaluating Node.js modules, and as so for security reasons should not -contain input from the end-user. -
- -
-The `locals` object is used by view engines to render a response. The object -keys may be particularly sensitive and should not contain user-controlled -input, as it may affect the operation of the view engine or provide a path to -cross-site scripting. Consult the documentation for the used view engine for -additional considerations. -
- -
-The local variable `cache` enables view caching. Set it to `true`, -to cache the view during development; view caching is enabled in production by default. -
- -```js -// send the rendered view to the client -res.render('index') - -// if a callback is specified, the rendered HTML string has to be sent explicitly -res.render('index', function (err, html) { - res.send(html) -}) - -// pass a local variable to the view -res.render('user', { name: 'Tobi' }, function (err, html) { - // ... -}) -``` diff --git a/_includes/api/en/4x/res-req.md b/_includes/api/en/4x/res-req.md deleted file mode 100644 index 8763653d9f..0000000000 --- a/_includes/api/en/4x/res-req.md +++ /dev/null @@ -1,4 +0,0 @@ -

res.req

- -This property holds a reference to the request object -that relates to this response object. diff --git a/_includes/api/en/4x/res-send.md b/_includes/api/en/4x/res-send.md deleted file mode 100644 index 8c902e568a..0000000000 --- a/_includes/api/en/4x/res-send.md +++ /dev/null @@ -1,39 +0,0 @@ -

res.send([body])

- -Sends the HTTP response. - -The `body` parameter can be a `Buffer` object, a `String`, an object, `Boolean`, or an `Array`. -For example: - -```js -res.send(Buffer.from('whoop')) -res.send({ some: 'json' }) -res.send('

some html

') -res.status(404).send('Sorry, we cannot find that!') -res.status(500).send({ error: 'something blew up' }) -``` - -This method performs many useful tasks for simple non-streaming responses: -For example, it automatically assigns the `Content-Length` HTTP response header field -(unless previously defined) and provides automatic HEAD and HTTP cache freshness support. - -When the parameter is a `Buffer` object, the method sets the `Content-Type` -response header field to "application/octet-stream", unless previously defined as shown below: - -```js -res.set('Content-Type', 'text/html') -res.send(Buffer.from('

some html

')) -``` - -When the parameter is a `String`, the method sets the `Content-Type` to "text/html": - -```js -res.send('

some html

') -``` - -When the parameter is an `Array` or `Object`, Express responds with the JSON representation: - -```js -res.send({ user: 'tobi' }) -res.send([1, 2, 3]) -``` diff --git a/_includes/api/en/4x/res-sendFile.md b/_includes/api/en/4x/res-sendFile.md deleted file mode 100644 index 7c72613c29..0000000000 --- a/_includes/api/en/4x/res-sendFile.md +++ /dev/null @@ -1,84 +0,0 @@ -

res.sendFile(path [, options] [, fn])

- -
-`res.sendFile()` is supported by Express v4.8.0 onwards. -
- -Transfers the file at the given `path`. Sets the `Content-Type` response HTTP header field -based on the filename's extension. Unless the `root` option is set in -the options object, `path` must be an absolute path to the file. - -
-This API provides access to data on the running file system. Ensure that either (a) the way in -which the `path` argument was constructed into an absolute path is secure if it contains user -input or (b) set the `root` option to the absolute path of a directory to contain access within. - -When the `root` option is provided, the `path` argument is allowed to be a relative path, -including containing `..`. Express will validate that the relative path provided as `path` will -resolve within the given `root` option. -
- -The following table provides details on the `options` parameter. - -
- -| Property | Description | Default | Availability | -|-----------------|-------------------------------------------------|-------------|--------------| -|`maxAge` | Sets the max-age property of the `Cache-Control` header in milliseconds or a string in [ms format](https://www.npmjs.org/package/ms)| 0 | | -| `root` | Root directory for relative filenames.| | | -| `lastModified` | Sets the `Last-Modified` header to the last modified date of the file on the OS. Set `false` to disable it.| Enabled | 4.9.0+ | -| `headers` | Object containing HTTP headers to serve with the file.| | | -| `dotfiles` | Option for serving dotfiles. Possible values are "allow", "deny", "ignore".| "ignore" |   | -| `acceptRanges` | Enable or disable accepting ranged requests. | `true` | 4.14+ | -| `cacheControl` | Enable or disable setting `Cache-Control` response header.| `true` | 4.14+ | -| `immutable` | Enable or disable the `immutable` directive in the `Cache-Control` response header. If enabled, the `maxAge` option should also be specified to enable caching. The `immutable` directive will prevent supported clients from making conditional requests during the life of the `maxAge` option to check if the file has changed. | `false` | 4.16+ | - -
- -The method invokes the callback function `fn(err)` when the transfer is complete -or when an error occurs. If the callback function is specified and an error occurs, -the callback function must explicitly handle the response process either by -ending the request-response cycle, or by passing control to the next route. - -Here is an example of using `res.sendFile` with all its arguments. - -```js -app.get('/file/:name', function (req, res, next) { - var options = { - root: path.join(__dirname, 'public'), - dotfiles: 'deny', - headers: { - 'x-timestamp': Date.now(), - 'x-sent': true - } - } - - var fileName = req.params.name - res.sendFile(fileName, options, function (err) { - if (err) { - next(err) - } else { - console.log('Sent:', fileName) - } - }) -}) -``` - -The following example illustrates using -`res.sendFile` to provide fine-grained support for serving files: - -```js -app.get('/user/:uid/photos/:file', function (req, res) { - var uid = req.params.uid - var file = req.params.file - - req.user.mayViewFilesFrom(uid, function (yes) { - if (yes) { - res.sendFile('/uploads/' + uid + '/' + file) - } else { - res.status(403).send("Sorry! You can't see that.") - } - }) -}) -``` -For more information, or if you have issues or concerns, see [send](https://github.com/pillarjs/send). diff --git a/_includes/api/en/4x/res-sendStatus.md b/_includes/api/en/4x/res-sendStatus.md deleted file mode 100644 index 4439f0216d..0000000000 --- a/_includes/api/en/4x/res-sendStatus.md +++ /dev/null @@ -1,15 +0,0 @@ -

res.sendStatus(statusCode)

- -Sets the response HTTP status code to `statusCode` and sends the registered status message as the text response body. If an unknown status code is specified, the response body will just be the code number. - -```js -res.sendStatus(404) -``` - -
-Some versions of Node.js will throw when `res.statusCode` is set to an -invalid HTTP status code (outside of the range `100` to `599`). Consult -the HTTP server documentation for the Node.js version being used. -
- -[More about HTTP Status Codes](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) diff --git a/_includes/api/en/4x/res-set.md b/_includes/api/en/4x/res-set.md deleted file mode 100644 index b91698d4b1..0000000000 --- a/_includes/api/en/4x/res-set.md +++ /dev/null @@ -1,16 +0,0 @@ -

res.set(field [, value])

- -Sets the response's HTTP header `field` to `value`. -To set multiple fields at once, pass an object as the parameter. - -```js -res.set('Content-Type', 'text/plain') - -res.set({ - 'Content-Type': 'text/plain', - 'Content-Length': '123', - ETag: '12345' -}) -``` - -Aliased as `res.header(field [, value])`. diff --git a/_includes/api/en/4x/res-status.md b/_includes/api/en/4x/res-status.md deleted file mode 100644 index 9cb9d44e94..0000000000 --- a/_includes/api/en/4x/res-status.md +++ /dev/null @@ -1,10 +0,0 @@ -

res.status(code)

- -Sets the HTTP status for the response. -It is a chainable alias of Node's [response.statusCode](http://nodejs.org/api/http.html#http_response_statuscode). - -```js -res.status(403).end() -res.status(400).send('Bad Request') -res.status(404).sendFile('/absolute/path/to/404.png') -``` diff --git a/_includes/api/en/4x/res-type.md b/_includes/api/en/4x/res-type.md deleted file mode 100644 index 2d224ede79..0000000000 --- a/_includes/api/en/4x/res-type.md +++ /dev/null @@ -1,18 +0,0 @@ -

res.type(type)

- -Sets the `Content-Type` HTTP header to the MIME type as determined by the specified `type`. If `type` contains the "/" character, then it sets the `Content-Type` to the exact value of `type`, otherwise it is assumed to be a file extension and the MIME type is looked up in a mapping using the `express.static.mime.lookup()` method. - -```js -res.type('.html') -// => 'text/html' -res.type('html') -// => 'text/html' -res.type('json') -// => 'application/json' -res.type('application/json') -// => 'application/json' -res.type('png') -// => 'image/png' -``` - -Aliased as `res.contentType(type)`. diff --git a/_includes/api/en/4x/res-vary.md b/_includes/api/en/4x/res-vary.md deleted file mode 100644 index 956aab25ae..0000000000 --- a/_includes/api/en/4x/res-vary.md +++ /dev/null @@ -1,7 +0,0 @@ -

res.vary(field)

- -Adds the field to the `Vary` response header, if it is not there already. - -```js -res.vary('User-Agent').render('docs') -``` diff --git a/_includes/api/en/4x/res.md b/_includes/api/en/4x/res.md deleted file mode 100644 index 3af14b5ee5..0000000000 --- a/_includes/api/en/4x/res.md +++ /dev/null @@ -1,130 +0,0 @@ -

Response

- -The `res` object represents the HTTP response that an Express app sends when it gets an HTTP request. - -In this documentation and by convention, -the object is always referred to as `res` (and the HTTP request is `req`) but its actual name is determined -by the parameters to the callback function in which you're working. - -For example: - -```js -app.get('/user/:id', function (req, res) { - res.send('user ' + req.params.id) -}) -``` - -But you could just as well have: - -```js -app.get('/user/:id', function (request, response) { - response.send('user ' + request.params.id) -}) -``` - -The `res` object is an enhanced version of Node's own response object -and supports all [built-in fields and methods](https://nodejs.org/api/http.html#http_class_http_serverresponse). - -

Properties

- -
- {% include api/en/4x/res-app.md %} -
- -
- {% include api/en/4x/res-headersSent.md %} -
- -
- {% include api/en/4x/res-locals.md %} -
- -

Methods

- -
- {% include api/en/4x/res-append.md %} -
- -
- {% include api/en/4x/res-attachment.md %} -
- -
- {% include api/en/4x/res-cookie.md %} -
- -
- {% include api/en/4x/res-clearCookie.md %} -
- -
- {% include api/en/4x/res-download.md %} -
- -
- {% include api/en/4x/res-end.md %} -
- -
- {% include api/en/4x/res-format.md %} -
- -
- {% include api/en/4x/res-get.md %} -
- -
- {% include api/en/4x/res-json.md %} -
- -
- {% include api/en/4x/res-jsonp.md %} -
- -
- {% include api/en/4x/res-links.md %} -
- -
- {% include api/en/4x/res-location.md %} -
- -
- {% include api/en/4x/res-redirect.md %} -
- -
- {% include api/en/4x/res-render.md %} -
- -
- {% include api/en/4x/res-req.md %} -
- -
- {% include api/en/4x/res-send.md %} -
- -
- {% include api/en/4x/res-sendFile.md %} -
- -
- {% include api/en/4x/res-sendStatus.md %} -
- -
- {% include api/en/4x/res-set.md %} -
- -
- {% include api/en/4x/res-status.md %} -
- -
- {% include api/en/4x/res-type.md %} -
- -
- {% include api/en/4x/res-vary.md %} -
diff --git a/_includes/api/en/4x/router-METHOD.md b/_includes/api/en/4x/router-METHOD.md deleted file mode 100644 index 82dd5fc866..0000000000 --- a/_includes/api/en/4x/router-METHOD.md +++ /dev/null @@ -1,42 +0,0 @@ -

router.METHOD(path, [callback, ...] callback)

- -The `router.METHOD()` methods provide the routing functionality in Express, -where METHOD is one of the HTTP methods, such as GET, PUT, POST, and so on, -in lowercase. Thus, the actual methods are `router.get()`, `router.post()`, -`router.put()`, and so on. - -
- The `router.get()` function is automatically called for the HTTP `HEAD` method in - addition to the `GET` method if `router.head()` was not called for the - path before `router.get()`. -
- -You can provide multiple callbacks, and all are treated equally, and behave just -like middleware, except that these callbacks may invoke `next('route')` -to bypass the remaining route callback(s). You can use this mechanism to perform -pre-conditions on a route then pass control to subsequent routes when there is no -reason to proceed with the route matched. - -The following snippet illustrates the most simple route definition possible. -Express translates the path strings to regular expressions, used internally -to match incoming requests. Query strings are _not_ considered when performing -these matches, for example "GET /" would match the following route, as would -"GET /?name=tobi". - -```js -router.get('/', function (req, res) { - res.send('hello world') -}) -``` - -You can also use regular expressions—useful if you have very specific -constraints, for example the following would match "GET /commits/71dbb9c" as well -as "GET /commits/71dbb9c..4c084f9". - -```js -router.get(/^\/commits\/(\w+)(?:\.\.(\w+))?$/, function (req, res) { - var from = req.params[0] - var to = req.params[1] || 'HEAD' - res.send('commit range ' + from + '..' + to) -}) -``` diff --git a/_includes/api/en/4x/router-Router.md b/_includes/api/en/4x/router-Router.md deleted file mode 100644 index 664ffb34c2..0000000000 --- a/_includes/api/en/4x/router-Router.md +++ /dev/null @@ -1,2 +0,0 @@ -

Router([options])

- diff --git a/_includes/api/en/4x/router-all.md b/_includes/api/en/4x/router-all.md deleted file mode 100644 index 7fe32b156b..0000000000 --- a/_includes/api/en/4x/router-all.md +++ /dev/null @@ -1,31 +0,0 @@ -

router.all(path, [callback, ...] callback)

- -This method is just like the `router.METHOD()` methods, except that it matches all HTTP methods (verbs). - -This method is extremely useful for -mapping "global" logic for specific path prefixes or arbitrary matches. -For example, if you placed the following route at the top of all other -route definitions, it would require that all routes from that point on -would require authentication, and automatically load a user. Keep in mind -that these callbacks do not have to act as end points; `loadUser` -can perform a task, then call `next()` to continue matching subsequent -routes. - -```js -router.all('*', requireAuthentication, loadUser) -``` - -Or the equivalent: - -```js -router.all('*', requireAuthentication) -router.all('*', loadUser) -``` - -Another example of this is white-listed "global" functionality. Here -the example is much like before, but it only restricts paths prefixed with -"/api": - -```js -router.all('/api/*', requireAuthentication) -``` diff --git a/_includes/api/en/4x/router-param.md b/_includes/api/en/4x/router-param.md deleted file mode 100644 index 20b35d1afe..0000000000 --- a/_includes/api/en/4x/router-param.md +++ /dev/null @@ -1,123 +0,0 @@ -

router.param(name, callback)

- -Adds callback triggers to route parameters, where `name` is the name of the parameter and `callback` is the callback function. Although `name` is technically optional, using this method without it is deprecated starting with Express v4.11.0 (see below). - -The parameters of the callback function are: - -- `req`, the request object. -- `res`, the response object. -- `next`, indicating the next middleware function. -- The value of the `name` parameter. -- The name of the parameter. - -
-Unlike `app.param()`, `router.param()` does not accept an array of route parameters. -
- -For example, when `:user` is present in a route path, you may map user loading logic to automatically provide `req.user` to the route, or perform validations on the parameter input. - -```js -router.param('user', function (req, res, next, id) { - // try to get the user details from the User model and attach it to the request object - User.find(id, function (err, user) { - if (err) { - next(err) - } else if (user) { - req.user = user - next() - } else { - next(new Error('failed to load user')) - } - }) -}) -``` - -Param callback functions are local to the router on which they are defined. They are not inherited by mounted apps or routers, nor are they triggered for route parameters inherited from parent routers. Hence, param callbacks defined on `router` will be triggered only by route parameters defined on `router` routes. - -A param callback will be called only once in a request-response cycle, even if the parameter is matched in multiple routes, as shown in the following examples. - -```js -router.param('id', function (req, res, next, id) { - console.log('CALLED ONLY ONCE') - next() -}) - -router.get('/user/:id', function (req, res, next) { - console.log('although this matches') - next() -}) - -router.get('/user/:id', function (req, res) { - console.log('and this matches too') - res.end() -}) -``` - -On `GET /user/42`, the following is printed: - -``` -CALLED ONLY ONCE -although this matches -and this matches too -``` - -
-The following section describes `router.param(callback)`, which is deprecated as of v4.11.0. -
- -The behavior of the `router.param(name, callback)` method can be altered entirely by passing only a function to `router.param()`. This function is a custom implementation of how `router.param(name, callback)` should behave - it accepts two parameters and must return a middleware. - -The first parameter of this function is the name of the URL parameter that should be captured, the second parameter can be any JavaScript object which might be used for returning the middleware implementation. - -The middleware returned by the function decides the behavior of what happens when a URL parameter is captured. - -In this example, the `router.param(name, callback)` signature is modified to `router.param(name, accessId)`. Instead of accepting a name and a callback, `router.param()` will now accept a name and a number. - -```js -var express = require('express') -var app = express() -var router = express.Router() - -// customizing the behavior of router.param() -router.param(function (param, option) { - return function (req, res, next, val) { - if (val === option) { - next() - } else { - res.sendStatus(403) - } - } -}) - -// using the customized router.param() -router.param('id', '1337') - -// route to trigger the capture -router.get('/user/:id', function (req, res) { - res.send('OK') -}) - -app.use(router) - -app.listen(3000, function () { - console.log('Ready') -}) -``` - -In this example, the `router.param(name, callback)` signature remains the same, but instead of a middleware callback, a custom data type checking function has been defined to validate the data type of the user id. - -```js -router.param(function (param, validator) { - return function (req, res, next, val) { - if (validator(val)) { - next() - } else { - res.sendStatus(403) - } - } -}) - -router.param('id', function (candidate) { - return !isNaN(parseFloat(candidate)) && isFinite(candidate) -}) -``` diff --git a/_includes/api/en/4x/router-route.md b/_includes/api/en/4x/router-route.md deleted file mode 100644 index 0f03d39adc..0000000000 --- a/_includes/api/en/4x/router-route.md +++ /dev/null @@ -1,48 +0,0 @@ -

router.route(path)

- -Returns an instance of a single route which you can then use to handle HTTP verbs -with optional middleware. Use `router.route()` to avoid duplicate route naming and -thus typing errors. - -Building on the `router.param()` example above, the following code shows how to use -`router.route()` to specify various HTTP method handlers. - -```js -var router = express.Router() - -router.param('user_id', function (req, res, next, id) { - // sample user, would actually fetch from DB, etc... - req.user = { - id: id, - name: 'TJ' - } - next() -}) - -router.route('/users/:user_id') - .all(function (req, res, next) { - // runs for all HTTP verbs first - // think of it as route specific middleware! - next() - }) - .get(function (req, res, next) { - res.json(req.user) - }) - .put(function (req, res, next) { - // just an example of maybe updating the user - req.user.name = req.params.name - // save user ... etc - res.json(req.user) - }) - .post(function (req, res, next) { - next(new Error('not implemented')) - }) - .delete(function (req, res, next) { - next(new Error('not implemented')) - }) -``` - -This approach re-uses the single `/users/:user_id` path and adds handlers for -various HTTP methods. - -{% include admonitions/note.html content="When you use `router.route()`, middleware ordering is based on when the _route_ is created, not when method handlers are added to the route. For this purpose, you can consider method handlers to belong to the route to which they were added." %} diff --git a/_includes/api/en/4x/router-use.md b/_includes/api/en/4x/router-use.md deleted file mode 100644 index 776a112fa0..0000000000 --- a/_includes/api/en/4x/router-use.md +++ /dev/null @@ -1,107 +0,0 @@ -

router.use([path], [function, ...] function)

- -Uses the specified middleware function or functions, with optional mount path `path`, that defaults to "/". - -This method is similar to [app.use()](#app.use). A simple example and use case is described below. -See [app.use()](#app.use) for more information. - -Middleware is like a plumbing pipe: requests start at the first middleware function defined -and work their way "down" the middleware stack processing for each path they match. - -```js -var express = require('express') -var app = express() -var router = express.Router() - -// simple logger for this router's requests -// all requests to this router will first hit this middleware -router.use(function (req, res, next) { - console.log('%s %s %s', req.method, req.url, req.path) - next() -}) - -// this will only be invoked if the path starts with /bar from the mount point -router.use('/bar', function (req, res, next) { - // ... maybe some additional /bar logging ... - next() -}) - -// always invoked -router.use(function (req, res, next) { - res.send('Hello World') -}) - -app.use('/foo', router) - -app.listen(3000) -``` - -The "mount" path is stripped and is _not_ visible to the middleware function. -The main effect of this feature is that a mounted middleware function may operate without -code changes regardless of its "prefix" pathname. - -The order in which you define middleware with `router.use()` is very important. -They are invoked sequentially, thus the order defines middleware precedence. For example, -usually a logger is the very first middleware you would use, so that every request gets logged. - -```js -var logger = require('morgan') -var path = require('path') - -router.use(logger()) -router.use(express.static(path.join(__dirname, 'public'))) -router.use(function (req, res) { - res.send('Hello') -}) -``` - -Now suppose you wanted to ignore logging requests for static files, but to continue -logging routes and middleware defined after `logger()`. You would simply move the call to `express.static()` to the top, -before adding the logger middleware: - -```js -router.use(express.static(path.join(__dirname, 'public'))) -router.use(logger()) -router.use(function (req, res) { - res.send('Hello') -}) -``` - -Another example is serving files from multiple directories, -giving precedence to "./public" over the others: - -```js -router.use(express.static(path.join(__dirname, 'public'))) -router.use(express.static(path.join(__dirname, 'files'))) -router.use(express.static(path.join(__dirname, 'uploads'))) -``` - -The `router.use()` method also supports named parameters so that your mount points -for other routers can benefit from preloading using named parameters. - -__NOTE__: Although these middleware functions are added via a particular router, _when_ -they run is defined by the path they are attached to (not the router). Therefore, -middleware added via one router may run for other routers if its routes -match. For example, this code shows two different routers mounted on the same path: - -```js -var authRouter = express.Router() -var openRouter = express.Router() - -authRouter.use(require('./authenticate').basic(usersdb)) - -authRouter.get('/:user_id/edit', function (req, res, next) { - // ... Edit user UI ... -}) -openRouter.get('/', function (req, res, next) { - // ... List users ... -}) -openRouter.get('/:user_id', function (req, res, next) { - // ... View user ... -}) - -app.use('/users', authRouter) -app.use('/users', openRouter) -``` - -Even though the authentication middleware was added via the `authRouter` it will run on the routes defined by the `openRouter` as well since both routers were mounted on `/users`. To avoid this behavior, use different paths for each router. diff --git a/_includes/api/en/4x/router.md b/_includes/api/en/4x/router.md deleted file mode 100644 index 4198420a59..0000000000 --- a/_includes/api/en/4x/router.md +++ /dev/null @@ -1,62 +0,0 @@ -

Router

- -
-A `router` object is an instance of middleware and routes. You can think of it -as a "mini-application," capable only of performing middleware and routing -functions. Every Express application has a built-in app router. - -A router behaves like middleware itself, so you can use it as an argument to -[app.use()](#app.use) or as the argument to another router's [use()](#router.use) method. - -The top-level `express` object has a [Router()](#express.router) method that creates a new `router` object. - -Once you've created a router object, you can add middleware and HTTP method routes (such as `get`, `put`, `post`, -and so on) to it just like an application. For example: - -```js -// invoked for any requests passed to this router -router.use(function (req, res, next) { - // .. some logic here .. like any other middleware - next() -}) - -// will handle any request that ends in /events -// depends on where the router is "use()'d" -router.get('/events', function (req, res, next) { - // .. -}) -``` - -You can then use a router for a particular root URL in this way separating your routes into files or even mini-apps. - -```js -// only requests to /calendar/* will be sent to our "router" -app.use('/calendar', router) -``` - -Keep in mind that any middleware applied to a router will run for all requests on that router's path, even those that aren't part of the router. - - -
- -

Methods

- -
- {% include api/en/4x/router-all.md %} -
- -
- {% include api/en/4x/router-METHOD.md %} -
- -
- {% include api/en/4x/router-param.md %} -
- -
- {% include api/en/4x/router-route.md %} -
- -
- {% include api/en/4x/router-use.md %} -
diff --git a/_includes/api/en/4x/routing-args.html b/_includes/api/en/4x/routing-args.html deleted file mode 100644 index 6dd9fe8712..0000000000 --- a/_includes/api/en/4x/routing-args.html +++ /dev/null @@ -1,55 +0,0 @@ -

Arguments

- -
- - - - - - - - - - - - - - - - - - - - -
ArgumentDescriptionDefault
path - The path for which the middleware function is invoked; can be any of: -
    -
  • A string representing a path.
  • -
  • A path pattern.
  • -
  • A regular expression pattern to match paths.
  • -
  • An array of combinations of any of the above.
  • -
- For examples, see Path examples. -
'/' (root path)
callback - Callback functions; can be: -
    -
  • A middleware function.
  • -
  • A series of middleware functions (separated by commas).
  • -
  • An array of middleware functions.
  • -
  • A combination of all of the above.
  • -
-

- You can provide multiple callback functions that behave just like middleware, except - that these callbacks can invoke next('route') to bypass - the remaining route callback(s). You can use this mechanism to impose pre-conditions - on a route, then pass control to subsequent routes if there is no reason to proceed with the current route. -

-

- Since router and app implement the middleware interface, - you can use them as you would any other middleware function. -

-

- For examples, see Middleware callback function examples. -

-
None
-
\ No newline at end of file diff --git a/_includes/api/en/5x/app-METHOD.md b/_includes/api/en/5x/app-METHOD.md deleted file mode 100644 index c2a1c0586b..0000000000 --- a/_includes/api/en/5x/app-METHOD.md +++ /dev/null @@ -1,60 +0,0 @@ -

app.METHOD(path, callback [, callback ...])

- -Routes an HTTP request, where METHOD is the HTTP method of the request, such as GET, -PUT, POST, and so on, in lowercase. Thus, the actual methods are `app.get()`, -`app.post()`, `app.put()`, and so on. See [Routing methods](#routing-methods) below for the complete list. - -{% include api/en/5x/routing-args.html %} - -#### Routing methods - -Express supports the following routing methods corresponding to the HTTP methods of the same names: - -
-
    -
  • checkout
  • -
  • copy
  • -
  • delete
  • -
  • get
  • -
  • head
  • -
  • lock
  • -
  • merge
  • -
  • mkactivity
  • -
-
    -
  • mkcol
  • -
  • move
  • -
  • m-search
  • -
  • notify
  • -
  • options
  • -
  • patch
  • -
  • post
  • -
-
    -
  • purge
  • -
  • put
  • -
  • report
  • -
  • search
  • -
  • subscribe
  • -
  • trace
  • -
  • unlock
  • -
  • unsubscribe
  • -
-
- -The API documentation has explicit entries only for the most popular HTTP methods `app.get()`, -`app.post()`, `app.put()`, and `app.delete()`. -However, the other methods listed above work in exactly the same way. - -To route methods that translate to invalid JavaScript variable names, use the bracket notation. For example, `app['m-search']('/', function ...`. - -
- The `app.get()` function is automatically called for the HTTP `HEAD` method in addition to the `GET` - method if `app.head()` was not called for the path before `app.get()`. -
- -The method, `app.all()`, is not derived from any HTTP method and loads middleware at -the specified path for _all_ HTTP request methods. -For more information, see [app.all](#app.all). - -For more information on routing, see the [routing guide](/{{page.lang}}/guide/routing.html). diff --git a/_includes/api/en/5x/app-all.md b/_includes/api/en/5x/app-all.md deleted file mode 100644 index 5098300dc1..0000000000 --- a/_includes/api/en/5x/app-all.md +++ /dev/null @@ -1,44 +0,0 @@ -

app.all(path, callback [, callback ...])

- -This method is like the standard [app.METHOD()](#app.METHOD) methods, -except it matches all HTTP verbs. - -{% include api/en/5x/routing-args.html %} - -#### Examples - -The following callback is executed for requests to `/secret` whether using -GET, POST, PUT, DELETE, or any other HTTP request method: - -```js -app.all('/secret', (req, res, next) => { - console.log('Accessing the secret section ...') - next() // pass control to the next handler -}) -``` - -The `app.all()` method is useful for mapping "global" logic for specific path prefixes or arbitrary matches. For example, if you put the following at the top of all other -route definitions, it requires that all routes from that point on -require authentication, and automatically load a user. Keep in mind -that these callbacks do not have to act as end-points: `loadUser` -can perform a task, then call `next()` to continue matching subsequent -routes. - -```js -app.all('{*splat}', requireAuthentication, loadUser) -``` - -Or the equivalent: - -```js -app.all('{*splat}', requireAuthentication) -app.all('{*splat}', loadUser) -``` - -Another example is white-listed "global" functionality. -The example is similar to the ones above, but it only restricts paths that start with -"/api": - -```js -app.all('/api/{*splat}', requireAuthentication) -``` diff --git a/_includes/api/en/5x/app-delete-method.md b/_includes/api/en/5x/app-delete-method.md deleted file mode 100644 index 3a08590fd0..0000000000 --- a/_includes/api/en/5x/app-delete-method.md +++ /dev/null @@ -1,14 +0,0 @@ -

app.delete(path, callback [, callback ...])

- -Routes HTTP DELETE requests to the specified path with the specified callback functions. -For more information, see the [routing guide](/{{page.lang}}/guide/routing.html). - -{% include api/en/5x/routing-args.html %} - -#### Example - -```js -app.delete('/', (req, res) => { - res.send('DELETE request to homepage') -}) -``` diff --git a/_includes/api/en/5x/app-disable.md b/_includes/api/en/5x/app-disable.md deleted file mode 100644 index 08bbcf11ba..0000000000 --- a/_includes/api/en/5x/app-disable.md +++ /dev/null @@ -1,12 +0,0 @@ -

app.disable(name)

- -Sets the Boolean setting `name` to `false`, where `name` is one of the properties from the [app settings table](#app.settings.table). -Calling `app.set('foo', false)` for a Boolean property is the same as calling `app.disable('foo')`. - -For example: - -```js -app.disable('trust proxy') -app.get('trust proxy') -// => false -``` diff --git a/_includes/api/en/5x/app-disabled.md b/_includes/api/en/5x/app-disabled.md deleted file mode 100644 index 370048fde6..0000000000 --- a/_includes/api/en/5x/app-disabled.md +++ /dev/null @@ -1,13 +0,0 @@ -

app.disabled(name)

- -Returns `true` if the Boolean setting `name` is disabled (`false`), where `name` is one of the properties from -the [app settings table](#app.settings.table). - -```js -app.disabled('trust proxy') -// => true - -app.enable('trust proxy') -app.disabled('trust proxy') -// => false -``` diff --git a/_includes/api/en/5x/app-enable.md b/_includes/api/en/5x/app-enable.md deleted file mode 100644 index 1152335a4c..0000000000 --- a/_includes/api/en/5x/app-enable.md +++ /dev/null @@ -1,10 +0,0 @@ -

app.enable(name)

- -Sets the Boolean setting `name` to `true`, where `name` is one of the properties from the [app settings table](#app.settings.table). -Calling `app.set('foo', true)` for a Boolean property is the same as calling `app.enable('foo')`. - -```js -app.enable('trust proxy') -app.get('trust proxy') -// => true -``` diff --git a/_includes/api/en/5x/app-enabled.md b/_includes/api/en/5x/app-enabled.md deleted file mode 100644 index ad9b17aa89..0000000000 --- a/_includes/api/en/5x/app-enabled.md +++ /dev/null @@ -1,13 +0,0 @@ -

app.enabled(name)

- -Returns `true` if the setting `name` is enabled (`true`), where `name` is one of the -properties from the [app settings table](#app.settings.table). - -```js -app.enabled('trust proxy') -// => false - -app.enable('trust proxy') -app.enabled('trust proxy') -// => true -``` diff --git a/_includes/api/en/5x/app-engine.md b/_includes/api/en/5x/app-engine.md deleted file mode 100644 index 1314b0586e..0000000000 --- a/_includes/api/en/5x/app-engine.md +++ /dev/null @@ -1,36 +0,0 @@ -

app.engine(ext, callback)

- -Registers the given template engine `callback` as `ext`. - -By default, Express will `require()` the engine based on the file extension. -For example, if you try to render a "foo.pug" file, Express invokes the -following internally, and caches the `require()` on subsequent calls to increase -performance. - -```js -app.engine('pug', require('pug').__express) -``` - -Use this method for engines that do not provide `.__express` out of the box, -or if you wish to "map" a different extension to the template engine. - -For example, to map the EJS template engine to ".html" files: - -```js -app.engine('html', require('ejs').renderFile) -``` - -In this case, EJS provides a `.renderFile()` method with -the same signature that Express expects: `(path, options, callback)`, -though note that it aliases this method as `ejs.__express` internally -so if you're using ".ejs" extensions you don't need to do anything. - -Some template engines do not follow this convention. The -[consolidate.js](https://github.com/tj/consolidate.js) library maps Node template engines to follow this convention, -so they work seamlessly with Express. - -```js -const engines = require('consolidate') -app.engine('haml', engines.haml) -app.engine('html', engines.hogan) -``` diff --git a/_includes/api/en/5x/app-get-method.md b/_includes/api/en/5x/app-get-method.md deleted file mode 100644 index 99b91354d6..0000000000 --- a/_includes/api/en/5x/app-get-method.md +++ /dev/null @@ -1,15 +0,0 @@ -

app.get(path, callback [, callback ...])

- -Routes HTTP GET requests to the specified path with the specified callback functions. - -{% include api/en/5x/routing-args.html %} - -For more information, see the [routing guide](/{{page.lang}}/guide/routing.html). - -#### Example - -```js -app.get('/', (req, res) => { - res.send('GET request to homepage') -}) -``` diff --git a/_includes/api/en/5x/app-get.md b/_includes/api/en/5x/app-get.md deleted file mode 100644 index c93ef34d70..0000000000 --- a/_includes/api/en/5x/app-get.md +++ /dev/null @@ -1,13 +0,0 @@ -

app.get(name)

- -Returns the value of `name` app setting, where `name` is one of the strings in the -[app settings table](#app.settings.table). For example: - -```js -app.get('title') -// => undefined - -app.set('title', 'My Site') -app.get('title') -// => "My Site" -``` diff --git a/_includes/api/en/5x/app-listen.md b/_includes/api/en/5x/app-listen.md deleted file mode 100644 index 7135d69074..0000000000 --- a/_includes/api/en/5x/app-listen.md +++ /dev/null @@ -1,51 +0,0 @@ -

app.listen(path, [callback])

- -Starts a UNIX socket and listens for connections on the given path. -This method is identical to Node's [http.Server.listen()](https://nodejs.org/api/http.html#http_server_listen). - -```js -const express = require('express') -const app = express() -app.listen('/tmp/sock') -``` - -

app.listen([port[, host[, backlog]]][, callback])

- -Binds and listens for connections on the specified host and port. -This method is identical to Node's [http.Server.listen()](https://nodejs.org/api/http.html#http_server_listen). - -If port is omitted or is 0, the operating system will assign an arbitrary unused -port, which is useful for cases like automated tasks (tests, etc.). - -```js -const express = require('express') -const app = express() -app.listen(3000) -``` - -The `app` returned by `express()` is in fact a JavaScript -`Function`, designed to be passed to Node's HTTP servers as a callback -to handle requests. This makes it easy to provide both HTTP and HTTPS versions of -your app with the same code base, as the app does not inherit from these -(it is simply a callback): - -```js -const express = require('express') -const https = require('https') -const http = require('http') -const app = express() - -http.createServer(app).listen(80) -https.createServer(options, app).listen(443) -``` - -The `app.listen()` method returns an [http.Server](https://nodejs.org/api/http.html#http_class_http_server) object and (for HTTP) is a convenience method for the following: - -```js -app.listen = function () { - const server = http.createServer(this) - return server.listen.apply(server, arguments) -} -``` - -{% include admonitions/note.html content="All the forms of Node's [http.Server.listen()](https://nodejs.org/api/http.html#http_server_listen) method are in fact actually supported." %} diff --git a/_includes/api/en/5x/app-locals.md b/_includes/api/en/5x/app-locals.md deleted file mode 100644 index 178d640054..0000000000 --- a/_includes/api/en/5x/app-locals.md +++ /dev/null @@ -1,34 +0,0 @@ -

app.locals

- -The `app.locals` object has properties that are local variables within the application, -and will be available in templates rendered with [res.render](#res.render). - -
-The `locals` object is used by view engines to render a response. The object -keys may be particularly sensitive and should not contain user-controlled -input, as it may affect the operation of the view engine or provide a path to -cross-site scripting. Consult the documentation for the used view engine for -additional considerations. -
- -```js -console.dir(app.locals.title) -// => 'My App' - -console.dir(app.locals.email) -// => 'me@myapp.com' -``` - -Once set, the value of `app.locals` properties persist throughout the life of the application, -in contrast with [res.locals](#res.locals) properties that -are valid only for the lifetime of the request. - -You can access local variables in templates rendered within the application. -This is useful for providing helper functions to templates, as well as application-level data. -Local variables are available in middleware via `req.app.locals` (see [req.app](#req.app)) - -```js -app.locals.title = 'My App' -app.locals.strftime = require('strftime') -app.locals.email = 'me@myapp.com' -``` diff --git a/_includes/api/en/5x/app-mountpath.md b/_includes/api/en/5x/app-mountpath.md deleted file mode 100644 index c18e0f8eb7..0000000000 --- a/_includes/api/en/5x/app-mountpath.md +++ /dev/null @@ -1,45 +0,0 @@ -

app.mountpath

- -The `app.mountpath` property contains one or more path patterns on which a sub-app was mounted. - -
- A sub-app is an instance of `express` that may be used for handling the request to a route. -
- -```js -const express = require('express') - -const app = express() // the main app -const admin = express() // the sub app - -admin.get('/', (req, res) => { - console.log(admin.mountpath) // /admin - res.send('Admin Homepage') -}) - -app.use('/admin', admin) // mount the sub app -``` - -It is similar to the [baseUrl](#req.baseUrl) property of the `req` object, except `req.baseUrl` -returns the matched URL path, instead of the matched patterns. - -If a sub-app is mounted on multiple path patterns, `app.mountpath` returns the list of -patterns it is mounted on, as shown in the following example. - -```js -const admin = express() - -admin.get('/', (req, res) => { - console.log(admin.mountpath) // [ '/adm{*splat}n', '/manager' ] - res.send('Admin Homepage') -}) - -const secret = express() -secret.get('/', (req, res) => { - console.log(secret.mountpath) // /secr{*splat}t - res.send('Admin Secret') -}) - -admin.use('/secr{*splat}t', secret) // load the 'secret' router on '/secr{*splat}t', on the 'admin' sub app -app.use(['/adm{*splat}n', '/manager'], admin) // load the 'admin' router on '/adm{*splat}n' and '/manager', on the parent app -``` diff --git a/_includes/api/en/5x/app-onmount.md b/_includes/api/en/5x/app-onmount.md deleted file mode 100644 index 80e38520eb..0000000000 --- a/_includes/api/en/5x/app-onmount.md +++ /dev/null @@ -1,29 +0,0 @@ -

app.on('mount', callback(parent))

- -The `mount` event is fired on a sub-app, when it is mounted on a parent app. The parent app is passed to the callback function. - -
-**NOTE** - -Sub-apps will: - -* Not inherit the value of settings that have a default value. You must set the value in the sub-app. -* Inherit the value of settings with no default value. - -For details, see [Application settings](/en/5x/api.html#app.settings.table). -
- -```js -const admin = express() - -admin.on('mount', (parent) => { - console.log('Admin Mounted') - console.log(parent) // refers to the parent app -}) - -admin.get('/', (req, res) => { - res.send('Admin Homepage') -}) - -app.use('/admin', admin) -``` diff --git a/_includes/api/en/5x/app-param.md b/_includes/api/en/5x/app-param.md deleted file mode 100644 index a1e064add2..0000000000 --- a/_includes/api/en/5x/app-param.md +++ /dev/null @@ -1,78 +0,0 @@ -

app.param(name, callback)

- -Add callback triggers to [route parameters](/{{ page.lang }}/guide/routing.html#route-parameters), where `name` is the name of the parameter or an array of them, and `callback` is the callback function. The parameters of the callback function are the request object, the response object, the next middleware, the value of the parameter and the name of the parameter, in that order. - -If `name` is an array, the `callback` trigger is registered for each parameter declared in it, in the order in which they are declared. Furthermore, for each declared parameter except the last one, a call to `next` inside the callback will call the callback for the next declared parameter. For the last parameter, a call to `next` will call the next middleware in place for the route currently being processed, just like it would if `name` were just a string. - -For example, when `:user` is present in a route path, you may map user loading logic to automatically provide `req.user` to the route, or perform validations on the parameter input. - -```js -app.param('user', (req, res, next, id) => { - // try to get the user details from the User model and attach it to the request object - User.find(id, (err, user) => { - if (err) { - next(err) - } else if (user) { - req.user = user - next() - } else { - next(new Error('failed to load user')) - } - }) -}) -``` - -Param callback functions are local to the router on which they are defined. They are not inherited by mounted apps or routers, nor are they triggered for route parameters inherited from parent routers. Hence, param callbacks defined on `app` will be triggered only by route parameters defined on `app` routes. - -All param callbacks will be called before any handler of any route in which the param occurs, and they will each be called only once in a request-response cycle, even if the parameter is matched in multiple routes, as shown in the following examples. - -```js -app.param('id', (req, res, next, id) => { - console.log('CALLED ONLY ONCE') - next() -}) - -app.get('/user/:id', (req, res, next) => { - console.log('although this matches') - next() -}) - -app.get('/user/:id', (req, res) => { - console.log('and this matches too') - res.end() -}) -``` - -On `GET /user/42`, the following is printed: - -``` -CALLED ONLY ONCE -although this matches -and this matches too -``` - -```js -app.param(['id', 'page'], (req, res, next, value) => { - console.log('CALLED ONLY ONCE with', value) - next() -}) - -app.get('/user/:id/:page', (req, res, next) => { - console.log('although this matches') - next() -}) - -app.get('/user/:id/:page', (req, res) => { - console.log('and this matches too') - res.end() -}) -``` - -On `GET /user/42/3`, the following is printed: - -``` -CALLED ONLY ONCE with 42 -CALLED ONLY ONCE with 3 -although this matches -and this matches too -``` diff --git a/_includes/api/en/5x/app-path.md b/_includes/api/en/5x/app-path.md deleted file mode 100644 index e1fabc0c10..0000000000 --- a/_includes/api/en/5x/app-path.md +++ /dev/null @@ -1,19 +0,0 @@ -

app.path()

- -Returns the canonical path of the app, a string. - -```js -const app = express() -const blog = express() -const blogAdmin = express() - -app.use('/blog', blog) -blog.use('/admin', blogAdmin) - -console.log(app.path()) // '' -console.log(blog.path()) // '/blog' -console.log(blogAdmin.path()) // '/blog/admin' -``` - -The behavior of this method can become very complicated in complex cases of mounted apps: -it is usually better to use [req.baseUrl](#req.baseUrl) to get the canonical path of the app. diff --git a/_includes/api/en/5x/app-post-method.md b/_includes/api/en/5x/app-post-method.md deleted file mode 100644 index 9bde32e02a..0000000000 --- a/_includes/api/en/5x/app-post-method.md +++ /dev/null @@ -1,14 +0,0 @@ -

app.post(path, callback [, callback ...])

- -Routes HTTP POST requests to the specified path with the specified callback functions. -For more information, see the [routing guide](/{{page.lang}}/guide/routing.html). - -{% include api/en/5x/routing-args.html %} - -#### Example - -```js -app.post('/', (req, res) => { - res.send('POST request to homepage') -}) -``` diff --git a/_includes/api/en/5x/app-put-method.md b/_includes/api/en/5x/app-put-method.md deleted file mode 100644 index 0575714854..0000000000 --- a/_includes/api/en/5x/app-put-method.md +++ /dev/null @@ -1,13 +0,0 @@ -

app.put(path, callback [, callback ...])

- -Routes HTTP PUT requests to the specified path with the specified callback functions. - -{% include api/en/5x/routing-args.html %} - -#### Example - -```js -app.put('/', (req, res) => { - res.send('PUT request to homepage') -}) -``` diff --git a/_includes/api/en/5x/app-render.md b/_includes/api/en/5x/app-render.md deleted file mode 100644 index c526f099ad..0000000000 --- a/_includes/api/en/5x/app-render.md +++ /dev/null @@ -1,39 +0,0 @@ -

app.render(view, [locals], callback)

- -Returns the rendered HTML of a view via the `callback` function. It accepts an optional parameter -that is an object containing local variables for the view. It is like [res.render()](#res.render), -except it cannot send the rendered view to the client on its own. - -
-Think of `app.render()` as a utility function for generating rendered view strings. -Internally `res.render()` uses `app.render()` to render views. -
- -
-The `view` argument performs file system operations like reading a file from -disk and evaluating Node.js modules, and as so for security reasons should not -contain input from the end-user. -
- -
-The `locals` object is used by view engines to render a response. The object -keys may be particularly sensitive and should not contain user-controlled -input, as it may affect the operation of the view engine or provide a path to -cross-site scripting. Consult the documentation for the used view engine for -additional considerations. -
- -
-The local variable `cache` is reserved for enabling view cache. Set it to `true`, if you want to -cache view during development; view caching is enabled in production by default. -
- -```js -app.render('email', (err, html) => { - // ... -}) - -app.render('email', { name: 'Tobi' }, (err, html) => { - // ... -}) -``` diff --git a/_includes/api/en/5x/app-route.md b/_includes/api/en/5x/app-route.md deleted file mode 100644 index 2a0db757dd..0000000000 --- a/_includes/api/en/5x/app-route.md +++ /dev/null @@ -1,20 +0,0 @@ -

app.route(path)

- -Returns an instance of a single route, which you can then use to handle HTTP verbs with optional middleware. -Use `app.route()` to avoid duplicate route names (and thus typo errors). - -```js -const app = express() - -app.route('/events') - .all((req, res, next) => { - // runs for all HTTP verbs first - // think of it as route specific middleware! - }) - .get((req, res, next) => { - res.json({}) - }) - .post((req, res, next) => { - // maybe add a new event... - }) -``` diff --git a/_includes/api/en/5x/app-router.md b/_includes/api/en/5x/app-router.md deleted file mode 100644 index b821faa75d..0000000000 --- a/_includes/api/en/5x/app-router.md +++ /dev/null @@ -1,19 +0,0 @@ -

app.router

- -The application's in-built instance of router. This is created lazily, on first access. - -```js -const express = require('express') -const app = express() -const router = app.router - -router.get('/', (req, res) => { - res.send('hello world') -}) - -app.listen(3000) -``` - -You can add middleware and HTTP method routes to the `router` just like an application. - -For more information, see [Router](#router). diff --git a/_includes/api/en/5x/app-set.md b/_includes/api/en/5x/app-set.md deleted file mode 100644 index e85bd9be19..0000000000 --- a/_includes/api/en/5x/app-set.md +++ /dev/null @@ -1,20 +0,0 @@ -

app.set(name, value)

- -Assigns setting `name` to `value`. You may store any value that you want, -but certain names can be used to configure the behavior of the server. These -special names are listed in the [app settings table](#app.settings.table). - -Calling `app.set('foo', true)` for a Boolean property is the same as calling -`app.enable('foo')`. Similarly, calling `app.set('foo', false)` for a Boolean -property is the same as calling `app.disable('foo')`. - -Retrieve the value of a setting with [`app.get()`](#app.get). - -```js -app.set('title', 'My Site') -app.get('title') // "My Site" -``` - -

Application Settings

- -{% include api/en/5x/app-settings.md %} diff --git a/_includes/api/en/5x/app-settings.md b/_includes/api/en/5x/app-settings.md deleted file mode 100644 index 3b8b8369db..0000000000 --- a/_includes/api/en/5x/app-settings.md +++ /dev/null @@ -1,323 +0,0 @@ -The following table lists application settings. - -Note that sub-apps will: - -* Not inherit the value of settings that have a default value. You must set the value in the sub-app. -* Inherit the value of settings with no default value; these are explicitly noted in the table below. - -Exceptions: Sub-apps will inherit the value of `trust proxy` even though it has a default value (for backward-compatibility); -Sub-apps will not inherit the value of `view cache` in production (when `NODE_ENV` is "production"). - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyTypeDescriptionDefault
- `case sensitive routing` - Boolean

Enable case sensitivity. - When enabled, "/Foo" and "/foo" are different routes. - When disabled, "/Foo" and "/foo" are treated the same.

-

NOTE: Sub-apps will inherit the value of this setting.

-
N/A (undefined) -
- `env` - StringEnvironment mode. - Be sure to set to "production" in a production environment; - see Production best practices: performance and reliability. - - `process.env.NODE_ENV` (`NODE_ENV` environment variable) or "development" if `NODE_ENV` is not set. -
- `etag` - Varied - Set the ETag response header. For possible values, see the [`etag` options table](#etag.options.table). - - [More about the HTTP ETag header](http://en.wikipedia.org/wiki/HTTP_ETag). - - `weak` -
- `jsonp callback name` - StringSpecifies the default JSONP callback name. - "callback" -
- `json escape` - Boolean - Enable escaping JSON responses from the `res.json`, `res.jsonp`, and `res.send` APIs. This will escape the characters `<`, `>`, and `&` as Unicode escape sequences in JSON. The purpose of this is to assist with [mitigating certain types of persistent XSS attacks](https://blog.mozilla.org/security/2017/07/18/web-service-audits-firefox-accounts/) when clients sniff responses for HTML. -

NOTE: Sub-apps will inherit the value of this setting.

-
N/A (undefined)
- `json replacer` - VariedThe 'replacer' argument used by `JSON.stringify`. -

NOTE: Sub-apps will inherit the value of this setting.

-
N/A (undefined) -
- `json spaces` - VariedThe 'space' argument used by `JSON.stringify`. -This is typically set to the number of spaces to use to indent prettified JSON. -

NOTE: Sub-apps will inherit the value of this setting.

-
N/A (undefined)
- `query parser` - Varied -Disable query parsing by setting the value to `false`, or set the query parser to use either "simple" or "extended" or a custom query string parsing function. - -The simple query parser is based on Node's native query parser, [querystring](http://nodejs.org/api/querystring.html). - -The extended query parser is based on [qs](https://www.npmjs.org/package/qs). - -A custom query string parsing function will receive the complete query string, and must return an object of query keys and their values. - "simple"
- `strict routing` - Boolean

Enable strict routing. - When enabled, the router treats "/foo" and "/foo/" as different. - Otherwise, the router treats "/foo" and "/foo/" as the same.

-

NOTE: Sub-apps will inherit the value of this setting.

-
N/A (undefined)
- `subdomain offset` - NumberThe number of dot-separated parts of the host to remove to access subdomain.2
- `trust proxy` - Varied - Indicates the app is behind a front-facing proxy, and to use the `X-Forwarded-*` headers to determine the connection and the IP address of the client. NOTE: `X-Forwarded-*` headers are easily spoofed and the detected IP addresses are unreliable. -

- When enabled, Express attempts to determine the IP address of the client connected through the front-facing proxy, or series of proxies. The `req.ips` property, then contains an array of IP addresses the client is connected through. To enable it, use the values described in the trust proxy options table. -

- The `trust proxy` setting is implemented using the proxy-addr package. For more information, see its documentation. -

-NOTE: Sub-apps will inherit the value of this setting, even though it has a default value. -

-
- `false` (disabled) -
- `views` - String or ArrayA directory or an array of directories for the application's views. If an array, the views are looked up in the order they occur in the array. - `process.cwd() + '/views'` -
- `view cache` - Boolean

Enables view template compilation caching.

-

NOTE: Sub-apps will not inherit the value of this setting in production (when `NODE_ENV` is "production").

-
- `true` in production, otherwise undefined. -
- `view engine` - StringThe default engine extension to use when omitted. -

NOTE: Sub-apps will inherit the value of this setting.

-
N/A (undefined)
- `x-powered-by` - BooleanEnables the "X-Powered-By: Express" HTTP header. - `true` -
-
- -
Options for `trust proxy` setting
- -

- Read [Express behind proxies](/{{page.lang}}/guide/behind-proxies.html) for more - information. -

- -
- - - - - - - - - - - - - - - - - - - - -
TypeValue
Boolean - If `true`, the client's IP address is understood as the left-most entry in the `X-Forwarded-*` header. - - If `false`, the app is understood as directly facing the Internet and the client's IP address is derived from `req.connection.remoteAddress`. This is the default setting. -
String
String containing comma-separated values
Array of strings
- An IP address, subnet, or an array of IP addresses, and subnets to trust. Pre-configured subnet names are: - - * loopback - `127.0.0.1/8`, `::1/128` - * linklocal - `169.254.0.0/16`, `fe80::/10` - * uniquelocal - `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `fc00::/7` - - Set IP addresses in any of the following ways: - -Specify a single subnet: - -```js -app.set('trust proxy', 'loopback') -``` - -Specify a subnet and an address: - -```js -app.set('trust proxy', 'loopback, 123.123.123.123') -``` - -Specify multiple subnets as CSV: - -```js -app.set('trust proxy', 'loopback, linklocal, uniquelocal') -``` - -Specify multiple subnets as an array: - -```js -app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']) -``` - - When specified, the IP addresses or the subnets are excluded from the address determination process, and the untrusted IP address nearest to the application server is determined as the client's IP address. -
Number - Trust the nth hop from the front-facing proxy server as the client. -
Function - Custom trust implementation. Use this only if you know what you are doing. - -```js -app.set('trust proxy', (ip) => { - if (ip === '127.0.0.1' || ip === '123.123.123.123') return true // trusted IPs - else return false -}) -``` -
-
- -
Options for `etag` setting
- -

-**NOTE**: These settings apply only to dynamic files, not static files. -The [express.static](#express.static) middleware ignores these settings. -

- -

- The ETag functionality is implemented using the - [etag](https://www.npmjs.org/package/etag) package. - For more information, see its documentation. -

- -
- - - - - - - - - - - - - - - - -
TypeValue
Boolean - `true` enables weak ETag. This is the default setting.
- `false` disables ETag altogether. -
String - If "strong", enables strong ETag.
- If "weak", enables weak ETag. -
FunctionCustom ETag function implementation. Use this only if you know what you are doing. - -```js -app.set('etag', (body, encoding) => { - return generateHash(body, encoding) // consider the function is defined -}) -``` -
-
diff --git a/_includes/api/en/5x/app-use.md b/_includes/api/en/5x/app-use.md deleted file mode 100644 index 8b5a2b13cc..0000000000 --- a/_includes/api/en/5x/app-use.md +++ /dev/null @@ -1,295 +0,0 @@ -

app.use([path,] callback [, callback...])

- -Mounts the specified [middleware](/{{page.lang}}/guide/using-middleware.html) function or functions -at the specified path: -the middleware function is executed when the base of the requested path matches `path`. - -{% include api/en/5x/routing-args.html %} - -#### Description - -A route will match any path that follows its path immediately with a "`/`". -For example: `app.use('/apple', ...)` will match "/apple", "/apple/images", -"/apple/images/news", and so on. - -Since `path` defaults to "/", middleware mounted without a path will be executed for every request to the app. -For example, this middleware function will be executed for _every_ request to the app: - -```js -app.use((req, res, next) => { - console.log('Time: %d', Date.now()) - next() -}) -``` - -
-**NOTE** - -Sub-apps will: - -* Not inherit the value of settings that have a default value. You must set the value in the sub-app. -* Inherit the value of settings with no default value. - -For details, see [Application settings](/en/5x/api.html#app.settings.table). -
- -Middleware functions are executed sequentially, therefore the order of middleware inclusion is important. - -```js -// this middleware will not allow the request to go beyond it -app.use((req, res, next) => { - res.send('Hello World') -}) - -// requests will never reach this route -app.get('/', (req, res) => { - res.send('Welcome') -}) -``` - -**Error-handling middleware** - -Error-handling middleware always takes _four_ arguments. You must provide four arguments to identify it as an error-handling middleware function. Even if you don't need to use the `next` object, you must specify it to maintain the signature. Otherwise, the `next` object will be interpreted as regular middleware and will fail to handle errors. For details about error-handling middleware, see: [Error handling](/{{ page.lang }}/guide/error-handling.html). - -Define error-handling middleware functions in the same way as other middleware functions, except with four arguments instead of three, specifically with the signature `(err, req, res, next)`): - -```js -app.use((err, req, res, next) => { - console.error(err.stack) - res.status(500).send('Something broke!') -}) -``` - -#### Path examples - -The following table provides some simple examples of valid `path` values for -mounting middleware. - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeExample
Path -Matches the exact path `/abcd` and any sub-paths starting with `/abcd/` (for example, `/abcd/foo`): - - -```js -app.use('/abcd', (req, res, next) => { - next() -}) -``` - -
Path Pattern -This will match paths starting with `/abcd` and `/abd`: - -```js -app.use('/ab{c}d', (req, res, next) => { - next() -}) -``` - -
Regular Expression -This will match paths starting with `/abc` and `/xyz`: - -```js -app.use(/\/abc|\/xyz/, (req, res, next) => { - next() -}) -``` - -
Array -This will match paths starting with `/abcd`, `/xyza`, `/lmn`, and `/pqr`: - -```js -app.use(['/abcd', '/xyza', /\/lmn|\/pqr/], (req, res, next) => { - next() -}) -``` - -
-
- -#### Middleware callback function examples - -The following table provides some simple examples of middleware functions that -can be used as the `callback` argument to `app.use()`, `app.METHOD()`, and `app.all()`. - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
UsageExample
Single Middleware -You can define and mount a middleware function locally. - -```js -app.use((req, res, next) => { - next() -}) -``` - -A router is valid middleware. - -```js -const router = express.Router() -router.get('/', (req, res, next) => { - next() -}) -app.use(router) -``` - -An Express app is valid middleware. - -```js -const subApp = express() -subApp.get('/', (req, res, next) => { - next() -}) -app.use(subApp) -``` - -
Series of Middleware -You can specify more than one middleware function at the same mount path. - -```js -const r1 = express.Router() -r1.get('/', (req, res, next) => { - next() -}) - -const r2 = express.Router() -r2.get('/', (req, res, next) => { - next() -}) - -app.use(r1, r2) -``` - -
Array -Use an array to group middleware logically. - -```js -const r1 = express.Router() -r1.get('/', (req, res, next) => { - next() -}) - -const r2 = express.Router() -r2.get('/', (req, res, next) => { - next() -}) - -app.use([r1, r2]) -``` - -
Combination -You can combine all the above ways of mounting middleware. - -```js -function mw1 (req, res, next) { next() } -function mw2 (req, res, next) { next() } - -const r1 = express.Router() -r1.get('/', (req, res, next) => { next() }) - -const r2 = express.Router() -r2.get('/', (req, res, next) => { next() }) - -const subApp = express() -subApp.get('/', (req, res, next) => { next() }) - -app.use(mw1, [mw2, r1, r2], subApp) -``` - -
-
- -Following are some examples of using the [express.static](/{{page.lang}}/guide/using-middleware.html#middleware.built-in) -middleware in an Express app. - -Serve static content for the app from the "public" directory in the application directory: - -```js -// GET /style.css etc -app.use(express.static(path.join(__dirname, 'public'))) -``` - -Mount the middleware at "/static" to serve static content only when their request path is prefixed with "/static": - -```js -// GET /static/style.css etc. -app.use('/static', express.static(path.join(__dirname, 'public'))) -``` - -Disable logging for static content requests by loading the logger middleware after the static middleware: - -```js -app.use(express.static(path.join(__dirname, 'public'))) -app.use(logger()) -``` - -Serve static files from multiple directories, but give precedence to "./public" over the others: - -```js -app.use(express.static(path.join(__dirname, 'public'))) -app.use(express.static(path.join(__dirname, 'files'))) -app.use(express.static(path.join(__dirname, 'uploads'))) -``` diff --git a/_includes/api/en/5x/app.md b/_includes/api/en/5x/app.md deleted file mode 100644 index 458ba8a447..0000000000 --- a/_includes/api/en/5x/app.md +++ /dev/null @@ -1,127 +0,0 @@ -

Application

- -The `app` object conventionally denotes the Express application. -Create it by calling the top-level `express()` function exported by the Express module: - -```js -const express = require('express') -const app = express() - -app.get('/', (req, res) => { - res.send('hello world') -}) - -app.listen(3000) -``` - -The `app` object has methods for - -* Routing HTTP requests; see for example, [app.METHOD](#app.METHOD) and [app.param](#app.param). -* Configuring middleware; see [app.route](#app.route). -* Rendering HTML views; see [app.render](#app.render). -* Registering a template engine; see [app.engine](#app.engine). - -It also has settings (properties) that affect how the application behaves; -for more information, see [Application settings](#app.settings.table). - -
-The Express application object can be referred from the [request object](#req) and the [response object](#res) as `req.app`, and `res.app`, respectively. -
- -

Properties

- -
- {% include api/en/5x/app-locals.md %} -
- -
- {% include api/en/5x/app-mountpath.md %} -
- -
- {% include api/en/5x/app-router.md %} -
- -

Events

- -
- {% include api/en/5x/app-onmount.md %} -
- -

Methods

- -
- {% include api/en/5x/app-all.md %} -
- -
- {% include api/en/5x/app-delete-method.md %} -
- -
- {% include api/en/5x/app-disable.md %} -
- -
- {% include api/en/5x/app-disabled.md %} -
- -
- {% include api/en/5x/app-enable.md %} -
- -
- {% include api/en/5x/app-enabled.md %} -
- -
- {% include api/en/5x/app-engine.md %} -
- -
- {% include api/en/5x/app-get.md %} -
- -
- {% include api/en/5x/app-get-method.md %} -
- -
- {% include api/en/5x/app-listen.md %} -
- -
- {% include api/en/5x/app-METHOD.md %} -
- -
- {% include api/en/5x/app-param.md %} -
- -
- {% include api/en/5x/app-path.md %} -
- -
- {% include api/en/5x/app-post-method.md %} -
- -
- {% include api/en/5x/app-put-method.md %} -
- -
- {% include api/en/5x/app-render.md %} -
- -
- {% include api/en/5x/app-route.md %} -
- -
- {% include api/en/5x/app-set.md %} -
- -
- {% include api/en/5x/app-use.md %} -
diff --git a/_includes/api/en/5x/express.json.md b/_includes/api/en/5x/express.json.md deleted file mode 100644 index bc12042d8d..0000000000 --- a/_includes/api/en/5x/express.json.md +++ /dev/null @@ -1,38 +0,0 @@ -

express.json([options])

- -This is a built-in middleware function in Express. It parses incoming requests -with JSON payloads and is based on -[body-parser](/resources/middleware/body-parser.html). - -Returns middleware that only parses JSON and only looks at requests where -the `Content-Type` header matches the `type` option. This parser accepts any -Unicode encoding of the body and supports automatic inflation of `gzip` and -`deflate` encodings. - -A new `body` object containing the parsed data is populated on the `request` -object after the middleware (i.e. `req.body`), or `undefined` if -there was no body to parse, the `Content-Type` was not matched, or an error -occurred. - -
-As `req.body`'s shape is based on user-controlled input, all properties and -values in this object are untrusted and should be validated before trusting. -For example, `req.body.foo.toString()` may fail in multiple ways, for example -`foo` may not be there or may not be a string, and `toString` may not be a -function and instead a string or other user-input. -
- -The following table describes the properties of the optional `options` object. - -
- -| Property | Description | Type | Default | -|---------------|-----------------------------------------------------------------------|-------------|-----------------| -| `inflate` | Enables or disables handling deflated (compressed) bodies; when disabled, deflated bodies are rejected. | Boolean | `true` | -| `limit` | Controls the maximum request body size. If this is a number, then the value specifies the number of bytes; if it is a string, the value is passed to the [bytes](https://www.npmjs.com/package/bytes) library for parsing. | Mixed | `"100kb"` | -| `reviver` | The `reviver` option is passed directly to `JSON.parse` as the second argument. You can find more information on this argument [in the MDN documentation about JSON.parse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Example.3A_Using_the_reviver_parameter). | Function | `null` | -| `strict` | Enables or disables only accepting arrays and objects; when disabled will accept anything `JSON.parse` accepts. | Boolean | `true` | -| `type` | This is used to determine what media type the middleware will parse. This option can be a string, array of strings, or a function. If not a function, `type` option is passed directly to the [type-is](https://www.npmjs.org/package/type-is#readme) library and this can be an extension name (like `json`), a mime type (like `application/json`), or a mime type with a wildcard (like `*/*` or `*/json`). If a function, the `type` option is called as `fn(req)` and the request is parsed if it returns a truthy value. | Mixed | `"application/json"` | -| `verify` | This option, if supplied, is called as `verify(req, res, buf, encoding)`, where `buf` is a `Buffer` of the raw request body and `encoding` is the encoding of the request. The parsing can be aborted by throwing an error. | Function | `undefined` | - -
\ No newline at end of file diff --git a/_includes/api/en/5x/express.md b/_includes/api/en/5x/express.md deleted file mode 100644 index acfae62a5a..0000000000 --- a/_includes/api/en/5x/express.md +++ /dev/null @@ -1,34 +0,0 @@ -

express()

- -Creates an Express application. The `express()` function is a top-level function exported by the `express` module. - -```js -const express = require('express') -const app = express() -``` - -

Methods

- -
- {% include api/en/5x/express.json.md %} -
- -
- {% include api/en/5x/express.raw.md %} -
- -
- {% include api/en/5x/express.router.md %} -
- -
- {% include api/en/5x/express.static.md %} -
- -
- {% include api/en/5x/express.text.md %} -
- -
- {% include api/en/5x/express.urlencoded.md %} -
diff --git a/_includes/api/en/5x/express.raw.md b/_includes/api/en/5x/express.raw.md deleted file mode 100644 index b20ff13ed6..0000000000 --- a/_includes/api/en/5x/express.raw.md +++ /dev/null @@ -1,36 +0,0 @@ -

express.raw([options])

- -This is a built-in middleware function in Express. It parses incoming request -payloads into a `Buffer` and is based on -[body-parser](/resources/middleware/body-parser.html). - -Returns middleware that parses all bodies as a `Buffer` and only looks at requests -where the `Content-Type` header matches the `type` option. This parser accepts -any Unicode encoding of the body and supports automatic inflation of `gzip` and -`deflate` encodings. - -A new `body` `Buffer` containing the parsed data is populated on the `request` -object after the middleware (i.e. `req.body`), or `undefined` if -there was no body to parse, the `Content-Type` was not matched, or an error -occurred. - -
-As `req.body`'s shape is based on user-controlled input, all properties and -values in this object are untrusted and should be validated before trusting. -For example, `req.body.toString()` may fail in multiple ways, for example -stacking multiple parsers `req.body` may be from a different parser. Testing -that `req.body` is a `Buffer` before calling buffer methods is recommended. -
- -The following table describes the properties of the optional `options` object. - -
- -| Property | Description | Type | Default | -|-----------|-----------------------------------------------------------------------|-------------|-----------------| -| `inflate` | Enables or disables handling deflated (compressed) bodies; when disabled, deflated bodies are rejected. | Boolean | `true` | -| `limit` | Controls the maximum request body size. If this is a number, then the value specifies the number of bytes; if it is a string, the value is passed to the [bytes](https://www.npmjs.com/package/bytes) library for parsing. | Mixed | `"100kb"` | -| `type` | This is used to determine what media type the middleware will parse. This option can be a string, array of strings, or a function. If not a function, `type` option is passed directly to the [type-is](https://www.npmjs.org/package/type-is#readme) library and this can be an extension name (like `bin`), a mime type (like `application/octet-stream`), or a mime type with a wildcard (like `*/*` or `application/*`). If a function, the `type` option is called as `fn(req)` and the request is parsed if it returns a truthy value. | Mixed | `"application/octet-stream"` | -| `verify` | This option, if supplied, is called as `verify(req, res, buf, encoding)`, where `buf` is a `Buffer` of the raw request body and `encoding` is the encoding of the request. The parsing can be aborted by throwing an error. | Function | `undefined` | - -
\ No newline at end of file diff --git a/_includes/api/en/5x/express.router.md b/_includes/api/en/5x/express.router.md deleted file mode 100644 index babd0b0279..0000000000 --- a/_includes/api/en/5x/express.router.md +++ /dev/null @@ -1,24 +0,0 @@ -

express.Router([options])

- -Creates a new [router](#router) object. - -```js -const router = express.Router([options]) -``` - -The optional `options` parameter specifies the behavior of the router. - -
- -| Property | Description | Default | Availability | -|-----------------|-------------------------------------------------|-------------|---------------| -| `caseSensitive` | Enable case sensitivity. | Disabled by default, treating "/Foo" and "/foo" as the same.| | -| `mergeParams` | Preserve the `req.params` values from the parent router. If the parent and the child have conflicting param names, the child's value take precedence.| `false` | 4.5.0+ | -| `strict` | Enable strict routing. | Disabled by default, "/foo" and "/foo/" are treated the same by the router.|   | - -
- -You can add middleware and HTTP method routes (such as `get`, `put`, `post`, and -so on) to `router` just like an application. - -For more information, see [Router](#router). diff --git a/_includes/api/en/5x/express.static.md b/_includes/api/en/5x/express.static.md deleted file mode 100644 index 72094c40f9..0000000000 --- a/_includes/api/en/5x/express.static.md +++ /dev/null @@ -1,94 +0,0 @@ -

express.static(root, [options])

- -This is a built-in middleware function in Express. -It serves static files and is based on [serve-static](/resources/middleware/serve-static.html). - -
NOTE: For best results, [use a reverse proxy](/{{page.lang}}/advanced/best-practice-performance.html#use-a-reverse-proxy) cache to improve performance of serving static assets. -
- -The `root` argument specifies the root directory from which to serve static assets. -The function determines the file to serve by combining `req.url` with the provided `root` directory. -When a file is not found, instead of sending a 404 response, it instead calls `next()` -to move on to the next middleware, allowing for stacking and fall-backs. - -The following table describes the properties of the `options` object. -See also the [example below](#example.of.express.static). - -
- -| Property | Description | Type | Default | -|---------------|-----------------------------------------------------------------------|-------------|-----------------| -| `dotfiles` | Determines how dotfiles (files or directories that begin with a dot ".") are treated.

See [dotfiles](#dotfiles) below. | String | "ignore"| -| `etag` | Enable or disable etag generation

NOTE: `express.static` always sends weak ETags. | Boolean | `true` | -| `extensions` | Sets file extension fallbacks: If a file is not found, search for files with the specified extensions and serve the first one found. Example: `['html', 'htm']`.| Mixed | `false` | -| `fallthrough` | Let client errors fall-through as unhandled requests, otherwise forward a client error.

See [fallthrough](#fallthrough) below.| Boolean | `true` | -| `immutable` | Enable or disable the `immutable` directive in the `Cache-Control` response header. If enabled, the `maxAge` option should also be specified to enable caching. The `immutable` directive will prevent supported clients from making conditional requests during the life of the `maxAge` option to check if the file has changed. | Boolean | `false` | -| `index` | Sends the specified directory index file. Set to `false` to disable directory indexing. | Mixed | "index.html" | -| `lastModified` | Set the `Last-Modified` header to the last modified date of the file on the OS. | Boolean | `true` | -| `maxAge` | Set the max-age property of the Cache-Control header in milliseconds or a string in [ms format](https://www.npmjs.org/package/ms). | Number | 0 | -| `redirect` | Redirect to trailing "/" when the pathname is a directory. | Boolean | `true` | -| `setHeaders` | Function for setting HTTP headers to serve with the file.

See [setHeaders](#setHeaders) below. | Function | | -| `acceptRanges` | Enable or disable accepting ranged requests. Disabling this will not send the Accept-Ranges header and will ignore the contents of the Range request header.| Boolean | true | -| `cacheControl` | Enable or disable setting the Cache-Control response header. Disabling this will ignore the immutable and maxAge options. | Boolean | true | - -
- -For more information, see [Serving static files in Express](/starter/static-files.html). -and [Using middleware - Built-in middleware](/{{page.lang}}/guide/using-middleware.html#middleware.built-in). - -
dotfiles
- -Possible values for this option are: - -- "allow" - No special treatment for dotfiles. -- "deny" - Deny a request for a dotfile, respond with `403`, then call `next()`. -- "ignore" - Act as if the dotfile does not exist, respond with `404`, then call `next()`. - -
fallthrough
- -When this option is `true`, client errors such as a bad request or a request to a non-existent -file will cause this middleware to simply call `next()` to invoke the next middleware in the stack. -When false, these errors (even 404s), will invoke `next(err)`. - -Set this option to `true` so you can map multiple physical directories -to the same web address or for routes to fill in non-existent files. - -Use `false` if you have mounted this middleware at a path designed -to be strictly a single file system directory, which allows for short-circuiting 404s -for less overhead. This middleware will also reply to all methods. - -
setHeaders
- -For this option, specify a function to set custom response headers. Alterations to the headers must occur synchronously. - -The signature of the function is: - -```js -fn(res, path, stat) -``` - -Arguments: - -- `res`, the [response object](#res). -- `path`, the file path that is being sent. -- `stat`, the `stat` object of the file that is being sent. - -

Example of express.static

- -Here is an example of using the `express.static` middleware function with an elaborate options object: - -```js -const options = { - dotfiles: 'ignore', - etag: false, - extensions: ['htm', 'html'], - index: false, - maxAge: '1d', - redirect: false, - setHeaders (res, path, stat) { - res.set('x-timestamp', Date.now()) - } -} - -app.use(express.static('public', options)) -``` diff --git a/_includes/api/en/5x/express.text.md b/_includes/api/en/5x/express.text.md deleted file mode 100644 index 18223455b4..0000000000 --- a/_includes/api/en/5x/express.text.md +++ /dev/null @@ -1,37 +0,0 @@ -

express.text([options])

- -This is a built-in middleware function in Express. It parses incoming request -payloads into a string and is based on -[body-parser](/resources/middleware/body-parser.html). - -Returns middleware that parses all bodies as a string and only looks at requests -where the `Content-Type` header matches the `type` option. This parser accepts -any Unicode encoding of the body and supports automatic inflation of `gzip` and -`deflate` encodings. - -A new `body` string containing the parsed data is populated on the `request` -object after the middleware (i.e. `req.body`), or `undefined` if -there was no body to parse, the `Content-Type` was not matched, or an error -occurred. - -
-As `req.body`'s shape is based on user-controlled input, all properties and -values in this object are untrusted and should be validated before trusting. -For example, `req.body.trim()` may fail in multiple ways, for example -stacking multiple parsers `req.body` may be from a different parser. Testing -that `req.body` is a string before calling string methods is recommended. -
- -The following table describes the properties of the optional `options` object. - -
- -| Property | Description | Type | Default | -|------------------|-----------------------------------------------------------------------|-------------|-----------------| -| `defaultCharset` | Specify the default character set for the text content if the charset is not specified in the `Content-Type` header of the request. | String | `"utf-8"` | -| `inflate` | Enables or disables handling deflated (compressed) bodies; when disabled, deflated bodies are rejected. | Boolean | `true` | -| `limit` | Controls the maximum request body size. If this is a number, then the value specifies the number of bytes; if it is a string, the value is passed to the [bytes](https://www.npmjs.com/package/bytes) library for parsing. | Mixed | `"100kb"` | -| `type` | This is used to determine what media type the middleware will parse. This option can be a string, array of strings, or a function. If not a function, `type` option is passed directly to the [type-is](https://www.npmjs.org/package/type-is#readme) library and this can be an extension name (like `txt`), a mime type (like `text/plain`), or a mime type with a wildcard (like `*/*` or `text/*`). If a function, the `type` option is called as `fn(req)` and the request is parsed if it returns a truthy value. | Mixed | `"text/plain"` | -| `verify` | This option, if supplied, is called as `verify(req, res, buf, encoding)`, where `buf` is a `Buffer` of the raw request body and `encoding` is the encoding of the request. The parsing can be aborted by throwing an error. | Function | `undefined` | - -
\ No newline at end of file diff --git a/_includes/api/en/5x/express.urlencoded.md b/_includes/api/en/5x/express.urlencoded.md deleted file mode 100644 index 58885486c3..0000000000 --- a/_includes/api/en/5x/express.urlencoded.md +++ /dev/null @@ -1,40 +0,0 @@ -

express.urlencoded([options])

- -This is a built-in middleware function in Express. It parses incoming requests -with urlencoded payloads and is based on [body-parser](/resources/middleware/body-parser.html). - -Returns middleware that only parses urlencoded bodies and only looks at -requests where the `Content-Type` header matches the `type` option. This -parser accepts only UTF-8 encoding of the body and supports automatic -inflation of `gzip` and `deflate` encodings. - -A new `body` object containing the parsed data is populated on the `request` -object after the middleware (i.e. `req.body`), or `undefined` if -there was no body to parse, the `Content-Type` was not matched, or an error -occurred. This object will contain key-value pairs, where the value can be -a string or array (when `extended` is `false`), or any type (when `extended` -is `true`). - -
-As `req.body`'s shape is based on user-controlled input, all properties and -values in this object are untrusted and should be validated before trusting. -For example, `req.body.foo.toString()` may fail in multiple ways, for example -`foo` may not be there or may not be a string, and `toString` may not be a -function and instead a string or other user-input. -
- -The following table describes the properties of the optional `options` object. - -
- -| Property | Description | Type | Default | -|------------------|-----------------------------------------------------------------------|-------------|-----------------| -| `extended` | This option allows to choose between parsing the URL-encoded data with the `querystring` library (when `false`) or the `qs` library (when `true`). The "extended" syntax allows for rich objects and arrays to be encoded into the URL-encoded format, allowing for a JSON-like experience with URL-encoded. For more information, please [see the qs library](https://www.npmjs.org/package/qs#readme). | Boolean | `false` | -| `inflate` | Enables or disables handling deflated (compressed) bodies; when disabled, deflated bodies are rejected. | Boolean | `true` | -| `limit` | Controls the maximum request body size. If this is a number, then the value specifies the number of bytes; if it is a string, the value is passed to the [bytes](https://www.npmjs.com/package/bytes) library for parsing. | Mixed | `"100kb"` | -| `parameterLimit` | This option controls the maximum number of parameters that are allowed in the URL-encoded data. If a request contains more parameters than this value, an error will be raised. | Number | `1000` | -| `type` | This is used to determine what media type the middleware will parse. This option can be a string, array of strings, or a function. If not a function, `type` option is passed directly to the [type-is](https://www.npmjs.org/package/type-is#readme) library and this can be an extension name (like `urlencoded`), a mime type (like `application/x-www-form-urlencoded`), or a mime type with a wildcard (like `*/x-www-form-urlencoded`). If a function, the `type` option is called as `fn(req)` and the request is parsed if it returns a truthy value. | Mixed | `"application/x-www-form-urlencoded"` | -| `verify` | This option, if supplied, is called as `verify(req, res, buf, encoding)`, where `buf` is a `Buffer` of the raw request body and `encoding` is the encoding of the request. The parsing can be aborted by throwing an error. | Function | `undefined` | -| `depth` | Configure the maximum depth of the `qs` library when `extended` is `true`. This allows you to limit the amount of keys that are parsed and can be useful to prevent certain types of abuse. Defaults to `32`. It is recommended to keep this value as low as possible. | Number | `32` | - -
diff --git a/_includes/api/en/5x/menu.md b/_includes/api/en/5x/menu.md deleted file mode 100644 index 278555d110..0000000000 --- a/_includes/api/en/5x/menu.md +++ /dev/null @@ -1,209 +0,0 @@ - - diff --git a/_includes/api/en/5x/req-accepts.md b/_includes/api/en/5x/req-accepts.md deleted file mode 100644 index 7258c71b4c..0000000000 --- a/_includes/api/en/5x/req-accepts.md +++ /dev/null @@ -1,36 +0,0 @@ -

req.accepts(types)

- -Checks if the specified content types are acceptable, based on the request's `Accept` HTTP header field. -The method returns the best match, or if none of the specified content types is acceptable, returns -`false` (in which case, the application should respond with `406 "Not Acceptable"`). - -The `type` value may be a single MIME type string (such as "application/json"), -an extension name such as "json", a comma-delimited list, or an array. For a -list or array, the method returns the *best* match (if any). - -```js -// Accept: text/html -req.accepts('html') -// => "html" - -// Accept: text/*, application/json -req.accepts('html') -// => "html" -req.accepts('text/html') -// => "text/html" -req.accepts(['json', 'text']) -// => "json" -req.accepts('application/json') -// => "application/json" - -// Accept: text/*, application/json -req.accepts('image/png') -req.accepts('png') -// => false - -// Accept: text/*;q=.5, application/json -req.accepts(['html', 'json']) -// => "json" -``` - -For more information, or if you have issues or concerns, see [accepts](https://github.com/expressjs/accepts). diff --git a/_includes/api/en/5x/req-acceptsCharsets.md b/_includes/api/en/5x/req-acceptsCharsets.md deleted file mode 100644 index d47d0480d3..0000000000 --- a/_includes/api/en/5x/req-acceptsCharsets.md +++ /dev/null @@ -1,7 +0,0 @@ -

req.acceptsCharsets(charset [, ...])

- -Returns the first accepted charset of the specified character sets, -based on the request's `Accept-Charset` HTTP header field. -If none of the specified charsets is accepted, returns `false`. - -For more information, or if you have issues or concerns, see [accepts](https://github.com/expressjs/accepts). diff --git a/_includes/api/en/5x/req-acceptsEncodings.md b/_includes/api/en/5x/req-acceptsEncodings.md deleted file mode 100644 index 2c6a3f236f..0000000000 --- a/_includes/api/en/5x/req-acceptsEncodings.md +++ /dev/null @@ -1,7 +0,0 @@ -

req.acceptsEncodings(encoding [, ...])

- -Returns the first accepted encoding of the specified encodings, -based on the request's `Accept-Encoding` HTTP header field. -If none of the specified encodings is accepted, returns `false`. - -For more information, or if you have issues or concerns, see [accepts](https://github.com/expressjs/accepts). diff --git a/_includes/api/en/5x/req-acceptsLanguages.md b/_includes/api/en/5x/req-acceptsLanguages.md deleted file mode 100644 index 7aefa5477d..0000000000 --- a/_includes/api/en/5x/req-acceptsLanguages.md +++ /dev/null @@ -1,15 +0,0 @@ -

req.acceptsLanguages([lang, ...])

- -Returns the first accepted language of the specified languages, -based on the request's `Accept-Language` HTTP header field. -If none of the specified languages is accepted, returns `false`. - -If no `lang` argument is given, then `req.acceptsLanguages()` -returns all languages from the HTTP `Accept-Language` header -as an `Array`. - -For more information, or if you have issues or concerns, see [accepts](https://github.com/expressjs/accepts). - -Express (5.x) source: [request.js line 172](https://github.com/expressjs/express/blob/v5.1.0/lib/request.js#L172) - -Accepts (2.0) source: [index.js line 195](https://github.com/jshttp/accepts/blob/2.0.0/index.js#L195) diff --git a/_includes/api/en/5x/req-app.md b/_includes/api/en/5x/req-app.md deleted file mode 100644 index 30873d7ab7..0000000000 --- a/_includes/api/en/5x/req-app.md +++ /dev/null @@ -1,20 +0,0 @@ -

req.app

- -This property holds a reference to the instance of the Express application that is using the middleware. - -If you follow the pattern in which you create a module that just exports a middleware function -and `require()` it in your main file, then the middleware can access the Express instance via `req.app` - -For example: - -```js -// index.js -app.get('/viewdirectory', require('./mymiddleware.js')) -``` - -```js -// mymiddleware.js -module.exports = (req, res) => { - res.send(`The views directory is ${req.app.get('views')}`) -} -``` diff --git a/_includes/api/en/5x/req-baseUrl.md b/_includes/api/en/5x/req-baseUrl.md deleted file mode 100644 index 3daa0ee5b6..0000000000 --- a/_includes/api/en/5x/req-baseUrl.md +++ /dev/null @@ -1,30 +0,0 @@ -

req.baseUrl

- -The URL path on which a router instance was mounted. - -The `req.baseUrl` property is similar to the [mountpath](#app.mountpath) property of the `app` object, -except `app.mountpath` returns the matched path pattern(s). - -For example: - -```js -const greet = express.Router() - -greet.get('/jp', (req, res) => { - console.log(req.baseUrl) // /greet - res.send('Konichiwa!') -}) - -app.use('/greet', greet) // load the router on '/greet' -``` - -Even if you use a path pattern or a set of path patterns to load the router, -the `baseUrl` property returns the matched string, not the pattern(s). In the -following example, the `greet` router is loaded on two path patterns. - -```js -app.use(['/gre:"param"t', '/hel{l}o'], greet) // load the router on '/gre:"param"t' and '/hel{l}o' -``` - -When a request is made to `/greet/jp`, `req.baseUrl` is "/greet". When a request is -made to `/hello/jp`, `req.baseUrl` is "/hello". diff --git a/_includes/api/en/5x/req-body.md b/_includes/api/en/5x/req-body.md deleted file mode 100644 index ea3610b04b..0000000000 --- a/_includes/api/en/5x/req-body.md +++ /dev/null @@ -1,25 +0,0 @@ -

req.body

- -Contains key-value pairs of data submitted in the request body. -By default, it is `undefined`, and is populated when you use body-parsing middleware such -as [`express.json()`](#express.json) or [`express.urlencoded()`](#express.urlencoded). - -
-As `req.body`'s shape is based on user-controlled input, all properties and values in this object are untrusted and should be validated before trusting. For example, `req.body.foo.toString()` may fail in multiple ways, for example `foo` may not be there or may not be a string, and `toString` may not be a function and instead a string or other user-input. -
- -The following example shows how to use body-parsing middleware to populate `req.body`. - -```js -const express = require('express') - -const app = express() - -app.use(express.json()) // for parsing application/json -app.use(express.urlencoded({ extended: true })) // for parsing application/x-www-form-urlencoded - -app.post('/profile', (req, res, next) => { - console.log(req.body) - res.json(req.body) -}) -``` diff --git a/_includes/api/en/5x/req-cookies.md b/_includes/api/en/5x/req-cookies.md deleted file mode 100644 index 00821e722f..0000000000 --- a/_includes/api/en/5x/req-cookies.md +++ /dev/null @@ -1,14 +0,0 @@ -

req.cookies

- -When using [cookie-parser](https://www.npmjs.com/package/cookie-parser) middleware, this property is an object that -contains cookies sent by the request. If the request contains no cookies, it defaults to `{}`. - -```js -// Cookie: name=tj -console.dir(req.cookies.name) -// => "tj" -``` - -If the cookie has been signed, you have to use [req.signedCookies](#req.signedCookies). - -For more information, issues, or concerns, see [cookie-parser](https://github.com/expressjs/cookie-parser). diff --git a/_includes/api/en/5x/req-fresh.md b/_includes/api/en/5x/req-fresh.md deleted file mode 100644 index 1b6ec61c59..0000000000 --- a/_includes/api/en/5x/req-fresh.md +++ /dev/null @@ -1,13 +0,0 @@ -

req.fresh

- -When the response is still "fresh" in the client's cache `true` is returned, otherwise `false` is returned to indicate that the client cache is now stale and the full response should be sent. - -When a client sends the `Cache-Control: no-cache` request header to indicate an end-to-end reload request, this module will return `false` to make handling these requests transparent. - -Further details for how cache validation works can be found in the -[HTTP/1.1 Caching Specification](https://tools.ietf.org/html/rfc7234). - -```js -console.dir(req.fresh) -// => true -``` diff --git a/_includes/api/en/5x/req-get.md b/_includes/api/en/5x/req-get.md deleted file mode 100644 index 7cd4250b22..0000000000 --- a/_includes/api/en/5x/req-get.md +++ /dev/null @@ -1,17 +0,0 @@ -

req.get(field)

- -Returns the specified HTTP request header field (case-insensitive match). -The `Referrer` and `Referer` fields are interchangeable. - -```js -req.get('Content-Type') -// => "text/plain" - -req.get('content-type') -// => "text/plain" - -req.get('Something') -// => undefined -``` - -Aliased as `req.header(field)`. diff --git a/_includes/api/en/5x/req-host.md b/_includes/api/en/5x/req-host.md deleted file mode 100644 index dfcf63be9e..0000000000 --- a/_includes/api/en/5x/req-host.md +++ /dev/null @@ -1,22 +0,0 @@ -

req.host

- -Contains the host derived from the `Host` HTTP header. - -When the [`trust proxy` setting](api.html#app.settings.table) -does not evaluate to `false`, this property will instead get the value -from the `X-Forwarded-Host` header field. This header can be set by -the client or by the proxy. - -If there is more than one `X-Forwarded-Host` header in the request, the -value of the first header is used. This includes a single header with -comma-separated values, in which the first value is used. - -```js -// Host: "example.com:3000" -console.dir(req.host) -// => 'example.com:3000' - -// Host: "[::1]:3000" -console.dir(req.host) -// => '[::1]:3000' -``` diff --git a/_includes/api/en/5x/req-hostname.md b/_includes/api/en/5x/req-hostname.md deleted file mode 100644 index 327a22426d..0000000000 --- a/_includes/api/en/5x/req-hostname.md +++ /dev/null @@ -1,23 +0,0 @@ -

req.hostname

- -Contains the hostname derived from the `Host` HTTP header. - -When the [`trust proxy` setting](/5x/api.html#trust.proxy.options.table) -does not evaluate to `false`, this property will instead get the value -from the `X-Forwarded-Host` header field. This header can be set by -the client or by the proxy. - -If there is more than one `X-Forwarded-Host` header in the request, the -value of the first header is used. This includes a single header with -comma-separated values, in which the first value is used. - -
-Prior to Express v4.17.0, the `X-Forwarded-Host` could not contain multiple -values or be present more than once. -
- -```js -// Host: "example.com:3000" -console.dir(req.hostname) -// => 'example.com' -``` diff --git a/_includes/api/en/5x/req-ip.md b/_includes/api/en/5x/req-ip.md deleted file mode 100644 index 0bbc7f0318..0000000000 --- a/_includes/api/en/5x/req-ip.md +++ /dev/null @@ -1,12 +0,0 @@ -

req.ip

- -Contains the remote IP address of the request. - -When the [`trust proxy` setting](/5x/api.html#trust.proxy.options.table) does not evaluate to `false`, -the value of this property is derived from the left-most entry in the -`X-Forwarded-For` header. This header can be set by the client or by the proxy. - -```js -console.dir(req.ip) -// => "127.0.0.1" -``` diff --git a/_includes/api/en/5x/req-ips.md b/_includes/api/en/5x/req-ips.md deleted file mode 100644 index 312238b346..0000000000 --- a/_includes/api/en/5x/req-ips.md +++ /dev/null @@ -1,9 +0,0 @@ -

req.ips

- -When the [`trust proxy` setting](/5x/api.html#trust.proxy.options.table) does not evaluate to `false`, -this property contains an array of IP addresses -specified in the `X-Forwarded-For` request header. Otherwise, it contains an -empty array. This header can be set by the client or by the proxy. - -For example, if `X-Forwarded-For` is `client, proxy1, proxy2`, `req.ips` would be -`["client", "proxy1", "proxy2"]`, where `proxy2` is the furthest downstream. diff --git a/_includes/api/en/5x/req-is.md b/_includes/api/en/5x/req-is.md deleted file mode 100644 index 7ee29a9cf2..0000000000 --- a/_includes/api/en/5x/req-is.md +++ /dev/null @@ -1,31 +0,0 @@ -

req.is(type)

- -Returns the matching content type if the incoming request's "Content-Type" HTTP header field -matches the MIME type specified by the `type` parameter. If the request has no body, returns `null`. -Returns `false` otherwise. - -```js -// With Content-Type: text/html; charset=utf-8 -req.is('html') // => 'html' -req.is('text/html') // => 'text/html' -req.is('text/*') // => 'text/*' - -// When Content-Type is application/json -req.is('json') // => 'json' -req.is('application/json') // => 'application/json' -req.is('application/*') // => 'application/*' - -// Using arrays -// When Content-Type is application/json -req.is(['json', 'html']) // => 'json' - -// Using multiple arguments -// When Content-Type is application/json -req.is('json', 'html') // => 'json' - -req.is('html') // => false -req.is(['xml', 'yaml']) // => false -req.is('xml', 'yaml') // => false -``` - -For more information, or if you have issues or concerns, see [type-is](https://github.com/expressjs/type-is). diff --git a/_includes/api/en/5x/req-method.md b/_includes/api/en/5x/req-method.md deleted file mode 100644 index 3d2b886d7d..0000000000 --- a/_includes/api/en/5x/req-method.md +++ /dev/null @@ -1,4 +0,0 @@ -

req.method

- -Contains a string corresponding to the HTTP method of the request: -`GET`, `POST`, `PUT`, and so on. diff --git a/_includes/api/en/5x/req-originalUrl.md b/_includes/api/en/5x/req-originalUrl.md deleted file mode 100644 index 660f46d7b1..0000000000 --- a/_includes/api/en/5x/req-originalUrl.md +++ /dev/null @@ -1,28 +0,0 @@ -

req.originalUrl

- -
-`req.url` is not a native Express property, it is inherited from Node's [http module](https://nodejs.org/api/http.html#http_message_url). -
- -This property is much like `req.url`; however, it retains the original request URL, -allowing you to rewrite `req.url` freely for internal routing purposes. For example, -the "mounting" feature of [app.use()](#app.use) will rewrite `req.url` to strip the mount point. - -```js -// GET /search?q=something -console.dir(req.originalUrl) -// => "/search?q=something" -``` - -`req.originalUrl` is available both in middleware and router objects, and is a -combination of `req.baseUrl` and `req.url`. Consider following example: - -```js -// GET 'http://www.example.com/admin/new?sort=desc' -app.use('/admin', (req, res, next) => { - console.dir(req.originalUrl) // '/admin/new?sort=desc' - console.dir(req.baseUrl) // '/admin' - console.dir(req.path) // '/new' - next() -}) -``` diff --git a/_includes/api/en/5x/req-params.md b/_includes/api/en/5x/req-params.md deleted file mode 100644 index 4037832cb4..0000000000 --- a/_includes/api/en/5x/req-params.md +++ /dev/null @@ -1,39 +0,0 @@ -

req.params

- -This property is an object containing properties mapped to the [named route "parameters"](/{{ page.lang }}/guide/routing.html#route-parameters). For example, if you have the route `/user/:name`, then the "name" property is available as `req.params.name`. This object defaults to `Object.create(null)` when using string paths, but remains a standard object with a normal prototype when the path is defined with a regular expression. - -```js -// GET /user/tj -console.dir(req.params.name) -// => "tj" -``` - -Properties corresponding to wildcard parameters are arrays containing separate path segments split on `/`: - -```js -app.get('/files/*file', (req, res) => { - console.dir(req.params.file) - // GET /files/note.txt - // => [ 'note.txt' ] - // GET /files/images/image.png - // => [ 'images', 'image.png' ] -}) -``` - -When you use a regular expression for the route definition, capture groups are provided as integer keys using `req.params[n]`, where `n` is the nth capture group. - -```js -app.use(/^\/file\/(.*)$/, (req, res) => { - // GET /file/javascripts/jquery.js - console.dir(req.params[0]) - // => "javascripts/jquery.js" -}) -``` - -Named capturing groups in regular expressions behave like named route parameters. For example the group from `/^\/file\/(?.*)$/` expression is available as `req.params.path`. - -If you need to make changes to a key in `req.params`, use the [app.param](/{{ page.lang }}/5x/api.html#app.param) handler. Changes are applicable only to [parameters](/{{ page.lang }}/guide/routing.html#route-parameters) already defined in the route path. - -Any changes made to the `req.params` object in a middleware or route handler will be reset. - -{% include admonitions/note.html content="Express automatically decodes the values in `req.params` (using `decodeURIComponent`)." %} \ No newline at end of file diff --git a/_includes/api/en/5x/req-path.md b/_includes/api/en/5x/req-path.md deleted file mode 100644 index 70eb680c50..0000000000 --- a/_includes/api/en/5x/req-path.md +++ /dev/null @@ -1,13 +0,0 @@ -

req.path

- -Contains the path part of the request URL. - -```js -// example.com/users?sort=desc -console.dir(req.path) -// => "/users" -``` - -
-When called from a middleware, the mount point is not included in `req.path`. See [app.use()](/5x/api.html#app.use) for more details. -
diff --git a/_includes/api/en/5x/req-protocol.md b/_includes/api/en/5x/req-protocol.md deleted file mode 100644 index 32e1ba8a75..0000000000 --- a/_includes/api/en/5x/req-protocol.md +++ /dev/null @@ -1,12 +0,0 @@ -

req.protocol

- -Contains the request protocol string: either `http` or (for TLS requests) `https`. - -When the [`trust proxy` setting](#trust.proxy.options.table) does not evaluate to `false`, -this property will use the value of the `X-Forwarded-Proto` header field if present. -This header can be set by the client or by the proxy. - -```js -console.dir(req.protocol) -// => "http" -``` diff --git a/_includes/api/en/5x/req-query.md b/_includes/api/en/5x/req-query.md deleted file mode 100644 index ca114cf2ca..0000000000 --- a/_includes/api/en/5x/req-query.md +++ /dev/null @@ -1,18 +0,0 @@ -

req.query

- -This property is an object containing a property for each query string parameter in the route. -When [query parser](#app.settings.table) is set to disabled, it is an empty object `{}`, otherwise it is the result of the configured query parser. - -
-As `req.query`'s shape is based on user-controlled input, all properties and values in this object are untrusted and should be validated before trusting. For example, `req.query.foo.toString()` may fail in multiple ways, for example `foo` may not be there or may not be a string, and `toString` may not be a function and instead a string or other user-input. -
- -The value of this property can be configured with the [query parser application setting](#app.settings.table) to work how your application needs it. A very popular query string parser is the [`qs` module](https://www.npmjs.org/package/qs), and this is used by default. The `qs` module is very configurable with many settings, and it may be desirable to use different settings than the default to populate `req.query`: - -```js -const qs = require('qs') -app.set('query parser', - (str) => qs.parse(str, { /* custom options */ })) -``` - -Check out the [query parser application setting](#app.settings.table) documentation for other customization options. diff --git a/_includes/api/en/5x/req-range.md b/_includes/api/en/5x/req-range.md deleted file mode 100644 index 1444f8f816..0000000000 --- a/_includes/api/en/5x/req-range.md +++ /dev/null @@ -1,33 +0,0 @@ -

req.range(size[, options])

- -`Range` header parser. - -The `size` parameter is the maximum size of the resource. - -The `options` parameter is an object that can have the following properties. - -
- -| Property | Type | Description | -|-------------|-------------------------------------------------------------------------| -| `combine` | Boolean | Specify if overlapping & adjacent ranges should be combined, defaults to `false`. When `true`, ranges will be combined and returned as if they were specified that way in the header.| - -
- -An array of ranges will be returned or negative numbers indicating an error parsing. - -* `-2` signals a malformed header string -* `-1` signals an unsatisfiable range - -```js -// parse header from request -const range = req.range(1000) - -// the type of the range -if (range.type === 'bytes') { - // the ranges - range.forEach((r) => { - // do something with r.start and r.end - }) -} -``` diff --git a/_includes/api/en/5x/req-res.md b/_includes/api/en/5x/req-res.md deleted file mode 100644 index 772ddb43b4..0000000000 --- a/_includes/api/en/5x/req-res.md +++ /dev/null @@ -1,4 +0,0 @@ -

req.res

- -This property holds a reference to the response object -that relates to this request object. diff --git a/_includes/api/en/5x/req-route.md b/_includes/api/en/5x/req-route.md deleted file mode 100644 index fb8084b0ae..0000000000 --- a/_includes/api/en/5x/req-route.md +++ /dev/null @@ -1,31 +0,0 @@ -

req.route

- -Contains the currently-matched route, a string. For example: - -```js -app.get('/user/{:id}', (req, res) => { - console.dir(req.route, { depth: null }) - res.send('GET') -}) -``` - -Example output from the previous snippet: - -``` -Route { - path: '/user/{:id}', - stack: [ - Layer { - handle: [Function (anonymous)], - keys: [], - name: '', - params: undefined, - path: undefined, - slash: false, - matchers: [ [Function: match] ], - method: 'get' - } - ], - methods: [Object: null prototype] { get: true } -} -``` diff --git a/_includes/api/en/5x/req-secure.md b/_includes/api/en/5x/req-secure.md deleted file mode 100644 index 4d8ab831ce..0000000000 --- a/_includes/api/en/5x/req-secure.md +++ /dev/null @@ -1,8 +0,0 @@ -

req.secure

- -A Boolean property that is true if a TLS connection is established. Equivalent to the following: - - -```js -req.protocol === 'https' -``` diff --git a/_includes/api/en/5x/req-signedCookies.md b/_includes/api/en/5x/req-signedCookies.md deleted file mode 100644 index 2fd5b3c29c..0000000000 --- a/_includes/api/en/5x/req-signedCookies.md +++ /dev/null @@ -1,17 +0,0 @@ -

req.signedCookies

- -When using [cookie-parser](https://www.npmjs.com/package/cookie-parser) middleware, this property -contains signed cookies sent by the request, unsigned and ready for use. Signed cookies reside -in a different object to show developer intent; otherwise, a malicious attack could be placed on -`req.cookie` values (which are easy to spoof). Note that signing a cookie does not make it "hidden" -or encrypted; but simply prevents tampering (because the secret used to sign is private). - -If no signed cookies are sent, the property defaults to `{}`. - -```js -// Cookie: user=tobi.CP7AWaXDfAKIRfH49dQzKJx7sKzzSoPq7/AcBBRVwlI3 -console.dir(req.signedCookies.user) -// => "tobi" -``` - -For more information, issues, or concerns, see [cookie-parser](https://github.com/expressjs/cookie-parser). diff --git a/_includes/api/en/5x/req-stale.md b/_includes/api/en/5x/req-stale.md deleted file mode 100644 index ca8b479f4c..0000000000 --- a/_includes/api/en/5x/req-stale.md +++ /dev/null @@ -1,9 +0,0 @@ -

req.stale

- -Indicates whether the request is "stale," and is the opposite of `req.fresh`. -For more information, see [req.fresh](#req.fresh). - -```js -console.dir(req.stale) -// => true -``` diff --git a/_includes/api/en/5x/req-subdomains.md b/_includes/api/en/5x/req-subdomains.md deleted file mode 100644 index 714e6e8c07..0000000000 --- a/_includes/api/en/5x/req-subdomains.md +++ /dev/null @@ -1,13 +0,0 @@ -

req.subdomains

- -An array of subdomains in the domain name of the request. - -```js -// Host: "tobi.ferrets.example.com" -console.dir(req.subdomains) -// => ["ferrets", "tobi"] -``` - -The application property `subdomain offset`, which defaults to 2, is used for determining the -beginning of the subdomain segments. To change this behavior, change its value -using [app.set](/{{ page.lang }}/5x/api.html#app.set). diff --git a/_includes/api/en/5x/req-xhr.md b/_includes/api/en/5x/req-xhr.md deleted file mode 100644 index 5c1da1c704..0000000000 --- a/_includes/api/en/5x/req-xhr.md +++ /dev/null @@ -1,9 +0,0 @@ -

req.xhr

- -A Boolean property that is `true` if the request's `X-Requested-With` header field is -"XMLHttpRequest", indicating that the request was issued by a client library such as jQuery. - -```js -console.dir(req.xhr) -// => true -``` diff --git a/_includes/api/en/5x/req.md b/_includes/api/en/5x/req.md deleted file mode 100644 index 7b330b88f8..0000000000 --- a/_includes/api/en/5x/req.md +++ /dev/null @@ -1,156 +0,0 @@ -

Request

- -The `req` object represents the HTTP request and has properties for the -request query string, parameters, body, HTTP headers, and so on. In this documentation and by convention, -the object is always referred to as `req` (and the HTTP response is `res`) but its actual name is determined -by the parameters to the callback function in which you're working. - -For example: - -```js -app.get('/user/:id', (req, res) => { - res.send(`user ${req.params.id}`) -}) -``` - -But you could just as well have: - -```js -app.get('/user/:id', (request, response) => { - response.send(`user ${request.params.id}`) -}) -``` - -The `req` object is an enhanced version of Node's own request object -and supports all [built-in fields and methods](https://nodejs.org/api/http.html#http_class_http_incomingmessage). - -

Properties

- -
-In Express 4, `req.files` is no longer available on the `req` object by default. To access uploaded files -on the `req.files` object, use multipart-handling middleware like [busboy](https://www.npmjs. -com/package/busboy), [multer](https://www.npmjs.com/package/multer), -[formidable](https://www.npmjs.com/package/formidable), -[multiparty](https://www.npmjs.com/package/multiparty), -[connect-multiparty](https://www.npmjs.com/package/connect-multiparty), -or [pez](https://www.npmjs.com/package/pez). -
- -
- {% include api/en/5x/req-app.md %} -
- -
- {% include api/en/5x/req-baseUrl.md %} -
- -
- {% include api/en/5x/req-body.md %} -
- -
- {% include api/en/5x/req-cookies.md %} -
- -
- {% include api/en/5x/req-fresh.md %} -
- -
- {% include api/en/5x/req-host.md %} -
- -
- {% include api/en/5x/req-hostname.md %} -
- -
- {% include api/en/5x/req-ip.md %} -
- -
- {% include api/en/5x/req-ips.md %} -
- -
- {% include api/en/5x/req-method.md %} -
- -
- {% include api/en/5x/req-originalUrl.md %} -
- -
- {% include api/en/5x/req-params.md %} -
- -
- {% include api/en/5x/req-path.md %} -
- -
- {% include api/en/5x/req-protocol.md %} -
- -
- {% include api/en/5x/req-query.md %} -
- -
- {% include api/en/5x/req-res.md %} -
- -
- {% include api/en/5x/req-route.md %} -
- -
- {% include api/en/5x/req-secure.md %} -
- -
- {% include api/en/5x/req-signedCookies.md %} -
- -
- {% include api/en/5x/req-stale.md %} -
- -
- {% include api/en/5x/req-subdomains.md %} -
- -
- {% include api/en/5x/req-xhr.md %} -
- -

Methods

- -
- {% include api/en/5x/req-accepts.md %} -
- -
- {% include api/en/5x/req-acceptsCharsets.md %} -
- -
- {% include api/en/5x/req-acceptsEncodings.md %} -
- -
- {% include api/en/5x/req-acceptsLanguages.md %} -
- -
- {% include api/en/5x/req-get.md %} -
- -
- {% include api/en/5x/req-is.md %} -
- -
- {% include api/en/5x/req-range.md %} -
- diff --git a/_includes/api/en/5x/res-app.md b/_includes/api/en/5x/res-app.md deleted file mode 100644 index 19d8681d23..0000000000 --- a/_includes/api/en/5x/res-app.md +++ /dev/null @@ -1,5 +0,0 @@ -

res.app

- -This property holds a reference to the instance of the Express application that is using the middleware. - -`res.app` is identical to the [req.app](#req.app) property in the request object. diff --git a/_includes/api/en/5x/res-append.md b/_includes/api/en/5x/res-append.md deleted file mode 100644 index 2522d827ba..0000000000 --- a/_includes/api/en/5x/res-append.md +++ /dev/null @@ -1,16 +0,0 @@ -

res.append(field [, value])

- -
-`res.append()` is supported by Express v4.11.0+ -
- -Appends the specified `value` to the HTTP response header `field`. If the header is not already set, -it creates the header with the specified value. The `value` parameter can be a string or an array. - -{% include admonitions/note.html content="calling `res.set()` after `res.append()` will reset the previously-set header value." %} - -```js -res.append('Link', ['', '']) -res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly') -res.append('Warning', '199 Miscellaneous warning') -``` diff --git a/_includes/api/en/5x/res-attachment.md b/_includes/api/en/5x/res-attachment.md deleted file mode 100644 index 73556bece4..0000000000 --- a/_includes/api/en/5x/res-attachment.md +++ /dev/null @@ -1,14 +0,0 @@ -

res.attachment([filename])

- -Sets the HTTP response `Content-Disposition` header field to "attachment". If a `filename` is given, -then it sets the `Content-Type` based on the extension name via `res.type()`, -and sets the `Content-Disposition` "filename=" parameter. - -```js -res.attachment() -// Content-Disposition: attachment - -res.attachment('path/to/logo.png') -// Content-Disposition: attachment; filename="logo.png" -// Content-Type: image/png -``` diff --git a/_includes/api/en/5x/res-clearCookie.md b/_includes/api/en/5x/res-clearCookie.md deleted file mode 100644 index 543fe2e2a1..0000000000 --- a/_includes/api/en/5x/res-clearCookie.md +++ /dev/null @@ -1,19 +0,0 @@ -

res.clearCookie(name [, options])

- -Clears the cookie with the specified `name` by sending a `Set-Cookie` header that sets its expiration date in the past. -This instructs the client that the cookie has expired and is no longer valid. For more information -about available `options`, see [res.cookie()](#res.cookie). - -
-The `expires` and `max-age` options are being ignored completely. -
- -
-Web browsers and other compliant clients will only clear the cookie if the given -`options` is identical to those given to [res.cookie()](#res.cookie) -
- -```js -res.cookie('name', 'tobi', { path: '/admin' }) -res.clearCookie('name', { path: '/admin' }) -``` diff --git a/_includes/api/en/5x/res-cookie.md b/_includes/api/en/5x/res-cookie.md deleted file mode 100644 index c8ee380e5e..0000000000 --- a/_includes/api/en/5x/res-cookie.md +++ /dev/null @@ -1,87 +0,0 @@ -

res.cookie(name, value [, options])

- -Sets cookie `name` to `value`. The `value` parameter may be a string or object converted to JSON. - -The `options` parameter is an object that can have the following properties. - -
- -| Property | Type | Description | -|---------------|-------------------------------------------------------------------------| -| `domain` | String | Domain name for the cookie. Defaults to the domain name of the app.| -| `encode` | Function | A synchronous function used for cookie value encoding. Defaults to `encodeURIComponent`.| -| `expires` | Date | Expiry date of the cookie in GMT. If not specified or set to 0, creates a session cookie.| -| `httpOnly` | Boolean | Flags the cookie to be accessible only by the web server.| -| `maxAge` | Number | Convenient option for setting the expiry time relative to the current time in milliseconds.| -| `path` | String | Path for the cookie. Defaults to "/".| -| `partitioned` | Boolean | Indicates that the cookie should be stored using partitioned storage. See [Cookies Having Independent Partitioned State (CHIPS)](https://developer.mozilla.org/en-US/docs/Web/Privacy/Partitioned_cookies) for more details.| -| `priority` | String | Value of the "Priority" **Set-Cookie** attribute.| -| `secure` | Boolean | Marks the cookie to be used with HTTPS only.| -| `signed` | Boolean | Indicates if the cookie should be signed.| -| `sameSite` | Boolean or String | Value of the "SameSite" **Set-Cookie** attribute. More information at [https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00#section-4.1.1](https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00#section-4.1.1).| - -
- -
-All `res.cookie()` does is set the HTTP `Set-Cookie` header with the options provided. -Any option not specified defaults to the value stated in [RFC 6265](http://tools.ietf.org/html/rfc6265). -
- -For example: - -```js -res.cookie('name', 'tobi', { domain: '.example.com', path: '/admin', secure: true }) -res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true }) -``` - -You can set multiple cookies in a single response by calling `res.cookie` multiple times, for example: - -```js -res - .status(201) - .cookie('access_token', `Bearer ${token}`, { - expires: new Date(Date.now() + 8 * 3600000) // cookie will be removed after 8 hours - }) - .cookie('test', 'test') - .redirect(301, '/admin') -``` - -The `encode` option allows you to choose the function used for cookie value encoding. -Does not support asynchronous functions. - -Example use case: You need to set a domain-wide cookie for another site in your organization. -This other site (not under your administrative control) does not use URI-encoded cookie values. - -```js -// Default encoding -res.cookie('some_cross_domain_cookie', 'http://mysubdomain.example.com', { domain: 'example.com' }) -// Result: 'some_cross_domain_cookie=http%3A%2F%2Fmysubdomain.example.com; Domain=example.com; Path=/' - -// Custom encoding -res.cookie('some_cross_domain_cookie', 'http://mysubdomain.example.com', { domain: 'example.com', encode: String }) -// Result: 'some_cross_domain_cookie=http://mysubdomain.example.com; Domain=example.com; Path=/;' -``` - -The `maxAge` option is a convenience option for setting "expires" relative to the current time in milliseconds. -The following is equivalent to the second example above. - -```js -res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true }) -``` - -You can pass an object as the `value` parameter; it is then serialized as JSON and parsed by `bodyParser()` middleware. - -```js -res.cookie('cart', { items: [1, 2, 3] }) -res.cookie('cart', { items: [1, 2, 3] }, { maxAge: 900000 }) -``` - -When using [cookie-parser](https://www.npmjs.com/package/cookie-parser) middleware, this method also -supports signed cookies. Simply include the `signed` option set to `true`. -Then, `res.cookie()` will use the secret passed to `cookieParser(secret)` to sign the value. - -```js -res.cookie('name', 'tobi', { signed: true }) -``` - -Later, you may access this value through the [req.signedCookies](#req.signedCookies) object. diff --git a/_includes/api/en/5x/res-download.md b/_includes/api/en/5x/res-download.md deleted file mode 100644 index b95c89a50c..0000000000 --- a/_includes/api/en/5x/res-download.md +++ /dev/null @@ -1,60 +0,0 @@ -

res.download(path [, filename] [, options] [, fn])

- -
-The optional `options` argument is supported by Express v4.16.0 onwards. -
- -Transfers the file at `path` as an "attachment". Typically, browsers will prompt the user for download. -By default, the `Content-Disposition` header "filename=" parameter is derived from the `path` argument, but can be overridden with the `filename` parameter. -If `path` is relative, then it will be based on the current working directory of the process or -the `root` option, if provided. - -
-This API provides access to data on the running file system. Ensure that either (a) the way in -which the `path` argument was constructed is secure if it contains user input or (b) set the `root` -option to the absolute path of a directory to contain access within. - -When the `root` option is provided, Express will validate that the relative path provided as -`path` will resolve within the given `root` option. -
- -The following table provides details on the `options` parameter. - -
-The optional `options` argument is supported by Express v4.16.0 onwards. -
- -
- -| Property | Description | Default | Availability | -|-----------------|-------------------------------------------------|-------------|--------------| -| `maxAge` | Sets the max-age property of the `Cache-Control` header in milliseconds or a string in [ms format](https://www.npmjs.org/package/ms)| 0 | 4.16+ | -| `root` | Root directory for relative filenames.| | 4.18+ | -| `lastModified` | Sets the `Last-Modified` header to the last modified date of the file on the OS. Set `false` to disable it.| Enabled | 4.16+ | -| `headers` | Object containing HTTP headers to serve with the file. The header `Content-Disposition` will be overridden by the `filename` argument.| | 4.16+ | -| `dotfiles` | Option for serving dotfiles. Possible values are "allow", "deny", "ignore".| "ignore" | 4.16+ | -| `acceptRanges` | Enable or disable accepting ranged requests. | `true` | 4.16+ | -| `cacheControl` | Enable or disable setting `Cache-Control` response header.| `true` | 4.16+ | -| `immutable` | Enable or disable the `immutable` directive in the `Cache-Control` response header. If enabled, the `maxAge` option should also be specified to enable caching. The `immutable` directive will prevent supported clients from making conditional requests during the life of the `maxAge` option to check if the file has changed. | `false` | 4.16+ | - -
- -The method invokes the callback function `fn(err)` when the transfer is complete -or when an error occurs. If the callback function is specified and an error occurs, -the callback function must explicitly handle the response process either by -ending the request-response cycle, or by passing control to the next route. - -```js -res.download('/report-12345.pdf') - -res.download('/report-12345.pdf', 'report.pdf') - -res.download('/report-12345.pdf', 'report.pdf', (err) => { - if (err) { - // Handle error, but keep in mind the response may be partially-sent - // so check res.headersSent - } else { - // decrement a download credit, etc. - } -}) -``` diff --git a/_includes/api/en/5x/res-end.md b/_includes/api/en/5x/res-end.md deleted file mode 100644 index 2906805fea..0000000000 --- a/_includes/api/en/5x/res-end.md +++ /dev/null @@ -1,10 +0,0 @@ -

res.end([data[, encoding]][, callback])

- -Ends the response process. This method actually comes from Node core, specifically the [response.end() method of http.ServerResponse](https://nodejs.org/api/http.html#responseenddata-encoding-callback). - -Use to quickly end the response without any data. If you need to respond with data, instead use methods such as [res.send()](#res.send) and [res.json()](#res.json). - -```js -res.end() -res.status(404).end() -``` diff --git a/_includes/api/en/5x/res-format.md b/_includes/api/en/5x/res-format.md deleted file mode 100644 index 1a5a3d7a5a..0000000000 --- a/_includes/api/en/5x/res-format.md +++ /dev/null @@ -1,52 +0,0 @@ -

res.format(object)

- -Performs content-negotiation on the `Accept` HTTP header on the request object, when present. -It uses [req.accepts()](#req.accepts) to select a handler for the request, based on the acceptable -types ordered by their quality values. If the header is not specified, the first callback is invoked. -When no match is found, the server responds with 406 "Not Acceptable", or invokes the `default` callback. - -The `Content-Type` response header is set when a callback is selected. However, you may alter -this within the callback using methods such as `res.set()` or `res.type()`. - -The following example would respond with `{ "message": "hey" }` when the `Accept` header field is set -to "application/json" or "\*/json" (however, if it is "\*/\*", then the response will be "hey"). - -```js -res.format({ - 'text/plain' () { - res.send('hey') - }, - - 'text/html' () { - res.send('

hey

') - }, - - 'application/json' () { - res.send({ message: 'hey' }) - }, - - default () { - // log the request and respond with 406 - res.status(406).send('Not Acceptable') - } -}) -``` - -In addition to canonicalized MIME types, you may also use extension names mapped -to these types for a slightly less verbose implementation: - -```js -res.format({ - text () { - res.send('hey') - }, - - html () { - res.send('

hey

') - }, - - json () { - res.send({ message: 'hey' }) - } -}) -``` diff --git a/_includes/api/en/5x/res-get.md b/_includes/api/en/5x/res-get.md deleted file mode 100644 index 8aefb205ef..0000000000 --- a/_includes/api/en/5x/res-get.md +++ /dev/null @@ -1,9 +0,0 @@ -

res.get(field)

- -Returns the HTTP response header specified by `field`. -The match is case-insensitive. - -```js -res.get('Content-Type') -// => "text/plain" -``` diff --git a/_includes/api/en/5x/res-headersSent.md b/_includes/api/en/5x/res-headersSent.md deleted file mode 100644 index 1bbe06d1b3..0000000000 --- a/_includes/api/en/5x/res-headersSent.md +++ /dev/null @@ -1,11 +0,0 @@ -

res.headersSent

- -Boolean property that indicates if the app sent HTTP headers for the response. - -```js -app.get('/', (req, res) => { - console.log(res.headersSent) // false - res.send('OK') - console.log(res.headersSent) // true -}) -``` diff --git a/_includes/api/en/5x/res-json.md b/_includes/api/en/5x/res-json.md deleted file mode 100644 index 8e699a7ed5..0000000000 --- a/_includes/api/en/5x/res-json.md +++ /dev/null @@ -1,13 +0,0 @@ -

res.json([body])

- -Sends a JSON response. This method sends a response (with the correct content-type) that is the parameter converted to a -JSON string using [JSON.stringify()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). - -The parameter can be any JSON type, including object, array, string, Boolean, number, or null, -and you can also use it to convert other values to JSON. - -```js -res.json(null) -res.json({ user: 'tobi' }) -res.status(500).json({ error: 'message' }) -``` diff --git a/_includes/api/en/5x/res-jsonp.md b/_includes/api/en/5x/res-jsonp.md deleted file mode 100644 index edb7e45466..0000000000 --- a/_includes/api/en/5x/res-jsonp.md +++ /dev/null @@ -1,32 +0,0 @@ -

res.jsonp([body])

- -Sends a JSON response with JSONP support. This method is identical to `res.json()`, -except that it opts-in to JSONP callback support. - -```js -res.jsonp(null) -// => callback(null) - -res.jsonp({ user: 'tobi' }) -// => callback({ "user": "tobi" }) - -res.status(500).jsonp({ error: 'message' }) -// => callback({ "error": "message" }) -``` - -By default, the JSONP callback name is simply `callback`. Override this with the -jsonp callback name setting. - -The following are some examples of JSONP responses using the same code: - -```js -// ?callback=foo -res.jsonp({ user: 'tobi' }) -// => foo({ "user": "tobi" }) - -app.set('jsonp callback name', 'cb') - -// ?cb=foo -res.status(500).jsonp({ error: 'message' }) -// => foo({ "error": "message" }) -``` diff --git a/_includes/api/en/5x/res-links.md b/_includes/api/en/5x/res-links.md deleted file mode 100644 index 17164155d4..0000000000 --- a/_includes/api/en/5x/res-links.md +++ /dev/null @@ -1,20 +0,0 @@ - - -Joins the `links` provided as properties of the parameter to populate the response's -`Link` HTTP header field. - -For example, the following call: - -```js -res.links({ - next: 'http://api.example.com/users?page=2', - last: 'http://api.example.com/users?page=5' -}) -``` - -Yields the following results: - -``` -Link: ; rel="next", - ; rel="last" -``` diff --git a/_includes/api/en/5x/res-locals.md b/_includes/api/en/5x/res-locals.md deleted file mode 100644 index 304900033d..0000000000 --- a/_includes/api/en/5x/res-locals.md +++ /dev/null @@ -1,28 +0,0 @@ -

res.locals

- -Use this property to set variables accessible in templates rendered with [res.render](#res.render). -The variables set on `res.locals` are available within a single request-response cycle, and will not -be shared between requests. - -
-The `locals` object is used by view engines to render a response. The object -keys may be particularly sensitive and should not contain user-controlled -input, as it may affect the operation of the view engine or provide a path to -cross-site scripting. Consult the documentation for the used view engine for -additional considerations. -
- -In order to keep local variables for use in template rendering between requests, use -[app.locals](#app.locals) instead. - -This property is useful for exposing request-level information such as the request path name, -authenticated user, user settings, and so on to templates rendered within the application. - -```js -app.use((req, res, next) => { - // Make `user` and `authenticated` available in templates - res.locals.user = req.user - res.locals.authenticated = !req.user.anonymous - next() -}) -``` diff --git a/_includes/api/en/5x/res-location.md b/_includes/api/en/5x/res-location.md deleted file mode 100644 index 637404bd4f..0000000000 --- a/_includes/api/en/5x/res-location.md +++ /dev/null @@ -1,16 +0,0 @@ -

res.location(path)

- -Sets the response `Location` HTTP header to the specified `path` parameter. - -```js -res.location('/foo/bar') -res.location('http://example.com') -``` - -
-After encoding the URL, if not encoded already, Express passes the specified URL to the browser in the `Location` header, -without any validation. - -Browsers take the responsibility of deriving the intended URL from the current URL -or the referring URL, and the URL specified in the `Location` header; and redirect the user accordingly. -
diff --git a/_includes/api/en/5x/res-redirect.md b/_includes/api/en/5x/res-redirect.md deleted file mode 100644 index 8fbf9a88df..0000000000 --- a/_includes/api/en/5x/res-redirect.md +++ /dev/null @@ -1,49 +0,0 @@ -

res.redirect([status,] path)

- -Redirects to the URL derived from the specified `path`, with specified `status`, a positive integer -that corresponds to an [HTTP status code](https://www.rfc-editor.org/rfc/rfc9110.html#name-status-codes). -If not specified, `status` defaults to `302 "Found"`. - -```js -res.redirect('/foo/bar') -res.redirect('http://example.com') -res.redirect(301, 'http://example.com') -res.redirect('../login') -``` -Redirects can be a fully-qualified URL for redirecting to a different site: - -```js -res.redirect('http://google.com') -``` -Redirects can be relative to the root of the host name. For example, if the -application is on `http://example.com/admin/post/new`, the following -would redirect to the URL `http://example.com/admin`: - -```js -res.redirect('/admin') -``` - -Redirects can be relative to the current URL. For example, -from `http://example.com/blog/admin/` (notice the trailing slash), the following -would redirect to the URL `http://example.com/blog/admin/post/new`. - -```js -res.redirect('post/new') -``` - -Redirecting to `post/new` from `http://example.com/blog/admin` (no trailing slash), -will redirect to `http://example.com/blog/post/new`. - -If you found the above behavior confusing, think of path segments as directories -(with trailing slashes) and files, it will start to make sense. - -Path-relative redirects are also possible. If you were on -`http://example.com/admin/post/new`, the following would redirect to -`http://example.com/admin/post`: - -```js -res.redirect('..') -``` - -See also [Security best practices: Prevent open redirect -vulnerabilities](http://expressjs.com/en/advanced/best-practice-security.html#prevent-open-redirects). \ No newline at end of file diff --git a/_includes/api/en/5x/res-render.md b/_includes/api/en/5x/res-render.md deleted file mode 100644 index 8fa563fe71..0000000000 --- a/_includes/api/en/5x/res-render.md +++ /dev/null @@ -1,37 +0,0 @@ -

res.render(view [, locals] [, callback])

- -Renders a `view` and sends the rendered HTML string to the client. -Optional parameters: - -- `locals`, an object whose properties define local variables for the view. -- `callback`, a callback function. If provided, the method returns both the possible error and rendered string, but does not perform an automated response. When an error occurs, the method invokes `next(err)` internally. - -The `view` argument is a string that is the file path of the view file to render. This can be an absolute path, or a path relative to the `views` setting. If the path does not contain a file extension, then the `view engine` setting determines the file extension. If the path does contain a file extension, then Express will load the module for the specified template engine (via `require()`) and render it using the loaded module's `__express` function. - -For more information, see [Using template engines with Express](/{{page.lang}}/guide/using-template-engines.html). - -{% include admonitions/warning.html content="The `view` argument performs file system operations like reading a file from disk and evaluating Node.js modules, and as so for security reasons should not contain input from the end-user." %} - -{% include admonitions/warning.html content="The `locals` object is used by view engines to render a response. The object -keys may be particularly sensitive and should not contain user-controlled -input, as it may affect the operation of the view engine or provide a path to -cross-site scripting. Consult the documentation for the used view engine for -additional considerations." %} - -{% include admonitions/caution.html content="The local variable `cache` enables view caching. Set it to `true`, -to cache the view during development; view caching is enabled in production by default." %} - -```js -// send the rendered view to the client -res.render('index') - -// if a callback is specified, the rendered HTML string has to be sent explicitly -res.render('index', (err, html) => { - res.send(html) -}) - -// pass a local variable to the view -res.render('user', { name: 'Tobi' }, (err, html) => { - // ... -}) -``` diff --git a/_includes/api/en/5x/res-req.md b/_includes/api/en/5x/res-req.md deleted file mode 100644 index 8763653d9f..0000000000 --- a/_includes/api/en/5x/res-req.md +++ /dev/null @@ -1,4 +0,0 @@ -

res.req

- -This property holds a reference to the request object -that relates to this response object. diff --git a/_includes/api/en/5x/res-send.md b/_includes/api/en/5x/res-send.md deleted file mode 100644 index ba46abd399..0000000000 --- a/_includes/api/en/5x/res-send.md +++ /dev/null @@ -1,39 +0,0 @@ -

res.send([body])

- -Sends the HTTP response. - -The `body` parameter can be a `Buffer` object, a `String`, an object, `Boolean`, or an `Array`. -For example: - -```js -res.send(Buffer.from('whoop')) -res.send({ some: 'json' }) -res.send('

some html

') -res.status(404).send('Sorry, we cannot find that!') -res.status(500).send({ error: 'something blew up' }) -``` - -This method performs many useful tasks for simple non-streaming responses: -For example, it automatically assigns the `Content-Length` HTTP response header field -and provides automatic HEAD and HTTP cache freshness support. - -When the parameter is a `Buffer` object, the method sets the `Content-Type` -response header field to "application/octet-stream", unless previously defined as shown below: - -```js -res.set('Content-Type', 'text/html') -res.send(Buffer.from('

some html

')) -``` - -When the parameter is a `String`, the method sets the `Content-Type` to "text/html": - -```js -res.send('

some html

') -``` - -When the parameter is an `Array` or `Object`, Express responds with the JSON representation: - -```js -res.send({ user: 'tobi' }) -res.send([1, 2, 3]) -``` diff --git a/_includes/api/en/5x/res-sendFile.md b/_includes/api/en/5x/res-sendFile.md deleted file mode 100644 index e415a21dc6..0000000000 --- a/_includes/api/en/5x/res-sendFile.md +++ /dev/null @@ -1,84 +0,0 @@ -

res.sendFile(path [, options] [, fn])

- -
-`res.sendFile()` is supported by Express v4.8.0 onwards. -
- -Transfers the file at the given `path`. Sets the `Content-Type` response HTTP header field -based on the filename's extension. Unless the `root` option is set in -the options object, `path` must be an absolute path to the file. - -
-This API provides access to data on the running file system. Ensure that either (a) the way in -which the `path` argument was constructed into an absolute path is secure if it contains user -input or (b) set the `root` option to the absolute path of a directory to contain access within. - -When the `root` option is provided, the `path` argument is allowed to be a relative path, -including containing `..`. Express will validate that the relative path provided as `path` will -resolve within the given `root` option. -
- -The following table provides details on the `options` parameter. - -
- -| Property | Description | Default | Availability | -|-----------------|-------------------------------------------------|-------------|--------------| -|`maxAge` | Sets the max-age property of the `Cache-Control` header in milliseconds or a string in [ms format](https://www.npmjs.org/package/ms)| 0 | | -| `root` | Root directory for relative filenames.| | | -| `lastModified` | Sets the `Last-Modified` header to the last modified date of the file on the OS. Set `false` to disable it.| Enabled | 4.9.0+ | -| `headers` | Object containing HTTP headers to serve with the file.| | | -| `dotfiles` | Option for serving dotfiles. Possible values are "allow", "deny", "ignore".| "ignore" |   | -| `acceptRanges` | Enable or disable accepting ranged requests. | `true` | 4.14+ | -| `cacheControl` | Enable or disable setting `Cache-Control` response header.| `true` | 4.14+ | -| `immutable` | Enable or disable the `immutable` directive in the `Cache-Control` response header. If enabled, the `maxAge` option should also be specified to enable caching. The `immutable` directive will prevent supported clients from making conditional requests during the life of the `maxAge` option to check if the file has changed. | `false` | 4.16+ | - -
- -The method invokes the callback function `fn(err)` when the transfer is complete -or when an error occurs. If the callback function is specified and an error occurs, -the callback function must explicitly handle the response process either by -ending the request-response cycle, or by passing control to the next route. - -Here is an example of using `res.sendFile` with all its arguments. - -```js -app.get('/file/:name', (req, res, next) => { - const options = { - root: path.join(__dirname, 'public'), - dotfiles: 'deny', - headers: { - 'x-timestamp': Date.now(), - 'x-sent': true - } - } - - const fileName = req.params.name - res.sendFile(fileName, options, (err) => { - if (err) { - next(err) - } else { - console.log('Sent:', fileName) - } - }) -}) -``` - -The following example illustrates using -`res.sendFile` to provide fine-grained support for serving files: - -```js -app.get('/user/:uid/photos/:file', (req, res) => { - const uid = req.params.uid - const file = req.params.file - - req.user.mayViewFilesFrom(uid, (yes) => { - if (yes) { - res.sendFile(`/uploads/${uid}/${file}`) - } else { - res.status(403).send("Sorry! You can't see that.") - } - }) -}) -``` -For more information, or if you have issues or concerns, see [send](https://github.com/pillarjs/send). diff --git a/_includes/api/en/5x/res-sendStatus.md b/_includes/api/en/5x/res-sendStatus.md deleted file mode 100644 index fe819bdfcf..0000000000 --- a/_includes/api/en/5x/res-sendStatus.md +++ /dev/null @@ -1,15 +0,0 @@ -

res.sendStatus(statusCode)

- -Sets the response HTTP status code to `statusCode` and sends the registered status message as the text response body. If an unknown status code is specified, the response body will just be the code number. - -```js -res.sendStatus(404) -``` - -
-Some versions of Node.js will throw when `res.statusCode` is set to an -invalid HTTP status code (outside of the range `100` to `599`). Consult -the HTTP server documentation for the Node.js version being used. -
- -[More about HTTP Status Codes](http://en.wikipedia.org/wiki/List_of_HTTP_status_codes) diff --git a/_includes/api/en/5x/res-set.md b/_includes/api/en/5x/res-set.md deleted file mode 100644 index b91698d4b1..0000000000 --- a/_includes/api/en/5x/res-set.md +++ /dev/null @@ -1,16 +0,0 @@ -

res.set(field [, value])

- -Sets the response's HTTP header `field` to `value`. -To set multiple fields at once, pass an object as the parameter. - -```js -res.set('Content-Type', 'text/plain') - -res.set({ - 'Content-Type': 'text/plain', - 'Content-Length': '123', - ETag: '12345' -}) -``` - -Aliased as `res.header(field [, value])`. diff --git a/_includes/api/en/5x/res-status.md b/_includes/api/en/5x/res-status.md deleted file mode 100644 index 9cb9d44e94..0000000000 --- a/_includes/api/en/5x/res-status.md +++ /dev/null @@ -1,10 +0,0 @@ -

res.status(code)

- -Sets the HTTP status for the response. -It is a chainable alias of Node's [response.statusCode](http://nodejs.org/api/http.html#http_response_statuscode). - -```js -res.status(403).end() -res.status(400).send('Bad Request') -res.status(404).sendFile('/absolute/path/to/404.png') -``` diff --git a/_includes/api/en/5x/res-type.md b/_includes/api/en/5x/res-type.md deleted file mode 100644 index f1aa7de27c..0000000000 --- a/_includes/api/en/5x/res-type.md +++ /dev/null @@ -1,13 +0,0 @@ -

res.type(type)

- -Sets the `Content-Type` HTTP header to the MIME type as determined by the specified `type`. If `type` contains the "/" character, then it sets the `Content-Type` to the exact value of `type`, otherwise it is assumed to be a file extension and the MIME type is looked up using the `contentType()` method of the `mime-types` package. - -```js -res.type('.html') // => 'text/html' -res.type('html') // => 'text/html' -res.type('json') // => 'application/json' -res.type('application/json') // => 'application/json' -res.type('png') // => image/png: -``` - -Aliased as `res.contentType(type)`. diff --git a/_includes/api/en/5x/res-vary.md b/_includes/api/en/5x/res-vary.md deleted file mode 100644 index 956aab25ae..0000000000 --- a/_includes/api/en/5x/res-vary.md +++ /dev/null @@ -1,7 +0,0 @@ -

res.vary(field)

- -Adds the field to the `Vary` response header, if it is not there already. - -```js -res.vary('User-Agent').render('docs') -``` diff --git a/_includes/api/en/5x/res.md b/_includes/api/en/5x/res.md deleted file mode 100644 index a26c2c7d14..0000000000 --- a/_includes/api/en/5x/res.md +++ /dev/null @@ -1,130 +0,0 @@ -

Response

- -The `res` object represents the HTTP response that an Express app sends when it gets an HTTP request. - -In this documentation and by convention, -the object is always referred to as `res` (and the HTTP request is `req`) but its actual name is determined -by the parameters to the callback function in which you're working. - -For example: - -```js -app.get('/user/:id', (req, res) => { - res.send(`user ${req.params.id}`) -}) -``` - -But you could just as well have: - -```js -app.get('/user/:id', (request, response) => { - response.send(`user ${request.params.id}`) -}) -``` - -The `res` object is an enhanced version of Node's own response object -and supports all [built-in fields and methods](https://nodejs.org/api/http.html#http_class_http_serverresponse). - -

Properties

- -
- {% include api/en/5x/res-app.md %} -
- -
- {% include api/en/5x/res-headersSent.md %} -
- -
- {% include api/en/5x/res-locals.md %} -
- -
- {% include api/en/5x/res-req.md %} -
- -

Methods

- -
- {% include api/en/5x/res-append.md %} -
- -
- {% include api/en/5x/res-attachment.md %} -
- -
- {% include api/en/5x/res-cookie.md %} -
- -
- {% include api/en/5x/res-clearCookie.md %} -
- -
- {% include api/en/5x/res-download.md %} -
- -
- {% include api/en/5x/res-end.md %} -
- -
- {% include api/en/5x/res-format.md %} -
- -
- {% include api/en/5x/res-get.md %} -
- -
- {% include api/en/5x/res-json.md %} -
- -
- {% include api/en/5x/res-jsonp.md %} -
- -
- {% include api/en/5x/res-links.md %} -
- -
- {% include api/en/5x/res-location.md %} -
- -
- {% include api/en/5x/res-redirect.md %} -
- -
- {% include api/en/5x/res-render.md %} -
- -
- {% include api/en/5x/res-send.md %} -
- -
- {% include api/en/5x/res-sendFile.md %} -
- -
- {% include api/en/5x/res-sendStatus.md %} -
- -
- {% include api/en/5x/res-set.md %} -
- -
- {% include api/en/5x/res-status.md %} -
- -
- {% include api/en/5x/res-type.md %} -
- -
- {% include api/en/5x/res-vary.md %} -
diff --git a/_includes/api/en/5x/router-METHOD.md b/_includes/api/en/5x/router-METHOD.md deleted file mode 100644 index 8f42424566..0000000000 --- a/_includes/api/en/5x/router-METHOD.md +++ /dev/null @@ -1,66 +0,0 @@ -

router.METHOD(path, [callback, ...] callback)

- -The `router.METHOD()` methods provide the routing functionality in Express, -where METHOD is one of the HTTP methods, such as GET, PUT, POST, and so on, -in lowercase. Thus, the actual methods are `router.get()`, `router.post()`, -`router.put()`, and so on. - -
- The `router.get()` function is automatically called for the HTTP `HEAD` method in - addition to the `GET` method if `router.head()` was not called for the - path before `router.get()`. -
- -You can provide multiple callbacks, and all are treated equally, and behave just -like middleware, except that these callbacks may invoke `next('route')` -to bypass the remaining route callback(s). You can use this mechanism to perform -pre-conditions on a route then pass control to subsequent routes when there is no -reason to proceed with the route matched. - -The following snippet illustrates the most simple route definition possible. -Express translates the path strings to regular expressions, used internally -to match incoming requests. Query strings are _not_ considered when performing -these matches, for example "GET /" would match the following route, as would -"GET /?name=tobi". - -```js -router.get('/', (req, res) => { - res.send('hello world') -}) -``` - -You can also use regular expressions—useful if you have very specific -constraints, for example the following would match "GET /commits/71dbb9c" as well -as "GET /commits/71dbb9c..4c084f9". - -```js -router.get(/^\/commits\/(\w+)(?:\.\.(\w+))?$/, (req, res) => { - const from = req.params[0] - const to = req.params[1] || 'HEAD' - res.send(`commit range ${from}..${to}`) -}) -``` - -You can use `next` primitive to implement a flow control between different -middleware functions, based on a specific program state. Invoking `next` with -the string `'router'` will cause all the remaining route callbacks on that router -to be bypassed. - -The following example illustrates `next('router')` usage. - -```js -function fn (req, res, next) { - console.log('I come here') - next('router') -} -router.get('/foo', fn, (req, res, next) => { - console.log('I dont come here') -}) -router.get('/foo', (req, res, next) => { - console.log('I dont come here') -}) -app.get('/foo', (req, res) => { - console.log(' I come here too') - res.end('good') -}) -``` diff --git a/_includes/api/en/5x/router-Router.md b/_includes/api/en/5x/router-Router.md deleted file mode 100644 index 664ffb34c2..0000000000 --- a/_includes/api/en/5x/router-Router.md +++ /dev/null @@ -1,2 +0,0 @@ -

Router([options])

- diff --git a/_includes/api/en/5x/router-all.md b/_includes/api/en/5x/router-all.md deleted file mode 100644 index 91b2d2ec19..0000000000 --- a/_includes/api/en/5x/router-all.md +++ /dev/null @@ -1,31 +0,0 @@ -

router.all(path, [callback, ...] callback)

- -This method is just like the `router.METHOD()` methods, except that it matches all HTTP methods (verbs). - -This method is extremely useful for -mapping "global" logic for specific path prefixes or arbitrary matches. -For example, if you placed the following route at the top of all other -route definitions, it would require that all routes from that point on -would require authentication, and automatically load a user. Keep in mind -that these callbacks do not have to act as end points; `loadUser` -can perform a task, then call `next()` to continue matching subsequent -routes. - -```js -router.all('{*splat}', requireAuthentication, loadUser) -``` - -Or the equivalent: - -```js -router.all('{*splat}', requireAuthentication) -router.all('{*splat}', loadUser) -``` - -Another example of this is white-listed "global" functionality. Here, -the example is much like before, but it only restricts paths prefixed with -"/api": - -```js -router.all('/api/{*splat}', requireAuthentication) -``` diff --git a/_includes/api/en/5x/router-param.md b/_includes/api/en/5x/router-param.md deleted file mode 100644 index 9ef80f6abf..0000000000 --- a/_includes/api/en/5x/router-param.md +++ /dev/null @@ -1,62 +0,0 @@ -

router.param(name, callback)

- -Adds callback triggers to route parameters, where `name` is the name of the parameter and `callback` is the callback function. Although `name` is technically optional, using this method without it is deprecated starting with Express v4.11.0 (see below). - -The parameters of the callback function are: - -- `req`, the request object. -- `res`, the response object. -- `next`, indicating the next middleware function. -- The value of the `name` parameter. -- The name of the parameter. - -
-Unlike `app.param()`, `router.param()` does not accept an array of route parameters. -
- -For example, when `:user` is present in a route path, you may map user loading logic to automatically provide `req.user` to the route, or perform validations on the parameter input. - -```js -router.param('user', (req, res, next, id) => { - // try to get the user details from the User model and attach it to the request object - User.find(id, (err, user) => { - if (err) { - next(err) - } else if (user) { - req.user = user - next() - } else { - next(new Error('failed to load user')) - } - }) -}) -``` - -Param callback functions are local to the router on which they are defined. They are not inherited by mounted apps or routers, nor are they triggered for route parameters inherited from parent routers. Hence, param callbacks defined on `router` will be triggered only by route parameters defined on `router` routes. - -A param callback will be called only once in a request-response cycle, even if the parameter is matched in multiple routes, as shown in the following examples. - -```js -router.param('id', (req, res, next, id) => { - console.log('CALLED ONLY ONCE') - next() -}) - -router.get('/user/:id', (req, res, next) => { - console.log('although this matches') - next() -}) - -router.get('/user/:id', (req, res) => { - console.log('and this matches too') - res.end() -}) -``` - -On `GET /user/42`, the following is printed: - -``` -CALLED ONLY ONCE -although this matches -and this matches too -``` diff --git a/_includes/api/en/5x/router-route.md b/_includes/api/en/5x/router-route.md deleted file mode 100644 index 30b5603ed9..0000000000 --- a/_includes/api/en/5x/router-route.md +++ /dev/null @@ -1,48 +0,0 @@ -

router.route(path)

- -Returns an instance of a single route which you can then use to handle HTTP verbs -with optional middleware. Use `router.route()` to avoid duplicate route naming and -thus typing errors. - -Building on the `router.param()` example above, the following code shows how to use -`router.route()` to specify various HTTP method handlers. - -```js -const router = express.Router() - -router.param('user_id', (req, res, next, id) => { - // sample user, would actually fetch from DB, etc... - req.user = { - id, - name: 'TJ' - } - next() -}) - -router.route('/users/:user_id') - .all((req, res, next) => { - // runs for all HTTP verbs first - // think of it as route specific middleware! - next() - }) - .get((req, res, next) => { - res.json(req.user) - }) - .put((req, res, next) => { - // just an example of maybe updating the user - req.user.name = req.params.name - // save user ... etc - res.json(req.user) - }) - .post((req, res, next) => { - next(new Error('not implemented')) - }) - .delete((req, res, next) => { - next(new Error('not implemented')) - }) -``` - -This approach re-uses the single `/users/:user_id` path and adds handlers for -various HTTP methods. - -{% include admonitions/note.html content="When you use `router.route()`, middleware ordering is based on when the _route_ is created, not when method handlers are added to the route. For this purpose, you can consider method handlers to belong to the route to which they were added." %} diff --git a/_includes/api/en/5x/router-use.md b/_includes/api/en/5x/router-use.md deleted file mode 100644 index 1a0799af7f..0000000000 --- a/_includes/api/en/5x/router-use.md +++ /dev/null @@ -1,106 +0,0 @@ -

router.use([path], [function, ...] function)

- -Uses the specified middleware function or functions, with optional mount path `path`, that defaults to "/". - -This method is similar to [app.use()](#app.use). A simple example and use case is described below. -See [app.use()](#app.use) for more information. - -Middleware is like a plumbing pipe: requests start at the first middleware function defined -and work their way "down" the middleware stack processing for each path they match. - -```js -const express = require('express') -const app = express() -const router = express.Router() - -// simple logger for this router's requests -// all requests to this router will first hit this middleware -router.use((req, res, next) => { - console.log('%s %s %s', req.method, req.url, req.path) - next() -}) - -// this will only be invoked if the path starts with /bar from the mount point -router.use('/bar', (req, res, next) => { - // ... maybe some additional /bar logging ... - next() -}) - -// always invoked -router.use((req, res, next) => { - res.send('Hello World') -}) - -app.use('/foo', router) - -app.listen(3000) -``` - -The "mount" path is stripped and is _not_ visible to the middleware function. -The main effect of this feature is that a mounted middleware function may operate without -code changes regardless of its "prefix" pathname. - -The order in which you define middleware with `router.use()` is very important. -They are invoked sequentially, thus the order defines middleware precedence. For example, -usually a logger is the very first middleware you would use, so that every request gets logged. - -```js -const logger = require('morgan') - -router.use(logger()) -router.use(express.static(path.join(__dirname, 'public'))) -router.use((req, res) => { - res.send('Hello') -}) -``` - -Now suppose you wanted to ignore logging requests for static files, but to continue -logging routes and middleware defined after `logger()`. You would simply move the call to `express.static()` to the top, -before adding the logger middleware: - -```js -router.use(express.static(path.join(__dirname, 'public'))) -router.use(logger()) -router.use((req, res) => { - res.send('Hello') -}) -``` - -Another example is serving files from multiple directories, -giving precedence to "./public" over the others: - -```js -app.use(express.static(path.join(__dirname, 'public'))) -app.use(express.static(path.join(__dirname, 'files'))) -app.use(express.static(path.join(__dirname, 'uploads'))) -``` - -The `router.use()` method also supports named parameters so that your mount points -for other routers can benefit from preloading using named parameters. - -__NOTE__: Although these middleware functions are added via a particular router, _when_ -they run is defined by the path they are attached to (not the router). Therefore, -middleware added via one router may run for other routers if its routes -match. For example, this code shows two different routers mounted on the same path: - -```js -const authRouter = express.Router() -const openRouter = express.Router() - -authRouter.use(require('./authenticate').basic(usersdb)) - -authRouter.get('/:user_id/edit', (req, res, next) => { - // ... Edit user UI ... -}) -openRouter.get('/', (req, res, next) => { - // ... List users ... -}) -openRouter.get('/:user_id', (req, res, next) => { - // ... View user ... -}) - -app.use('/users', authRouter) -app.use('/users', openRouter) -``` - -Even though the authentication middleware was added via the `authRouter` it will run on the routes defined by the `openRouter` as well since both routers were mounted on `/users`. To avoid this behavior, use different paths for each router. diff --git a/_includes/api/en/5x/router.md b/_includes/api/en/5x/router.md deleted file mode 100644 index 26eb778ca9..0000000000 --- a/_includes/api/en/5x/router.md +++ /dev/null @@ -1,62 +0,0 @@ -

Router

- -
-A `router` object is an instance of middleware and routes. You can think of it -as a "mini-application," capable only of performing middleware and routing -functions. Every Express application has a built-in app router. - -A router behaves like middleware itself, so you can use it as an argument to -[app.use()](#app.use) or as the argument to another router's [use()](#router.use) method. - -The top-level `express` object has a [Router()](#express.router) method that creates a new `router` object. - -Once you've created a router object, you can add middleware and HTTP method routes (such as `get`, `put`, `post`, -and so on) to it just like an application. For example: - -```js -// invoked for any requests passed to this router -router.use((req, res, next) => { - // .. some logic here .. like any other middleware - next() -}) - -// will handle any request that ends in /events -// depends on where the router is "use()'d" -router.get('/events', (req, res, next) => { - // .. -}) -``` - -You can then use a router for a particular root URL in this way separating your routes into files or even mini-apps. - -```js -// only requests to /calendar/* will be sent to our "router" -app.use('/calendar', router) -``` - -Keep in mind that any middleware applied to a router will run for all requests on that router's path, even those that aren't part of the router. - - -
- -

Methods

- -
- {% include api/en/5x/router-all.md %} -
- -
- {% include api/en/5x/router-METHOD.md %} -
- -
- {% include api/en/5x/router-param.md %} -
- -
- {% include api/en/5x/router-route.md %} -
- -
- {% include api/en/5x/router-use.md %} -
diff --git a/_includes/api/en/5x/routing-args.html b/_includes/api/en/5x/routing-args.html deleted file mode 100644 index bd7e504d40..0000000000 --- a/_includes/api/en/5x/routing-args.html +++ /dev/null @@ -1,58 +0,0 @@ -

Arguments

- -
- - - - - - - - - - - - - - - - - - - - - -
ArgumentDescriptionDefault
path - The path for which the middleware function is invoked. It can be any of the following: -
    -
  • A string representing a path.
  • -
  • A path pattern.
  • -
  • A regular expression pattern to match paths.
  • -
  • An array containing any combination of the above.
  • -
- For examples, see Path examples. -
'/' (root path)
callback - One or more callback functions. Accepted formats: -
    -
  • A single middleware function.
  • -
  • Multiple middleware functions separated by commas.
  • -
  • An array of middleware functions.
  • -
  • A combination of the above.
  • -
-

- You may provide multiple callbacks that behave like middleware. These can call - next('route') to skip remaining callbacks for the current route. - This is useful for conditional routing logic. -

-

- If a callback throws an error or returns a rejected promise, next(err) is invoked automatically. -

-

- Since both router and app implement the middleware interface, - they can also be used as callback middleware. -

-

- For examples, see Middleware callback function examples. -

-
None
-
\ No newline at end of file diff --git a/_includes/blog/authors.html b/_includes/blog/authors.html deleted file mode 100644 index 5efdfd23a1..0000000000 --- a/_includes/blog/authors.html +++ /dev/null @@ -1,21 +0,0 @@ -{%- assign authors = include.authors -%} -{% if authors %} -
- By - {% for author in authors %} - - {%- assign label = author.github | prepend: '@' -%} - {%- if author.name -%} - {%- assign label = author.name -%} - {%- endif -%} - - {% if label and author.github %} - {% unless forloop.last %}, {% endunless %} - {% elsif label %} - {{label}}{% unless forloop.last %}, {% endunless %} - {% endif %} - {% endfor %} -
-{% endif %} diff --git a/_includes/blog/posts-menu.md b/_includes/blog/posts-menu.md deleted file mode 100644 index b10a50d84b..0000000000 --- a/_includes/blog/posts-menu.md +++ /dev/null @@ -1,12 +0,0 @@ - - diff --git a/_includes/bottom-navigation.html b/_includes/bottom-navigation.html deleted file mode 100644 index 10ce134c1f..0000000000 --- a/_includes/bottom-navigation.html +++ /dev/null @@ -1,46 +0,0 @@ -{% assign current_menu = page.menu %} -{% assign current_lang = page.lang %} - -{% if current_menu and current_lang %} - - {% assign all_pages_in_menu = site.pages | where: "menu", current_menu %} - {% assign lang_specific_pages = all_pages_in_menu | where: "lang", current_lang %} - - {% assign sorted_pages = lang_specific_pages | sort: "order" %} - - {% for doc in sorted_pages %} - {% if doc.path == page.path %} - {% assign current_index = forloop.index0 %} - {% break %} - {% endif %} - {% endfor %} - - {% if current_index != nil %} - {% assign prev_index = current_index | minus: 1 %} - {% if prev_index >= 0 %} - {% assign prev_page = sorted_pages[prev_index] %} - {% endif %} - - {% assign next_index = current_index | plus: 1 %} - {% if next_index < sorted_pages.size %} - {% assign next_page = sorted_pages[next_index] %} - {% endif %} - {% endif %} - - {% if prev_page or next_page %} - - {% endif %} - -{% endif %} \ No newline at end of file diff --git a/_includes/changelog/menu.md b/_includes/changelog/menu.md deleted file mode 100644 index b3bdb5d17b..0000000000 --- a/_includes/changelog/menu.md +++ /dev/null @@ -1,3 +0,0 @@ -- [5.x](#5.x) -- [4.x](#4.x) -{: #side-menu .active} \ No newline at end of file diff --git a/_includes/community-caveat.html b/_includes/community-caveat.html deleted file mode 100644 index 92770e3bd1..0000000000 --- a/_includes/community-caveat.html +++ /dev/null @@ -1,5 +0,0 @@ -{% capture alert_content %} -{{ site.data[page.lang].general.community-caveat-alert }} -{% endcapture %} - -{% include admonitions/warning.html content=alert_content %} diff --git a/_includes/feed-entry.xml b/_includes/feed-entry.xml deleted file mode 100644 index 00af8b9350..0000000000 --- a/_includes/feed-entry.xml +++ /dev/null @@ -1,24 +0,0 @@ -{%- assign entry = include.entry -%} - - {{ site.url }}{{ entry.url }} - - {{ entry.title }} - {%- for author in entry.authors %} - - {% if author.name %} - {{ author.name }} - {% elsif author.github %} - @{{ author.github }} - {% endif %} - {% if author.github %} - https://github.com/{{ author.github }} - {% endif %} - - {%- endfor %} - {{ entry.date | date_to_xmlschema }} - {{ entry.last_modified_at | default: post.date | date_to_xmlschema }} - {%- for tag in post.tags %} - - {%- endfor %} - Read {{ entry.title }} ]]> - diff --git a/_includes/footer.html b/_includes/footer.html deleted file mode 100644 index 2c9aff8dd4..0000000000 --- a/_includes/footer.html +++ /dev/null @@ -1,74 +0,0 @@ -{% include icons/arrow.svg %} - - - - diff --git a/_includes/github-edit-btn.html b/_includes/github-edit-btn.html deleted file mode 100644 index 3c90f515b8..0000000000 --- a/_includes/github-edit-btn.html +++ /dev/null @@ -1,9 +0,0 @@ - - {% include icons/github.svg %} - Edit this page - \ No newline at end of file diff --git a/_includes/head.html b/_includes/head.html deleted file mode 100644 index d94b9f0f10..0000000000 --- a/_includes/head.html +++ /dev/null @@ -1,73 +0,0 @@ - - {{ page.title }} - - - - - - - - - - - - - - - - - - {% if page.authors %} - - {%- for author in page.authors %} - - {%- endfor %} - {% else %} - - {% endif %} - - - {% if page.image %} - - {% else %} - - {% endif %} - - - - - - {% if page contains "image" %} - - {% else %} - - {% endif %} - - - - - - - \ No newline at end of file diff --git a/_includes/header.html b/_includes/header.html deleted file mode 100644 index 9c6adc9987..0000000000 --- a/_includes/header.html +++ /dev/null @@ -1,251 +0,0 @@ -
-
- -
- - -
- - {% include language-picker.html %} -
-
diff --git a/_includes/i18n-notice.html b/_includes/i18n-notice.html deleted file mode 100644 index 6c5adf9e0b..0000000000 --- a/_includes/i18n-notice.html +++ /dev/null @@ -1,10 +0,0 @@ -{% assign notice = site.data[page.lang].general.i18n_notice %} -{% assign lang_path = '/' | append: page.lang | append: '/' %} -{% if notice %} - {% assign notice_link_text = site.data[page.lang].general.i18n_notice_link_text %} -{% else %} - {% assign notice = site.data.en.general.i18n_notice %} - {% assign notice_link_text = site.data.en.general.i18n_notice_link_text %} -{% endif %} -

{{ notice }} {{ notice_link_text}}.

-
\ No newline at end of file diff --git a/_includes/icons/X.svg b/_includes/icons/X.svg deleted file mode 100644 index cc93734c32..0000000000 --- a/_includes/icons/X.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/_includes/icons/announcement.svg b/_includes/icons/announcement.svg deleted file mode 100644 index 460c9522f8..0000000000 --- a/_includes/icons/announcement.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/_includes/icons/arrow.svg b/_includes/icons/arrow.svg deleted file mode 100644 index 3b44d7b7bb..0000000000 --- a/_includes/icons/arrow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/_includes/icons/bluesky.svg b/_includes/icons/bluesky.svg deleted file mode 100644 index ac3613791d..0000000000 --- a/_includes/icons/bluesky.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/_includes/icons/caution.svg b/_includes/icons/caution.svg deleted file mode 100644 index c62c756faf..0000000000 --- a/_includes/icons/caution.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/_includes/icons/express-logo.svg b/_includes/icons/express-logo.svg deleted file mode 100644 index 16bcc7070a..0000000000 --- a/_includes/icons/express-logo.svg +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/_includes/icons/github.svg b/_includes/icons/github.svg deleted file mode 100644 index b2b41dbd47..0000000000 --- a/_includes/icons/github.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - \ No newline at end of file diff --git a/_includes/icons/hamburger.svg b/_includes/icons/hamburger.svg deleted file mode 100644 index 9a681768f3..0000000000 --- a/_includes/icons/hamburger.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/_includes/icons/i18n.svg b/_includes/icons/i18n.svg deleted file mode 100644 index e0b2a0b2bc..0000000000 --- a/_includes/icons/i18n.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/_includes/icons/moon.svg b/_includes/icons/moon.svg deleted file mode 100644 index 0b224e4910..0000000000 --- a/_includes/icons/moon.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/_includes/icons/note.svg b/_includes/icons/note.svg deleted file mode 100644 index b08a648992..0000000000 --- a/_includes/icons/note.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/_includes/icons/opencollective.svg b/_includes/icons/opencollective.svg deleted file mode 100644 index e4c1b67a75..0000000000 --- a/_includes/icons/opencollective.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/_includes/icons/openjs_foundation-logo-horizontal-white.svg b/_includes/icons/openjs_foundation-logo-horizontal-white.svg deleted file mode 100644 index e87bedfa01..0000000000 --- a/_includes/icons/openjs_foundation-logo-horizontal-white.svg +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/_includes/icons/slack.svg b/_includes/icons/slack.svg deleted file mode 100644 index e6bb3f152c..0000000000 --- a/_includes/icons/slack.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - diff --git a/_includes/icons/sun.svg b/_includes/icons/sun.svg deleted file mode 100644 index 0701fffd93..0000000000 --- a/_includes/icons/sun.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/_includes/icons/warning.svg b/_includes/icons/warning.svg deleted file mode 100644 index fa4907c793..0000000000 --- a/_includes/icons/warning.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/_includes/icons/youtube.svg b/_includes/icons/youtube.svg deleted file mode 100644 index 5968f9b040..0000000000 --- a/_includes/icons/youtube.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/_includes/language-picker.html b/_includes/language-picker.html deleted file mode 100644 index 0a344ed9a3..0000000000 --- a/_includes/language-picker.html +++ /dev/null @@ -1,21 +0,0 @@ -{% assign url_parts = page.url | split: '/' %} -{% assign url_remainder = url_parts | slice: 2, url_parts.size | join: '/' %} -{% assign current_lang = page.lang %} -
- - -
\ No newline at end of file diff --git a/_includes/mw-list.md b/_includes/mw-list.md deleted file mode 100644 index 7eae8705cc..0000000000 --- a/_includes/mw-list.md +++ /dev/null @@ -1,18 +0,0 @@ -- [body-parser](/resources/middleware/body-parser.html) -- [compression](/resources/middleware/compression.html) -- [connect-rid](/resources/middleware/connect-rid.html) -- [cookie-parser](/resources/middleware/cookie-parser.html) -- [cookie-session](/resources/middleware/cookie-session.html) -- [cors](/resources/middleware/cors.html) -- [errorhandler](/resources/middleware/errorhandler.html) -- [method-override](/resources/middleware/method-override.html) -- [morgan](/resources/middleware/morgan.html) -- [multer](/resources/middleware/multer.html) -- [response-time](/resources/middleware/response-time.html) -- [serve-favicon](/resources/middleware/serve-favicon.html) -- [serve-index](/resources/middleware/serve-index.html) -- [serve-static](/resources/middleware/serve-static.html) -- [session](/resources/middleware/session.html) -- [timeout](/resources/middleware/timeout.html) -- [vhost](/resources/middleware/vhost.html) -{: #side-menu .active} \ No newline at end of file diff --git a/_includes/readmes/body-parser.md b/_includes/readmes/body-parser.md deleted file mode 100644 index d7149004fe..0000000000 --- a/_includes/readmes/body-parser.md +++ /dev/null @@ -1,509 +0,0 @@ -# body-parser - -[![NPM Version][npm-version-image]][npm-url] -[![NPM Downloads][npm-downloads-image]][npm-url] -[![Build Status][ci-image]][ci-url] -[![Test Coverage][coveralls-image]][coveralls-url] -[![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer] - -Node.js body parsing middleware. - -Parse incoming request bodies in a middleware before your handlers, available -under the `req.body` property. - -**Note** As `req.body`'s shape is based on user-controlled input, all -properties and values in this object are untrusted and should be validated -before trusting. For example, `req.body.foo.toString()` may fail in multiple -ways, for example the `foo` property may not be there or may not be a string, -and `toString` may not be a function and instead a string or other user input. - -[Learn about the anatomy of an HTTP transaction in Node.js](https://nodejs.org/en/learn/http/anatomy-of-an-http-transaction). - -_This does not handle multipart bodies_, due to their complex and typically -large nature. For multipart bodies, you may be interested in the following -modules: - - * [busboy](https://www.npmjs.com/package/busboy#readme) and - [connect-busboy](https://www.npmjs.com/package/connect-busboy#readme) - * [multiparty](https://www.npmjs.com/package/multiparty#readme) and - [connect-multiparty](https://www.npmjs.com/package/connect-multiparty#readme) - * [formidable](https://www.npmjs.com/package/formidable#readme) - * [multer](https://www.npmjs.com/package/multer#readme) - -This module provides the following parsers: - - * [JSON body parser](#bodyparserjsonoptions) - * [Raw body parser](#bodyparserrawoptions) - * [Text body parser](#bodyparsertextoptions) - * [URL-encoded form body parser](#bodyparserurlencodedoptions) - -Other body parsers you might be interested in: - -- [body](https://www.npmjs.com/package/body#readme) -- [co-body](https://www.npmjs.com/package/co-body#readme) - -## Installation - -```sh -$ npm install body-parser -``` - -## API - -```js -// Import all parsers -const bodyParser = require('body-parser') - -// Or import individual parsers directly -const json = require('body-parser/json') -const urlencoded = require('body-parser/urlencoded') -const raw = require('body-parser/raw') -const text = require('body-parser/text') -``` - -The `bodyParser` object exposes various factories to create middlewares. All -middlewares will populate the `req.body` property with the parsed body when -the `Content-Type` request header matches the `type` option. - -The various errors returned by this module are described in the -[errors section](#errors). - -### bodyParser.json([options]) - -Returns middleware that only parses `json` and only looks at requests where -the `Content-Type` header matches the `type` option. This parser accepts any -Unicode encoding of the body and supports automatic inflation of `gzip`, -`br` (brotli) and `deflate` encodings. - -A new `body` object containing the parsed data is populated on the `request` -object after the middleware (i.e. `req.body`). - -#### Options - -The `json` function takes an optional `options` object that may contain any of -the following keys: - -##### defaultCharset - -Specify the default character set for the json content if the charset is not -specified in the `Content-Type` header of the request. Defaults to `utf-8`. - -##### inflate - -When set to `true`, then deflated (compressed) bodies will be inflated; when -`false`, deflated bodies are rejected. Defaults to `true`. - -##### limit - -Controls the maximum request body size. If this is a number, then the value -specifies the number of bytes; if it is a string, the value is passed to the -[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults -to `'100kb'`. - -> It’s recommended not to configure a very high limit and to use the default value whenever possible. Allowing larger payloads increases memory usage because of the resources required for decoding and transformations, and it can also lead to longer response times as more data is processed. By ‘very high’, we mean values above the default, for example payloads of 5 MB or more can already start to introduce these risks. With the default limits, these issues do not occur. - -##### reviver - -The `reviver` option is passed directly to `JSON.parse` as the second -argument. You can find more information on this argument -[in the MDN documentation about JSON.parse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Example.3A_Using_the_reviver_parameter). - -##### strict - -When set to `true`, will only accept arrays and objects; when `false` will -accept anything `JSON.parse` accepts. Defaults to `true`. - -##### type - -The `type` option is used to determine what media type the middleware will -parse. This option can be a string, array of strings, or a function. If not a -function, `type` option is passed directly to the -[type-is](https://www.npmjs.com/package/type-is#readme) library and this can -be an extension name (like `json`), a mime type (like `application/json`), or -a mime type with a wildcard (like `*/*` or `*/json`). If a function, the `type` -option is called as `fn(req)` and the request is parsed if it returns a truthy -value. Defaults to `application/json`. - -##### verify - -The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`, -where `buf` is a `Buffer` of the raw request body and `encoding` is the -encoding of the request. The parsing can be aborted by throwing an error. - -### bodyParser.raw([options]) - -Returns middleware that parses all bodies as a `Buffer` and only looks at -requests where the `Content-Type` header matches the `type` option. This -parser supports automatic inflation of `gzip`, `br` (brotli) and `deflate` -encodings. - -A new `body` object containing the parsed data is populated on the `request` -object after the middleware (i.e. `req.body`). This will be a `Buffer` object -of the body. - -#### Options - -The `raw` function takes an optional `options` object that may contain any of -the following keys: - -##### inflate - -When set to `true`, then deflated (compressed) bodies will be inflated; when -`false`, deflated bodies are rejected. Defaults to `true`. - -##### limit - -Controls the maximum request body size. If this is a number, then the value -specifies the number of bytes; if it is a string, the value is passed to the -[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults -to `'100kb'`. - -> It’s recommended not to configure a very high limit and to use the default value whenever possible. Allowing larger payloads increases memory usage because of the resources required for decoding and transformations, and it can also lead to longer response times as more data is processed. By ‘very high’, we mean values above the default, for example payloads of 5 MB or more can already start to introduce these risks. With the default limits, these issues do not occur. - -##### type - -The `type` option is used to determine what media type the middleware will -parse. This option can be a string, array of strings, or a function. -If not a function, `type` option is passed directly to the -[type-is](https://www.npmjs.com/package/type-is#readme) library and this -can be an extension name (like `bin`), a mime type (like -`application/octet-stream`), or a mime type with a wildcard (like `*/*` or -`application/*`). If a function, the `type` option is called as `fn(req)` -and the request is parsed if it returns a truthy value. Defaults to -`application/octet-stream`. - -##### verify - -The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`, -where `buf` is a `Buffer` of the raw request body and `encoding` is the -encoding of the request. The parsing can be aborted by throwing an error. - -### bodyParser.text([options]) - -Returns middleware that parses all bodies as a string and only looks at -requests where the `Content-Type` header matches the `type` option. This -parser supports automatic inflation of `gzip`, `br` (brotli) and `deflate` -encodings. - -A new `body` string containing the parsed data is populated on the `request` -object after the middleware (i.e. `req.body`). This will be a string of the -body. - -#### Options - -The `text` function takes an optional `options` object that may contain any of -the following keys: - -##### defaultCharset - -Specify the default character set for the text content if the charset is not -specified in the `Content-Type` header of the request. Defaults to `utf-8`. - -##### inflate - -When set to `true`, then deflated (compressed) bodies will be inflated; when -`false`, deflated bodies are rejected. Defaults to `true`. - -##### limit - -Controls the maximum request body size. If this is a number, then the value -specifies the number of bytes; if it is a string, the value is passed to the -[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults -to `'100kb'`. - -> It’s recommended not to configure a very high limit and to use the default value whenever possible. Allowing larger payloads increases memory usage because of the resources required for decoding and transformations, and it can also lead to longer response times as more data is processed. By ‘very high’, we mean values above the default, for example payloads of 5 MB or more can already start to introduce these risks. With the default limits, these issues do not occur. - -##### type - -The `type` option is used to determine what media type the middleware will -parse. This option can be a string, array of strings, or a function. If not -a function, `type` option is passed directly to the -[type-is](https://www.npmjs.com/package/type-is#readme) library and this can -be an extension name (like `txt`), a mime type (like `text/plain`), or a mime -type with a wildcard (like `*/*` or `text/*`). If a function, the `type` -option is called as `fn(req)` and the request is parsed if it returns a -truthy value. Defaults to `text/plain`. - -##### verify - -The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`, -where `buf` is a `Buffer` of the raw request body and `encoding` is the -encoding of the request. The parsing can be aborted by throwing an error. - -### bodyParser.urlencoded([options]) - -Returns middleware that only parses `urlencoded` bodies and only looks at -requests where the `Content-Type` header matches the `type` option. This -parser accepts only UTF-8 and ISO-8859-1 encodings of the body and supports -automatic inflation of `gzip`, `br` (brotli) and `deflate` encodings. - -A new `body` object containing the parsed data is populated on the `request` -object after the middleware (i.e. `req.body`). This object will contain -key-value pairs, where the value can be a string or array (when `extended` is -`false`), or any type (when `extended` is `true`). - -#### Options - -The `urlencoded` function takes an optional `options` object that may contain -any of the following keys: - -##### extended - -The "extended" syntax allows for rich objects and arrays to be encoded into the -URL-encoded format, allowing for a JSON-like experience with URL-encoded. For -more information, please [see the qs -library](https://www.npmjs.com/package/qs#readme). - -Defaults to `false`. - -##### inflate - -When set to `true`, then deflated (compressed) bodies will be inflated; when -`false`, deflated bodies are rejected. Defaults to `true`. - -##### limit - -Controls the maximum request body size. If this is a number, then the value -specifies the number of bytes; if it is a string, the value is passed to the -[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults -to `'100kb'`. - -> It’s recommended not to configure a very high limit and to use the default value whenever possible. Allowing larger payloads increases memory usage because of the resources required for decoding and transformations, and it can also lead to longer response times as more data is processed. By ‘very high’, we mean values above the default, for example payloads of 5 MB or more can already start to introduce these risks. With the default limits, these issues do not occur. - -##### parameterLimit - -The `parameterLimit` option controls the maximum number of parameters that -are allowed in the URL-encoded data. If a request contains more parameters -than this value, a 413 will be returned to the client. Defaults to `1000`. - -##### type - -The `type` option is used to determine what media type the middleware will -parse. This option can be a string, array of strings, or a function. If not -a function, `type` option is passed directly to the -[type-is](https://www.npmjs.com/package/type-is#readme) library and this can -be an extension name (like `urlencoded`), a mime type (like -`application/x-www-form-urlencoded`), or a mime type with a wildcard (like -`*/x-www-form-urlencoded`). If a function, the `type` option is called as -`fn(req)` and the request is parsed if it returns a truthy value. Defaults -to `application/x-www-form-urlencoded`. - -##### verify - -The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`, -where `buf` is a `Buffer` of the raw request body and `encoding` is the -encoding of the request. The parsing can be aborted by throwing an error. - -##### defaultCharset - -The default charset to parse as, if not specified in content-type. Must be -either `utf-8` or `iso-8859-1`. Defaults to `utf-8`. - -##### charsetSentinel - -Whether to let the value of the `utf8` parameter take precedence as the charset -selector. It requires the form to contain a parameter named `utf8` with a value -of `✓`. Defaults to `false`. - -##### interpretNumericEntities - -Whether to decode numeric entities such as `☺` when parsing an iso-8859-1 -form. Defaults to `false`. - - -##### depth - -The `depth` option is used to configure the maximum depth of the `qs` library when `extended` is `true`. This allows you to limit the amount of keys that are parsed and can be useful to prevent certain types of abuse. Defaults to `32`. It is recommended to keep this value as low as possible. - -## Errors - -The middlewares provided by this module create errors using the -[`http-errors` module](https://www.npmjs.com/package/http-errors). The errors -will typically have a `status`/`statusCode` property that contains the suggested -HTTP response code, an `expose` property to determine if the `message` property -should be displayed to the client, a `type` property to determine the type of -error without matching against the `message`, and a `body` property containing -the read body, if available. - -The following are the common errors created, though any error can come through -for various reasons. - -### content encoding unsupported - -This error will occur when the request had a `Content-Encoding` header that -contained an encoding but the "inflation" option was set to `false`. The -`status` property is set to `415`, the `type` property is set to -`'encoding.unsupported'`, and the `charset` property will be set to the -encoding that is unsupported. - -### entity parse failed - -This error will occur when the request contained an entity that could not be -parsed by the middleware. The `status` property is set to `400`, the `type` -property is set to `'entity.parse.failed'`, and the `body` property is set to -the entity value that failed parsing. - -### entity verify failed - -This error will occur when the request contained an entity that could not be -failed verification by the defined `verify` option. The `status` property is -set to `403`, the `type` property is set to `'entity.verify.failed'`, and the -`body` property is set to the entity value that failed verification. - -### request aborted - -This error will occur when the request is aborted by the client before reading -the body has finished. The `received` property will be set to the number of -bytes received before the request was aborted and the `expected` property is -set to the number of expected bytes. The `status` property is set to `400` -and `type` property is set to `'request.aborted'`. - -### request entity too large - -This error will occur when the request body's size is larger than the "limit" -option. The `limit` property will be set to the byte limit and the `length` -property will be set to the request body's length. The `status` property is -set to `413` and the `type` property is set to `'entity.too.large'`. - -### request size did not match content length - -This error will occur when the request's length did not match the length from -the `Content-Length` header. This typically occurs when the request is malformed, -typically when the `Content-Length` header was calculated based on characters -instead of bytes. The `status` property is set to `400` and the `type` property -is set to `'request.size.invalid'`. - -### stream encoding should not be set - -This error will occur when something called the `req.setEncoding` method prior -to this middleware. This module operates directly on bytes only and you cannot -call `req.setEncoding` when using this module. The `status` property is set to -`500` and the `type` property is set to `'stream.encoding.set'`. - -### stream is not readable - -This error will occur when the request is no longer readable when this middleware -attempts to read it. This typically means something other than a middleware from -this module read the request body already and the middleware was also configured to -read the same request. The `status` property is set to `500` and the `type` -property is set to `'stream.not.readable'`. - -### too many parameters - -This error will occur when the content of the request exceeds the configured -`parameterLimit` for the `urlencoded` parser. The `status` property is set to -`413` and the `type` property is set to `'parameters.too.many'`. - -### unsupported charset "BOGUS" - -This error will occur when the request had a charset parameter in the -`Content-Type` header, but the `iconv-lite` module does not support it OR the -parser does not support it. The charset is contained in the message as well -as in the `charset` property. The `status` property is set to `415`, the -`type` property is set to `'charset.unsupported'`, and the `charset` property -is set to the charset that is unsupported. - -### unsupported content encoding "bogus" - -This error will occur when the request had a `Content-Encoding` header that -contained an unsupported encoding. The encoding is contained in the message -as well as in the `encoding` property. The `status` property is set to `415`, -the `type` property is set to `'encoding.unsupported'`, and the `encoding` -property is set to the encoding that is unsupported. - -### The input exceeded the depth - -This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. When the `depth` option is set to `32` (default value), the error will not be thrown. - -## Examples - -### Express/Connect top-level generic - -This example demonstrates adding a generic JSON and URL-encoded parser as a -top-level middleware, which will parse the bodies of all incoming requests. -This is the simplest setup. - -```js -const express = require('express') -const bodyParser = require('body-parser') - -const app = express() - -// parse application/x-www-form-urlencoded -app.use(bodyParser.urlencoded()) - -// parse application/json -app.use(bodyParser.json()) - -app.use(function (req, res) { - res.setHeader('Content-Type', 'text/plain') - res.write('you posted:\n') - res.end(String(JSON.stringify(req.body, null, 2))) -}) -``` - -### Express route-specific - -This example demonstrates adding body parsers specifically to the routes that -need them. In general, this is the most recommended way to use body-parser with -Express. - -```js -const express = require('express') -const bodyParser = require('body-parser') - -const app = express() - -// create application/json parser -const jsonParser = bodyParser.json() - -// create application/x-www-form-urlencoded parser -const urlencodedParser = bodyParser.urlencoded() - -// POST /login gets urlencoded bodies -app.post('/login', urlencodedParser, function (req, res) { - if (!req.body || !req.body.username) res.sendStatus(400) - res.send('welcome, ' + req.body.username) -}) - -// POST /api/users gets JSON bodies -app.post('/api/users', jsonParser, function (req, res) { - if (!req.body) res.sendStatus(400) - // create user in req.body -}) -``` - -### Change accepted type for parsers - -All the parsers accept a `type` option which allows you to change the -`Content-Type` that the middleware will parse. - -```js -const express = require('express') -const bodyParser = require('body-parser') - -const app = express() - -// parse various different custom JSON types as JSON -app.use(bodyParser.json({ type: 'application/*+json' })) - -// parse some custom thing into a Buffer -app.use(bodyParser.raw({ type: 'application/vnd.custom-type' })) - -// parse an HTML body into a string -app.use(bodyParser.text({ type: 'text/html' })) -``` - -## License - -[MIT](LICENSE) - -[ci-image]: https://img.shields.io/github/actions/workflow/status/expressjs/body-parser/ci.yml?branch=master&label=ci -[ci-url]: https://github.com/expressjs/body-parser/actions/workflows/ci.yml -[coveralls-image]: https://img.shields.io/coverallsCoverage/github/expressjs/body-parser?branch=master -[coveralls-url]: https://coveralls.io/r/expressjs/body-parser?branch=master -[npm-downloads-image]: https://img.shields.io/npm/dm/body-parser -[npm-url]: https://npmjs.com/package/body-parser -[npm-version-image]: https://img.shields.io/npm/v/body-parser -[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/body-parser/badge -[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/body-parser diff --git a/_includes/readmes/compression.md b/_includes/readmes/compression.md deleted file mode 100644 index df1bd78947..0000000000 --- a/_includes/readmes/compression.md +++ /dev/null @@ -1,311 +0,0 @@ -# compression - -[![NPM Version][npm-image]][npm-url] -[![NPM Downloads][downloads-image]][downloads-url] -[![Build Status][github-actions-ci-image]][github-actions-ci-url] -[![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer] -[![Funding][funding-image]][funding-url] - - -Node.js compression middleware. - -The following compression codings are supported: - - - deflate - - gzip - - br (brotli) - -**Note** Brotli is supported only since Node.js versions v11.7.0 and v10.16.0. - -## Install - -This is a [Node.js](https://nodejs.org/en/) module available through the -[npm registry](https://www.npmjs.com/). Installation is done using the -[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): - -```bash -$ npm install compression -``` - -## API - -```js -var compression = require('compression') -``` - -### compression([options]) - -Returns the compression middleware using the given `options`. The middleware -will attempt to compress response bodies for all requests that traverse through -the middleware, based on the given `options`. - -This middleware will never compress responses that include a `Cache-Control` -header with the [`no-transform` directive](https://tools.ietf.org/html/rfc7234#section-5.2.2.4), -as compressing will transform the body. - -#### Options - -`compression()` accepts these properties in the options object. In addition to -those listed below, [zlib](https://nodejs.org/api/zlib.html) options may be -passed in to the options object or -[brotli](https://nodejs.org/api/zlib.html#zlib_class_brotlioptions) options. - -##### chunkSize - -Type: `Number`
-Default: `zlib.constants.Z_DEFAULT_CHUNK`, or `16384`. - -See [Node.js documentation](https://nodejs.org/api/zlib.html#zlib_memory_usage_tuning) -regarding the usage. - -##### filter - -Type: `Function` - -A function to decide if the response should be considered for compression. -This function is called as `filter(req, res)` and is expected to return -`true` to consider the response for compression, or `false` to not compress -the response. - -The default filter function uses the [compressible](https://www.npmjs.com/package/compressible) -module to determine if `res.getHeader('Content-Type')` is compressible. - -##### level - -Type: `Number`
-Default: `zlib.constants.Z_DEFAULT_COMPRESSION`, or `-1` - -The level of zlib compression to apply to responses. A higher level will result -in better compression, but will take longer to complete. A lower level will -result in less compression, but will be much faster. - -This is an integer in the range of `0` (no compression) to `9` (maximum -compression). The special value `-1` can be used to mean the "default -compression level", which is a default compromise between speed and -compression (currently equivalent to level 6). - - - `-1` Default compression level (also `zlib.constants.Z_DEFAULT_COMPRESSION`). - - `0` No compression (also `zlib.constants.Z_NO_COMPRESSION`). - - `1` Fastest compression (also `zlib.constants.Z_BEST_SPEED`). - - `2` - - `3` - - `4` - - `5` - - `6` (currently what `zlib.constants.Z_DEFAULT_COMPRESSION` points to). - - `7` - - `8` - - `9` Best compression (also `zlib.constants.Z_BEST_COMPRESSION`). - -**Note** in the list above, `zlib` is from `zlib = require('zlib')`. - -##### memLevel - -Type: `Number`
-Default: `zlib.constants.Z_DEFAULT_MEMLEVEL`, or `8` - -This specifies how much memory should be allocated for the internal compression -state and is an integer in the range of `1` (minimum level) and `9` (maximum -level). - -See [Node.js documentation](https://nodejs.org/api/zlib.html#zlib_memory_usage_tuning) -regarding the usage. - -##### brotli - -Type: `Object` - -This specifies the options for configuring Brotli. See [Node.js documentation](https://nodejs.org/api/zlib.html#class-brotlioptions) for a complete list of available options. - - -##### strategy - -Type: `Number`
-Default: `zlib.constants.Z_DEFAULT_STRATEGY` - -This is used to tune the compression algorithm. This value only affects the -compression ratio, not the correctness of the compressed output, even if it -is not set appropriately. - - - `zlib.constants.Z_DEFAULT_STRATEGY` Use for normal data. - - `zlib.constants.Z_FILTERED` Use for data produced by a filter (or predictor). - Filtered data consists mostly of small values with a somewhat random - distribution. In this case, the compression algorithm is tuned to - compress them better. The effect is to force more Huffman coding and less - string matching; it is somewhat intermediate between `zlib.constants.Z_DEFAULT_STRATEGY` - and `zlib.constants.Z_HUFFMAN_ONLY`. - - `zlib.constants.Z_FIXED` Use to prevent the use of dynamic Huffman codes, allowing - for a simpler decoder for special applications. - - `zlib.constants.Z_HUFFMAN_ONLY` Use to force Huffman encoding only (no string match). - - `zlib.constants.Z_RLE` Use to limit match distances to one (run-length encoding). - This is designed to be almost as fast as `zlib.constants.Z_HUFFMAN_ONLY`, but give - better compression for PNG image data. - -**Note** in the list above, `zlib` is from `zlib = require('zlib')`. - -##### threshold - -Type: `Number` or `String`
-Default: `1kb` - -The byte threshold for the response body size before compression is considered -for the response. This is a number of bytes or any string -accepted by the [bytes](https://www.npmjs.com/package/bytes) module. - -**Note** this is only an advisory setting; if the response size cannot be determined -at the time the response headers are written, then it is assumed the response is -_over_ the threshold. To guarantee the response size can be determined, be sure -set a `Content-Length` response header. - -##### windowBits - -Type: `Number`
-Default: `zlib.constants.Z_DEFAULT_WINDOWBITS`, or `15` - -See [Node.js documentation](https://nodejs.org/api/zlib.html#zlib_memory_usage_tuning) -regarding the usage. - -##### enforceEncoding - -Type: `String`
-Default: `identity` - -This is the default encoding to use when the client does not specify an encoding in the request's [Accept-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) header. - -#### .filter - -The default `filter` function. This is used to construct a custom filter -function that is an extension of the default function. - -```js -var compression = require('compression') -var express = require('express') - -var app = express() - -app.use(compression({ filter: shouldCompress })) - -function shouldCompress (req, res) { - if (req.headers['x-no-compression']) { - // don't compress responses with this request header - return false - } - - // fallback to standard filter function - return compression.filter(req, res) -} -``` - -### res.flush - -This module adds a `res.flush()` method to force the partially-compressed -response to be flushed to the client. - -## Examples - -### express - -When using this module with express, simply `app.use` the module as -high as you like. Requests that pass through the middleware will be compressed. - -```js -var compression = require('compression') -var express = require('express') - -var app = express() - -// compress all responses -app.use(compression()) - -// add all routes -``` - -### Node.js HTTP server - -```js -var compression = require('compression')({ threshold: 0 }) -var http = require('http') - -function createServer (fn) { - return http.createServer(function (req, res) { - compression(req, res, function (err) { - if (err) { - res.statusCode = err.status || 500 - res.end(err.message) - return - } - - fn(req, res) - }) - }) -} - -var server = createServer(function (req, res) { - res.setHeader('Content-Type', 'text/plain') - res.end('hello world!') -}) - -server.listen(3000, () => { - console.log('> Listening at http://localhost:3000') -}) -``` - -### Server-Sent Events - -Because of the nature of compression this module does not work out of the box -with server-sent events. To compress content, a window of the output needs to -be buffered up in order to get good compression. Typically when using server-sent -events, there are certain block of data that need to reach the client. - -You can achieve this by calling `res.flush()` when you need the data written to -actually make it to the client. - -```js -var compression = require('compression') -var express = require('express') - -var app = express() - -// compress responses -app.use(compression()) - -// server-sent event stream -app.get('/events', function (req, res) { - res.setHeader('Content-Type', 'text/event-stream') - res.setHeader('Cache-Control', 'no-cache') - - // send a ping approx every 2 seconds - var timer = setInterval(function () { - res.write('data: ping\n\n') - - // !!! this is the important part - res.flush() - }, 2000) - - res.on('close', function () { - clearInterval(timer) - }) -}) -``` - -## Contributing - -The Express.js project welcomes all constructive contributions. Contributions take many forms, -from code for bug fixes and enhancements, to additions and fixes to documentation, additional -tests, triaging incoming pull requests and issues, and more! - -See the [Contributing Guide](https://github.com/expressjs/express/blob/master/Contributing.md) for more technical details on contributing. - -## License - -[MIT](LICENSE) - -[npm-image]: https://badgen.net/npm/v/compression -[npm-url]: https://npmjs.org/package/compression -[downloads-image]: https://badgen.net/npm/dm/compression -[downloads-url]: https://npmcharts.com/compare/compression?minimal=true -[github-actions-ci-image]: https://badgen.net/github/checks/expressjs/compression/master?label=CI -[github-actions-ci-url]: https://github.com/expressjs/compression/actions?query=workflow%3Aci -[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/compression/badge -[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/compression -[funding-url]: https://opencollective.com/express -[funding-image]: https://badgen.net/badge/icon/sponsor/pink?icon=github&label=Open%20Collective \ No newline at end of file diff --git a/_includes/readmes/connect-rid.md b/_includes/readmes/connect-rid.md deleted file mode 100644 index 62c34d3788..0000000000 --- a/_includes/readmes/connect-rid.md +++ /dev/null @@ -1,59 +0,0 @@ -> [!CAUTION] -> **This repository is archived and no longer actively maintained.** -> -> We are no longer accepting issues, feature requests, or pull requests. -> For additional support or questions, please visit the [Express.js Discussions page](https://github.com/expressjs/express/discussions). - - - -connect-rid -======= - -[![Build Status](https://secure.travis-ci.org/fengmk2/connect-rid.png)](http://travis-ci.org/fengmk2/connect-rid) [![Coverage Status](https://coveralls.io/repos/fengmk2/connect-rid/badge.png)](https://coveralls.io/r/fengmk2/connect-rid) [![Dependency Status](https://gemnasium.com/fengmk2/connect-rid.png)](https://gemnasium.com/fengmk2/connect-rid) - -[![NPM](https://nodei.co/npm/connect-rid.png?downloads=true&stars=true)](https://nodei.co/npm/connect-rid/) - -![logo](https://raw.github.com/fengmk2/connect-rid/master/logo.png) - -connect request id middleware, base on [rid](https://github.com/fengmk2/rid). - -## Install - -```bash -$ npm install connect-rid -``` - -## Usage - -```js -var rid = require('connect-rid'); - -app.use(rid({ - // headerName: 'X-RID' -})); -``` - -## License - -(The MIT License) - -Copyright (c) 2014 fengmk2 <fengmk2@gmail.com> and other contributors - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/_includes/readmes/cookie-parser.md b/_includes/readmes/cookie-parser.md deleted file mode 100644 index b8ecd7b24e..0000000000 --- a/_includes/readmes/cookie-parser.md +++ /dev/null @@ -1,119 +0,0 @@ -# cookie-parser - -[![NPM Version][npm-version-image]][npm-url] -[![NPM Downloads][npm-downloads-image]][npm-url] -[![Build Status][ci-image]][ci-url] -[![Test Coverage][coveralls-image]][coveralls-url] - -Parse `Cookie` header and populate `req.cookies` with an object keyed by the -cookie names. Optionally you may enable signed cookie support by passing a -`secret` string, which assigns `req.secret` so it may be used by other -middleware. - -## Installation - -```sh -$ npm install cookie-parser -``` - -## API - -```js -var cookieParser = require('cookie-parser') -``` - -### cookieParser(secret, options) - -Create a new cookie parser middleware function using the given `secret` and -`options`. - -- `secret` a string or array used for signing cookies. This is optional and if - not specified, will not parse signed cookies. If a string is provided, this - is used as the secret. If an array is provided, an attempt will be made to - unsign the cookie with each secret in order. -- `options` an object that is passed to `cookie.parse` as the second option. See - [cookie](https://www.npmjs.org/package/cookie) for more information. - - `decode` a function to decode the value of the cookie - -The middleware will parse the `Cookie` header on the request and expose the -cookie data as the property `req.cookies` and, if a `secret` was provided, as -the property `req.signedCookies`. These properties are name value pairs of the -cookie name to cookie value. - -When `secret` is provided, this module will unsign and validate any signed cookie -values and move those name value pairs from `req.cookies` into `req.signedCookies`. -A signed cookie is a cookie that has a value prefixed with `s:`. Signed cookies -that fail signature validation will have the value `false` instead of the tampered -value. - -In addition, this module supports special "JSON cookies". These are cookie where -the value is prefixed with `j:`. When these values are encountered, the value will -be exposed as the result of `JSON.parse`. If parsing fails, the original value will -remain. - -### cookieParser.JSONCookie(str) - -Parse a cookie value as a JSON cookie. This will return the parsed JSON value -if it was a JSON cookie, otherwise, it will return the passed value. - -### cookieParser.JSONCookies(cookies) - -Given an object, this will iterate over the keys and call `JSONCookie` on each -value, replacing the original value with the parsed value. This returns the -same object that was passed in. - -### cookieParser.signedCookie(str, secret) - -Parse a cookie value as a signed cookie. This will return the parsed unsigned -value if it was a signed cookie and the signature was valid. If the value was -not signed, the original value is returned. If the value was signed but the -signature could not be validated, `false` is returned. - -The `secret` argument can be an array or string. If a string is provided, this -is used as the secret. If an array is provided, an attempt will be made to -unsign the cookie with each secret in order. - -### cookieParser.signedCookies(cookies, secret) - -Given an object, this will iterate over the keys and check if any value is a -signed cookie. If it is a signed cookie and the signature is valid, the key -will be deleted from the object and added to the new object that is returned. - -The `secret` argument can be an array or string. If a string is provided, this -is used as the secret. If an array is provided, an attempt will be made to -unsign the cookie with each secret in order. - -## Example - -```js -var express = require('express') -var cookieParser = require('cookie-parser') - -var app = express() -app.use(cookieParser()) - -app.get('/', function (req, res) { - // Cookies that have not been signed - console.log('Cookies: ', req.cookies) - - // Cookies that have been signed - console.log('Signed Cookies: ', req.signedCookies) -}) - -app.listen(8080) - -// curl command that sends an HTTP request with two cookies -// curl http://127.0.0.1:8080 --cookie "Cho=Kim;Greet=Hello" -``` - -## License - -[MIT](LICENSE) - -[ci-image]: https://badgen.net/github/checks/expressjs/cookie-parser/master?label=ci -[ci-url]: https://github.com/expressjs/cookie-parser/actions?query=workflow%3Aci -[coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/cookie-parser/master -[coveralls-url]: https://coveralls.io/r/expressjs/cookie-parser?branch=master -[npm-downloads-image]: https://badgen.net/npm/dm/cookie-parser -[npm-url]: https://npmjs.org/package/cookie-parser -[npm-version-image]: https://badgen.net/npm/v/cookie-parser diff --git a/_includes/readmes/cookie-session.md b/_includes/readmes/cookie-session.md deleted file mode 100644 index 13a9d88714..0000000000 --- a/_includes/readmes/cookie-session.md +++ /dev/null @@ -1,290 +0,0 @@ -# cookie-session - -[![NPM Version][npm-version-image]][npm-url] -[![NPM Downloads][npm-downloads-image]][npm-url] -[![Build Status][ci-image]][ci-url] -[![Test Coverage][coveralls-image]][coveralls-url] - -Simple cookie-based session middleware. - -A user session can be stored in two main ways with cookies: on the server or on -the client. This module stores the session data on the client within a cookie, -while a module like [express-session](https://www.npmjs.com/package/express-session) -stores only a session identifier on the client within a cookie and stores the -session data on the server, typically in a database. - -The following points can help you choose which to use: - - * `cookie-session` does not require any database / resources on the server side, - though the total session data cannot exceed the browser's max cookie size. - * `cookie-session` can simplify certain load-balanced scenarios. - * `cookie-session` can be used to store a "light" session and include an identifier - to look up a database-backed secondary store to reduce database lookups. - -**NOTE** This module does not encrypt the session contents in the cookie, only provides -signing to prevent tampering. The client will be able to read the session data by -examining the cookie's value. Secret data should not be set in `req.session` without -encrypting it, or use a server-side session instead. - -**NOTE** This module does not prevent session replay, as the expiration set is that -of the cookie only; if that is a concern of your application, you can store an expiration -date in `req.session` object and validate it on the server, and implement any other logic -to extend the session as your application needs. - -## Install - -This is a [Node.js](https://nodejs.org/en/) module available through the -[npm registry](https://www.npmjs.com/). Installation is done using the -[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): - -```sh -$ npm install cookie-session -``` - -## API - -```js -var cookieSession = require('cookie-session') -var express = require('express') - -var app = express() - -app.use(cookieSession({ - name: 'session', - keys: [/* secret keys */], - - // Cookie Options - maxAge: 24 * 60 * 60 * 1000 // 24 hours -})) -``` - -### cookieSession(options) - -Create a new cookie session middleware with the provided options. This middleware -will attach the property `session` to `req`, which provides an object representing -the loaded session. This session is either a new session if no valid session was -provided in the request, or a loaded session from the request. - -The middleware will automatically add a `Set-Cookie` header to the response if the -contents of `req.session` were altered. _Note_ that no `Set-Cookie` header will be -in the response (and thus no session created for a specific user) unless there are -contents in the session, so be sure to add something to `req.session` as soon as -you have identifying information to store for the session. - -#### Options - -Cookie session accepts these properties in the options object. - -##### name - -The name of the cookie to set, defaults to `session`. - -##### keys - -The list of keys to use to sign & verify cookie values, or a configured -[`Keygrip`](https://www.npmjs.com/package/keygrip) instance. Set cookies are always -signed with `keys[0]`, while the other keys are valid for verification, allowing -for key rotation. If a `Keygrip` instance is provided, it can be used to -change signature parameters like the algorithm of the signature. - -##### secret - -A string which will be used as single key if `keys` is not provided. - -##### Cookie Options - -Other options are passed to `cookies.get()` and `cookies.set()` allowing you -to control security, domain, path, and signing among other settings. - -The options can also contain any of the following (for the full list, see -[cookies module documentation](https://www.npmjs.org/package/cookies#readme): - - - `maxAge`: a number representing the milliseconds from `Date.now()` for expiry - - `expires`: a `Date` object indicating the cookie's expiration date (expires at the end of session by default). - - `path`: a string indicating the path of the cookie (`/` by default). - - `domain`: a string indicating the domain of the cookie (no default). - - `partitioned`: a boolean indicating whether to partition the cookie in Chrome for the [CHIPS Update](https://developers.google.com/privacy-sandbox/3pcd/chips) (`false` by default). If this is true, Cookies from embedded sites will be partitioned and only readable from the same top level site from which it was created. - - `priority`: a string indicating the cookie priority. This can be set to `'low'`, `'medium'`, or `'high'`. - - `sameSite`: a boolean or string indicating whether the cookie is a "same site" cookie (`false` by default). This can be set to `'strict'`, `'lax'`, `'none'`, or `true` (which maps to `'strict'`). - - `secure`: a boolean indicating whether the cookie is only to be sent over HTTPS (`false` by default for HTTP, `true` by default for HTTPS). If this is set to `true` and Node.js is not directly over a TLS connection, be sure to read how to [setup Express behind proxies](https://expressjs.com/en/guide/behind-proxies.html) or the cookie may not ever set correctly. - - `httpOnly`: a boolean indicating whether the cookie is only to be sent over HTTP(S), and not made available to client JavaScript (`true` by default). - - `signed`: a boolean indicating whether the cookie is to be signed (`true` by default). - - `overwrite`: a boolean indicating whether to overwrite previously set cookies of the same name (`true` by default). - -### req.session - -Represents the session for the given request. - -#### .isChanged - -Is `true` if the session has been changed during the request. - -#### .isNew - -Is `true` if the session is new. - -#### .isPopulated - -Determine if the session has been populated with data or is empty. - -### req.sessionOptions - -Represents the session options for the current request. These options are a -shallow clone of what was provided at middleware construction and can be -altered to change cookie setting behavior on a per-request basis. - -### Destroying a session - -To destroy a session simply set it to `null`: - -```js -req.session = null -``` - -### Saving a session - -Since the entire contents of the session is kept in a client-side cookie, the -session is "saved" by writing a cookie out in a `Set-Cookie` response header. -This is done automatically if there has been a change made to the session when -the Node.js response headers are being written to the client and the session -was not destroyed. - -## Examples - -### Simple view counter example - -```js -var cookieSession = require('cookie-session') -var express = require('express') - -var app = express() - -app.set('trust proxy', 1) // trust first proxy - -app.use(cookieSession({ - name: 'session', - keys: ['key1', 'key2'] -})) - -app.get('/', function (req, res, next) { - // Update views - req.session.views = (req.session.views || 0) + 1 - - // Write response - res.end(req.session.views + ' views') -}) - -app.listen(3000) -``` - -### Per-user sticky max age - -```js -var cookieSession = require('cookie-session') -var express = require('express') - -var app = express() - -app.set('trust proxy', 1) // trust first proxy - -app.use(cookieSession({ - name: 'session', - keys: ['key1', 'key2'] -})) - -// This allows you to set req.session.maxAge to let certain sessions -// have a different value than the default. -app.use(function (req, res, next) { - req.sessionOptions.maxAge = req.session.maxAge || req.sessionOptions.maxAge - next() -}) - -// ... your logic here ... -``` - -### Extending the session expiration - -This module does not send a `Set-Cookie` header if the contents of the session -have not changed. This means that to extend the expiration of a session in the -user's browser (in response to user activity, for example) some kind of -modification to the session needs be made. - -```js -var cookieSession = require('cookie-session') -var express = require('express') - -var app = express() - -app.use(cookieSession({ - name: 'session', - keys: ['key1', 'key2'] -})) - -// Update a value in the cookie so that the set-cookie will be sent. -// Only changes every minute so that it's not sent with every request. -app.use(function (req, res, next) { - req.session.nowInMinutes = Math.floor(Date.now() / 60e3) - next() -}) - -// ... your logic here ... -``` - -### Using a custom signature algorithm - -This example shows creating a custom `Keygrip` instance as the `keys` option -to provide keys and additional signature configuration. - -```js -var cookieSession = require('cookie-session') -var express = require('express') -var Keygrip = require('keygrip') - -var app = express() - -app.use(cookieSession({ - name: 'session', - keys: new Keygrip(['key1', 'key2'], 'SHA384', 'base64') -})) - -// ... your logic here ... -``` - -## Usage Limitations - -### Max Cookie Size - -Because the entire session object is encoded and stored in a cookie, it is -possible to exceed the maximum cookie size limits on different browsers. The -[RFC6265 specification](https://tools.ietf.org/html/rfc6265#section-6.1) -recommends that a browser **SHOULD** allow - -> At least 4096 bytes per cookie (as measured by the sum of the length of -> the cookie's name, value, and attributes) - -In practice this limit differs slightly across browsers. See a list of -[browser limits here](http://browsercookielimits.iain.guru). As a rule -of thumb **don't exceed 4093 bytes per domain**. - -If your session object is large enough to exceed a browser limit when encoded, -in most cases the browser will refuse to store the cookie. This will cause the -following requests from the browser to either a) not have any session -information or b) use old session information that was small enough to not -exceed the cookie limit. - -If you find your session object is hitting these limits, it is best to -consider if data in your session should be loaded from a database on the -server instead of transmitted to/from the browser with every request. Or -move to an [alternative session strategy](https://github.com/expressjs/session#compatible-session-stores) - -## License - -[MIT](LICENSE) - -[ci-image]: https://img.shields.io/github/actions/workflow/status/expressjs/cookie-session/ci.yml -[ci-url]: https://github.com/expressjs/cookie-session/actions?query=workflow%3Aci -[coveralls-image]: https://img.shields.io/coverallsCoverage/github/expressjs/cookie-session?branch=master -[coveralls-url]: https://coveralls.io/r/expressjs/cookie-session?branch=master -[npm-downloads-image]: https://img.shields.io/npm/dw/cookie-session -[npm-url]: https://npmjs.org/package/cookie-session -[npm-version-image]:https://img.shields.io/npm/v/cookie-session \ No newline at end of file diff --git a/_includes/readmes/cors.md b/_includes/readmes/cors.md deleted file mode 100644 index 3d206e5b6f..0000000000 --- a/_includes/readmes/cors.md +++ /dev/null @@ -1,277 +0,0 @@ -# cors - -[![NPM Version][npm-image]][npm-url] -[![NPM Downloads][downloads-image]][downloads-url] -[![Build Status][github-actions-ci-image]][github-actions-ci-url] -[![Test Coverage][coveralls-image]][coveralls-url] - -CORS is a [Node.js](https://nodejs.org/en/) middleware for [Express](https://expressjs.com/)/[Connect](https://github.com/senchalabs/connect) that sets [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS) response headers. These headers tell browsers which origins can read responses from your server. - -> [!IMPORTANT] -> **How CORS Works:** This package sets response headers—it doesn't block requests. CORS is enforced by browsers: they check the headers and decide if JavaScript can read the response. Non-browser clients (curl, Postman, other servers) ignore CORS entirely. See the [MDN CORS guide](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS) for details. - -* [Installation](#installation) -* [Usage](#usage) - * [Simple Usage](#simple-usage-enable-all-cors-requests) - * [Enable CORS for a Single Route](#enable-cors-for-a-single-route) - * [Configuring CORS](#configuring-cors) - * [Configuring CORS w/ Dynamic Origin](#configuring-cors-w-dynamic-origin) - * [Enabling CORS Pre-Flight](#enabling-cors-pre-flight) - * [Customizing CORS Settings Dynamically per Request](#customizing-cors-settings-dynamically-per-request) -* [Configuration Options](#configuration-options) -* [Common Misconceptions](#common-misconceptions) -* [License](#license) -* [Original Author](#original-author) - -## Installation - -This is a [Node.js](https://nodejs.org/en/) module available through the -[npm registry](https://www.npmjs.com/). Installation is done using the -[`npm install` command](https://docs.npmjs.com/downloading-and-installing-packages-locally): - -```sh -$ npm install cors -``` - -## Usage - -### Simple Usage (Enable *All* CORS Requests) - -```javascript -var express = require('express') -var cors = require('cors') -var app = express() - -// Adds headers: Access-Control-Allow-Origin: * -app.use(cors()) - -app.get('/products/:id', function (req, res, next) { - res.json({msg: 'Hello'}) -}) - -app.listen(80, function () { - console.log('web server listening on port 80') -}) -``` - -### Enable CORS for a Single Route - -```javascript -var express = require('express') -var cors = require('cors') -var app = express() - -// Adds headers: Access-Control-Allow-Origin: * -app.get('/products/:id', cors(), function (req, res, next) { - res.json({msg: 'Hello'}) -}) - -app.listen(80, function () { - console.log('web server listening on port 80') -}) -``` - -### Configuring CORS - -See the [configuration options](#configuration-options) for details. - -```javascript -var express = require('express') -var cors = require('cors') -var app = express() - -var corsOptions = { - origin: 'http://example.com', - optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204 -} - -// Adds headers: Access-Control-Allow-Origin: http://example.com, Vary: Origin -app.get('/products/:id', cors(corsOptions), function (req, res, next) { - res.json({msg: 'Hello'}) -}) - -app.listen(80, function () { - console.log('web server listening on port 80') -}) -``` - -### Configuring CORS w/ Dynamic Origin - -This module supports validating the origin dynamically using a function provided -to the `origin` option. This function will be passed a string that is the origin -(or `undefined` if the request has no origin), and a `callback` with the signature -`callback(error, origin)`. - -The `origin` argument to the callback can be any value allowed for the `origin` -option of the middleware, except a function. See the -[configuration options](#configuration-options) section for more information on all -the possible value types. - -This function is designed to allow the dynamic loading of allowed origin(s) from -a backing datasource, like a database. - -```javascript -var express = require('express') -var cors = require('cors') -var app = express() - -var corsOptions = { - origin: function (origin, callback) { - // db.loadOrigins is an example call to load - // a list of origins from a backing database - db.loadOrigins(function (error, origins) { - callback(error, origins) - }) - } -} - -// Adds headers: Access-Control-Allow-Origin: , Vary: Origin -app.get('/products/:id', cors(corsOptions), function (req, res, next) { - res.json({msg: 'Hello'}) -}) - -app.listen(80, function () { - console.log('web server listening on port 80') -}) -``` - -### Enabling CORS Pre-Flight - -Certain CORS requests are considered 'complex' and require an initial -`OPTIONS` request (called the "pre-flight request"). An example of a -'complex' CORS request is one that uses an HTTP verb other than -GET/HEAD/POST (such as DELETE) or that uses custom headers. To enable -pre-flighting, you must add a new OPTIONS handler for the route you want -to support: - -```javascript -var express = require('express') -var cors = require('cors') -var app = express() - -app.options('/products/:id', cors()) // preflight for DELETE -app.del('/products/:id', cors(), function (req, res, next) { - res.json({msg: 'Hello'}) -}) - -app.listen(80, function () { - console.log('web server listening on port 80') -}) -``` - -You can also enable pre-flight across-the-board like so: - -```javascript -app.options('*', cors()) // include before other routes -``` - -NOTE: When using this middleware as an application level middleware (for -example, `app.use(cors())`), pre-flight requests are already handled for all -routes. - -### Customizing CORS Settings Dynamically per Request - -For APIs that require different CORS configurations for specific routes or requests, you can dynamically generate CORS options based on the incoming request. The `cors` middleware allows you to achieve this by passing a function instead of static options. This function is called for each incoming request and must use the callback pattern to return the appropriate CORS options. - -The function accepts: -1. **`req`**: - - The incoming request object. - -2. **`callback(error, corsOptions)`**: - - A function used to return the computed CORS options. - - **Arguments**: - - **`error`**: Pass `null` if there’s no error, or an error object to indicate a failure. - - **`corsOptions`**: An object specifying the CORS policy for the current request. - -Here’s an example that handles both public routes and restricted, credential-sensitive routes: - -```javascript -var dynamicCorsOptions = function(req, callback) { - var corsOptions; - if (req.path.startsWith('/auth/connect/')) { - // Access-Control-Allow-Origin: http://mydomain.com, Access-Control-Allow-Credentials: true, Vary: Origin - corsOptions = { - origin: 'http://mydomain.com', - credentials: true - }; - } else { - // Access-Control-Allow-Origin: * - corsOptions = { origin: '*' }; - } - callback(null, corsOptions); -}; - -app.use(cors(dynamicCorsOptions)); - -app.get('/auth/connect/twitter', function (req, res) { - res.send('Hello'); -}); - -app.get('/public', function (req, res) { - res.send('Hello'); -}); - -app.listen(80, function () { - console.log('web server listening on port 80') -}) -``` - -## Configuration Options - -* `origin`: Configures the **Access-Control-Allow-Origin** CORS header. Possible values: - - `Boolean` - set `origin` to `true` to reflect the [request origin](https://datatracker.ietf.org/doc/html/draft-abarth-origin-09), as defined by `req.header('Origin')`, or set it to `false` to disable CORS. - - `String` - set `origin` to a specific origin. For example, if you set it to - - `"http://example.com"` only requests from "http://example.com" will be allowed. - - `"*"` for all domains to be allowed. - - `RegExp` - set `origin` to a regular expression pattern which will be used to test the request origin. If it's a match, the request origin will be reflected. For example the pattern `/example\.com$/` will reflect any request that is coming from an origin ending with "example.com". - - `Array` - set `origin` to an array of valid origins. Each origin can be a `String` or a `RegExp`. For example `["http://example1.com", /\.example2\.com$/]` will accept any request from "http://example1.com" or from a subdomain of "example2.com". - - `Function` - set `origin` to a function implementing some custom logic. The function takes the request origin as the first parameter and a callback (called as `callback(err, origin)`, where `origin` is a non-function value of the `origin` option) as the second. -* `methods`: Configures the **Access-Control-Allow-Methods** CORS header. Expects a comma-delimited string (ex: 'GET,PUT,POST') or an array (ex: `['GET', 'PUT', 'POST']`). -* `allowedHeaders`: Configures the **Access-Control-Allow-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Type,Authorization') or an array (ex: `['Content-Type', 'Authorization']`). If not specified, defaults to reflecting the headers specified in the request's **Access-Control-Request-Headers** header. -* `exposedHeaders`: Configures the **Access-Control-Expose-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Range,X-Content-Range') or an array (ex: `['Content-Range', 'X-Content-Range']`). If not specified, no custom headers are exposed. -* `credentials`: Configures the **Access-Control-Allow-Credentials** CORS header. Set to `true` to pass the header, otherwise it is omitted. -* `maxAge`: Configures the **Access-Control-Max-Age** CORS header. Set to an integer to pass the header, otherwise it is omitted. -* `preflightContinue`: Pass the CORS preflight response to the next handler. -* `optionsSuccessStatus`: Provides a status code to use for successful `OPTIONS` requests, since some legacy browsers (IE11, various SmartTVs) choke on `204`. - -The default configuration is the equivalent of: - -```json -{ - "origin": "*", - "methods": "GET,HEAD,PUT,PATCH,POST,DELETE", - "preflightContinue": false, - "optionsSuccessStatus": 204 -} -``` - -## Common Misconceptions - -### "CORS blocks requests from disallowed origins" - -**No.** Your server receives and processes every request. CORS headers tell the browser whether JavaScript can read the response—not whether the request is allowed. - -### "CORS protects my API from unauthorized access" - -**No.** CORS is not access control. Any HTTP client (curl, Postman, another server) can call your API regardless of CORS settings. Use authentication and authorization to protect your API. - -### "Setting `origin: 'http://example.com'` means only that domain can access my server" - -**No.** It means browsers will only let JavaScript from that origin read responses. The server still responds to all requests. - -## License - -[MIT License](http://www.opensource.org/licenses/mit-license.php) - -## Original Author - -[Troy Goode](https://github.com/TroyGoode) ([troygoode@gmail.com](mailto:troygoode@gmail.com)) - -[coveralls-image]: https://img.shields.io/coveralls/expressjs/cors/master.svg -[coveralls-url]: https://coveralls.io/r/expressjs/cors?branch=master -[downloads-image]: https://img.shields.io/npm/dm/cors.svg -[downloads-url]: https://npmjs.com/package/cors -[github-actions-ci-image]: https://img.shields.io/github/actions/workflow/status/expressjs/cors/ci.yml?branch=master&label=ci -[github-actions-ci-url]: https://github.com/expressjs/cors?query=workflow%3Aci -[npm-image]: https://img.shields.io/npm/v/cors.svg -[npm-url]: https://npmjs.com/package/cors diff --git a/_includes/readmes/errorhandler.md b/_includes/readmes/errorhandler.md deleted file mode 100644 index 31942ee9eb..0000000000 --- a/_includes/readmes/errorhandler.md +++ /dev/null @@ -1,128 +0,0 @@ -# errorhandler - -[![NPM Version][npm-version-image]][npm-url] -[![NPM Downloads][npm-downloads-image]][npm-url] - -[![Test Coverage][coveralls-image]][coveralls-url] - -Development-only error handler middleware. - -This middleware is only intended to be used in a development environment, as -the _full error stack traces and internal details of any object passed to this -module_ will be sent back to the client when an error occurs. - -When an object is provided to Express as an error, this module will display -as much about this object as possible, and will do so by using content negotiation -for the response between HTML, JSON, and plain text. - - * When the object is a standard `Error` object, the string provided by the - `stack` property will be returned in HTML/text responses. - * When the object is a non-`Error` object, the result of - [util.inspect](https://nodejs.org/api/util.html#util_util_inspect_object_options) - will be returned in HTML/text responses. - * For JSON responses, the result will be an object with all enumerable properties - from the object in the response. - -## Install - -This is a [Node.js](https://nodejs.org/en/) module available through the -[npm registry](https://www.npmjs.com/). Installation is done using the -[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): - -```sh -$ npm install errorhandler -``` - -## API - - - -```js -var errorhandler = require('errorhandler') -``` - -### errorhandler(options) - -Create new middleware to handle errors and respond with content negotiation. - -#### Options - -Error handler accepts these properties in the options object. - -##### log - -Provide a function to be called with the error and a string representation of -the error. Can be used to write the error to any desired location, or set to -`false` to only send the error back in the response. Called as -`log(err, str, req, res)` where `err` is the `Error` object, `str` is a string -representation of the error, `req` is the request object and `res` is the -response object (note, this function is invoked _after_ the response has been -written). - -The default value for this option is `true` unless `process.env.NODE_ENV === 'test'`. - -Possible values: - - * `true`: Log errors using `console.error(str)`. - * `false`: Only send the error back in the response. - * A function: pass the error to a function for handling. - -## Examples - -### Simple example - -Basic example of adding this middleware as the error handler only in development -with `connect` (`express` also can be used in this example). - -```js -var connect = require('connect') -var errorhandler = require('errorhandler') - -var app = connect() - -// assumes NODE_ENV is set by the user -if (process.env.NODE_ENV === 'development') { - // only use in development - app.use(errorhandler()) -} -``` - -### Custom output location - -Sometimes you may want to output the errors to a different location than STDERR -during development, like a system notification, for example. - - - -```js -var connect = require('connect') -var errorhandler = require('errorhandler') -var notifier = require('node-notifier') - -var app = connect() - -// assumes NODE_ENV is set by the user -if (process.env.NODE_ENV === 'development') { - // only use in development - app.use(errorhandler({ log: errorNotification })) -} - -function errorNotification (err, str, req) { - var title = 'Error in ' + req.method + ' ' + req.url - - notifier.notify({ - title: title, - message: str - }) -} -``` - -## License - -[MIT](LICENSE) - -[coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/errorhandler/master -[coveralls-url]: https://coveralls.io/r/expressjs/errorhandler?branch=master -[npm-downloads-image]: https://badgen.net/npm/dm/errorhandler -[npm-url]: https://npmjs.org/package/errorhandler -[npm-version-image]: https://badgen.net/npm/v/errorhandler diff --git a/_includes/readmes/express-master/examples.md b/_includes/readmes/express-master/examples.md deleted file mode 100644 index bd1f1f6310..0000000000 --- a/_includes/readmes/express-master/examples.md +++ /dev/null @@ -1,29 +0,0 @@ -# Express examples - -This page contains list of examples using Express. - -- [auth](./auth) - Authentication with login and password -- [content-negotiation](./content-negotiation) - HTTP content negotiation -- [cookie-sessions](./cookie-sessions) - Working with cookie-based sessions -- [cookies](./cookies) - Working with cookies -- [downloads](./downloads) - Transferring files to client -- [ejs](./ejs) - Working with Embedded JavaScript templating (ejs) -- [error-pages](./error-pages) - Creating error pages -- [error](./error) - Working with error middleware -- [hello-world](./hello-world) - Simple request handler -- [markdown](./markdown) - Markdown as template engine -- [multi-router](./multi-router) - Working with multiple Express routers -- [mvc](./mvc) - MVC-style controllers -- [online](./online) - Tracking online user activity with `online` and `redis` packages -- [params](./params) - Working with route parameters -- [resource](./resource) - Multiple HTTP operations on the same resource -- [route-map](./route-map) - Organizing routes using a map -- [route-middleware](./route-middleware) - Working with route middleware -- [route-separation](./route-separation) - Organizing routes per each resource -- [search](./search) - Search API -- [session](./session) - User sessions -- [static-files](./static-files) - Serving static files -- [vhost](./vhost) - Working with virtual hosts -- [view-constructor](./view-constructor) - Rendering views dynamically -- [view-locals](./view-locals) - Saving data in request object between middleware calls -- [web-service](./web-service) - Simple API service diff --git a/_includes/readmes/method-override.md b/_includes/readmes/method-override.md deleted file mode 100644 index ce96a96c9e..0000000000 --- a/_includes/readmes/method-override.md +++ /dev/null @@ -1,180 +0,0 @@ -# method-override - -[![NPM Version][npm-image]][npm-url] -[![NPM Downloads][downloads-image]][downloads-url] -[![Build Status][travis-image]][travis-url] -[![Test Coverage][coveralls-image]][coveralls-url] - -Lets you use HTTP verbs such as PUT or DELETE in places where the client doesn't support it. - -## Install - -This is a [Node.js](https://nodejs.org/en/) module available through the -[npm registry](https://www.npmjs.com/). Installation is done using the -[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): - -```sh -$ npm install method-override -``` - -## API - -**NOTE** It is very important that this module is used **before** any module that -needs to know the method of the request (for example, it _must_ be used prior to -the `csurf` module). - -### methodOverride(getter, options) - -Create a new middleware function to override the `req.method` property with a new -value. This value will be pulled from the provided `getter`. - -- `getter` - The getter to use to look up the overridden request method for the request. (default: `X-HTTP-Method-Override`) -- `options.methods` - The allowed methods the original request must be in to check for a method override value. (default: `['POST']`) - -If the found method is supported by node.js core, then `req.method` will be set to -this value, as if it has originally been that value. The previous `req.method` -value will be stored in `req.originalMethod`. - -#### getter - -This is the method of getting the override value from the request. If a function is provided, -the `req` is passed as the first argument, the `res` as the second argument and the method is -expected to be returned. If a string is provided, the string is used to look up the method -with the following rules: - -- If the string starts with `X-`, then it is treated as the name of a header and that header - is used for the method override. If the request contains the same header multiple times, the - first occurrence is used. -- All other strings are treated as a key in the URL query string. - -#### options.methods - -This allows the specification of what methods(s) the request *MUST* be in in order to check for -the method override value. This defaults to only `POST` methods, which is the only method the -override should arrive in. More methods may be specified here, but it may introduce security -issues and cause weird behavior when requests travel through caches. This value is an array -of methods in upper-case. `null` can be specified to allow all methods. - -## Examples - -### override using a header - -To use a header to override the method, specify the header name -as a string argument to the `methodOverride` function. To then make -the call, send a `POST` request to a URL with the overridden method -as the value of that header. This method of using a header would -typically be used in conjunction with `XMLHttpRequest` on implementations -that do not support the method you are trying to use. - -```js -const express = require('express') -const methodOverride = require('method-override') -const app = express() - -// override with the X-HTTP-Method-Override header in the request -app.use(methodOverride('X-HTTP-Method-Override')) -``` - -Example call with header override using `XMLHttpRequest`: - - - -```js -const xhr = new XMLHttpRequest() -xhr.onload = onload -xhr.open('post', '/resource', true) -xhr.setRequestHeader('X-HTTP-Method-Override', 'DELETE') -xhr.send() - -function onload () { - alert('got response: ' + this.responseText) -} -``` - -### override using a query value - -To use a query string value to override the method, specify the query -string key as a string argument to the `methodOverride` function. To -then make the call, send a `POST` request to a URL with the overridden -method as the value of that query string key. This method of using a -query value would typically be used in conjunction with plain HTML -`
` elements when trying to support legacy browsers but still use -newer methods. - -```js -const express = require('express') -const methodOverride = require('method-override') -const app = express() - -// override with POST having ?_method=DELETE -app.use(methodOverride('_method')) -``` - -Example call with query override using HTML ``: - -```html - - -
-``` - -### multiple format support - -```js -const express = require('express') -const methodOverride = require('method-override') -const app = express() - -// override with different headers; last one takes precedence -app.use(methodOverride('X-HTTP-Method')) // Microsoft -app.use(methodOverride('X-HTTP-Method-Override')) // Google/GData -app.use(methodOverride('X-Method-Override')) // IBM -``` - -### custom logic - -You can implement any kind of custom logic with a function for the `getter`. The following -implements the logic for looking in `req.body` that was in `method-override@1`: - -```js -const bodyParser = require('body-parser') -const express = require('express') -const methodOverride = require('method-override') -const app = express() - -// NOTE: when using req.body, you must fully parse the request body -// before you call methodOverride() in your middleware stack, -// otherwise req.body will not be populated. -app.use(bodyParser.urlencoded()) -app.use(methodOverride(function (req, res) { - if (req.body && typeof req.body === 'object' && '_method' in req.body) { - // look in urlencoded POST bodies and delete it - const method = req.body._method - delete req.body._method - return method - } -})) -``` - -Example call with query override using HTML `
`: - -```html - - - - -
-``` - -## License - -[MIT](LICENSE) - -[npm-image]: https://img.shields.io/npm/v/method-override.svg -[npm-url]: https://npmjs.org/package/method-override -[travis-image]: https://img.shields.io/travis/expressjs/method-override/master.svg -[travis-url]: https://travis-ci.org/expressjs/method-override -[coveralls-image]: https://img.shields.io/coveralls/expressjs/method-override/master.svg -[coveralls-url]: https://coveralls.io/r/expressjs/method-override?branch=master -[downloads-image]: https://img.shields.io/npm/dm/method-override.svg -[downloads-url]: https://npmjs.org/package/method-override diff --git a/_includes/readmes/morgan.md b/_includes/readmes/morgan.md deleted file mode 100644 index e384080406..0000000000 --- a/_includes/readmes/morgan.md +++ /dev/null @@ -1,440 +0,0 @@ -# morgan - -[![NPM Version][npm-version-image]][npm-url] -[![NPM Downloads][npm-downloads-image]][npm-url] -[![Build Status][ci-image]][ci-url] -[![Coverage Status][coveralls-image]][coveralls-url] - -HTTP request logger middleware for node.js - -> Named after [Dexter](http://en.wikipedia.org/wiki/Dexter_Morgan), a show you should not watch until completion. - -## Installation - -This is a [Node.js](https://nodejs.org/en/) module available through the -[npm registry](https://www.npmjs.com/). Installation is done using the -[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): - -```sh -$ npm install morgan -``` - -## API - - - -```js -var morgan = require('morgan') -``` - -### morgan(format, options) - -Create a new morgan logger middleware function using the given `format` and `options`. -The `format` argument may be a string of a predefined name (see below for the names), -a string of a format string, or a function that will produce a log entry. - -The `format` function will be called with three arguments `tokens`, `req`, and `res`, -where `tokens` is an object with all defined tokens, `req` is the HTTP request and `res` -is the HTTP response. The function is expected to return a string that will be the log -line, or `undefined` / `null` to skip logging. - -#### Using a predefined format string - - - -```js -morgan('tiny') -``` - -#### Using format string of predefined tokens - - - -```js -morgan(':method :url :status :res[content-length] - :response-time ms') -``` - -#### Using a custom format function - - - -``` js -morgan(function (tokens, req, res) { - return [ - tokens.method(req, res), - tokens.url(req, res), - tokens.status(req, res), - tokens.res(req, res, 'content-length'), '-', - tokens['response-time'](req, res), 'ms' - ].join(' ') -}) -``` - -#### Options - -Morgan accepts these properties in the options object. - -##### immediate - -Write log line on request instead of response. This means that a requests will -be logged even if the server crashes, _but data from the response (like the -response code, content length, etc.) cannot be logged_. - -##### skip - -Function to determine if logging is skipped, defaults to `false`. This function -will be called as `skip(req, res)`. - - - -```js -// EXAMPLE: only log error responses -morgan('combined', { - skip: function (req, res) { return res.statusCode < 400 } -}) -``` - -##### stream - -Output stream for writing log lines, defaults to `process.stdout`. - -#### Predefined Formats - -There are various pre-defined formats provided: - -##### combined - -Standard Apache combined log output. -``` -:remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent" -# will output -::1 - - [27/Nov/2024:06:21:42 +0000] "GET /combined HTTP/1.1" 200 2 "-" "curl/8.7.1" -``` - -##### common - -Standard Apache common log output. - -``` -:remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] -# will output -::1 - - [27/Nov/2024:06:21:46 +0000] "GET /common HTTP/1.1" 200 2 -``` - -##### dev - -Concise output colored by response status for development use. The `:status` -token will be colored green for success codes, red for server error codes, -yellow for client error codes, cyan for redirection codes, and uncolored -for information codes. - -``` -:method :url :status :response-time ms - :res[content-length] -# will output -GET /dev 200 0.224 ms - 2 -``` - -##### short - -Shorter than default, also including response time. - -``` -:remote-addr :remote-user :method :url HTTP/:http-version :status :res[content-length] - :response-time ms -# will output -::1 - GET /short HTTP/1.1 200 2 - 0.283 ms -``` - -##### tiny - -The minimal output. - -``` -:method :url :status :res[content-length] - :response-time ms -# will output -GET /tiny 200 2 - 0.188 ms -``` - -#### Tokens - -##### Creating new tokens - -To define a token, simply invoke `morgan.token()` with the name and a callback function. -This callback function is expected to return a string value. The value returned is then -available as ":type" in this case: - - - -```js -morgan.token('type', function (req, res) { return req.headers['content-type'] }) -``` - -Calling `morgan.token()` using the same name as an existing token will overwrite that -token definition. - -The token function is expected to be called with the arguments `req` and `res`, representing -the HTTP request and HTTP response. Additionally, the token can accept further arguments of -it's choosing to customize behavior. - -##### :date[format] - -The current date and time in UTC. The available formats are: - - - `clf` for the common log format (`"10/Oct/2000:13:55:36 +0000"`) - - `iso` for the common ISO 8601 date time format (`2000-10-10T13:55:36.000Z`) - - `web` for the common RFC 1123 date time format (`Tue, 10 Oct 2000 13:55:36 GMT`) - -If no format is given, then the default is `web`. - -##### :http-version - -The HTTP version of the request. - -##### :method - -The HTTP method of the request. - -##### :pid - -The process ID of the Node.js process handling the request. - -##### :referrer - -The Referrer header of the request. This will use the standard mis-spelled Referer header if exists, otherwise Referrer. - -##### :remote-addr - -The remote address of the request. This will use `req.ip`, otherwise the standard `req.connection.remoteAddress` value (socket address). - -##### :remote-user - -The user authenticated as part of Basic auth for the request. - -##### :req[header] - -The given `header` of the request. If the header is not present, the -value will be displayed as `"-"` in the log. - -##### :res[header] - -The given `header` of the response. If the header is not present, the -value will be displayed as `"-"` in the log. - -##### :response-time[digits] - -The time between the request coming into `morgan` and when the response -headers are written, in milliseconds. - -The `digits` argument is a number that specifies the number of digits to -include on the number, defaulting to `3`, which provides microsecond precision. - -##### :status - -The status code of the response. - -If the request/response cycle completes before a response was sent to the -client (for example, the TCP socket closed prematurely by a client aborting -the request), then the status will be empty (displayed as `"-"` in the log). - -##### :total-time[digits] - -The time between the request coming into `morgan` and when the response -has finished being written out to the connection, in milliseconds. - -The `digits` argument is a number that specifies the number of digits to -include on the number, defaulting to `3`, which provides microsecond precision. - -##### :url - -The URL of the request. This will use `req.originalUrl` if exists, otherwise `req.url`. - -##### :user-agent - -The contents of the User-Agent header of the request. - -### morgan.compile(format) - -Compile a format string into a `format` function for use by `morgan`. A format string -is a string that represents a single log line and can utilize token syntax. -Tokens are references by `:token-name`. If tokens accept arguments, they can -be passed using `[]`, for example: `:token-name[pretty]` would pass the string -`'pretty'` as an argument to the token `token-name`. - -The function returned from `morgan.compile` takes three arguments `tokens`, `req`, and -`res`, where `tokens` is object with all defined tokens, `req` is the HTTP request and -`res` is the HTTP response. The function will return a string that will be the log line, -or `undefined` / `null` to skip logging. - -Normally formats are defined using `morgan.format(name, format)`, but for certain -advanced uses, this compile function is directly available. - -## Examples - -### express/connect - -Sample app that will log all request in the Apache combined format to STDOUT - -```js -var express = require('express') -var morgan = require('morgan') - -var app = express() - -app.use(morgan('combined')) - -app.get('/', function (req, res) { - res.send('hello, world!') -}) -``` - -### vanilla http server - -Sample app that will log all request in the Apache combined format to STDOUT - -```js -var finalhandler = require('finalhandler') -var http = require('http') -var morgan = require('morgan') - -// create "middleware" -var logger = morgan('combined') - -http.createServer(function (req, res) { - var done = finalhandler(req, res) - logger(req, res, function (err) { - if (err) return done(err) - - // respond to request - res.setHeader('content-type', 'text/plain') - res.end('hello, world!') - }) -}) -``` - -### write logs to a file - -#### single file - -Sample app that will log all requests in the Apache combined format to the file -`access.log`. - -```js -var express = require('express') -var fs = require('fs') -var morgan = require('morgan') -var path = require('path') - -var app = express() - -// create a write stream (in append mode) -var accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a' }) - -// setup the logger -app.use(morgan('combined', { stream: accessLogStream })) - -app.get('/', function (req, res) { - res.send('hello, world!') -}) -``` - -#### log file rotation - -Sample app that will log all requests in the Apache combined format to one log -file per day in the `log/` directory using the -[rotating-file-stream module](https://www.npmjs.com/package/rotating-file-stream). - -```js -var express = require('express') -var morgan = require('morgan') -var path = require('path') -var rfs = require('rotating-file-stream') // version 2.x - -var app = express() - -// create a rotating write stream -var accessLogStream = rfs.createStream('access.log', { - interval: '1d', // rotate daily - path: path.join(__dirname, 'log') -}) - -// setup the logger -app.use(morgan('combined', { stream: accessLogStream })) - -app.get('/', function (req, res) { - res.send('hello, world!') -}) -``` - -### split / dual logging - -The `morgan` middleware can be used as many times as needed, enabling -combinations like: - - * Log entry on request and one on response - * Log all requests to file, but errors to console - * ... and more! - -Sample app that will log all requests to a file using Apache format, but -error responses are logged to the console: - -```js -var express = require('express') -var fs = require('fs') -var morgan = require('morgan') -var path = require('path') - -var app = express() - -// log only 4xx and 5xx responses to console -app.use(morgan('dev', { - skip: function (req, res) { return res.statusCode < 400 } -})) - -// log all requests to access.log -app.use(morgan('common', { - stream: fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a' }) -})) - -app.get('/', function (req, res) { - res.send('hello, world!') -}) -``` - -### use custom token formats - -Sample app that will use custom token formats. This adds an ID to all requests and displays it using the `:id` token. - -```js -var express = require('express') -var morgan = require('morgan') -var uuid = require('node-uuid') - -morgan.token('id', function getId (req) { - return req.id -}) - -var app = express() - -app.use(assignId) -app.use(morgan(':id :method :url :response-time')) - -app.get('/', function (req, res) { - res.send('hello, world!') -}) - -function assignId (req, res, next) { - req.id = uuid.v4() - next() -} -``` - -## License - -[MIT](LICENSE) - -[ci-image]: https://badgen.net/github/checks/expressjs/morgan/master?label=ci -[ci-url]: https://github.com/expressjs/morgan/actions/workflows/ci.yml -[coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/morgan/master -[coveralls-url]: https://coveralls.io/r/expressjs/morgan?branch=master -[npm-downloads-image]: https://badgen.net/npm/dm/morgan -[npm-url]: https://npmjs.org/package/morgan -[npm-version-image]: https://badgen.net/npm/v/morgan diff --git a/_includes/readmes/multer.md b/_includes/readmes/multer.md deleted file mode 100644 index 630a495a1b..0000000000 --- a/_includes/readmes/multer.md +++ /dev/null @@ -1,351 +0,0 @@ -# Multer [![NPM Version][npm-version-image]][npm-url] [![NPM Downloads][npm-downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Test Coverage][test-image]][test-url] [![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer] - -Multer is a node.js middleware for handling `multipart/form-data`, which is primarily used for uploading files. It is written -on top of [busboy](https://github.com/mscdex/busboy) for maximum efficiency. - -**NOTE**: Multer will not process any form which is not multipart (`multipart/form-data`). - -## Translations - -This README is also available in other languages: - -| | | -| ------------------------------------------------------------------------------ | --------------- | -| [العربية](https://github.com/expressjs/multer/blob/main/doc/README-ar.md) | Arabic | -| [简体中文](https://github.com/expressjs/multer/blob/main/doc/README-zh-cn.md) | Chinese | -| [Français](https://github.com/expressjs/multer/blob/main/doc/README-fr.md) | French | -| [한국어](https://github.com/expressjs/multer/blob/main/doc/README-ko.md) | Korean | -| [Português](https://github.com/expressjs/multer/blob/main/doc/README-pt-br.md) | Portuguese (BR) | -| [Русский язык](https://github.com/expressjs/multer/blob/main/doc/README-ru.md) | Russian | -| [Español](https://github.com/expressjs/multer/blob/main/doc/README-es.md) | Spanish | -| [O'zbek tili](https://github.com/expressjs/multer/blob/main/doc/README-uz.md) | Uzbek | -| [Việt Nam](https://github.com/expressjs/multer/blob/main/doc/README-vi.md) | Vietnamese | -| [Türkçe](https://github.com/expressjs/multer/blob/main/doc/README-tr.md) | Turkish | - - -## Installation - -```sh -$ npm install multer -``` - -## Usage - -Multer adds a `body` object and a `file` or `files` object to the `request` object. The `body` object contains the values of the text fields of the form, the `file` or `files` object contains the files uploaded via the form. - -Basic usage example: - -Don't forget the `enctype="multipart/form-data"` in your form. - -```html -
- -
-``` - -```javascript -const express = require('express') -const multer = require('multer') -const upload = multer({ dest: 'uploads/' }) - -const app = express() - -app.post('/profile', upload.single('avatar'), function (req, res, next) { - // req.file is the `avatar` file - // req.body will hold the text fields, if there were any -}) - -app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) { - // req.files is array of `photos` files - // req.body will contain the text fields, if there were any -}) - -const uploadMiddleware = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }]) -app.post('/cool-profile', uploadMiddleware, function (req, res, next) { - // req.files is an object (String -> Array) where fieldname is the key, and the value is array of files - // - // e.g. - // req.files['avatar'][0] -> File - // req.files['gallery'] -> Array - // - // req.body will contain the text fields, if there were any -}) -``` - -In case you need to handle a text-only multipart form, you should use the `.none()` method: - -```javascript -const express = require('express') -const app = express() -const multer = require('multer') -const upload = multer() - -app.post('/profile', upload.none(), function (req, res, next) { - // req.body contains the text fields -}) -``` - -Here's an example on how multer is used in a HTML form. Take special note of the `enctype="multipart/form-data"` and `name="uploaded_file"` fields: - -```html -
-
- - - -
-
-``` - -Then in your javascript file you would add these lines to access both the file and the body. It is important that you use the `name` field value from the form in your upload function. This tells multer which field on the request it should look for the files in. If these fields aren't the same in the HTML form and on your server, your upload will fail: - -```javascript -const multer = require('multer') -const upload = multer({ dest: './public/data/uploads/' }) -app.post('/stats', upload.single('uploaded_file'), function (req, res) { - // req.file is the name of your file in the form above, here 'uploaded_file' - // req.body will hold the text fields, if there were any - console.log(req.file, req.body) -}); -``` - - - -## API - -### File information - -Each file contains the following information: - -Key | Description | Note ---- | --- | --- -`fieldname` | Field name specified in the form | -`originalname` | Name of the file on the user's computer | -`encoding` | Encoding type of the file | -`mimetype` | Mime type of the file | -`size` | Size of the file in bytes | -`destination` | The folder to which the file has been saved | `DiskStorage` -`filename` | The name of the file within the `destination` | `DiskStorage` -`path` | The full path to the uploaded file | `DiskStorage` -`buffer` | A `Buffer` of the entire file | `MemoryStorage` - -### `multer(opts)` - -Multer accepts an options object, the most basic of which is the `dest` -property, which tells Multer where to upload the files. In case you omit the -options object, the files will be kept in memory and never written to disk. - -By default, Multer will rename the files so as to avoid naming conflicts. The -renaming function can be customized according to your needs. - -The following are the options that can be passed to Multer. - -Key | Description ---- | --- -`dest` or `storage` | Where to store the files -`fileFilter` | Function to control which files are accepted -`limits` | Limits of the uploaded data -`preservePath` | Keep the full path of files instead of just the base name -`defParamCharset` | Default character set to use for values of part header parameters (e.g. filename) that are not extended parameters (that contain an explicit charset). Default: `'latin1'` - -In an average web app, only `dest` might be required, and configured as shown in -the following example. - -```javascript -const upload = multer({ dest: 'uploads/' }) -``` - -If you want more control over your uploads, you'll want to use the `storage` -option instead of `dest`. Multer ships with storage engines `DiskStorage` -and `MemoryStorage`; More engines are available from third parties. - -#### `.single(fieldname)` - -Accept a single file with the name `fieldname`. The single file will be stored -in `req.file`. - -#### `.array(fieldname[, maxCount])` - -Accept an array of files, all with the name `fieldname`. Optionally error out if -more than `maxCount` files are uploaded. The array of files will be stored in -`req.files`. - -#### `.fields(fields)` - -Accept a mix of files, specified by `fields`. An object with arrays of files -will be stored in `req.files`. - -`fields` should be an array of objects with `name` and optionally a `maxCount`. -Example: - -```javascript -[ - { name: 'avatar', maxCount: 1 }, - { name: 'gallery', maxCount: 8 } -] -``` - -#### `.none()` - -Accept only text fields. If any file upload is made, error with code -"LIMIT\_UNEXPECTED\_FILE" will be issued. - -#### `.any()` - -Accepts all files that comes over the wire. An array of files will be stored in -`req.files`. - -**WARNING:** Make sure that you always handle the files that a user uploads. -Never add multer as a global middleware since a malicious user could upload -files to a route that you didn't anticipate. Only use this function on routes -where you are handling the uploaded files. - -### `storage` - -#### `DiskStorage` - -The disk storage engine gives you full control on storing files to disk. - -```javascript -const storage = multer.diskStorage({ - destination: function (req, file, cb) { - cb(null, '/tmp/my-uploads') - }, - filename: function (req, file, cb) { - const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9) - cb(null, file.fieldname + '-' + uniqueSuffix) - } -}) - -const upload = multer({ storage: storage }) -``` - -There are two options available, `destination` and `filename`. They are both -functions that determine where the file should be stored. - -`destination` is used to determine within which folder the uploaded files should -be stored. This can also be given as a `string` (e.g. `'/tmp/uploads'`). If no -`destination` is given, the operating system's default directory for temporary -files is used. - -**Note:** You are responsible for creating the directory when providing -`destination` as a function. When passing a string, multer will make sure that -the directory is created for you. - -`filename` is used to determine what the file should be named inside the folder. -If no `filename` is given, each file will be given a random name that doesn't -include any file extension. - -**Note:** Multer will not append any file extension for you, your function -should return a filename complete with a file extension. - -Each function gets passed both the request (`req`) and some information about -the file (`file`) to aid with the decision. - -Note that `req.body` might not have been fully populated yet. It depends on the -order that the client transmits fields and files to the server. - -For understanding the calling convention used in the callback (needing to pass -null as the first param), refer to -[Node.js error handling](https://web.archive.org/web/20220417042018/https://www.joyent.com/node-js/production/design/errors) - -#### `MemoryStorage` - -The memory storage engine stores the files in memory as `Buffer` objects. It -doesn't have any options. - -```javascript -const storage = multer.memoryStorage() -const upload = multer({ storage: storage }) -``` - -When using memory storage, the file info will contain a field called -`buffer` that contains the entire file. - -**WARNING**: Uploading very large files, or relatively small files in large -numbers very quickly, can cause your application to run out of memory when -memory storage is used. - -### `limits` - -An object specifying the size limits of the following optional properties. Multer passes this object into busboy directly, and the details of the properties can be found on [busboy's page](https://github.com/mscdex/busboy#busboy-methods). - -The following integer values are available: - -Key | Description | Default ---- | --- | --- -`fieldNameSize` | Max field name size | 100 bytes -`fieldSize` | Max field value size (in bytes) | 1MB -`fields` | Max number of non-file fields | Infinity -`fileSize` | For multipart forms, the max file size (in bytes) | Infinity -`files` | For multipart forms, the max number of file fields | Infinity -`parts` | For multipart forms, the max number of parts (fields + files) | Infinity -`headerPairs` | For multipart forms, the max number of header key=>value pairs to parse | 2000 - -Specifying the limits can help protect your site against denial of service (DoS) attacks. - -### `fileFilter` - -Set this to a function to control which files should be uploaded and which -should be skipped. The function should look like this: - -```javascript -function fileFilter (req, file, cb) { - - // The function should call `cb` with a boolean - // to indicate if the file should be accepted - - // To reject this file pass `false`, like so: - cb(null, false) - - // To accept the file pass `true`, like so: - cb(null, true) - - // You can always pass an error if something goes wrong: - cb(new Error('I don\'t have a clue!')) - -} -``` - -## Error handling - -When encountering an error, Multer will delegate the error to Express. You can -display a nice error page using [the standard express way](http://expressjs.com/guide/error-handling.html). - -If you want to catch errors specifically from Multer, you can call the -middleware function by yourself. Also, if you want to catch only [the Multer errors](https://github.com/expressjs/multer/blob/main/lib/multer-error.js), you can use the `MulterError` class that is attached to the `multer` object itself (e.g. `err instanceof multer.MulterError`). - -```javascript -const multer = require('multer') -const upload = multer().single('avatar') - -app.post('/profile', function (req, res) { - upload(req, res, function (err) { - if (err instanceof multer.MulterError) { - // A Multer error occurred when uploading. - } else if (err) { - // An unknown error occurred when uploading. - } - - // Everything went fine. - }) -}) -``` - -## Custom storage engine - -For information on how to build your own storage engine, see [Multer Storage Engine](https://github.com/expressjs/multer/blob/main/StorageEngine.md). - -## License - -[MIT](LICENSE) - -[ci-image]: https://github.com/expressjs/multer/actions/workflows/ci.yml/badge.svg -[ci-url]: https://github.com/expressjs/multer/actions/workflows/ci.yml -[test-url]: https://coveralls.io/r/expressjs/multer?branch=main -[test-image]: https://badgen.net/coveralls/c/github/expressjs/multer/main -[npm-downloads-image]: https://badgen.net/npm/dm/multer -[npm-url]: https://npmjs.org/package/multer -[npm-version-image]: https://badgen.net/npm/v/multer -[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/multer/badge -[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/multer diff --git a/_includes/readmes/response-time.md b/_includes/readmes/response-time.md deleted file mode 100644 index 4e26aee0fe..0000000000 --- a/_includes/readmes/response-time.md +++ /dev/null @@ -1,142 +0,0 @@ -# response-time - -[![NPM Version][npm-version-image]][npm-url] -[![NPM Downloads][npm-downloads-image]][npm-url] -[![Node.js Version][node-image]][node-url] -[![Build Status][ci-image]][ci-url] -[![Test Coverage][coveralls-image]][coveralls-url] - -Response time for Node.js servers. - -This module creates a middleware that records the response time for -requests in HTTP servers. The "response time" is defined here as the -elapsed time from when a request enters this middleware to when the -headers are written out to the client. - -## Installation - -This is a [Node.js](https://nodejs.org/en/) module available through the -[npm registry](https://www.npmjs.com/). Installation is done using the -[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): - -```sh -$ npm install response-time -``` - -## API - - - -```js -var responseTime = require('response-time') -``` - -### responseTime([options]) - -Create a middleware that adds a `X-Response-Time` header to responses. If -you don't want to use this module to automatically set a header, please -see the section about [`responseTime(fn)`](#responsetimefn). - -#### Options - -The `responseTime` function accepts an optional `options` object that may -contain any of the following keys: - -##### digits - -The fixed number of digits to include in the output, which is always in -milliseconds, defaults to `3` (ex: `2.300ms`). - -##### header - -The name of the header to set, defaults to `X-Response-Time`. - -##### suffix - -Boolean to indicate if units of measurement suffix should be added to -the output, defaults to `true` (ex: `2.300ms` vs `2.300`). - -### responseTime(fn) - -Create a new middleware that records the response time of a request and -makes this available to your own function `fn`. The `fn` argument will be -invoked as `fn(req, res, time)`, where `time` is a number in milliseconds. - -## Examples - -### express/connect - -```js -var express = require('express') -var responseTime = require('response-time') - -var app = express() - -app.use(responseTime()) - -app.get('/', function (req, res) { - res.send('hello, world!') -}) -``` - -### vanilla http server - -```js -var finalhandler = require('finalhandler') -var http = require('http') -var responseTime = require('response-time') - -// create "middleware" -var _responseTime = responseTime() - -http.createServer(function (req, res) { - var done = finalhandler(req, res) - _responseTime(req, res, function (err) { - if (err) return done(err) - - // respond to request - res.setHeader('content-type', 'text/plain') - res.end('hello, world!') - }) -}) -``` - -### response time metrics - -```js -var express = require('express') -var responseTime = require('response-time') -var StatsD = require('node-statsd') - -var app = express() -var stats = new StatsD() - -stats.socket.on('error', function (error) { - console.error(error.stack) -}) - -app.use(responseTime(function (req, res, time) { - var stat = (req.method + req.url).toLowerCase() - .replace(/[:.]/g, '') - .replace(/\//g, '_') - stats.timing(stat, time) -})) - -app.get('/', function (req, res) { - res.send('hello, world!') -}) -``` - -## License - -[MIT](LICENSE) - -[npm-version-image]: https://badgen.net/npm/v/response-time -[npm-url]: https://npmjs.org/package/response-time -[npm-downloads-image]: https://badgen.net/npm/dm/response-time -[node-image]: https://badgen.net/npm/node/response-time -[node-url]: https://nodejs.org/en/download -[ci-image]: https://badgen.net/github/checks/express/response-time/master?label=ci -[ci-url]: https://github.com/express/response-time/actions/workflows/ci.yml -[coveralls-image]: https://badgen.net/coveralls/c/github/express/response-time/master -[coveralls-url]: https://coveralls.io/r/express/response-time?branch=master diff --git a/_includes/readmes/serve-favicon.md b/_includes/readmes/serve-favicon.md deleted file mode 100644 index a7f94b4520..0000000000 --- a/_includes/readmes/serve-favicon.md +++ /dev/null @@ -1,136 +0,0 @@ -# serve-favicon - -[![NPM Version][npm-image]][npm-url] -[![NPM Downloads][downloads-image]][downloads-url] -[![Linux Build Status][ci-image]][ci-url] -[![Coverage Status][coveralls-image]][coveralls-url] -[![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer] - -Node.js middleware for serving a favicon. - -A favicon is a visual cue that client software, like browsers, use to identify -a site. For an example and more information, please visit -[the Wikipedia article on favicons](https://en.wikipedia.org/wiki/Favicon). - -Why use this module? - - - User agents request `favicon.ico` frequently and indiscriminately, so you - may wish to exclude these requests from your logs by using this middleware - before your logger middleware. - - This module caches the icon in memory to improve performance by skipping - disk access. - - This module provides an `ETag` based on the contents of the icon, rather - than file system properties. - - This module will serve with the most compatible `Content-Type`. - -**Note** This module is exclusively for serving the "default, implicit favicon", -which is `GET /favicon.ico`. For additional vendor-specific icons that require -HTML markup, additional middleware is required to serve the relevant files, for -example [serve-static](https://npmjs.org/package/serve-static). - -## Install - -This is a [Node.js](https://nodejs.org/en/) module available through the -[npm registry](https://www.npmjs.com/). Installation is done using the -[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): - -```sh -$ npm install serve-favicon -``` - -## API - -### favicon(path, options) - -Create new middleware to serve a favicon from the given `path` to a favicon file. -`path` may also be a `Buffer` of the icon to serve. - -#### Options - -Serve favicon accepts these properties in the options object. - -##### maxAge - -The `cache-control` `max-age` directive in `ms`, defaulting to 1 year. This can -also be a string accepted by the [ms](https://www.npmjs.org/package/ms#readme) -module. - -## Examples - -Typically this middleware will come very early in your stack (maybe even first) -to avoid processing any other middleware if we already know the request is for -`/favicon.ico`. - -### express - -```javascript -var express = require('express') -var favicon = require('serve-favicon') -var path = require('path') - -var app = express() -app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))) - -// Add your routes here, etc. - -app.listen(3000) -``` - -### connect - -```javascript -var connect = require('connect') -var favicon = require('serve-favicon') -var path = require('path') - -var app = connect() -app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))) - -// Add your middleware here, etc. - -app.listen(3000) -``` - -### vanilla http server - -This middleware can be used anywhere, even outside express/connect. It takes -`req`, `res`, and `callback`. - -```javascript -var http = require('http') -var favicon = require('serve-favicon') -var finalhandler = require('finalhandler') -var path = require('path') - -var _favicon = favicon(path.join(__dirname, 'public', 'favicon.ico')) - -var server = http.createServer(function onRequest (req, res) { - var done = finalhandler(req, res) - - _favicon(req, res, function onNext (err) { - if (err) return done(err) - - // continue to process the request here, etc. - - res.statusCode = 404 - res.end('oops') - }) -}) - -server.listen(3000) -``` - -## License - -[MIT](LICENSE) - -[ci-image]: https://badgen.net/github/checks/expressjs/serve-favicon/master?label=ci -[ci-url]: https://github.com/expressjs/serve-favicon/actions/workflows/ci.yml -[coveralls-image]: https://img.shields.io/coveralls/expressjs/serve-favicon.svg -[coveralls-url]: https://coveralls.io/r/expressjs/serve-favicon?branch=master -[downloads-image]: https://img.shields.io/npm/dm/serve-favicon.svg -[downloads-url]: https://npmjs.org/package/serve-favicon -[npm-image]: https://img.shields.io/npm/v/serve-favicon.svg -[npm-url]: https://npmjs.org/package/serve-favicon -[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/serve-favicon/badge -[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/serve-favicon diff --git a/_includes/readmes/serve-index.md b/_includes/readmes/serve-index.md deleted file mode 100644 index f720b0d950..0000000000 --- a/_includes/readmes/serve-index.md +++ /dev/null @@ -1,151 +0,0 @@ -# serve-index - -[![NPM Version][npm-image]][npm-url] -[![NPM Downloads][downloads-image]][downloads-url] -[![Linux Build Status][ci-image]][ci-url] -[![Windows Build][appveyor-image]][appveyor-url] -[![Coverage Status][coveralls-image]][coveralls-url] - - Serves pages that contain directory listings for a given path. - -## Install - -This is a [Node.js](https://nodejs.org/en/) module available through the -[npm registry](https://www.npmjs.com/). Installation is done using the -[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): - -```sh -$ npm install serve-index -``` - -## API - -```js -var serveIndex = require('serve-index') -``` - -### serveIndex(path, options) - -Returns middlware that serves an index of the directory in the given `path`. - -The `path` is based off the `req.url` value, so a `req.url` of `'/some/dir` -with a `path` of `'public'` will look at `'public/some/dir'`. If you are using -something like `express`, you can change the URL "base" with `app.use` (see -the express example). - -#### Options - -Serve index accepts these properties in the options object. - -##### filter - -Apply this filter function to files. Defaults to `false`. The `filter` function -is called for each file, with the signature `filter(filename, index, files, dir)` -where `filename` is the name of the file, `index` is the array index, `files` is -the array of files and `dir` is the absolute path the file is located (and thus, -the directory the listing is for). - -##### hidden - -Display hidden (dot) files. Defaults to `false`. - -##### icons - -Display icons. Defaults to `false`. - -##### stylesheet - -Optional path to a CSS stylesheet. Defaults to a built-in stylesheet. - -##### template - -Optional path to an HTML template or a function that will render a HTML -string. Defaults to a built-in template. - -When given a string, the string is used as a file path to load and then the -following tokens are replaced in templates: - - * `{directory}` with the name of the directory. - * `{files}` with the HTML of an unordered list of file links. - * `{linked-path}` with the HTML of a link to the directory. - * `{style}` with the specified stylesheet and embedded images. - -When given as a function, the function is called as `template(locals, callback)` -and it needs to invoke `callback(error, htmlString)`. The following are the -provided locals: - - * `directory` is the directory being displayed (where `/` is the root). - * `displayIcons` is a Boolean for if icons should be rendered or not. - * `fileList` is a sorted array of files in the directory. The array contains - objects with the following properties: - - `name` is the relative name for the file. - - `stat` is a `fs.Stats` object for the file. - * `path` is the full filesystem path to `directory`. - * `style` is the default stylesheet or the contents of the `stylesheet` option. - * `viewName` is the view name provided by the `view` option. - -##### view - -Display mode. `tiles` and `details` are available. Defaults to `tiles`. - -## Examples - -### Serve directory indexes with vanilla node.js http server - -```js -var finalhandler = require('finalhandler') -var http = require('http') -var serveIndex = require('serve-index') -var serveStatic = require('serve-static') - -// Serve directory indexes for public/ftp folder (with icons) -var index = serveIndex('public/ftp', {'icons': true}) - -// Serve up public/ftp folder files -var serve = serveStatic('public/ftp') - -// Create server -var server = http.createServer(function onRequest(req, res){ - var done = finalhandler(req, res) - serve(req, res, function onNext(err) { - if (err) return done(err) - index(req, res, done) - }) -}) - -// Listen -server.listen(3000) -``` - -### Serve directory indexes with express - -```js -var express = require('express') -var serveIndex = require('serve-index') - -var app = express() - -// Serve URLs like /ftp/thing as public/ftp/thing -// The express.static serves the file contents -// The serveIndex is this module serving the directory -app.use('/ftp', express.static('public/ftp'), serveIndex('public/ftp', {'icons': true})) - -// Listen -app.listen(3000) -``` - -## License - -[MIT](LICENSE). The [Silk](http://www.famfamfam.com/lab/icons/silk/) icons -are created by/copyright of [FAMFAMFAM](http://www.famfamfam.com/). - -[appveyor-image]: https://img.shields.io/appveyor/ci/dougwilson/serve-index/master.svg?label=windows -[appveyor-url]: https://ci.appveyor.com/project/dougwilson/serve-index -[ci-image]: https://badgen.net/github/checks/expressjs/serve-index/master?label=ci -[ci-url]: https://github.com/expressjs/serve-index/actions/workflows/ci.yml -[coveralls-image]: https://img.shields.io/coveralls/expressjs/serve-index/master.svg -[coveralls-url]: https://coveralls.io/r/expressjs/serve-index?branch=master -[downloads-image]: https://img.shields.io/npm/dm/serve-index.svg -[downloads-url]: https://npmjs.org/package/serve-index -[npm-image]: https://img.shields.io/npm/v/serve-index.svg -[npm-url]: https://npmjs.org/package/serve-index \ No newline at end of file diff --git a/_includes/readmes/serve-static.md b/_includes/readmes/serve-static.md deleted file mode 100644 index 3ff1f1fa39..0000000000 --- a/_includes/readmes/serve-static.md +++ /dev/null @@ -1,253 +0,0 @@ -# serve-static - -[![NPM Version][npm-version-image]][npm-url] -[![NPM Downloads][npm-downloads-image]][npm-url] -[![CI][github-actions-ci-image]][github-actions-ci-url] -[![Test Coverage][coveralls-image]][coveralls-url] - -## Install - -This is a [Node.js](https://nodejs.org/en/) module available through the -[npm registry](https://www.npmjs.com/). Installation is done using the -[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): - -```sh -$ npm install serve-static -``` - -## API - -```js -const serveStatic = require('serve-static') -``` - -### serveStatic(root, options) - -Create a new middleware function to serve files from within a given root -directory. The file to serve will be determined by combining `req.url` -with the provided root directory. When a file is not found, instead of -sending a 404 response, this module will instead call `next()` to move on -to the next middleware, allowing for stacking and fall-backs. - -#### Options - -##### acceptRanges - -Enable or disable accepting ranged requests, defaults to true. -Disabling this will not send `Accept-Ranges` and ignore the contents -of the `Range` request header. - -##### cacheControl - -Enable or disable setting `Cache-Control` response header, defaults to -true. Disabling this will ignore the `immutable` and `maxAge` options. - -##### dotfiles - -Set how "dotfiles" are treated when encountered. A dotfile is a file -or directory that begins with a dot ("."). Note this check is done on -the path itself without checking if the path actually exists on the -disk. If `root` is specified, only the dotfiles above the root are -checked (i.e. the root itself can be within a dotfile when set -to "deny"). - - - `'allow'` No special treatment for dotfiles. - - `'deny'` Deny a request for a dotfile and 403/`next()`. - - `'ignore'` Pretend like the dotfile does not exist and 404/`next()`. - -The default value is `'ignore'`. - -##### etag - -Enable or disable etag generation, defaults to true. - -##### extensions - -Set file extension fallbacks. When set, if a file is not found, the given -extensions will be added to the file name and search for. The first that -exists will be served. Example: `['html', 'htm']`. - -The default value is `false`. - -##### fallthrough - -Set the middleware to have client errors fall-through as just unhandled -requests, otherwise forward a client error. The difference is that client -errors like a bad request or a request to a non-existent file will cause -this middleware to simply `next()` to your next middleware when this value -is `true`. When this value is `false`, these errors (even 404s), will invoke -`next(err)`. - -Typically `true` is desired such that multiple physical directories can be -mapped to the same web address or for routes to fill in non-existent files. - -The value `false` can be used if this middleware is mounted at a path that -is designed to be strictly a single file system directory, which allows for -short-circuiting 404s for less overhead. This middleware will also reply to -all methods. - -The default value is `true`. - -##### immutable - -Enable or disable the `immutable` directive in the `Cache-Control` response -header, defaults to `false`. If set to `true`, the `maxAge` option should -also be specified to enable caching. The `immutable` directive will prevent -supported clients from making conditional requests during the life of the -`maxAge` option to check if the file has changed. - -##### index - -By default this module will send "index.html" files in response to a request -on a directory. To disable this set `false` or to supply a new index pass a -string or an array in preferred order. - -##### lastModified - -Enable or disable `Last-Modified` header, defaults to true. Uses the file -system's last modified value. - -##### maxAge - -Provide a max-age in milliseconds for http caching, defaults to 0. This -can also be a string accepted by the [ms](https://www.npmjs.org/package/ms#readme) -module. - -##### redirect - -Redirect to trailing "/" when the pathname is a dir. Defaults to `true`. - -##### setHeaders - -Function to set custom headers on response. Alterations to the headers need to -occur synchronously. The function is called as `fn(res, path, stat)`, where -the arguments are: - - - `res` the response object - - `path` the file path that is being sent - - `stat` the stat object of the file that is being sent - -## Examples - -### Serve files with vanilla node.js http server - -```js -const finalhandler = require('finalhandler') -const http = require('http') -const serveStatic = require('serve-static') - -// Serve up public/ftp folder -const serve = serveStatic('public/ftp', { index: ['index.html', 'index.htm'] }) - -// Create server -const server = http.createServer((req, res) => { - serve(req, res, finalhandler(req, res)) -}) - -// Listen -server.listen(3000) -``` - -### Serve all files as downloads - -```js -const contentDisposition = require('content-disposition') -const finalhandler = require('finalhandler') -const http = require('http') -const serveStatic = require('serve-static') - -// Serve up public/ftp folder -const serve = serveStatic('public/ftp', { - index: false, - setHeaders: setHeaders -}) - -// Set header to force download -function setHeaders (res, path) { - res.setHeader('Content-Disposition', contentDisposition(path)) -} - -// Create server -const server = http.createServer((req, res) => { - serve(req, res, finalhandler(req, res)) -}) - -// Listen -server.listen(3000) -``` - -### Serving using express - -#### Simple - -This is a simple example of using Express. - -```js -const express = require('express') -const serveStatic = require('serve-static') - -const app = express() - -app.use(serveStatic('public/ftp', { index: ['default.html', 'default.htm'] })) -app.listen(3000) -``` - -#### Multiple roots - -This example shows a simple way to search through multiple directories. -Files are searched for in `public-optimized/` first, then `public/` second -as a fallback. - -```js -const express = require('express') -const path = require('path') -const serveStatic = require('serve-static') - -const app = express() - -app.use(serveStatic(path.join(__dirname, 'public-optimized'))) -app.use(serveStatic(path.join(__dirname, 'public'))) -app.listen(3000) -``` - -#### Different settings for paths - -This example shows how to set a different max age depending on the served -file. In this example, HTML files are not cached, while everything else -is for 1 day. - -```js -const express = require('express') -const path = require('path') -const serveStatic = require('serve-static') - -const app = express() - -app.use(serveStatic(path.join(__dirname, 'public'), { - maxAge: '1d', - setHeaders: setCustomCacheControl -})) - -app.listen(3000) - -function setCustomCacheControl (res, file) { - if (path.extname(file) === '.html') { - // Custom Cache-Control for HTML files - res.setHeader('Cache-Control', 'public, max-age=0') - } -} -``` - -## License - -[MIT](LICENSE) - -[coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/serve-static/master -[coveralls-url]: https://coveralls.io/r/expressjs/serve-static?branch=master -[github-actions-ci-image]: https://badgen.net/github/checks/expressjs/serve-static/master?label=linux -[github-actions-ci-url]: https://github.com/expressjs/serve-static/actions/workflows/ci.yml -[node-image]: https://badgen.net/npm/node/serve-static -[node-url]: https://nodejs.org/en/download/ -[npm-downloads-image]: https://badgen.net/npm/dm/serve-static -[npm-url]: https://npmjs.org/package/serve-static -[npm-version-image]: https://badgen.net/npm/v/serve-static diff --git a/_includes/readmes/session.md b/_includes/readmes/session.md deleted file mode 100644 index 292b20240e..0000000000 --- a/_includes/readmes/session.md +++ /dev/null @@ -1,1061 +0,0 @@ -# express-session - -[![NPM Version][npm-version-image]][npm-url] -[![NPM Downloads][npm-downloads-image]][node-url] -[![Build Status][ci-image]][ci-url] -[![Test Coverage][coveralls-image]][coveralls-url] - -## Installation - -This is a [Node.js](https://nodejs.org/en/) module available through the -[npm registry](https://www.npmjs.com/). Installation is done using the -[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): - -```sh -$ npm install express-session -``` - -## API - -```js -var session = require('express-session') -``` - -### session(options) - -Create a session middleware with the given `options`. - -**Note** Session data is _not_ saved in the cookie itself, just the session ID. -Session data is stored server-side. - -**Note** Since version 1.5.0, the [`cookie-parser` middleware](https://www.npmjs.com/package/cookie-parser) -no longer needs to be used for this module to work. This module now directly reads -and writes cookies on `req`/`res`. Using `cookie-parser` may result in issues -if the `secret` is not the same between this module and `cookie-parser`. - -**Warning** The default server-side session storage, `MemoryStore`, is _purposely_ -not designed for a production environment. It will leak memory under most -conditions, does not scale past a single process, and is meant for debugging and -developing. - -For a list of stores, see [compatible session stores](#compatible-session-stores). - -#### Options - -`express-session` accepts these properties in the options object. - -##### cookie - -Settings object for the session ID cookie. The default value is -`{ path: '/', httpOnly: true, secure: false, maxAge: null }`. - -In addition to providing a static object, you can also pass a callback function to dynamically generate the cookie options for each request. The callback receives the `req` object as its argument and should return an object containing the cookie settings. - -```js -var app = express() -app.use(session({ - secret: 'keyboard cat', - resave: false, - saveUninitialized: true, - cookie: function(req) { - var match = req.url.match(/^\/([^/]+)/); - return { - path: match ? '/' + match[1] : '/', - httpOnly: true, - secure: req.secure || false, - maxAge: 60000 - } - } -})) -``` - -The following are options that can be set in this object. - -##### cookie.domain - -Specifies the value for the `Domain` `Set-Cookie` attribute. By default, no domain -is set, and most clients will consider the cookie to apply to only the current -domain. - -##### cookie.expires - -Specifies the `Date` object to be the value for the `Expires` `Set-Cookie` attribute. -By default, no expiration is set, and most clients will consider this a -"non-persistent cookie" and will delete it on a condition like exiting a web browser -application. - -**Note** If both `expires` and `maxAge` are set in the options, then the last one -defined in the object is what is used. - -**Note** The `expires` option should not be set directly; instead only use the `maxAge` -option. - -##### cookie.httpOnly - -Specifies the `boolean` value for the `HttpOnly` `Set-Cookie` attribute. When truthy, -the `HttpOnly` attribute is set, otherwise it is not. By default, the `HttpOnly` -attribute is set. - -**Note** be careful when setting this to `true`, as compliant clients will not allow -client-side JavaScript to see the cookie in `document.cookie`. - -##### cookie.maxAge - -Specifies the `number` (in milliseconds) to use when calculating the `Expires` -`Set-Cookie` attribute. This is done by taking the current server time and adding -`maxAge` milliseconds to the value to calculate an `Expires` datetime. By default, -no maximum age is set. - -**Note** If both `expires` and `maxAge` are set in the options, then the last one -defined in the object is what is used. - -##### cookie.partitioned - -Specifies the `boolean` value for the [`Partitioned` `Set-Cookie`](rfc-cutler-httpbis-partitioned-cookies) -attribute. When truthy, the `Partitioned` attribute is set, otherwise it is not. -By default, the `Partitioned` attribute is not set. - -**Note** This is an attribute that has not yet been fully standardized, and may -change in the future. This also means many clients may ignore this attribute until -they understand it. - -More information about can be found in [the proposal](https://github.com/privacycg/CHIPS). - -##### cookie.path - -Specifies the value for the `Path` `Set-Cookie`. By default, this is set to `'/'`, which -is the root path of the domain. - -##### cookie.priority - -Specifies the `string` to be the value for the [`Priority` `Set-Cookie` attribute][rfc-west-cookie-priority-00-4.1]. - - - `'low'` will set the `Priority` attribute to `Low`. - - `'medium'` will set the `Priority` attribute to `Medium`, the default priority when not set. - - `'high'` will set the `Priority` attribute to `High`. - -More information about the different priority levels can be found in -[the specification][rfc-west-cookie-priority-00-4.1]. - -**Note** This is an attribute that has not yet been fully standardized, and may change in the future. -This also means many clients may ignore this attribute until they understand it. - -##### cookie.sameSite - -Specifies the `boolean` or `string` to be the value for the `SameSite` `Set-Cookie` attribute. -By default, this is `false`. - - - `true` will set the `SameSite` attribute to `Strict` for strict same site enforcement. - - `false` will not set the `SameSite` attribute. - - `'lax'` will set the `SameSite` attribute to `Lax` for lax same site enforcement. - - `'none'` will set the `SameSite` attribute to `None` for an explicit cross-site cookie. - - `'strict'` will set the `SameSite` attribute to `Strict` for strict same site enforcement. - - `'auto'` will set the `SameSite` attribute to `None` for secure connections and `Lax` for non-secure connections. - -More information about the different enforcement levels can be found in -[the specification][rfc-6265bis-03-4.1.2.7]. - -**Note** This is an attribute that has not yet been fully standardized, and may change in -the future. This also means many clients may ignore this attribute until they understand it. - -**Note** There is a [draft spec](https://tools.ietf.org/html/draft-west-cookie-incrementalism-01) -that requires that the `Secure` attribute be set to `true` when the `SameSite` attribute has been -set to `'none'`. Some web browsers or other clients may be adopting this specification. - -The `cookie.sameSite` option can also be set to the special value `'auto'` to have -this setting automatically match the determined security of the connection. When the connection -is secure (HTTPS), the `SameSite` attribute will be set to `None` to enable cross-site usage. -When the connection is not secure (HTTP), the `SameSite` attribute will be set to `Lax` for -better security while maintaining functionality. This is useful when the Express `"trust proxy"` -setting is properly setup to simplify development vs production configuration, particularly -for SAML authentication scenarios. - -##### cookie.secure - -Specifies the `boolean` value for the `Secure` `Set-Cookie` attribute. When truthy, -the `Secure` attribute is set, otherwise it is not. By default, the `Secure` -attribute is not set. - -**Note** be careful when setting this to `true`, as compliant clients will not send -the cookie back to the server in the future if the browser does not have an HTTPS -connection. - -Please note that `secure: true` is a **recommended** option. However, it requires -an https-enabled website, i.e., HTTPS is necessary for secure cookies. If `secure` -is set, and you access your site over HTTP, the cookie will not be set. If you -have your node.js behind a proxy and are using `secure: true`, you need to set -"trust proxy" in express: - -```js -var app = express() -app.set('trust proxy', 1) // trust first proxy -app.use(session({ - secret: 'keyboard cat', - resave: false, - saveUninitialized: true, - cookie: { secure: true } -})) -``` - -For using secure cookies in production, but allowing for testing in development, -the following is an example of enabling this setup based on `NODE_ENV` in express: - -```js -var app = express() -var sess = { - secret: 'keyboard cat', - cookie: {} -} - -if (app.get('env') === 'production') { - app.set('trust proxy', 1) // trust first proxy - sess.cookie.secure = true // serve secure cookies -} - -app.use(session(sess)) -``` - -The `cookie.secure` option can also be set to the special value `'auto'` to have -this setting automatically match the determined security of the connection. Be -careful when using this setting if the site is available both as HTTP and HTTPS, -as once the cookie is set on HTTPS, it will no longer be visible over HTTP. This -is useful when the Express `"trust proxy"` setting is properly setup to simplify -development vs production configuration. - -##### genid - -Function to call to generate a new session ID. Provide a function that returns -a string that will be used as a session ID. The function is given `req` as the -first argument if you want to use some value attached to `req` when generating -the ID. - -The default value is a function which uses the `uid-safe` library to generate IDs. - -**NOTE** be careful to generate unique IDs so your sessions do not conflict. - -```js -app.use(session({ - genid: function(req) { - return genuuid() // use UUIDs for session IDs - }, - secret: 'keyboard cat' -})) -``` - -##### name - -The name of the session ID cookie to set in the response (and read from in the -request). - -The default value is `'connect.sid'`. - -**Note** if you have multiple apps running on the same hostname (this is just -the name, i.e. `localhost` or `127.0.0.1`; different schemes and ports do not -name a different hostname), then you need to separate the session cookies from -each other. The simplest method is to simply set different `name`s per app. - -##### proxy - -Trust the reverse proxy when setting secure cookies (via the "X-Forwarded-Proto" -header). - -The default value is `undefined`. - - - `true` The "X-Forwarded-Proto" header will be used. - - `false` All headers are ignored and the connection is considered secure only - if there is a direct TLS/SSL connection. - - `undefined` Uses the "trust proxy" setting from express - -##### resave - -Forces the session to be saved back to the session store, even if the session -was never modified during the request. Depending on your store this may be -necessary, but it can also create race conditions where a client makes two -parallel requests to your server and changes made to the session in one -request may get overwritten when the other request ends, even if it made no -changes (this behavior also depends on what store you're using). - -The default value is `true`, but using the default has been deprecated, -as the default will change in the future. Please research into this setting -and choose what is appropriate to your use-case. Typically, you'll want -`false`. - -How do I know if this is necessary for my store? The best way to know is to -check with your store if it implements the `touch` method. If it does, then -you can safely set `resave: false`. If it does not implement the `touch` -method and your store sets an expiration date on stored sessions, then you -likely need `resave: true`. - -##### rolling - -Force the session identifier cookie to be set on every response. The expiration -is reset to the original [`maxAge`](#cookiemaxage), resetting the expiration -countdown. - -The default value is `false`. - -With this enabled, the session identifier cookie will expire in -[`maxAge`](#cookiemaxage) since the last response was sent instead of in -[`maxAge`](#cookiemaxage) since the session was last modified by the server. - -This is typically used in conjunction with short, non-session-length -[`maxAge`](#cookiemaxage) values to provide a quick timeout of the session data -with reduced potential of it occurring during on going server interactions. - -**Note** When this option is set to `true` but the `saveUninitialized` option is -set to `false`, the cookie will not be set on a response with an uninitialized -session. This option only modifies the behavior when an existing session was -loaded for the request. - -##### saveUninitialized - -Forces a session that is "uninitialized" to be saved to the store. A session is -uninitialized when it is new but not modified. Choosing `false` is useful for -implementing login sessions, reducing server storage usage, or complying with -laws that require permission before setting a cookie. Choosing `false` will also -help with race conditions where a client makes multiple parallel requests -without a session. - -The default value is `true`, but using the default has been deprecated, as the -default will change in the future. Please research into this setting and -choose what is appropriate to your use-case. - -**Note** if you are using Session in conjunction with PassportJS, Passport -will add an empty Passport object to the session for use after a user is -authenticated, which will be treated as a modification to the session, causing -it to be saved. *This has been fixed in PassportJS 0.3.0* - -##### secret - -**Required option** - -This is the secret used to sign the session ID cookie. The secret can be any type -of value that is supported by Node.js `crypto.createHmac` (like a string or a -`Buffer`). This can be either a single secret, or an array of multiple secrets. If -an array of secrets is provided, only the first element will be used to sign the -session ID cookie, while all the elements will be considered when verifying the -signature in requests. The secret itself should be not easily parsed by a human and -would best be a random set of characters. A best practice may include: - - - The use of environment variables to store the secret, ensuring the secret itself - does not exist in your repository. - - Periodic updates of the secret, while ensuring the previous secret is in the - array. - -Using a secret that cannot be guessed will reduce the ability to hijack a session to -only guessing the session ID (as determined by the `genid` option). - -Changing the secret value will invalidate all existing sessions. In order to rotate -the secret without invalidating sessions, provide an array of secrets, with the new -secret as first element of the array, and including previous secrets as the later -elements. - -**Note** HMAC-256 is used to sign the session ID. For this reason, the secret should -contain at least 32 bytes of entropy. - -##### store - -The session store instance, defaults to a new `MemoryStore` instance. - -##### unset - -Control the result of unsetting `req.session` (through `delete`, setting to `null`, -etc.). - -The default value is `'keep'`. - - - `'destroy'` The session will be destroyed (deleted) when the response ends. - - `'keep'` The session in the store will be kept, but modifications made during - the request are ignored and not saved. - -### req.session - -To store or access session data, simply use the request property `req.session`, -which is (generally) serialized as JSON by the store, so nested objects -are typically fine. For example below is a user-specific view counter: - -```js -// Use the session middleware -app.use(session({ secret: 'keyboard cat', cookie: { maxAge: 60000 }})) - -// Access the session as req.session -app.get('/', function(req, res, next) { - if (req.session.views) { - req.session.views++ - res.setHeader('Content-Type', 'text/html') - res.write('

views: ' + req.session.views + '

') - res.write('

expires in: ' + (req.session.cookie.maxAge / 1000) + 's

') - res.end() - } else { - req.session.views = 1 - res.end('welcome to the session demo. refresh!') - } -}) -``` - -#### Session.regenerate(callback) - -To regenerate the session simply invoke the method. Once complete, -a new SID and `Session` instance will be initialized at `req.session` -and the `callback` will be invoked. - -```js -req.session.regenerate(function(err) { - // will have a new session here -}) -``` - -#### Session.destroy(callback) - -Destroys the session and will unset the `req.session` property. -Once complete, the `callback` will be invoked. - -```js -req.session.destroy(function(err) { - // cannot access session here -}) -``` - -#### Session.reload(callback) - -Reloads the session data from the store and re-populates the -`req.session` object. Once complete, the `callback` will be invoked. - -```js -req.session.reload(function(err) { - // session updated -}) -``` - -#### Session.save(callback) - -Save the session back to the store, replacing the contents on the store with the -contents in memory (though a store may do something else--consult the store's -documentation for exact behavior). - -This method is automatically called at the end of the HTTP response if the -session data has been altered (though this behavior can be altered with various -options in the middleware constructor). Because of this, typically this method -does not need to be called. - -There are some cases where it is useful to call this method, for example, -redirects, long-lived requests or in WebSockets. - -```js -req.session.save(function(err) { - // session saved -}) -``` - -#### Session.touch() - -Updates the `.maxAge` property. Typically this is -not necessary to call, as the session middleware does this for you. - -### req.session.id - -Each session has a unique ID associated with it. This property is an -alias of [`req.sessionID`](#reqsessionid-1) and cannot be modified. -It has been added to make the session ID accessible from the `session` -object. - -### req.session.cookie - -Each session has a unique cookie object accompany it. This allows -you to alter the session cookie per visitor. For example we can -set `req.session.cookie.expires` to `false` to enable the cookie -to remain for only the duration of the user-agent. - -#### Cookie.maxAge - -Alternatively `req.session.cookie.maxAge` will return the time -remaining in milliseconds, which we may also re-assign a new value -to adjust the `.expires` property appropriately. The following -are essentially equivalent - -```js -var hour = 3600000 -req.session.cookie.expires = new Date(Date.now() + hour) -req.session.cookie.maxAge = hour -``` - -For example when `maxAge` is set to `60000` (one minute), and 30 seconds -has elapsed it will return `30000` until the current request has completed, -at which time `req.session.touch()` is called to reset -`req.session.cookie.maxAge` to its original value. - -```js -req.session.cookie.maxAge // => 30000 -``` - -#### Cookie.originalMaxAge - -The `req.session.cookie.originalMaxAge` property returns the original -`maxAge` (time-to-live), in milliseconds, of the session cookie. - -### req.sessionID - -To get the ID of the loaded session, access the request property -`req.sessionID`. This is simply a read-only value set when a session -is loaded/created. - -## Session Store Implementation - -Every session store _must_ be an `EventEmitter` and implement specific -methods. The following methods are the list of **required**, **recommended**, -and **optional**. - - * Required methods are ones that this module will always call on the store. - * Recommended methods are ones that this module will call on the store if - available. - * Optional methods are ones this module does not call at all, but helps - present uniform stores to users. - -For an example implementation view the [connect-redis](http://github.com/visionmedia/connect-redis) repo. - -### store.all(callback) - -**Optional** - -This optional method is used to get all sessions in the store as an array. The -`callback` should be called as `callback(error, sessions)`. - -### store.destroy(sid, callback) - -**Required** - -This required method is used to destroy/delete a session from the store given -a session ID (`sid`). The `callback` should be called as `callback(error)` once -the session is destroyed. - -### store.clear(callback) - -**Optional** - -This optional method is used to delete all sessions from the store. The -`callback` should be called as `callback(error)` once the store is cleared. - -### store.length(callback) - -**Optional** - -This optional method is used to get the count of all sessions in the store. -The `callback` should be called as `callback(error, len)`. - -### store.get(sid, callback) - -**Required** - -This required method is used to get a session from the store given a session -ID (`sid`). The `callback` should be called as `callback(error, session)`. - -The `session` argument should be a session if found, otherwise `null` or -`undefined` if the session was not found (and there was no error). A special -case is made when `error.code === 'ENOENT'` to act like `callback(null, null)`. - -### store.set(sid, session, callback) - -**Required** - -This required method is used to upsert a session into the store given a -session ID (`sid`) and session (`session`) object. The callback should be -called as `callback(error)` once the session has been set in the store. - -### store.touch(sid, session, callback) - -**Recommended** - -This recommended method is used to "touch" a given session given a -session ID (`sid`) and session (`session`) object. The `callback` should be -called as `callback(error)` once the session has been touched. - -This is primarily used when the store will automatically delete idle sessions -and this method is used to signal to the store the given session is active, -potentially resetting the idle timer. - -## Compatible Session Stores - -The following modules implement a session store that is compatible with this -module. Please make a PR to add additional modules :) - -[![★][aerospike-session-store-image] aerospike-session-store][aerospike-session-store-url] A session store using [Aerospike](http://www.aerospike.com/). - -[aerospike-session-store-url]: https://www.npmjs.com/package/aerospike-session-store -[aerospike-session-store-image]: https://badgen.net/github/stars/aerospike/aerospike-session-store-expressjs?label=%E2%98%85 - -[![★][better-sqlite3-session-store-image] better-sqlite3-session-store][better-sqlite3-session-store-url] A session store based on [better-sqlite3](https://github.com/JoshuaWise/better-sqlite3). - -[better-sqlite3-session-store-url]: https://www.npmjs.com/package/better-sqlite3-session-store -[better-sqlite3-session-store-image]: https://badgen.net/github/stars/timdaub/better-sqlite3-session-store?label=%E2%98%85 - -[![★][cassandra-store-image] cassandra-store][cassandra-store-url] An Apache Cassandra-based session store. - -[cassandra-store-url]: https://www.npmjs.com/package/cassandra-store -[cassandra-store-image]: https://badgen.net/github/stars/webcc/cassandra-store?label=%E2%98%85 - -[![★][cluster-store-image] cluster-store][cluster-store-url] A wrapper for using in-process / embedded -stores - such as SQLite (via knex), leveldb, files, or memory - with node cluster (desirable for Raspberry Pi 2 -and other multi-core embedded devices). - -[cluster-store-url]: https://www.npmjs.com/package/cluster-store -[cluster-store-image]: https://badgen.net/github/stars/coolaj86/cluster-store?label=%E2%98%85 - -[![★][connect-arango-image] connect-arango][connect-arango-url] An ArangoDB-based session store. - -[connect-arango-url]: https://www.npmjs.com/package/connect-arango -[connect-arango-image]: https://badgen.net/github/stars/AlexanderArvidsson/connect-arango?label=%E2%98%85 - -[![★][connect-azuretables-image] connect-azuretables][connect-azuretables-url] An [Azure Table Storage](https://azure.microsoft.com/en-gb/services/storage/tables/)-based session store. - -[connect-azuretables-url]: https://www.npmjs.com/package/connect-azuretables -[connect-azuretables-image]: https://badgen.net/github/stars/mike-goodwin/connect-azuretables?label=%E2%98%85 - -[![★][connect-cloudant-store-image] connect-cloudant-store][connect-cloudant-store-url] An [IBM Cloudant](https://cloudant.com/)-based session store. - -[connect-cloudant-store-url]: https://www.npmjs.com/package/connect-cloudant-store -[connect-cloudant-store-image]: https://badgen.net/github/stars/adriantanasa/connect-cloudant-store?label=%E2%98%85 - -[![★][connect-cosmosdb-image] connect-cosmosdb][connect-cosmosdb-url] An Azure [Cosmos DB](https://azure.microsoft.com/en-us/products/cosmos-db/)-based session store. - -[connect-cosmosdb-url]: https://www.npmjs.com/package/connect-cosmosdb -[connect-cosmosdb-image]: https://badgen.net/github/stars/thekillingspree/connect-cosmosdb?label=%E2%98%85 - -[![★][connect-couchbase-image] connect-couchbase][connect-couchbase-url] A [couchbase](http://www.couchbase.com/)-based session store. - -[connect-couchbase-url]: https://www.npmjs.com/package/connect-couchbase -[connect-couchbase-image]: https://badgen.net/github/stars/christophermina/connect-couchbase?label=%E2%98%85 - -[![★][connect-datacache-image] connect-datacache][connect-datacache-url] An [IBM Bluemix Data Cache](http://www.ibm.com/cloud-computing/bluemix/)-based session store. - -[connect-datacache-url]: https://www.npmjs.com/package/connect-datacache -[connect-datacache-image]: https://badgen.net/github/stars/adriantanasa/connect-datacache?label=%E2%98%85 - -[![★][@google-cloud/connect-datastore-image] @google-cloud/connect-datastore][@google-cloud/connect-datastore-url] A [Google Cloud Datastore](https://cloud.google.com/datastore/docs/concepts/overview)-based session store. - -[@google-cloud/connect-datastore-url]: https://www.npmjs.com/package/@google-cloud/connect-datastore -[@google-cloud/connect-datastore-image]: https://badgen.net/github/stars/GoogleCloudPlatform/cloud-datastore-session-node?label=%E2%98%85 - -[![★][connect-db2-image] connect-db2][connect-db2-url] An IBM DB2-based session store built using [ibm_db](https://www.npmjs.com/package/ibm_db) module. - -[connect-db2-url]: https://www.npmjs.com/package/connect-db2 -[connect-db2-image]: https://badgen.net/github/stars/wallali/connect-db2?label=%E2%98%85 - -[![★][connect-dynamodb-image] connect-dynamodb][connect-dynamodb-url] A DynamoDB-based session store. - -[connect-dynamodb-url]: https://www.npmjs.com/package/connect-dynamodb -[connect-dynamodb-image]: https://badgen.net/github/stars/ca98am79/connect-dynamodb?label=%E2%98%85 - -[![★][@google-cloud/connect-firestore-image] @google-cloud/connect-firestore][@google-cloud/connect-firestore-url] A [Google Cloud Firestore](https://cloud.google.com/firestore/docs/overview)-based session store. - -[@google-cloud/connect-firestore-url]: https://www.npmjs.com/package/@google-cloud/connect-firestore -[@google-cloud/connect-firestore-image]: https://badgen.net/github/stars/googleapis/nodejs-firestore-session?label=%E2%98%85 - -[![★][connect-hazelcast-image] connect-hazelcast][connect-hazelcast-url] Hazelcast session store for Connect and Express. - -[connect-hazelcast-url]: https://www.npmjs.com/package/connect-hazelcast -[connect-hazelcast-image]: https://badgen.net/github/stars/huseyinbabal/connect-hazelcast?label=%E2%98%85 - -[![★][connect-loki-image] connect-loki][connect-loki-url] A Loki.js-based session store. - -[connect-loki-url]: https://www.npmjs.com/package/connect-loki -[connect-loki-image]: https://badgen.net/github/stars/Requarks/connect-loki?label=%E2%98%85 - -[![★][connect-lowdb-image] connect-lowdb][connect-lowdb-url] A lowdb-based session store. - -[connect-lowdb-url]: https://www.npmjs.com/package/connect-lowdb -[connect-lowdb-image]: https://badgen.net/github/stars/travishorn/connect-lowdb?label=%E2%98%85 - -[![★][connect-memcached-image] connect-memcached][connect-memcached-url] A memcached-based session store. - -[connect-memcached-url]: https://www.npmjs.com/package/connect-memcached -[connect-memcached-image]: https://badgen.net/github/stars/balor/connect-memcached?label=%E2%98%85 - -[![★][connect-memjs-image] connect-memjs][connect-memjs-url] A memcached-based session store using -[memjs](https://www.npmjs.com/package/memjs) as the memcached client. - -[connect-memjs-url]: https://www.npmjs.com/package/connect-memjs -[connect-memjs-image]: https://badgen.net/github/stars/liamdon/connect-memjs?label=%E2%98%85 - -[![★][connect-ml-image] connect-ml][connect-ml-url] A MarkLogic Server-based session store. - -[connect-ml-url]: https://www.npmjs.com/package/connect-ml -[connect-ml-image]: https://badgen.net/github/stars/bluetorch/connect-ml?label=%E2%98%85 - -[![★][connect-monetdb-image] connect-monetdb][connect-monetdb-url] A MonetDB-based session store. - -[connect-monetdb-url]: https://www.npmjs.com/package/connect-monetdb -[connect-monetdb-image]: https://badgen.net/github/stars/MonetDB/npm-connect-monetdb?label=%E2%98%85 - -[![★][connect-mongo-image] connect-mongo][connect-mongo-url] A MongoDB-based session store. - -[connect-mongo-url]: https://www.npmjs.com/package/connect-mongo -[connect-mongo-image]: https://badgen.net/github/stars/kcbanner/connect-mongo?label=%E2%98%85 - -[![★][connect-mongodb-session-image] connect-mongodb-session][connect-mongodb-session-url] Lightweight MongoDB-based session store built and maintained by MongoDB. - -[connect-mongodb-session-url]: https://www.npmjs.com/package/connect-mongodb-session -[connect-mongodb-session-image]: https://badgen.net/github/stars/mongodb-js/connect-mongodb-session?label=%E2%98%85 - -[![★][connect-mssql-v2-image] connect-mssql-v2][connect-mssql-v2-url] A Microsoft SQL Server-based session store based on [connect-mssql](https://www.npmjs.com/package/connect-mssql). - -[connect-mssql-v2-url]: https://www.npmjs.com/package/connect-mssql-v2 -[connect-mssql-v2-image]: https://badgen.net/github/stars/jluboff/connect-mssql-v2?label=%E2%98%85 - -[![★][connect-neo4j-image] connect-neo4j][connect-neo4j-url] A [Neo4j](https://neo4j.com)-based session store. - -[connect-neo4j-url]: https://www.npmjs.com/package/connect-neo4j -[connect-neo4j-image]: https://badgen.net/github/stars/MaxAndersson/connect-neo4j?label=%E2%98%85 - -[![★][connect-ottoman-image] connect-ottoman][connect-ottoman-url] A [couchbase ottoman](http://www.couchbase.com/)-based session store. - -[connect-ottoman-url]: https://www.npmjs.com/package/connect-ottoman -[connect-ottoman-image]: https://badgen.net/github/stars/noiissyboy/connect-ottoman?label=%E2%98%85 - -[![★][connect-pg-simple-image] connect-pg-simple][connect-pg-simple-url] A PostgreSQL-based session store. - -[connect-pg-simple-url]: https://www.npmjs.com/package/connect-pg-simple -[connect-pg-simple-image]: https://badgen.net/github/stars/voxpelli/node-connect-pg-simple?label=%E2%98%85 - -[![★][connect-redis-image] connect-redis][connect-redis-url] A Redis-based session store. - -[connect-redis-url]: https://www.npmjs.com/package/connect-redis -[connect-redis-image]: https://badgen.net/github/stars/tj/connect-redis?label=%E2%98%85 - -[![★][connect-session-firebase-image] connect-session-firebase][connect-session-firebase-url] A session store based on the [Firebase Realtime Database](https://firebase.google.com/docs/database/) - -[connect-session-firebase-url]: https://www.npmjs.com/package/connect-session-firebase -[connect-session-firebase-image]: https://badgen.net/github/stars/benweier/connect-session-firebase?label=%E2%98%85 - -[![★][connect-session-knex-image] connect-session-knex][connect-session-knex-url] A session store using -[Knex.js](http://knexjs.org/), which is a SQL query builder for PostgreSQL, MySQL, MariaDB, SQLite3, and Oracle. - -[connect-session-knex-url]: https://www.npmjs.com/package/connect-session-knex -[connect-session-knex-image]: https://badgen.net/github/stars/llambda/connect-session-knex?label=%E2%98%85 - -[![★][connect-session-sequelize-image] connect-session-sequelize][connect-session-sequelize-url] A session store using -[Sequelize.js](http://sequelizejs.com/), which is a Node.js / io.js ORM for PostgreSQL, MySQL, SQLite and MSSQL. - -[connect-session-sequelize-url]: https://www.npmjs.com/package/connect-session-sequelize -[connect-session-sequelize-image]: https://badgen.net/github/stars/mweibel/connect-session-sequelize?label=%E2%98%85 - -[![★][connect-sqlite3-image] connect-sqlite3][connect-sqlite3-url] A [SQLite3](https://github.com/mapbox/node-sqlite3) session store modeled after the TJ's `connect-redis` store. - -[connect-sqlite3-url]: https://www.npmjs.com/package/connect-sqlite3 -[connect-sqlite3-image]: https://badgen.net/github/stars/rawberg/connect-sqlite3?label=%E2%98%85 - -[![★][connect-typeorm-image] connect-typeorm][connect-typeorm-url] A [TypeORM](https://github.com/typeorm/typeorm)-based session store. - -[connect-typeorm-url]: https://www.npmjs.com/package/connect-typeorm -[connect-typeorm-image]: https://badgen.net/github/stars/makepost/connect-typeorm?label=%E2%98%85 - -[![★][couchdb-expression-image] couchdb-expression][couchdb-expression-url] A [CouchDB](https://couchdb.apache.org/)-based session store. - -[couchdb-expression-url]: https://www.npmjs.com/package/couchdb-expression -[couchdb-expression-image]: https://badgen.net/github/stars/tkshnwesper/couchdb-expression?label=%E2%98%85 - -[![★][dynamodb-store-image] dynamodb-store][dynamodb-store-url] A DynamoDB-based session store. - -[dynamodb-store-url]: https://www.npmjs.com/package/dynamodb-store -[dynamodb-store-image]: https://badgen.net/github/stars/rafaelrpinto/dynamodb-store?label=%E2%98%85 - -[![★][dynamodb-store-v3-image] dynamodb-store-v3][dynamodb-store-v3-url] Implementation of a session store using DynamoDB backed by the [AWS SDK for JavaScript v3](https://github.com/aws/aws-sdk-js-v3). - -[dynamodb-store-v3-url]: https://www.npmjs.com/package/dynamodb-store-v3 -[dynamodb-store-v3-image]: https://badgen.net/github/stars/FryDay/dynamodb-store-v3?label=%E2%98%85 - -[![★][express-etcd-image] express-etcd][express-etcd-url] An [etcd](https://github.com/stianeikeland/node-etcd) based session store. - -[express-etcd-url]: https://www.npmjs.com/package/express-etcd -[express-etcd-image]: https://badgen.net/github/stars/gildean/express-etcd?label=%E2%98%85 - -[![★][express-mysql-session-image] express-mysql-session][express-mysql-session-url] A session store using native -[MySQL](https://www.mysql.com/) via the [node-mysql](https://github.com/felixge/node-mysql) module. - -[express-mysql-session-url]: https://www.npmjs.com/package/express-mysql-session -[express-mysql-session-image]: https://badgen.net/github/stars/chill117/express-mysql-session?label=%E2%98%85 - -[![★][express-nedb-session-image] express-nedb-session][express-nedb-session-url] A NeDB-based session store. - -[express-nedb-session-url]: https://www.npmjs.com/package/express-nedb-session -[express-nedb-session-image]: https://badgen.net/github/stars/louischatriot/express-nedb-session?label=%E2%98%85 - -[![★][express-oracle-session-image] express-oracle-session][express-oracle-session-url] A session store using native -[oracle](https://www.oracle.com/) via the [node-oracledb](https://www.npmjs.com/package/oracledb) module. - -[express-oracle-session-url]: https://www.npmjs.com/package/express-oracle-session -[express-oracle-session-image]: https://badgen.net/github/stars/slumber86/express-oracle-session?label=%E2%98%85 - -[![★][express-session-cache-manager-image] express-session-cache-manager][express-session-cache-manager-url] -A store that implements [cache-manager](https://www.npmjs.com/package/cache-manager), which supports -a [variety of storage types](https://www.npmjs.com/package/cache-manager#store-engines). - -[express-session-cache-manager-url]: https://www.npmjs.com/package/express-session-cache-manager -[express-session-cache-manager-image]: https://badgen.net/github/stars/theogravity/express-session-cache-manager?label=%E2%98%85 - -[![★][express-session-etcd3-image] express-session-etcd3][express-session-etcd3-url] An [etcd3](https://github.com/mixer/etcd3) based session store. - -[express-session-etcd3-url]: https://www.npmjs.com/package/express-session-etcd3 -[express-session-etcd3-image]: https://badgen.net/github/stars/willgm/express-session-etcd3?label=%E2%98%85 - -[![★][express-session-level-image] express-session-level][express-session-level-url] A [LevelDB](https://github.com/Level/levelup) based session store. - -[express-session-level-url]: https://www.npmjs.com/package/express-session-level -[express-session-level-image]: https://badgen.net/github/stars/tgohn/express-session-level?label=%E2%98%85 - -[![★][express-session-rsdb-image] express-session-rsdb][express-session-rsdb-url] Session store based on Rocket-Store: A very simple, super fast and yet powerful, flat file database. - -[express-session-rsdb-url]: https://www.npmjs.com/package/express-session-rsdb -[express-session-rsdb-image]: https://badgen.net/github/stars/paragi/express-session-rsdb?label=%E2%98%85 - -[![★][express-sessions-image] express-sessions][express-sessions-url] A session store supporting both MongoDB and Redis. - -[express-sessions-url]: https://www.npmjs.com/package/express-sessions -[express-sessions-image]: https://badgen.net/github/stars/konteck/express-sessions?label=%E2%98%85 - -[![★][firestore-store-image] firestore-store][firestore-store-url] A [Firestore](https://github.com/hendrysadrak/firestore-store)-based session store. - -[firestore-store-url]: https://www.npmjs.com/package/firestore-store -[firestore-store-image]: https://badgen.net/github/stars/hendrysadrak/firestore-store?label=%E2%98%85 - -[![★][fortune-session-image] fortune-session][fortune-session-url] A [Fortune.js](https://github.com/fortunejs/fortune) -based session store. Supports all backends supported by Fortune (MongoDB, Redis, Postgres, NeDB). - -[fortune-session-url]: https://www.npmjs.com/package/fortune-session -[fortune-session-image]: https://badgen.net/github/stars/aliceklipper/fortune-session?label=%E2%98%85 - -[![★][hazelcast-store-image] hazelcast-store][hazelcast-store-url] A Hazelcast-based session store built on the [Hazelcast Node Client](https://www.npmjs.com/package/hazelcast-client). - -[hazelcast-store-url]: https://www.npmjs.com/package/hazelcast-store -[hazelcast-store-image]: https://badgen.net/github/stars/jackspaniel/hazelcast-store?label=%E2%98%85 - -[![★][level-session-store-image] level-session-store][level-session-store-url] A LevelDB-based session store. - -[level-session-store-url]: https://www.npmjs.com/package/level-session-store -[level-session-store-image]: https://badgen.net/github/stars/toddself/level-session-store?label=%E2%98%85 - -[![★][lowdb-session-store-image] lowdb-session-store][lowdb-session-store-url] A [lowdb](https://www.npmjs.com/package/lowdb)-based session store. - -[lowdb-session-store-url]: https://www.npmjs.com/package/lowdb-session-store -[lowdb-session-store-image]: https://badgen.net/github/stars/fhellwig/lowdb-session-store?label=%E2%98%85 - -[![★][medea-session-store-image] medea-session-store][medea-session-store-url] A Medea-based session store. - -[medea-session-store-url]: https://www.npmjs.com/package/medea-session-store -[medea-session-store-image]: https://badgen.net/github/stars/BenjaminVadant/medea-session-store?label=%E2%98%85 - -[![★][memorystore-image] memorystore][memorystore-url] A memory session store made for production. - -[memorystore-url]: https://www.npmjs.com/package/memorystore -[memorystore-image]: https://badgen.net/github/stars/roccomuso/memorystore?label=%E2%98%85 - -[![★][mssql-session-store-image] mssql-session-store][mssql-session-store-url] A SQL Server-based session store. - -[mssql-session-store-url]: https://www.npmjs.com/package/mssql-session-store -[mssql-session-store-image]: https://badgen.net/github/stars/jwathen/mssql-session-store?label=%E2%98%85 - -[![★][nedb-session-store-image] nedb-session-store][nedb-session-store-url] An alternate NeDB-based (either in-memory or file-persisted) session store. - -[nedb-session-store-url]: https://www.npmjs.com/package/nedb-session-store -[nedb-session-store-image]: https://badgen.net/github/stars/JamesMGreene/nedb-session-store?label=%E2%98%85 - -[![★][@quixo3/prisma-session-store-image] @quixo3/prisma-session-store][@quixo3/prisma-session-store-url] A session store for the [Prisma Framework](https://www.prisma.io). - -[@quixo3/prisma-session-store-url]: https://www.npmjs.com/package/@quixo3/prisma-session-store -[@quixo3/prisma-session-store-image]: https://badgen.net/github/stars/kleydon/prisma-session-store?label=%E2%98%85 - -[![★][restsession-image] restsession][restsession-url] Store sessions utilizing a RESTful API - -[restsession-url]: https://www.npmjs.com/package/restsession -[restsession-image]: https://badgen.net/github/stars/jankal/restsession?label=%E2%98%85 - -[![★][sequelstore-connect-image] sequelstore-connect][sequelstore-connect-url] A session store using [Sequelize.js](http://sequelizejs.com/). - -[sequelstore-connect-url]: https://www.npmjs.com/package/sequelstore-connect -[sequelstore-connect-image]: https://badgen.net/github/stars/MattMcFarland/sequelstore-connect?label=%E2%98%85 - -[![★][session-file-store-image] session-file-store][session-file-store-url] A file system-based session store. - -[session-file-store-url]: https://www.npmjs.com/package/session-file-store -[session-file-store-image]: https://badgen.net/github/stars/valery-barysok/session-file-store?label=%E2%98%85 - -[![★][session-pouchdb-store-image] session-pouchdb-store][session-pouchdb-store-url] Session store for PouchDB / CouchDB. Accepts embedded, custom, or remote PouchDB instance and realtime synchronization. - -[session-pouchdb-store-url]: https://www.npmjs.com/package/session-pouchdb-store -[session-pouchdb-store-image]: https://badgen.net/github/stars/solzimer/session-pouchdb-store?label=%E2%98%85 - -[![★][@cyclic.sh/session-store-image] @cyclic.sh/session-store][@cyclic.sh/session-store-url] A DynamoDB-based session store for [Cyclic.sh](https://www.cyclic.sh/) apps. - -[@cyclic.sh/session-store-url]: https://www.npmjs.com/package/@cyclic.sh/session-store -[@cyclic.sh/session-store-image]: https://badgen.net/github/stars/cyclic-software/session-store?label=%E2%98%85 - -[![★][@databunker/session-store-image] @databunker/session-store][@databunker/session-store-url] A [Databunker](https://databunker.org/)-based encrypted session store. - -[@databunker/session-store-url]: https://www.npmjs.com/package/@databunker/session-store -[@databunker/session-store-image]: https://badgen.net/github/stars/securitybunker/databunker-session-store?label=%E2%98%85 - -[![★][sessionstore-image] sessionstore][sessionstore-url] A session store that works with various databases. - -[sessionstore-url]: https://www.npmjs.com/package/sessionstore -[sessionstore-image]: https://badgen.net/github/stars/adrai/sessionstore?label=%E2%98%85 - -[![★][tch-nedb-session-image] tch-nedb-session][tch-nedb-session-url] A file system session store based on NeDB. - -[tch-nedb-session-url]: https://www.npmjs.com/package/tch-nedb-session -[tch-nedb-session-image]: https://badgen.net/github/stars/tomaschyly/NeDBSession?label=%E2%98%85 - -## Examples - -### View counter - -A simple example using `express-session` to store page views for a user. - -```js -var express = require('express') -var parseurl = require('parseurl') -var session = require('express-session') - -var app = express() - -app.use(session({ - secret: 'keyboard cat', - resave: false, - saveUninitialized: true -})) - -app.use(function (req, res, next) { - if (!req.session.views) { - req.session.views = {} - } - - // get the url pathname - var pathname = parseurl(req).pathname - - // count the views - req.session.views[pathname] = (req.session.views[pathname] || 0) + 1 - - next() -}) - -app.get('/foo', function (req, res, next) { - res.send('you viewed this page ' + req.session.views['/foo'] + ' times') -}) - -app.get('/bar', function (req, res, next) { - res.send('you viewed this page ' + req.session.views['/bar'] + ' times') -}) - -app.listen(3000) -``` - -### User login - -A simple example using `express-session` to keep a user log in session. - -```js -var escapeHtml = require('escape-html') -var express = require('express') -var session = require('express-session') - -var app = express() - -app.use(session({ - secret: 'keyboard cat', - resave: false, - saveUninitialized: true -})) - -// middleware to test if authenticated -function isAuthenticated (req, res, next) { - if (req.session.user) next() - else next('route') -} - -app.get('/', isAuthenticated, function (req, res) { - // this is only called when there is an authentication user due to isAuthenticated - res.send('hello, ' + escapeHtml(req.session.user) + '!' + - ' Logout') -}) - -app.get('/', function (req, res) { - res.send('
' + - 'Username:
' + - 'Password:
' + - '
') -}) - -app.post('/login', express.urlencoded({ extended: false }), function (req, res) { - // login logic to validate req.body.user and req.body.pass - // would be implemented here. for this example any combo works - - // regenerate the session, which is good practice to help - // guard against forms of session fixation - req.session.regenerate(function (err) { - if (err) next(err) - - // store user information in session, typically a user id - req.session.user = req.body.user - - // save the session before redirection to ensure page - // load does not happen before session is saved - req.session.save(function (err) { - if (err) return next(err) - res.redirect('/') - }) - }) -}) - -app.get('/logout', function (req, res, next) { - // logout logic - - // clear the user from the session object and save. - // this will ensure that re-using the old session id - // does not have a logged in user - req.session.user = null - req.session.save(function (err) { - if (err) next(err) - - // regenerate the session, which is good practice to help - // guard against forms of session fixation - req.session.regenerate(function (err) { - if (err) next(err) - res.redirect('/') - }) - }) -}) - -app.listen(3000) -``` - -## Debugging - -This module uses the [debug](https://www.npmjs.com/package/debug) module -internally to log information about session operations. - -To see all the internal logs, set the `DEBUG` environment variable to -`express-session` when launching your app (`npm start`, in this example): - -```sh -$ DEBUG=express-session npm start -``` - -On Windows, use the corresponding command; - -```sh -> set DEBUG=express-session & npm start -``` - -## License - -[MIT](LICENSE) - -[rfc-6265bis-03-4.1.2.7]: https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7 -[rfc-cutler-httpbis-partitioned-cookies]: https://tools.ietf.org/html/draft-cutler-httpbis-partitioned-cookies/ -[rfc-west-cookie-priority-00-4.1]: https://tools.ietf.org/html/draft-west-cookie-priority-00#section-4.1 -[ci-image]: https://badgen.net/github/checks/expressjs/session/master?label=ci -[ci-url]: https://github.com/expressjs/session/actions?query=workflow%3Aci -[coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/session/master -[coveralls-url]: https://coveralls.io/r/expressjs/session?branch=master -[node-url]: https://nodejs.org/en/download -[npm-downloads-image]: https://badgen.net/npm/dm/express-session -[npm-url]: https://npmjs.org/package/express-session -[npm-version-image]: https://badgen.net/npm/v/express-session diff --git a/_includes/readmes/timeout.md b/_includes/readmes/timeout.md deleted file mode 100644 index 01586dd32c..0000000000 --- a/_includes/readmes/timeout.md +++ /dev/null @@ -1,165 +0,0 @@ -# connect-timeout - -[![NPM Version][npm-image]][npm-url] -[![NPM Downloads][downloads-image]][downloads-url] -[![Build Status][travis-image]][travis-url] -[![Test Coverage][coveralls-image]][coveralls-url] - -Times out a request in the Connect/Express application framework. - -## Install - -This is a [Node.js](https://nodejs.org/en/) module available through the -[npm registry](https://www.npmjs.com/). Installation is done using the -[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): - -```sh -$ npm install connect-timeout -``` - -## API - -**NOTE** This module is not recommend as a "top-level" middleware (i.e. -`app.use(timeout('5s'))`) unless you take precautions to halt your own -middleware processing. See [as top-level middleware](#as-top-level-middleware) -for how to use as a top-level middleware. - -While the library will emit a 'timeout' event when requests exceed the given -timeout, node will continue processing the slow request until it terminates. -Slow requests will continue to use CPU and memory, even if you are returning -a HTTP response in the timeout callback. For better control over CPU/memory, -you may need to find the events that are taking a long time (3rd party HTTP -requests, disk I/O, database calls) and find a way to cancel them, and/or -close the attached sockets. - -### timeout(time, [options]) - -Returns middleware that times out in `time` milliseconds. `time` can also -be a string accepted by the [ms](https://www.npmjs.org/package/ms#readme) -module. On timeout, `req` will emit `"timeout"`. - -#### Options - -The `timeout` function takes an optional `options` object that may contain -any of the following keys: - -##### respond - -Controls if this module will "respond" in the form of forwarding an error. -If `true`, the timeout error is passed to `next()` so that you may customize -the response behavior. This error has a `.timeout` property as well as -`.status == 503`. This defaults to `true`. - -### req.clearTimeout() - -Clears the timeout on the request. The timeout is completely removed and -will not fire for this request in the future. - -### req.timedout - -`true` if timeout fired; `false` otherwise. - -## Examples - -### as top-level middleware - -Because of the way middleware processing works, once this module -passes the request to the next middleware (which it has to do in order -for you to do work), it can no longer stop the flow, so you must take -care to check if the request has timedout before you continue to act -on the request. - -```javascript -var bodyParser = require('body-parser') -var cookieParser = require('cookie-parser') -var express = require('express') -var timeout = require('connect-timeout') - -// example of using this top-level; note the use of haltOnTimedout -// after every middleware; it will stop the request flow on a timeout -var app = express() -app.use(timeout('5s')) -app.use(bodyParser()) -app.use(haltOnTimedout) -app.use(cookieParser()) -app.use(haltOnTimedout) - -// Add your routes here, etc. - -function haltOnTimedout (req, res, next) { - if (!req.timedout) next() -} - -app.listen(3000) -``` - -### express 3.x - -```javascript -var express = require('express') -var bodyParser = require('body-parser') -var timeout = require('connect-timeout') - -var app = express() -app.post('/save', timeout('5s'), bodyParser.json(), haltOnTimedout, function (req, res, next) { - savePost(req.body, function (err, id) { - if (err) return next(err) - if (req.timedout) return - res.send('saved as id ' + id) - }) -}) - -function haltOnTimedout (req, res, next) { - if (!req.timedout) next() -} - -function savePost (post, cb) { - setTimeout(function () { - cb(null, ((Math.random() * 40000) >>> 0)) - }, (Math.random() * 7000) >>> 0) -} - -app.listen(3000) -``` - -### connect - -```javascript -var bodyParser = require('body-parser') -var connect = require('connect') -var timeout = require('connect-timeout') - -var app = connect() -app.use('/save', timeout('5s'), bodyParser.json(), haltOnTimedout, function (req, res, next) { - savePost(req.body, function (err, id) { - if (err) return next(err) - if (req.timedout) return - res.send('saved as id ' + id) - }) -}) - -function haltOnTimedout (req, res, next) { - if (!req.timedout) next() -} - -function savePost (post, cb) { - setTimeout(function () { - cb(null, ((Math.random() * 40000) >>> 0)) - }, (Math.random() * 7000) >>> 0) -} - -app.listen(3000) -``` - -## License - -[MIT](LICENSE) - -[npm-image]: https://img.shields.io/npm/v/connect-timeout.svg -[npm-url]: https://npmjs.org/package/connect-timeout -[travis-image]: https://img.shields.io/travis/expressjs/timeout/master.svg -[travis-url]: https://travis-ci.org/expressjs/timeout -[coveralls-image]: https://img.shields.io/coveralls/expressjs/timeout/master.svg -[coveralls-url]: https://coveralls.io/r/expressjs/timeout?branch=master -[downloads-image]: https://img.shields.io/npm/dm/connect-timeout.svg -[downloads-url]: https://npmjs.org/package/connect-timeout diff --git a/_includes/readmes/vhost.md b/_includes/readmes/vhost.md deleted file mode 100644 index 422e67f476..0000000000 --- a/_includes/readmes/vhost.md +++ /dev/null @@ -1,163 +0,0 @@ -# vhost - -[![NPM Version][npm-image]][npm-url] -[![NPM Downloads][downloads-image]][downloads-url] -[![Build Status][github-actions-ci-image]][github-actions-ci-url] -[![Test Coverage][coveralls-image]][coveralls-url] - -## Install - -```sh -$ npm install vhost -``` - -## API - -```js -var vhost = require('vhost') -``` - -### vhost(hostname, handle) - -Create a new middleware function to hand off request to `handle` when the incoming -host for the request matches `hostname`. The function is called as -`handle(req, res, next)`, like a standard middleware. - -`hostname` can be a string or a RegExp object. When `hostname` is a string it can -contain `*` to match 1 or more characters in that section of the hostname. When -`hostname` is a RegExp, it will be forced to case-insensitive (since hostnames are) -and will be forced to match based on the start and end of the hostname. - -When host is matched and the request is sent down to a vhost handler, the `req.vhost` -property will be populated with an object. This object will have numeric properties -corresponding to each wildcard (or capture group if RegExp object provided) and the -`hostname` that was matched. - -```js -var connect = require('connect') -var vhost = require('vhost') -var app = connect() - -app.use(vhost('*.*.example.com', function handle (req, res, next) { - // for match of "foo.bar.example.com:8080" against "*.*.example.com": - console.dir(req.vhost.host) // => 'foo.bar.example.com:8080' - console.dir(req.vhost.hostname) // => 'foo.bar.example.com' - console.dir(req.vhost.length) // => 2 - console.dir(req.vhost[0]) // => 'foo' - console.dir(req.vhost[1]) // => 'bar' -})) -``` - -## Examples - -### using with connect for static serving - -```js -var connect = require('connect') -var serveStatic = require('serve-static') -var vhost = require('vhost') - -var mailapp = connect() - -// add middlewares to mailapp for mail.example.com - -// create app to serve static files on subdomain -var staticapp = connect() -staticapp.use(serveStatic('public')) - -// create main app -var app = connect() - -// add vhost routing to main app for mail -app.use(vhost('mail.example.com', mailapp)) - -// route static assets for "assets-*" subdomain to get -// around max host connections limit on browsers -app.use(vhost('assets-*.example.com', staticapp)) - -// add middlewares and main usage to app - -app.listen(3000) -``` - -### using with connect for user subdomains - -```js -var connect = require('connect') -var serveStatic = require('serve-static') -var vhost = require('vhost') - -var mainapp = connect() - -// add middlewares to mainapp for the main web site - -// create app that will server user content from public/{username}/ -var userapp = connect() - -userapp.use(function (req, res, next) { - var username = req.vhost[0] // username is the "*" - - // pretend request was for /{username}/* for file serving - req.originalUrl = req.url - req.url = '/' + username + req.url - - next() -}) -userapp.use(serveStatic('public')) - -// create main app -var app = connect() - -// add vhost routing for main app -app.use(vhost('userpages.local', mainapp)) -app.use(vhost('www.userpages.local', mainapp)) - -// listen on all subdomains for user pages -app.use(vhost('*.userpages.local', userapp)) - -app.listen(3000) -``` - -### using with any generic request handler - -```js -var connect = require('connect') -var http = require('http') -var vhost = require('vhost') - -// create main app -var app = connect() - -app.use(vhost('mail.example.com', function (req, res) { - // handle req + res belonging to mail.example.com - res.setHeader('Content-Type', 'text/plain') - res.end('hello from mail!') -})) - -// an external api server in any framework -var httpServer = http.createServer(function (req, res) { - res.setHeader('Content-Type', 'text/plain') - res.end('hello from the api!') -}) - -app.use(vhost('api.example.com', function (req, res) { - // handle req + res belonging to api.example.com - // pass the request to a standard Node.js HTTP server - httpServer.emit('request', req, res) -})) - -app.listen(3000) -``` - -## License - -[MIT](LICENSE) - -[npm-image]: https://img.shields.io/npm/v/vhost.svg -[npm-url]: https://npmjs.org/package/vhost -[coveralls-image]: https://img.shields.io/coveralls/expressjs/vhost/master.svg -[coveralls-url]: https://coveralls.io/r/expressjs/vhost -[downloads-image]: https://img.shields.io/npm/dm/vhost.svg -[downloads-url]: https://npmjs.org/package/vhost -[github-actions-ci-image]: https://img.shields.io/github/actions/workflow/status/expressjs/vhost/ci.yml?branch=master&label=ci -[github-actions-ci-url]: https://github.com/expressjs/vhost/actions/workflows/ci.yml diff --git a/_includes/util-list.md b/_includes/util-list.md deleted file mode 100644 index 8abc5d6a5d..0000000000 --- a/_includes/util-list.md +++ /dev/null @@ -1,38 +0,0 @@ -## Utility modules in jshttp - -- [negotiator](/{{page.lang}}/resources/utils/negotiator.html) -- [cookie](/{{page.lang}}/resources/utils/cookie.html) -- [fresh](/{{page.lang}}/resources/utils/fresh.html) -- [range-parser](/{{page.lang}}/resources/utils/range-parser.html) -- [methods](/{{page.lang}}/resources/utils/methods.html) -- [basic-auth](/{{page.lang}}/resources/utils/basic-auth.html) -- [compressible](/{{page.lang}}/resources/utils/compressible.html) -- [on-finished](/{{page.lang}}/resources/utils/on-finished.html) -- [http-assert](/{{page.lang}}/resources/utils/http-assert.html) -- [accepts](/{{page.lang}}/resources/utils/accepts.html) -- [type-is](/{{page.lang}}/resources/utils/type-is.html) -- [statuses](/{{page.lang}}/resources/utils/statuses.html) -- [mime-types](/{{page.lang}}/resources/utils/mime-types.html) -- [proxy-addr](/{{page.lang}}/resources/utils/proxy-addr.html) -- [on-headers](/{{page.lang}}/resources/utils/on-headers.html) -- [vary](/{{page.lang}}/resources/utils/vary.html) -- [media-typer](/{{page.lang}}/resources/utils/media-typer.html) -- [etag](/{{page.lang}}/resources/utils/etag.html) -- [mime-db](/{{page.lang}}/resources/utils/mime-db.html) -- [http-/{{page.lang}}/resources/utils](/{{page.lang}}/resources/utils/http-/{{page.lang}}/resources/utils.html) -- [spdy-push](/{{page.lang}}/resources/utils/spdy-push.html) -- [http-errors](/{{page.lang}}/resources/utils/http-errors.html) -- [content-disposition](/{{page.lang}}/resources/utils/content-disposition.html) -- [forwarded](/{{page.lang}}/resources/utils/forwarded.html) -- [content-type](/{{page.lang}}/resources/utils/content-type.html) - -## Utility modules in pillarjs - -- [cookies](/{{page.lang}}/resources/utils/cookies.html) -- [csrf](/{{page.lang}}/resources/utils/csrf.html) -- [finalhandler](/{{page.lang}}/resources/utils/finalhandler.html) -- [parseurl](/{{page.lang}}/resources/utils/parseurl.html) -- [path-to-regexp](/{{page.lang}}/resources/utils/path-to-regexp.html) -- [resolve-path](/{{page.lang}}/resources/utils/resolve-path.html) -- [router](/{{page.lang}}/resources/utils/router.html) -- [send](/{{page.lang}}/resources/utils/send.html) diff --git a/_layouts/404.html b/_layouts/404.html deleted file mode 100644 index 84f18bfff3..0000000000 --- a/_layouts/404.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - {% include head.html %} - - {% if page.lang == 'en' %} - - {% else %} - - {% endif %} - {% include header.html %} -
-
- {{ content }} -
- {% include footer.html %} - - \ No newline at end of file diff --git a/_layouts/api.html b/_layouts/api.html deleted file mode 100644 index 6e01f7f6cd..0000000000 --- a/_layouts/api.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - {% include head.html %} - - - {% include header.html %} -
- -
- -
- {{ content }} - {% include github-edit-btn.html %} -
-
- - {% include footer.html %} - - - - diff --git a/_layouts/feed.xml b/_layouts/feed.xml deleted file mode 100644 index 09a7aab788..0000000000 --- a/_layouts/feed.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - {{ page.title }} - - - {{ site.url }}{{ page.url }} - Copyright The Express Contributors - {{ site.time | date_to_xmlschema }} - {{ content }} - diff --git a/_layouts/home.html b/_layouts/home.html deleted file mode 100644 index 8d5abafc95..0000000000 --- a/_layouts/home.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - {% include head.html %} - - {% if page.lang == 'en' %} - - {% else %} - - {% endif %} - - {% include header.html %} - -
- - {% if page.lang != 'en' %} - - {% endif %} - -
- {{ content }} -
- - {% include footer.html %} - - - diff --git a/_layouts/middleware.html b/_layouts/middleware.html deleted file mode 100644 index 07c9a7e66c..0000000000 --- a/_layouts/middleware.html +++ /dev/null @@ -1,39 +0,0 @@ ---- -layout: page ---- - - - -
- {% if page.module == 'mw-home' %} - {{content}} - - {% elsif page.module %} - {% capture note-middleware %} - This page was generated from the {{page.module}} README. - {% endcapture %} - - {% include admonitions/note.html content=note-middleware %} - - {% capture included-readme %}{% include readmes/{{page.module}}.md %}{% endcapture %} - - {{ included-readme | markdownify }} - - {% else %} -

ERROR: No source specified for README {{page.module}}

- {% endif %} - -
diff --git a/_layouts/page.html b/_layouts/page.html deleted file mode 100644 index e013c5b3ab..0000000000 --- a/_layouts/page.html +++ /dev/null @@ -1,42 +0,0 @@ - -{% if page.lang %} - - - {% include head.html %} - - {% if page.lang == 'en' %} - - {% else %} - - {% endif %} - - {% include header.html %} - -
- - {% if page.lang != 'en' %} - - {% endif %} - - {% if page.layout == 'middleware' %} -
- {{ content }} -
- {% else %} -
-
- {{ content }} - {% include bottom-navigation.html %} - {% include github-edit-btn.html %} -
-
- {% endif %} - - {% include footer.html %} - - - - -{% endif %} diff --git a/_layouts/post.html b/_layouts/post.html deleted file mode 100644 index 4f79504c5b..0000000000 --- a/_layouts/post.html +++ /dev/null @@ -1,36 +0,0 @@ - - - {% include head.html %} - - - {% include header.html %} -
- -
- -
- {% if page.title %} -

{{page.title}}

- {% endif %} - {% if page.sub_title %} -

{{page.sub_title}}

- {% endif %} -
- {% include blog/authors.html authors=page.authors %} -
{{page.date| date: "%d %b %Y" }}
-
- {{ content }} - {% include github-edit-btn.html %} -
-
- - {% include footer.html %} - - - - diff --git a/_posts/2024-07-16-welcome-post.md b/_posts/2024-07-16-welcome-post.md deleted file mode 100644 index 52fc60a25a..0000000000 --- a/_posts/2024-07-16-welcome-post.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: Welcome to The Express Blog! -description: Introducing the new Express blog — a primary platform for announcements, updates, and communication from the Express technical committee. -tags: site-admin -authors: - - name: Rand McKinney - github: crandmck - - name: Chris Del - github: chrisdel101 ---- - -Welcome to the new Express blog! The blog is meant to be a primary means of communication for the Express technical committee (TC). While we currently have other channels such as X, LinkedIn, and of course GitHub, there's no authoritative "soapbox" for announcements and general communication. - -Initially, the Express blog will be a venue: -- For periodic announcements of new releases, pre-releases, plans, and ongoing work on the project. -- For the Express TC to discuss issues of particular importance to the Express community. -- To highlight security issues or other urgent information. - -Eventually, we hope the blog will evolve into a more general communication hub for the entire Express community; for example to share examples, tips, and experiences with the Express ecosystem and other information that's not simply technical documentation or GitHub discussion. - -Initially, posts will be written by TC members (potentially collaborating others), mainly because we don't have bandwidth to review general posts from the broader community. Eventually, we would love to open up the blog for broader contributions, but for now the focus is on trying to release Express 5.0, and the reality of an open-source project is that everyone has finite time to contribute. - -Express TC member [Ulises Gascón](https://github.com/UlisesGascon) suggested a number of interesting topics for blog posts in [expressjs.com issue 1500](https://github.com/expressjs/expressjs.com/issues/1500), but there are undoubtedly many others. - -If you have an idea for a post, feel free to pitch the idea! You can add a comment to [expressjs.com issue 1500](https://github.com/expressjs/expressjs.com/issues/1500) or open a new issue, and then after appropriate discussion, open a PR. We've also written up simple [instructions to create a blog post](/en/blog/write-post.html). - -Happy blogging! diff --git a/_posts/2024-09-29-security-releases.md b/_posts/2024-09-29-security-releases.md deleted file mode 100644 index 658fe345f5..0000000000 --- a/_posts/2024-09-29-security-releases.md +++ /dev/null @@ -1,137 +0,0 @@ ---- -title: September 2024 Security Releases -description: Security releases for Express, body-parser, send, serve-static, and path-to-regexp have been published. We recommend that all users upgrade as soon as possible. -tags: security vulnerabilities -authors: - - name: Ulises Gascón - github: UlisesGascon ---- - -Recently, the Express team has been made aware of a number of security vulnerabilities in the Express project. We have released a number of patches to address these vulnerabilities. - -{% include admonitions/warning.html -content="We strongly recommend that you upgrade these modules to the recommended (or latest) version as soon as possible." -%} - -The following vulnerabilities have been addressed: - -- [High severity vulnerability CVE-2024-45590 in body-parser middleware](#high-severity-vulnerability-cve-2024-45590-in-body-parser-middleware) -- [High severity vulnerability CVE-2024-47178 in basic-auth-connect middleware](#high-severity-vulnerability-cve-2024-47178-in-basic-auth-connect-middleware) -- [Moderate severity vulnerability CVE-2024-43796 in Express core](#moderate-severity-vulnerability-cve-2024-43796-in-express-core) -- [Moderate severity vulnerability CVE-2024-43799 in send utility module](#moderate-severity-vulnerability-cve-2024-43799-in-send-utility-module) -- [Moderate severity vulnerability CVE-2024-43800 in serve-static middleware](#moderate-severity-vulnerability-cve-2024-43800-in-serve-static-middleware) -- [Moderate severity vulnerability CVE-2024-45296 in path-to-regexp utility module](#moderate-severity-vulnerability-cve-2024-45296-in-path-to-regexp-utility-module) - -## High severity vulnerability CVE-2024-45590 in body-parser middleware - -**[body-parser](https://www.npmjs.com/package/body-parser) version `<1.20.3` is vulnerable to denial of service when URL-encoding is enabled** - -A malicious actor using a specially-crafted payload could flood the server with a large number of requests, resulting in denial of service. - -**Affected versions**: `<1.20.3` - -**Patched versions**: `>=1.20.3` - -This vulnerability was discovered during the [OSTIF audit to Express](https://github.com/expressjs/security-wg/issues/6) and was mitigated by [the Express security triage team](https://github.com/expressjs/security-wg?tab=readme-ov-file#security-triage-team). - -For more details, see [GHSA-qwcr-r2fm-qrc7](https://github.com/expressjs/body-parser/security/advisories/GHSA-qwcr-r2fm-qrc7). - -## High severity vulnerability CVE-2024-47178 in basic-auth-connect middleware - -**[basic-auth-connect](https://www.npmjs.com/package/basic-auth-connect) uses a timing-unsafe equality comparison** - -basic-auth-connect `<1.1.0` uses a timing-unsafe equality comparison that can leak timing information - -**Affected versions** -- `<1.1.0` - -**Patched versions** -- `>=1.1.0` - -This vulnerability was discovered during the [OSTIF audit to Express](https://github.com/expressjs/security-wg/issues/6) and was mitigated by [the Express Securty triage team](https://github.com/expressjs/security-wg?tab=readme-ov-file#security-triage-team). - -More details area available in [GHSA-7p89-p6hx-q4fw](https://github.com/expressjs/basic-auth-connect/security/advisories/GHSA-7p89-p6hx-q4fw) - - - -## Moderate severity vulnerability CVE-2024-43796 in Express core - -The core **[express](https://www.npmjs.com/package/express) package is vulnerable to cross-site scripting (XSS) attack via `response.redirect()`**. - -In Express version <4.20.0, passing untrusted user input—even after sanitizing it—to `response.redirect()` may execute untrusted code. - -**Affected versions**: -- `<4.20.0` -- `>=5.0.0-alpha.1`, `<5.0.0` - -**Patched versions**: -- `>=4.20.0` -- `>=5.0.0` - -This vulnerability was discovered during the [OSTIF audit of Express](https://github.com/expressjs/security-wg/issues/6) and was mitigated by [the Express security triage team](https://github.com/expressjs/security-wg?tab=readme-ov-file#security-triage-team). - -For more details, see [GHSA-qw6h-vgh9-j6wx](https://github.com/expressjs/express/security/advisories/GHSA-qw6h-vgh9-j6wx). - - -## Moderate severity vulnerability CVE-2024-43799 in send utility module - -The **[send](https://www.npmjs.com/package/send) utility module is vulnerable to template injection that can lead to vulnerability to cross-site scripting (XSS) attack**. - -Passing untrusted user input—even after sanitizing it—to `SendStream.redirect()` can execute untrusted code. - -**Affected versions**: `< 0.19.0` - -**Patched versions**: `>=0.19.0` - -This vulnerability was discovered during the [OSTIF audit of Express](https://github.com/expressjs/security-wg/issues/6) and was mitigated by [the Express security triage team](https://github.com/expressjs/security-wg?tab=readme-ov-file#security-triage-team). - -For more details, see [GHSA-m6fv-jmcg-4jfg](https://github.com/pillarjs/send/security/advisories/GHSA-m6fv-jmcg-4jfg). - - -## Moderate severity vulnerability CVE-2024-43800 in serve-static middleware - -The **[serve-static](https://www.npmjs.com/package/serve-static) middleware module is vulnerable to template injection that can lead to vulnerability to cross-site scripting (XSS) attack**. - -Passing untrusted user input—even after sanitizing it—to `redirect()` can execute untrusted code. - -**Affected versions**: -- `< 1.16.0` -- `>=2.0.0`, `<2.1.0` - -**Patched versions**: -- `>=1.16.0` -- `>=2.1.0` - -This vulnerability was discovered during the [OSTIF audit of Express](https://github.com/expressjs/security-wg/issues/6) and was mitigated by [the Express security triage team](https://github.com/expressjs/security-wg?tab=readme-ov-file#security-triage-team). - -For more details, see [GHSA-cm22-4g7w-348p](https://github.com/expressjs/serve-static/security/advisories/GHSA-cm22-4g7w-348p) - - -## Moderate severity vulnerability CVE-2024-45296 in path-to-regexp utility module - -The **[path-to-regexp](https://www.npmjs.com/package/path-to-regexp) utility module is vulnerable to regular expression denial of service (ReDoS) attack**. - -A bad regular expression is generated any time you have two parameters within a single segment, separated by something that is not a period (`.`). For example, `/:a-:b`. - -Using `/:a-:b` will produce the regular expression `/^\/([^\/]+?)-([^\/]+?)\/?$/`. This can be exploited by a path such as `/a${'-a'.repeat(8_000)}/a`. [OWASP](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS) has a good example of why this occurs, but in essence, the `/a` at the end ensures this route would never match, but due to naive backtracking it will still attempt every combination of the `:a-:b` on the repeated 8,000 `-a`. - -Because JavaScript is single-threaded and regex matching runs on the main thread, poor performance will block the event loop and can lead to a DoS. In local benchmarks, exploiting the unsafe regex will result in performance that is over 1000x worse than the safe regex. In a more realistic environment, using Express v4 and ten concurrent connections results in an average latency of ~600ms vs 1ms. - -**Affected versions**: -- `>=4.0.0`, `<8.0.0` -- `>=0.2.0`, `<1.9.0` -- `<0.1.10` -- `>=2.0.0`, `<3.3.0` -- `>=4.0.0`, `<6.3.0` - -**Patched versions**: -- `>=8.0.0` -- `>=1.9.0` -- `>=0.1.10` -- `>=3.3.0` -- `>=6.3.0` - -Thanks to [Blake Embrey](https://github.com/blakeembrey) who reported and created the security patch. - -For more details, see [GHSA-9wv6-86v2-598j](https://github.com/pillarjs/path-to-regexp/security/advisories/GHSA-9wv6-86v2-598j) - diff --git a/_posts/2025-05-19-security-releases.md b/_posts/2025-05-19-security-releases.md deleted file mode 100644 index bfab761a5b..0000000000 --- a/_posts/2025-05-19-security-releases.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: May 2025 Security Releases -description: Security release for Multer has been published. We recommend that all users upgrade as soon as possible. -tags: security vulnerabilities -authors: - - name: Ulises Gascón - github: UlisesGascon ---- - -The Express team has released a new major version of [Multer](https://www.npmjs.com/package/multer) addressing two high-severity security vulnerabilities. This update improves the reliability and security of handling file uploads in Express applications. - -{% include admonitions/warning.html -content="We strongly recommend that you upgrade to Multer v2.0.0 or later as soon as possible." -%} - -The following vulnerabilities have been addressed: - -- [High severity vulnerability CVE-2025-47935 in Multer middleware](#high-severity-vulnerability-cve-2025-47935-in-multer-middleware) -- [High severity vulnerability CVE-2025-47944 in Multer middleware](#high-severity-vulnerability-cve-2025-47944-in-multer-middleware) - -## High severity vulnerability CVE-2025-47935 in Multer middleware - -**[Multer](https://www.npmjs.com/package/multer) versions `<2.0.0` are vulnerable to denial of service due to a memory leak caused by improper stream handling.** - -When the HTTP request stream emits an error, the internal `busboy` stream is not closed, violating Node.js stream safety guidance. - -This leads to unclosed streams accumulating over time, consuming memory and file descriptors. Under sustained or repeated failure conditions, this can result in denial of service, requiring manual server restarts to recover. All users of Multer handling file uploads are potentially impacted. - -**Affected versions**: `<2.0.0` -**Patched version**: `>=2.0.0` - -For more details, see [GHSA-44fp-w29j-9vj5](https://github.com/expressjs/multer/security/advisories/GHSA-44fp-w29j-9vj5). - -## High severity vulnerability CVE-2025-47944 in Multer middleware - -**[Multer](https://www.npmjs.com/package/multer) versions `>=1.4.4-lts.1` and `<2.0.0` are vulnerable to a denial of service via a malformed multipart request.** - -A specially crafted request can cause an unhandled exception inside Multer, resulting in a crash of the server process. - -**Affected versions**: `>=1.4.4-lts.1` and `<2.0.0` -**Patched version**: `>=2.0.0` - -For more details, see [GHSA-4pg4-qvpc-4q3h](https://github.com/expressjs/multer/security/advisories/GHSA-4pg4-qvpc-4q3h). - ---- - -**Multer v2.0.0** also introduces a breaking change: - -- The minimum supported Node.js version is now **10.16.0**. - -We recommend upgrading to the latest version of Multer immediately to secure your applications. diff --git a/_posts/2025-07-18-security-releases.md b/_posts/2025-07-18-security-releases.md deleted file mode 100644 index e6e4d86033..0000000000 --- a/_posts/2025-07-18-security-releases.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: June 2025 Security Releases -description: Security update for Multer released. All users are encouraged to upgrade. -tags: security vulnerabilities -authors: - - name: Ulises Gascón - github: UlisesGascon ---- - -The Express team has released a new patch version of [Multer](https://www.npmjs.com/package/multer), addressing a high-severity vulnerability that could lead to a Denial of Service (DoS) attack. - -{% include admonitions/warning.html -content="We strongly recommend that all users upgrade to Multer v2.0.1 or later immediately." -%} - -This release addresses the following vulnerability: - -### High severity vulnerability CVE-2025-48997 in Multer middleware - -**[Multer](https://www.npmjs.com/package/multer) versions `>=1.4.4-lts.1` and `<2.0.1` are vulnerable to a Denial of Service (DoS) attack.** - -An attacker can trigger this vulnerability by sending an upload request with an empty string as the field name. This malformed request causes an unhandled exception, leading to a crash of the server process. - -**Affected versions**: `>=1.4.4-lts.1` and `<2.0.1` -**Patched version**: `2.0.1` - -For more details, see [GHSA-g5hg-p3ph-g8qg](https://github.com/expressjs/multer/security/advisories/GHSA-g5hg-p3ph-g8qg). - - diff --git a/_posts/2025-12-01-security-releases.md b/_posts/2025-12-01-security-releases.md deleted file mode 100644 index 3ec8a23a3b..0000000000 --- a/_posts/2025-12-01-security-releases.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: November 2025 Security Releases -description: Security release for body-parser has been published. We recommend that all users upgrade as soon as possible. -tags: security vulnerabilities -authors: - - name: Ulises Gascón - github: UlisesGascon ---- - -The Express team has released a new patch version of [body-parser](https://www.npmjs.com/package/body-parser) addressing a moderate-severity security vulnerability. - - -{% include admonitions/warning.html -content="We recommend upgrading to the latest version of body-parser to secure your applications." -%} - -The following vulnerabilities have been addressed: - -- [CVE-2025-13466 in body-parser middleware (Moderate)](#cve-2025-13466-in-body-parser-middleware-moderate) - -## CVE-2025-13466 in Body-parser middleware (Moderate) - -**[body-parser](https://www.npmjs.com/package/body-parser) version `2.2.0` is vulnerable to denial of service when url encoding is used** - -body-parser 2.2.0 is vulnerable to denial of service due to inefficient handling of URL-encoded bodies with very large numbers of parameters. An attacker can send payloads containing thousands of parameters within the default 100KB request size limit, causing elevated CPU and memory usage. This can lead to service slowdown or partial outages under sustained malicious traffic. - -**Affected versions**: `2.2.0` -**Patched version**: `>= 2.2.1` - -For more details, see [GHSA-wqch-xfxh-vrr4](https://github.com/expressjs/body-parser/security/advisories/GHSA-wqch-xfxh-vrr4). - ---- - -We recommend upgrading to the latest version of body-parser to secure your applications. diff --git a/_posts/2026-02-27-security-releases.md b/_posts/2026-02-27-security-releases.md deleted file mode 100644 index dc3ec2021b..0000000000 --- a/_posts/2026-02-27-security-releases.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: February 2026 Security Releases -description: Security release for multer has been published. We recommend that all users upgrade as soon as possible. -tags: security vulnerabilities -authors: - - name: Ulises Gascón - github: UlisesGascon ---- - -The Express team has released a new patch version of [multer](https://www.npmjs.com/package/multer) addressing two high-severity security vulnerabilities. - - -{% include admonitions/warning.html -content="We recommend upgrading to the latest version of multer to secure your applications." -%} - -The following vulnerabilities have been addressed: - -- [CVE-2026-3304 in multer middleware (High)](#cve-2026-3304-in-multer-middleware-high) -- [CVE-2026-2359 in multer middleware (High)](#cve-2026-2359-in-multer-middleware-high) - -## CVE-2026-3304 in multer middleware (High) - -**[multer](https://www.npmjs.com/package/multer) versions `<2.1.0` are vulnerable to denial of service via incomplete cleanup** - -A vulnerability in Multer versions <2.1.0 allows an attacker to trigger a Denial of Service (DoS) by sending malformed requests, potentially causing resource exhaustion. - -**Affected versions**: `< 2.1.0` -**Patched version**: `>= 2.1.0` - -For more details, see [GHSA-xf7r-hgr6-v32p](https://github.com/expressjs/multer/security/advisories/GHSA-xf7r-hgr6-v32p). - -## CVE-2026-2359 in multer middleware (High) - -**[multer](https://www.npmjs.com/package/multer) versions `<2.1.0` are vulnerable to denial of service via resource exhaustion** - -A vulnerability in Multer versions <2.1.0 allows an attacker to trigger a Denial of Service (DoS) by dropping connection during file upload, potentially causing resource exhaustion. - -**Affected versions**: `< 2.1.0` -**Patched version**: `>= 2.1.0` - -For more details, see [GHSA-v52c-386h-88mc](https://github.com/expressjs/multer/security/advisories/GHSA-v52c-386h-88mc). - ---- - -We recommend upgrading to the latest version of multer to secure your applications. diff --git a/_posts/2026-03-30-security-releases.md b/_posts/2026-03-30-security-releases.md deleted file mode 100644 index b3f4564fe8..0000000000 --- a/_posts/2026-03-30-security-releases.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: March 2026 Security Releases -description: Security releases for path-to-regexp have been published. We recommend that all users upgrade as soon as possible. -tags: security vulnerabilities -authors: - - name: Ulises Gascon - github: UlisesGascon ---- - -The Express team has released new patch versions of [path-to-regexp](https://www.npmjs.com/package/path-to-regexp) addressing three regular expression denial of service vulnerabilities. - -{% capture warning_content %} -We recommend upgrading to the latest version of path-to-regexp to secure your applications. If you have a `package-lock.json`, you can update the dependency by running: - -```sh -npm update path-to-regexp -``` -{% endcapture %} -{% include admonitions/warning.html content=warning_content %} - -The following vulnerabilities have been addressed: - -- [CVE-2026-4867 in path-to-regexp utility module (High)](#cve-2026-4867-in-path-to-regexp-utility-module-high) -- [CVE-2026-4926 in path-to-regexp utility module (High)](#cve-2026-4926-in-path-to-regexp-utility-module-high) -- [CVE-2026-4923 in path-to-regexp utility module (Medium)](#cve-2026-4923-in-path-to-regexp-utility-module-medium) - -## CVE-2026-4867 in path-to-regexp utility module (High) - -**[path-to-regexp](https://www.npmjs.com/package/path-to-regexp) versions `<= 0.1.12` are vulnerable to regular expression denial of service via multiple route parameters** - -A bad regular expression is generated any time you have three or more parameters within a single segment, separated by something that is not a period. For example, `/:a-:b-:c`. The backtrack protection added in v0.1.12 only prevents ambiguity for two parameters. With three or more, the generated lookahead does not block single separator characters, causing catastrophic backtracking. - -**Affected versions**: `<= 0.1.12` -**Patched version**: `>= 0.1.13` - -For more details, see [GHSA-37ch-88jc-xwx2](https://github.com/pillarjs/path-to-regexp/security/advisories/GHSA-37ch-88jc-xwx2). - -## CVE-2026-4926 in path-to-regexp utility module (High) - -**[path-to-regexp](https://www.npmjs.com/package/path-to-regexp) versions `>= 8.0.0` are vulnerable to denial of service via sequential optional groups** - -A bad regular expression is generated any time you have multiple sequential optional groups, such as `{a}{b}{c}:z`. The generated regex grows exponentially with the number of groups, causing denial of service. Avoid passing user-controlled input as route patterns. - -**Affected versions**: `>= 8.0.0` -**Patched version**: `>= 8.4.0` - -For more details, see [GHSA-j3q9-mxjg-w52f](https://github.com/pillarjs/path-to-regexp/security/advisories/GHSA-j3q9-mxjg-w52f). - -## CVE-2026-4923 in path-to-regexp utility module (Medium) - -**[path-to-regexp](https://www.npmjs.com/package/path-to-regexp) versions `>= 8.0.0, <= 8.3.0` are vulnerable to regular expression denial of service via multiple wildcards** - -When using multiple wildcards combined with at least one parameter, a regular expression can be generated that is vulnerable to ReDoS. The second wildcard must be somewhere other than the end of the path. For example, `/*foo-*bar-:baz`. - -**Affected versions**: `>= 8.0.0, <= 8.3.0` -**Patched version**: `>= 8.4.0` - -For more details, see [GHSA-27v5-c462-wpq7](https://github.com/pillarjs/path-to-regexp/security/advisories/GHSA-27v5-c462-wpq7). - ---- - -We recommend upgrading to the latest version of path-to-regexp to secure your applications. diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 0000000000..557730f332 --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,77 @@ +// @ts-check +import { defineConfig } from 'astro/config'; +import mdx from '@astrojs/mdx'; +import sitemap from '@astrojs/sitemap'; +import icon from 'astro-icon'; +import expressiveCode from 'astro-expressive-code'; +import react from '@astrojs/react'; +import svgr from 'vite-plugin-svgr'; +import Icons from 'unplugin-icons/vite'; +import rehypeSlug from 'rehype-slug'; +import rehypeAutolinkHeadings from 'rehype-autolink-headings'; +import redirects from './src/config/redirect.js'; +import { accessibleTablesIntegration } from './src/plugins/rehype-accessible-tables.mjs'; + +/* https://docs.netlify.com/configure-builds/environment-variables/#read-only-variables */ +const NETLIFY_PREVIEW_SITE = process.env.CONTEXT !== 'production' && process.env.DEPLOY_PRIME_URL; + +const site = NETLIFY_PREVIEW_SITE || 'https://expressjs.com'; + +// https://astro.build/config +export default defineConfig({ + redirects, + site, + markdown: { + rehypePlugins: [ + rehypeSlug, + [ + rehypeAutolinkHeadings, + { + behavior: 'wrap', + }, + ], + ], + }, + vite: { + plugins: [ + // Transforms SVG files imported with the `?react` suffix into React components + // (used for local SVG assets like logos). + svgr(), + // Resolves `~icons/collection/icon-name` imports into React components, + // bundling only the SVG paths for icons actually used (no full icon set in the bundle). + Icons({ compiler: 'jsx', jsx: 'react' }), + ], + }, + integrations: [ + expressiveCode({ + themes: ['github-dark'], + styleOverrides: { + uiFontSize: 'var(--font-size-sm)', + codeFontSize: 'var(--font-size-sm)', + borderRadius: 'var(--radius-base)', + borderWidth: 'var(--border-width-1)', + }, + }), + accessibleTablesIntegration(), + mdx(), + icon(), + react(), + sitemap({ + i18n: { + defaultLocale: 'en', + locales: { + de: 'de', + en: 'en', + es: 'es', + fr: 'fr', + ja: 'ja', + it: 'it', + ko: 'ko', + 'pt-br': 'pt-BR', + 'zh-cn': 'zh-CN', + 'zh-tw': 'zh-TW', + }, + }, + }), + ], +}); diff --git a/crowdin.yml b/crowdin.yml index e8c2ad2c2e..821ccb4bf9 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,13 +1,10 @@ files: - - source: /en/**/*.* - ignore: - - /en/blog/**/*.* - - /**/changelog/index.md - translation: /%two_letters_code%/**/%original_file_name% - - source: /_data/en/**/*.* - translation: /_data/%two_letters_code%/**/%original_file_name% - - source: /index.md - translation: /%two_letters_code%/index.md + - source: /src/content/pages/en/**/*.* + translation: /src/content/pages/%two_letters_code%/**/%original_file_name% + - source: /src/content/docs/en/**/*.* + translation: /src/content/docs/%two_letters_code%/**/%original_file_name% + - source: /src/i18n/ui/en.json + translation: /src/i18n/ui/%two_letters_code%.json preserve_hierarchy: true project_id_env: CROWDIN_PROJECT_ID base_url: 'https://express.api.crowdin.com' diff --git a/css/langs/de.css b/css/langs/de.css deleted file mode 100644 index df6795b5a2..0000000000 --- a/css/langs/de.css +++ /dev/null @@ -1,14 +0,0 @@ -/* For image callouts in writing-middleware.md */ - -#callout1 {top: 40px; } - -#callout2 {top: 60px;} - -#callout3 {top: 75px;} - -#callout4 {top: 135px;} - -#callout5 {top: 150px;} - -#callout6 {top: 160px;} - diff --git a/css/langs/en.css b/css/langs/en.css deleted file mode 100644 index 8f2e5ac2dd..0000000000 --- a/css/langs/en.css +++ /dev/null @@ -1,13 +0,0 @@ -/* For image callouts in writing-middleware.md */ - -#callout1 {top: 40px; } - -#callout2 {top: 60px;} - -#callout3 {top: 75px;} - -#callout4 {top: 145px;} - -#callout5 {top: 170px;} - -#callout6 {top: 195px;} diff --git a/css/langs/es.css b/css/langs/es.css deleted file mode 100644 index dd8a578f8e..0000000000 --- a/css/langs/es.css +++ /dev/null @@ -1,13 +0,0 @@ -/* For image callouts in writing-middleware.md */ - -#callout1 {top: 40px; } - -#callout2 {top: 60px;} - -#callout3 {top: 75px;} - -#callout4 {top: 140px;} - -#callout5 {top: 150px;} - -#callout6 {top: 160px;} diff --git a/css/langs/fr.css b/css/langs/fr.css deleted file mode 100644 index 1c9938c3f7..0000000000 --- a/css/langs/fr.css +++ /dev/null @@ -1,13 +0,0 @@ -/* For image callouts in writing-middleware.md */ - -#callout1 {top: 40px; } - -#callout2 {top: 60px;} - -#callout3 {top: 75px;} - -#callout4 {top: 145px;} - -#callout5 {top: 165px;} - -#callout6 {top: 185px;} diff --git a/css/langs/it.css b/css/langs/it.css deleted file mode 100644 index 6b0bbc10b8..0000000000 --- a/css/langs/it.css +++ /dev/null @@ -1,13 +0,0 @@ -/* For image callouts in writing-middleware.md */ - -#callout1 {top: 40px; } - -#callout2 {top: 60px;} - -#callout3 {top: 75px;} - -#callout4 {top: 145px;} - -#callout5 {top: 150px;} - -#callout6 {top: 160px;} diff --git a/css/langs/ja.css b/css/langs/ja.css deleted file mode 100644 index 1c162162cd..0000000000 --- a/css/langs/ja.css +++ /dev/null @@ -1,15 +0,0 @@ -/* For image callouts in writing-middleware.md */ - -.callout {position: relative;} - -#callout1 {top: 40px; } - -#callout2 {top: 60px;} - -#callout3 {top: 75px;} - -#callout4 {top: 145px;} - -#callout5 {top: 165px;} - -#callout6 {top: 185px;} diff --git a/css/langs/ko.css b/css/langs/ko.css deleted file mode 100644 index d4c2b2ac66..0000000000 --- a/css/langs/ko.css +++ /dev/null @@ -1,19 +0,0 @@ -/* For image callouts in writing-middleware.md */ - -@import url('../../fonts/ko/pretendard.css'); - -html, body {font-family: Pretendard, Arial, sans-serif;} - -.callout {position: relative;} - -#callout1 {top: 40px; } - -#callout2 {top: 60px;} - -#callout3 {top: 75px;} - -#callout4 {top: 145px;} - -#callout5 {top: 165px;} - -#callout6 {top: 185px;} diff --git a/css/langs/pt-br.css b/css/langs/pt-br.css deleted file mode 100644 index dd8a578f8e..0000000000 --- a/css/langs/pt-br.css +++ /dev/null @@ -1,13 +0,0 @@ -/* For image callouts in writing-middleware.md */ - -#callout1 {top: 40px; } - -#callout2 {top: 60px;} - -#callout3 {top: 75px;} - -#callout4 {top: 140px;} - -#callout5 {top: 150px;} - -#callout6 {top: 160px;} diff --git a/css/langs/zh-cn.css b/css/langs/zh-cn.css deleted file mode 100644 index 8f2e5ac2dd..0000000000 --- a/css/langs/zh-cn.css +++ /dev/null @@ -1,13 +0,0 @@ -/* For image callouts in writing-middleware.md */ - -#callout1 {top: 40px; } - -#callout2 {top: 60px;} - -#callout3 {top: 75px;} - -#callout4 {top: 145px;} - -#callout5 {top: 170px;} - -#callout6 {top: 195px;} diff --git a/css/langs/zh-tw.css b/css/langs/zh-tw.css deleted file mode 100644 index 8f2e5ac2dd..0000000000 --- a/css/langs/zh-tw.css +++ /dev/null @@ -1,13 +0,0 @@ -/* For image callouts in writing-middleware.md */ - -#callout1 {top: 40px; } - -#callout2 {top: 60px;} - -#callout3 {top: 75px;} - -#callout4 {top: 145px;} - -#callout5 {top: 170px;} - -#callout6 {top: 195px;} diff --git a/css/search.css b/css/search.css deleted file mode 100644 index cb60915c73..0000000000 --- a/css/search.css +++ /dev/null @@ -1,22 +0,0 @@ -#q { - display: none; - height: 2.5em; - min-width: 100%; - padding: 5px; -} - -.algolia-autocomplete { - min-width: 12em; - max-width: 12em; - top: -0.2em; -} - -.algolia-autocomplete #q { - display: initial -} - -@media all and (max-width: 899px) { - .algolia-autocomplete { - display: none !important; - } -} diff --git a/css/sintax.css b/css/sintax.css deleted file mode 100644 index e33e6fbee9..0000000000 --- a/css/sintax.css +++ /dev/null @@ -1,141 +0,0 @@ -:root { - --orange: #953800; - --green: #116329; - --blue-light: #0550ae; - --blue-darker: #0a3069; - --pupple: #8250df; - --gray: #6e7781; - --red: #cf222e; - --red-darker: #82071e; -} - -.dark-mode:root { - --orange: #ffa657; - --green: #79c0ff; - --blue-light: #a5d6ff; - --blue-darker: #1f6feb; - --pupple: #d2a8ff; - --gray: #8b949e; - --red: #ff7b72; - --red-darker: #8e1519; -} - -.highlight table td { padding: 5px; } -.highlight table pre { margin: 0; } - -.highlight, .highlight .w { - color: #24292f; - background-color: var(--code-bg); -} - -.highlight .k, .highlight .kd, .highlight .kn, .highlight .kp, .highlight .kr, .highlight .kt, .highlight .kv { - color: var(--red); -} - -.highlight .gr { - color: #f6f8fa; -} - -.highlight .gd { - color: var(--red-darker); - background-color: #ffebe9; -} - -.highlight .nb, -.highlight .nc, -.highlight .no, -.highlight .nn { - color: var(--orange); -} - -.highlight .sr,.highlight .na, .highlight .nt, .highlight .gi { - color: var(--green); -} - -.highlight .gi { - background-color: #dafbe1; -} - -.highlight .ges { - font-weight: bold; - font-style: italic; -} - -.highlight .l, -.highlight .ld, -.highlight .m, -.highlight .mb, -.highlight .mf, -.highlight .mh, -.highlight .mi, -.highlight .il, -.highlight .mo, -.highlight .mx, -.highlight .kc, -.highlight .sb, -.highlight .bp, -.highlight .ne, -.highlight .nl, -.highlight .py -.highlight .nv, -.highlight .vc, -.highlight .vg, -.highlight .vi, -.highlight .vm, -.highlight .s1, -.highlight .o, -.highlight .ow, -.highlight .gh, -.highlight .gu, -.highlight .s2, -.highlight .dl { - color: var(--blue-light); -} - -.highlight .gh { - font-weight: bold; -} -.highlight .gu { - font-weight: bold; -} -.highlight .s, .highlight .sa, .highlight .sc, .highlight .sd, .highlight .se, .highlight .sh, .highlight .sx, .highlight .ss { - color: var(--blue-darker); -} -.highlight .nd, .highlight .nf, .highlight .fm { - color: var(--pupple); -} - -.highlight .err { - color: #f6f8fa; - background-color: #82071e; -} -.highlight .c, -.highlight .gl -.highlight .ch, -.highlight .cd, -.highlight .cm, -.highlight .cp, -.highlight .cpf, -.highlight .cs, -.highlight .gt { - color: var(--gray); -} - -.highlight .c1 { - color: var(--green); -} - -.highlight .ni, -.highlight .ge, -.highlight .si, -.highlight .gs { - color: #24292f; -} - -.highlight .ge { - font-style: italic; -} - -.highlight .gs { - font-weight: bold; -} diff --git a/css/style.css b/css/style.css deleted file mode 100644 index 7931ca1eed..0000000000 --- a/css/style.css +++ /dev/null @@ -1,1783 +0,0 @@ -:root { - --header-height: 57px; -} - -* { - box-sizing: border-box; - -webkit-tap-highlight-color: transparent; - scroll-behavior: smooth; -} - -/* handle header offset with scroll-margin */ -h1, h2, h3, h4, h5, h6, -[id] { - scroll-margin-top: 60px; -} - -/* Respect user motion preferences for access */ -@media (prefers-reduced-motion: reduce) { - html { - scroll-behavior: auto; - } -} - -body { - height: 100vh; - font: 400 14px/1.6 "Open Sans", sans-serif; - background: var(--bg); - margin: 0; - padding: 0; - color: var(--fg); - display: grid; - grid-template-areas: - 'header' - 'i18n' - 'content' - 'footer' -} - -header { - grid-area: header; - height: var(--header-height); -} - -.content { - grid-area: content; -} - -footer { - margin-top: auto; - grid-area: footer; -} - -body.no-scroll{ - overflow: hidden; -} - -h1 { - font-size: 30px; - line-height: 36px; -} - -#intro h2, #api-doc h1 { - font-weight: normal; -} - -h1, h2, h3 { - margin: 5px 0; - color: var(--card-fg); - -webkit-font-smoothing: antialiased; - -} - -.content h1 { - margin-bottom: 20px; -} - -.content table a.ignore-underline { - text-decoration: none; -} - -p em { - font-weight: bold; - color: var(--card-fg); -} - -p { - margin: 10px 0; - line-height: 1.35em; -} - -strong, th { - color: var(--card-fg); -} - -#overlay { - position: fixed; - left: 0; - top: 0; - opacity: 0.6; - width: 100%; - height: 100%; - display: none; - z-index: 1; - background-color: var(--bg); -} - -main.home { - max-width: 75rem; - margin: 40px auto 2%; - padding-inline: 5%; - grid-area: content; -} - -#home-content { - display: flex; -} - -#homepage-leftpane { - min-width: 500px; - margin-right: 30px; - font-size: 90%; - padding-top: 13px; -} - -#homepage-rightpane { - min-width: 500px; - padding: 25px 30px 0px 0px; -} - -#homepage-rightpane iframe { - min-width: 100%; - min-height: 273px; -} - -#announcements { - margin-top: 40px; - padding: 0 16px; - background: var(--notice-bg); - border: 1px solid var(--notice-accent); - border-radius: 3px; - font-size: 0.9em; - & a { - color: var(--notice-accent); - text-decoration: underline; - } -} - -#announcements ul { - padding-left: 0; -} - -#announcements li { - list-style: none; - margin-bottom: 16px; -} - -#announcements p { - margin: 6px 0; -} - -#announcements time { - font-weight: normal; - margin-right: 12px; -} - -.announcement-title { - font-weight: bold; - margin-bottom: 11px; - display: flex; - align-items: center; - column-gap: 8px -} - -.install-command { - font-family: Consolas, Monaco, "Andale Mono", monospace; - padding: 10px; - border: 1px solid var(--border); - border-radius: 3px; - max-width: 375px; - background-color: inherit; - - code { - background-color: inherit; - } -} - -.content { - display: flex; - flex-direction: row-reverse; - padding-inline-start: 1rem; - margin-block-start: 3.5rem; -} - -.content main { - max-width: 900px; - margin-inline: auto; -} - -.flex-row-content { - flex-direction: row; -} - -span.block-section { - display: block; -} - -#intro h2 { - font-size: 25px; - margin-bottom: 10px; -} - -#api-doc section { - padding-left: 20px; -} - -#api-doc > h3 { - padding-top: 10px; - padding-bottom: 5px; - font-weight: bold; - font-size: 24px; - color: var(--menu-grey) -} - -#api-doc h2 { - font-weight: bold; - font-size: 29px; - margin: 40px 0 20px; -} - -#api-doc section h3 { - padding-top: 10px; - padding-bottom: 5px; - font-weight: bold; - font-size: 18px; -} - -#api-doc h4 { - font-size: 16px; - font-weight: bold; -} - -#api-doc h5 { - font-size: 14px; - font-weight: bold; - color: var(--menu-grey) -} - -/* scroll */ - -*::-webkit-scrollbar { - background-color: opacity(var(--box-fg), 0.145); - width: 16px; -} - -*::-webkit-scrollbar-thumb { - border-radius: 8px; - border: 4px solid transparent; - background-clip: content-box; - background-color: var(--menu-grey); -} - -*::-webkit-scrollbar-thumb:hover { - background-color: var(--hover-bg); -} - -*::-webkit-scrollbar-thumb:active { - background-color: var(--menu-grey); -} - -/* links */ - -a { - color: var(--link); - text-decoration: underline; -} - -.h2:target { - display: block; - padding-top: 40px; -} - - -/* logo */ -.logo-container a { - text-decoration: none; - display: flex; - justify-content: center; - align-items : center; -} - -.express-logo { - fill : var(--card-fg); -} - -#description .express { - display: block; - font: 4.5em "Helvetica Neue", "Open Sans", sans-serif; - font-weight: 100; - margin-bottom: .25em; -} - -#description .express > span { - color: var(--card-fg); -} - -#description .express a#express-version { - font-size: 0.2em; - margin-left: 0.5em; - color: var(--link); - font-weight: 400; - text-decoration: underline; -} - -#description { - margin-bottom: 43px; - -webkit-font-smoothing: antialiased; -} - -#description .description { - position: relative; - top: -5px; - font: 100 4.1em "Helvetica Neue", "Open Sans", sans-serif; - color: var(--menu-grey); - line-height: .87; - margin: unset -} - -#description .description a { - text-decoration-thickness: 1px; - text-underline-offset: 3px; -} - -.header-right { - display: flex; - align-items: center; - gap: 20px; -} - -header { - background: var(--card-bg); - height: 57px; - display: flex; - align-items: center; - justify-content: space-between; - padding-inline: 1rem; - border-bottom: 1px solid var(--hover-fg); -} - -.scroll header { - box-shadow: 0 0 4px var(--card-bg); -} - -/* code */ - -code { - background-color: var(--code-bg); - color: var(--card-fg); - margin-block: -.125rem; - font-size: 13px; - padding: .125rem .375rem; - border-radius: 6px; -} - -pre { - padding: 16px; - border-radius: 3px; - border: 1px solid var(--border); - background-color: var(--code-bg); -/* keyboard focus offset improve visibility */ - &:focus { - border-color: var(--hover-border); - } -} - -pre code { - padding: 0; -} - -pre:has(code) { - position: relative; - - &:is(:hover, :focus) { - button { - display: flex; - } - } - /* focus copy btn by keyboard */ - &:focus-within button { - display: flex; - } -} - -pre:has(code) button { - position: absolute; - top: 5px; - right: 5px; - border: none; - z-index: 100; - display: none; - cursor: pointer; - background-color: inherit; - padding: 2px; - border-radius: 5px; - - &::after { - content: ""; - background-color: var(--card-fg); - mask-image: url("/images/copy-btn.svg"); - mask-size: 1.5rem; - mask-repeat: no-repeat; - width: 1.5rem; - height: 1.5rem; - } - - &:is(:hover, :focus) { - background-color: var(--hover-bg); - outline: 2px solid var(--hover-border); - } - - @media all and (max-width: 370px) { - padding: 1px; - - &::after { - mask-size: 1rem; - width: 1rem; - height: 1rem; - } - } -} - -pre:has(code) button.copied { - outline-color: var(--supported-fg); - - &::after { - background-color: var(--supported-fg); - } - - &::before { - font-size: 0.85rem; - position: absolute; - left: -58px; - content: "copied!"; - - width: fit-content; - height: fit-content; - padding: 4px; - border-radius: 2px; - color: var(--card-fg); - background-color: var(--card-bg); - outline: 1px solid var(--supported-fg); - } - - @media all and (max-width: 400px) { - &::before { - left: -50px; - font-size: 0.7rem; - padding: 3px; - } - } -} - -pre:has(code) button.failed { - outline-color: var(--eol-fg); - - &::after { - background-color: var(--eol-fg); - } - - &::before { - font-size: 0.85rem; - position: absolute; - left: -58px; - content: "failed!"; - - width: fit-content; - height: fit-content; - padding: 4px; - border-radius: 2px; - color: var(--card-fg); - background-color: var(--card-bg); - outline: 1px solid var(--eol-fg); - } - - @media all and (max-width: 400px) { - &::before { - left: -50px; - font-size: 0.7rem; - padding: 3px; - } - } -} - -/* scroll to top button */ - -.scroll #top { - opacity: .5; - display: initial -} - -.scroll #top:hover { - opacity: 1; -} - -#top { - line-height: 0; - border-radius: 2px; - position: fixed; - bottom: 15px; - right: 15px; - padding: 8px; - text-decoration: none; - opacity: 0; - transition: opacity 300ms; - border: 1px solid var(--border); - background-color: var(--card-bg); - color: var(--card-fg); - display: none; -} - -/* clearfix */ - -.clearfix:after { - content: "."; - display: block; - clear: both; - visibility: hidden; - line-height: 0; - height: 0; -} - -.clearfix { - display: inline-block; -} - -html[xmlns] .clearfix { - display: block; -} - -* html .clearfix { - height: 1%; -} - -/* boxes */ - -#boxes { - display: grid; - grid-template-columns: repeat(4, 1fr); - row-gap: 20px; - column-gap: 50px; - margin-top: 30px; -} - -#boxes h2 { - line-height: 1.25em; - color: var(--card-fg); - background: none; - margin-top: 0; - padding-top: 0; -} - -#boxes h2 a { - text-decoration: none; - color: var(--card-fg); -} - -#boxes div { - list-style: none; -} - -/* tables specific */ - -table { - margin: 1em 0; - border: 1px solid var(--border); - border-collapse: collapse; - width: 100%; - @media screen and (max-width:600px) { - font-size: smaller; - } -} - -table td, table th { - padding: 7px; - line-height: 120%; - vertical-align: top; - border: 1px solid var(--border); -} - -table tr th { - background: var(--card-bg); - font-size: 110%; -} - -table td p:first-child { - margin-top: 0; -} - -table td p, li p { - width: 100% !important; -} - -table ul { - margin: 20px 0 -} - -/* doc boxes */ - -.doc-box { - padding: 16px; - color: var(--box-fg); - border-radius: 0 6px 6px 0; - margin-bottom: 1rem; -} - -.doc-box p { - margin: 0 0 8px 0; -} - -.doc-box p:last-child { - margin: 0; -} - -.doc-title { - display: flex; - align-items: center; - gap: 3px; - font-weight: 600; -} - -/* i18n box */ -.doc-notice { - padding-block: 1rem; - padding-inline: 2.5rem; - color: var(--box-fg); - border-radius: 0 6px 6px 0; - background: var(--notice-bg); - border-left: 3px solid var(--notice-accent); - margin-inline: auto; - margin-block-start: 2rem; - position: relative; - grid-area: i18n; - display: block; -} - -.doc-notice a{ - color: var(--notice-accent); - text-decoration: underline; -} - -.doc-notice[hidden] { - display: none; -} - -.doc-info { - background: var(--info-bg); - border-left: 3px solid var(--info-accent); -} - -.doc-info a{ - color: var(--info-accent); - text-decoration: underline; -} - -.doc-warn { - background: var(--warn-bg); - border-left: 3px solid var(--warn-accent); -} - -.doc-warn a { - color: var(--warn-accent); - text-decoration: underline; -} - -#close-i18n-notice-box { - position: absolute; - top: 3px; - right: 9px; - color: var(--notice-accent); - cursor: pointer; -} - -/* general */ - -#app-settings-property { - width: 200px; -} - -div.header-btn { - display: flex; - padding: 3px; - border-radius: 3px; - cursor: pointer; - color: var(--box-fg); -} - -a.edit-github-btn, -a.bottom-nav-prev, -a.bottom-nav-next { - display: flex; - gap: 0.5rem; - align-items: center; - width: fit-content; - padding: 0.5rem; - border-radius: 0.3rem; - text-decoration: none; - - &:is(:hover, :active, :focus) { - color: var(--fg); - background-color: var(--hover-bg); - outline: 1px solid var(--card-fg); - } -} - -#mobile-menu { - display: none; - position: relative; -} - -pre, code { - white-space: pre-wrap !important; -} - -/* footer */ - -footer { - font-size: 12px; - display: flex; - gap: 24px; - flex-direction: column; - padding-block: 2rem; - padding-inline: 3rem; - - @media screen and (max-width : 360px) { - padding: 1rem; - } -} - -#footer-content { - width: 100%; - justify-content: space-between; - gap: 32px; - display: flex; - flex-direction: column; -} - -#footer-trademark-legal { - font-size: 0.8rem; - opacity: 0.8; - margin-top: 8px; - text-align: center; - max-width: 35rem; -} - -#footer-copyright { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - gap: 20px; -} - -#footer-policy { - display: flex; - flex-wrap: wrap; - column-gap: 20px; - row-gap: 8px; - justify-content: center; - font-size: 15px; -} - -#footer-links { - display: flex; - gap: 20px; - flex-wrap: wrap; - align-items: center; - justify-content: center; -} - -.footer-social { - display: flex; - gap: 20px; -} - -.footer-social a { - color: inherit; -} - -/* icons */ -a.openjs-logo { - display: inline-block; - width: 125px; - height: 39px; - cursor: pointer; -} - -/* theme and lang btn */ -button.theme-btn { - width: 2rem; - height: 2rem; - border: none; - cursor: pointer; - color: var(--fg); - background-color: inherit; - display: flex; - justify-content: center; - align-items: center; -} - -#icon-moon, -#icon-sun { - display: none; - align-self: center; -} -html.light-mode #icon-moon { - display: inline; -} - -button.lang-btn { - appearance: none; - background-color: inherit; - border: 0; - cursor: pointer; - color: var(--card-fg); - padding: 0.2rem; - width: fit-content; - aspect-ratio: 1; -} - -div.desktop-lang-switcher { - position: relative; - - > ul.lang-list { - display: none; - opacity: 0; - position: absolute; - list-style: none; - visibility: hidden; - left: -75px; - z-index: 100; - background-color: var(--card-bg); - border: 1px solid; - border-radius: 10px; - padding: 0px; - min-width: max-content; - - li a { - display: block; - padding: 5px 20px 5px 20px; - text-decoration: none; - color: var(--card-fg); - &:is(:hover, :focus) { - background: var(--hover-bg); - } - } - - > li:first-child > a { - border-start-start-radius: 10px; - border-start-end-radius: 10px; - } - - > li:last-child > a { - border-end-start-radius: 10px; - border-end-end-radius: 10px; - } - } - - > ul.lang-list.open { - display: block; - opacity: 1; - visibility: visible; - } -} - -/* navigation */ -#navbar { - line-height: 30px; - display: flex; - align-items: center; -} - -#navbar a { - color: var(--card-fg); - text-decoration: none; -} - -#navbar a.active { - font-weight: bold; -} - -#navbar ul { - list-style: none; -} - -#navmenu { - display: flex; - gap: 20px; -} - -/* content doc */ - -#blog-doc { - padding: 0.5rem; - width: fit-content; -} - -/* dropdown menu */ - -.submenu { - position: relative; - list-style: none; -} - -.submenu-content { - position: absolute; - top: 100%; - left: 50%; - transform: translate(-50%); - margin: auto; - box-shadow: 1px 2px var(--hover-fg); - z-index: 1000; - min-width: max-content; - width: fit-content; - border: 1px solid var(--border); - border-radius: 12px; - background-color: var(--bg); - padding: 0; - display: none; -} - -.submenu-content li:first-child a { - border-radius: 12px 12px 0 0; -} - -.submenu-content li:last-child a { - border-radius: 0px 0px 12px 12px; -} - -.submenu.open .submenu-content { - display: initial; -} - -#navmenu { - padding: 0; - margin: 0; - right: 0px; - z-index: 1000; - margin-right: 15px; -} - -.submenu-content li a { - display: block; - padding: 2px 20px 2px 20px; -} - -#navbar .submenu-content a:hover { - background: var(--hover-bg); - text-decoration: none; -} - - -/* TOC side menu */ - -.toc-container { - position: sticky; - top: 100px; - width: 270px -} - -#menu { - min-width: 13rem; - padding-inline: 0.5rem; - font-size: 1rem; - overflow-y: auto; - max-height: 65vh; - padding-block-end: 0.5rem; - margin-block: 0; -} - -#menu.blog-side-menu { - max-width: 20rem; - max-height: fit-content; -} - -#menu ul a, -#menu > li > a { - display: block; - color: var(--menu-grey); - padding-inline: 0.5rem; - text-decoration: none; - - &:is(:hover,:active,:focus) { - border-radius: 0.3rem; - color: var(--fg); - outline: 1px solid var(--card-fg); - } -} - -#menu ul a.active { - border-radius: 0.3rem; - color: var(--fg); - outline: 1px solid var(--card-fg); -} - -#menu em { - font-weight: bold; - color: var(--menu-em); - font-size: 1rem; -} - -#menu li { - cursor: pointer; - list-style: none; - margin-block-start: 0.3rem; -} - -#menu ul, -ul#side-menu { - display: none; - opacity: 0; - height: 0; - overflow: hidden; -} - -#menu ul.active, -ul#side-menu.active { - height: auto; - padding-inline: 0.5rem; - display: block; - opacity: 1; - padding-block: 0.5rem; -} - -#menu > li > a { - font-weight: bold; - font-size: 1rem; - padding-inline-start: 1rem; -} - -/* can't find this in proj*/ -h2 a { - color: var(--card-fg) !important; -} - -/* search */ - -#q { - display: none; - height: 2.5em; - max-width: 100%; - padding: 5px; -} - -.algolia-autocomplete { - max-width: 9em; - > input { - color: var(--fg); - background-color: var(--bg); - } - > input::placeholder { - color: var(--fg); - } - #q { - display: initial; - border-radius: 8px; - border: 1px solid var(--border); - transition: color .3s ease; - padding-inline: 12px; - outline: none; - &:focus-visible, - &:focus { - border-color: var(--hover-border); - border-width: 2px; - } - } -} - - #navbar { - .ds-dropdown-menu .ds-dataset-1 { - background-color: var(--bg); - .ds-suggestions { - /* background-color: var(--bg); */ - color: var(--fg); - } - .ds-suggestion a { - background-color: var(--bg); - color: var(--fg); - } - .ds-suggestion a { - background-color: var(--bg); - color: var(--fg); - } - .algolia-docsearch-suggestion--category-header { - color: var(--fg); - } - .algolia-docsearch-suggestion--wrapper { - background-color: var(--bg); - .algolia-docsearch-suggestion--subcategory-column { - color: var(--menu-grey); - - } - .algolia-docsearch-suggestion--title, - .algolia-docsearch-suggestion--text { - color: var(--fg); - } - .algolia-docsearch-suggestion--highlight { - color: var(--link); - background-color: initial; - } - } - .algolia-docsearch-suggestion { - background-color: initial; - } - .algolia-docsearch-suggestion--content .algolia-docsearch-suggestion--no-results{ - background-color: initial; - } - - } - .ds-suggestion.ds-cursor .algolia-docsearch-suggestion--title, - .ds-suggestion.ds-cursor .algolia-docsearch-suggestion--content { - background-color: var(--hover-bg); - } - } - - -.content-404 { - height: 70vh; - padding: 153px 32px 7%; - text-align: center; - display: flex; - justify-content: center; - flex-direction: column; - grid-area: content; -} - -.content-404 p { - font-size: 16px; -} - -/* search-bar desktop re-sizing */ -@media all and (min-width: 950px) { - .algolia-autocomplete { - margin-right:15px; - max-width: 12em; - } -} - -/* toc title */ -.toc-heading { - display: block; - cursor: default; - padding-inline-start: 1rem; - & > em { - font-weight: bold; - color: var(--menu-em); - font-size: 1rem; - } -} - - -/* TOC btn */ - -#menu-toggle { - cursor: pointer; - display: none; - font-size: 1rem; - padding: 0.5rem; - opacity: 0; - width: fit-content; - border: 1px solid #000; - border-radius: 0.3rem; - color: #000; - background-color: #fff; - - &::after { - content: ""; - display: block; - width: 0.8em; - height: 0.5em; - background-color: var(--card-bg); - clip-path: polygon(100% 0%, 0 0%, 50% 100%); - cursor: pointer; - pointer-events: none; - transition: transform 0.2s ease-in-out; - transform: rotate(-90deg); - } - - &:is(:hover, :active, :focus) { - background-color: #ebf2f5; - } -} - -/* 'Prev' left and 'Next' right btn */ -.bottom-navigation { - display: flex; - justify-content: space-between; - flex-wrap: wrap; - margin-block: 1rem; - font-weight: 500; - font-size: 1.2rem; -} - -.bottom-nav-prev { - text-align: left; - max-width: 50%; -} - -.bottom-nav-next { - text-align: right; - max-width: 50%; -} - -/* routing methods columns */ -.methods-columns { - display: flex; - gap: 2rem; - justify-content: space-between; - flex-wrap: wrap; - - @media screen and (max-width: 460px) { - flex-direction: column; - gap: 0.3rem; - } -} - -/* responsive */ - -@media all and (max-width: 1440px) { - #menu{ - top: 75px; - } -} - -@media all and (max-width: 1110px) { - #boxes { - grid-template-columns: 1fr 1fr; - } - - #home-content { - flex-direction: column; - } - - .install-command { - display: none; - } - - #home-content .pane { - min-width: auto; - font-size: 74%; - } - - #homepage-leftpane { - padding-top: 0px; - } - - #homepage-rightpane { - padding-top: 0; - padding-right: 0; - } - - .table-scroller { - width: 100%; - overflow: scroll; - scrollbar-width: thin; - } - - code { - word-break: break-all; - } - - ul { - padding-left: 5%; - } - - h1 { - font-size: 22px; - line-height: 26px; - } - - h2 { - font-size: 18px; - line-height: 25px; - } - - h3 { - font-size: 16px; - line-height: 23px; - word-break: break-all; - } - - h4 { - font-size: 16px; - line-height: 18px; - font-weight: normal; - } - - #home-content { - margin: 60px 0 0 5%; - padding-right: 5%; - } - - #description { - margin-bottom: 35px; - } - - #description .description { - font-size: 3em; - line-height: .9em; - font-weight: 200; - } - - .logo-container { - margin-inline: auto; - } - - #home-menu { - display: block; - position: absolute; - top: 7px; - } - - #overlay.blurs{ - display: block; - } - - .menu ul { - display: block; - } - - #footer-content { - flex-wrap: wrap; - justify-content: center; - } - - #footer-copyright > a { - width: 100%; - display: flex; - justify-content: center; - } - - .header-right { - display: flex; - gap: 8px; - } -} - -/* TOC responsive */ -@media all and (max-width: 800px) { - .content { - flex-direction: column; - padding-inline-start : 0; - margin-block-start: 1rem; - } - - .content main { - padding-inline: 1rem; - width: 100vw; - margin-top: 0; - margin-inline: 0; - } - - #api-doc, - #blog-doc, - #page-doc { - width: 100%; - } - - #api-doc section { - padding-left: 0; - } - - .toc-container { - display: flex; - flex-direction: column; - gap: 0; - padding: 1rem; - } - - #menu { - display: none; - opacity: 0; - max-height: 0; - background-color: var(--card-bg); - min-width: 100%; - } - - #menu.open { - display: block; - opacity: 1; - max-height: 35vh; - padding-inline: 1rem; - border-left: 2px solid var(--border); - margin-top: 0.3rem; - border-bottom-right-radius: 0.5rem; - border-top-right-radius: 0.5rem; - } - - .toc-container { - width: 100%; - } - - #menu-toggle.show { - display: flex; - gap: 0.5rem; - align-items: center; - opacity: 1; - } - - #menu-toggle.show.rotate::after { - transform: rotate(0deg); - } - - #menu > li > a, - #menu ul a { - padding: 0.5rem; - &:is(:hover,:active,:focus) { - background: var(--hover-bg); - } - } - - #menu li ul li > em { - font-size: 0.8rem; - padding-inline: 1rem; - padding-block: 0.3rem; - background-color: var(--hover-bg); - border-radius: 0.3rem; - } - - .toc-heading{ - display: none; - } -} - - -@media all and (max-width: 540px) { - #boxes { - grid-template-columns: 1fr; - } -} - -@media all and (max-width: 420px) { - #app-settings-property { - width: auto; - } -} - - -@media print { - - header { - position: absolute; - } -} - -/* For image callouts in writing-middleware.md */ - -.callout {position: relative;} - -#mw-fig { - border-collapse: separate; - padding: 0; - border: 0; - width: 960px; - margin-bottom: 20px; -} -#mw-fig-imgcell { - margin: 0; - padding: 0px; - border: 0; - width: 410px; -} -#mw-fig-img { - margin: 0px; - padding: 0px; - width: 410px; - height: 308px; -} - -.mw-fig-callouts { - margin: 0; - padding: 0 0 0 5px; - border: 0; - width: 550px; -} - -/* Blog page styles*/ -#blog-doc { - margin: 0 1rem; - @media all and (max-width: 800px) { - margin: 0; - } -} -#blog-doc:has(> h1#express-blog), -#blog-doc:has(> h1#write-a-blog-post) { - min-height: 300px; -} -#blog-doc .blog-details ~ p > img { - width: 200px; - float: right; - margin: 0.5rem; -} -#blog-doc p { - font-size: 1.1em; -} -.blog-posts { - display: flex; - flex-direction: column; - row-gap: 10px; -} -.blog-post { - width: 100%; - background-color: var(--card-bg); - display: flex; - padding: 10px; - flex-direction: column; - justify-content: space-between; - border-radius: 5px; - border: 1px solid var(--border); - box-shadow: 2px 3px var(--hover-fg); - transition: 0.3s; -} -.blog-post:hover { - background-color: var(--hover-bg); - border: 1px solid var(--hover-border); - box-shadow: 2px 3px var(--menu-grey); -} -.blog-post img { - max-width: 100%; - max-height: 100%; - object-fit: cover; -} -.blog-post .blog-details { - display: flex; - flex-direction: column; -} -.blog-details div:first-child { - margin-bottom: 5px; -} -.blog-tag { - font-size: 12px; -} -.blog-title { - font-size: 1.3rem; - line-height: 1.5rem; - font-weight: 500; - padding-right: .2em; -} -.blog-title a { - color: var(--card-fg) -} -.blog-excerpt { - font-size: .75rem; -} -.blog-img { - max-width: 100%; - margin: auto; -} -.blog-authors { - font-style: italic; -} -.blog-author-link { - color: inherit; - margin-left: 0.5ch; - text-decoration: none; -} -.blog-author-link-label { - text-decoration: underline; -} -.blog-author-avatar { - border-radius: 50%; - display: inline-block; - vertical-align: middle; - width: 1.5em; - height: 1.5em; -} -.blog-date { - font-weight: bold; - font-size: 85%; -} -/* mobile-only */ -@media (max-width: 500px) { - #blog-doc { - display: flex; - flex-wrap: wrap; - flex-direction: column; - align-items: center; - margin-right: 0; - padding-right: 10px; - } - #blog-doc .blog-details + p > img { - margin-bottom: 15px; - } -} - - - -/* blog tablet and up*/ -@media (min-width: 768px) { - .blog-post { - margin: auto; - } - .blog-tags { - margin-bottom: 20px; - } - .blog-title { - font-size: 1.3rem; - margin-bottom: 20px; - line-height: 1.5rem; - } - .blog-post .blog-details { - display: flex; - flex-direction: row; - margin-left: 1rem; - font-size: 90%; - } - .blog-post .blog-details div:first-child { - margin-right: 20px; - } - .blog-details { - font-size: 1rem; - } - .blog-excerpt { - line-height: initial; - font-size: .85rem; - font-weight: 300; - margin-top: auto; - margin-bottom: 10px; - max-width: 80%; - } -} - -strong.supported { - color: var(--supported-fg) ; -} -strong.eol { - color: var(--eol-fg) ; -} - -.logo-table { - display: flex; - flex-wrap: wrap; - row-gap: 8px; - column-gap: 48px; -} - -.logo-table h3{ - margin-bottom: 12px; -} - -blockquote { - margin-left: 0; - font-weight: 600; - font-style: italic; - padding-left: 1.2em; - border-left: .25rem solid var(--border); -} - -@media all and (max-width: 1110px) { - .algolia-autocomplete { - display: none !important; - } - - #mobile-menu { - display: block; - } - - #navbar { - padding: 0; - top: 1px; - position: static; - } - - #navmenu>li:first-child { - display: none; - } - - #navmenu>li { - border-bottom: 1px solid var(--border); - margin: 0; - min-height: 47px; - background: var(--card-bg); - cursor: pointer; - display: flex; - align-items: center; - } - - #navmenu>li.open:hover { - background: var(--card-bg); - } - #navmenu>li:hover { - background: var(--hover-bg); - & ul { - background-color: var(--card-bg); - } - } - - #navmenu { - left: 0; - padding: 0; - top: 57px; - width: 100%; - position: absolute; - display: none; - } - - #navmenu.opens { - display: block; - max-height: calc(100dvh - var(--header-height)); - overflow-y: auto; - overscroll-behavior: contain; - } - - #navbar a { - font-size: 19px; - margin: 0; - width: 100%; - height: 100%; - padding-left: 5%; - } - - #navbar .submenu.open { - flex-direction: column; - align-items: initial; - > a { - border-bottom: 1px solid var(--border); - } - } - - .submenu.open > a { - display: flex; - align-items: center; - min-height: 47px; - } - - .submenu-content { - width: 100%; - position: static; - display: none; - margin-top: 7px; - background-color: var(--card-bg); - padding: 0; - margin: 0; - border: none; - border-radius: 0; - box-shadow: none; - max-height: 190px; - overflow-y: auto; - overflow-x: hidden; - transform: none; - cursor: pointer; - } - - .submenu-content li > a { - width: 100%; - } - - .submenu-content li:first-child a { - border-radius: 0; - } - - .submenu-content li:last-child { - border-bottom: 0; - } - - .submenu-content li:last-child a { - border-radius: 0; - } - - .submenu-content.open { - display: inline-block; - } - - #navbar .submenu-content li a { - font-size: 16px; - padding: 5px 5px 5px 5%; - padding-left: 10%; - } - - .submenu-content li { - border-bottom: 1px solid var(--border); - } -} - - diff --git a/css/themes/dark-theme.css b/css/themes/dark-theme.css deleted file mode 100644 index 0c6a563696..0000000000 --- a/css/themes/dark-theme.css +++ /dev/null @@ -1,21 +0,0 @@ -html.dark-mode { - - #icon-sun { - display: inline; - } - - .submenu-content { - box-shadow: 1px 2px var(--hover-bg); - background-color: var(--card-bg); - } - - #navmenu>li:hover { - ul { - background-color: var(--card-bg); - } - } - - .blog-excerpt { - color: var(--notice-accent); - } -} \ No newline at end of file diff --git a/css/variables.css b/css/variables.css deleted file mode 100644 index 5680810934..0000000000 --- a/css/variables.css +++ /dev/null @@ -1,52 +0,0 @@ -:root.light-mode { - --grey-fg: #aeaeae; - --bg: #ffffff; - --fg: #383838; - --card-bg: #f0f1f3; - --card-fg: #383838; - /* text (api side menus h3...) */ - --menu-grey: #888; - --hover-border: #484848; - --hover-bg: #D3D3D3; - --hover-fg: #fafafa; - --border: #d9e1e4; - --link: #0e78ce; - /* docs related */ - --box-fg: #181818; - --info-bg: #cfd4fc; - --info-accent: #0f1c8a; - --notice-bg: #fceac5; - --notice-accent: #2E1D00; - --warn-bg: #fad1df; - --warn-accent: #8a0f3a; - --code-bg: #f0f1f3; - /* support page related */ - --supported-fg: #009000; - --eol-fg: #ff0000; -} -:root.dark-mode { - --bg: #0c0c0c; - --fg: #fafafa; - --card-bg: #181818; - --card-fg: #f7f4f4; - --hover-bg: #262626; - --hover-fg: #f2f2f2; - --hover-border: #fafafa; - --border: #d9e1e4; - /* text (api side menus h3...) */ - --menu-grey: silver; - --link: #259dff; - --menu-em: #a2a0a0eb; - /* docs related */ - --box-fg: #ffffff; - --info-bg: #171d4f; - --info-accent: #bdc3ff; - --notice-bg: #4e4022; - --notice-accent: #f9e8c3; - --warn-bg: #4e2232; - --warn-accent: #f9c3d6; - --code-bg: #2b2b2b; - /* support page related */ - --supported-fg:#299009; - --eol-fg: #ff1a1a; -} diff --git a/de/3x/api.md b/de/3x/api.md deleted file mode 100644 index e3655297ab..0000000000 --- a/de/3x/api.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -layout: api -version: 3x -title: Express 3.x - API-Referenz -description: Access the API reference for Express.js version 3.x, noting that this version is end-of-life and no longer maintained - includes details on modules and methods. -menu: api -redirect_from: " " ---- - -
- -
- **Express 3.x WIRD NICHT MEHR GEWARTET** - -Bekannte und unbekannte Probleme bei Sicherheit und Leistung in 3.x wurden seit dem letzten Update (1. August 2015) noch nicht behoben. Es wird dringend empfohlen, die aktuelle Version von Express zu verwenden. - -If you are unable to upgrade past 3.x, please consider [Commercial Support Options](/{{ page.lang }}/support#commercial-support-options). - -
- -

3.x-API

- - -{% include api/en/3x/app.md %} - -
diff --git a/de/4x/api.md b/de/4x/api.md deleted file mode 100644 index ffdc703de2..0000000000 --- a/de/4x/api.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -layout: api -version: 4x -title: Express 4.x - API-Referenz -description: Access the API reference for Express.js 4.x, detailing all modules, methods, and properties for building web applications with this version. -menu: api -redirect_from: " " ---- - -
- -

4.x-API

- -{% capture node-version %} - -Express 4.0 requires Node.js 0.10 or higher. - -{% endcapture %} - -{% include admonitions/note.html content=node-version %} - - -{% include api/en/4x/express.md %} -{% include api/en/4x/app.md %} -{% include api/en/4x/req.md %} -{% include api/en/4x/res.md %} -{% include api/en/4x/router.md %} - -
diff --git a/de/5x/api.md b/de/5x/api.md deleted file mode 100644 index cb8a847d02..0000000000 --- a/de/5x/api.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -layout: api -version: 5x -title: Express 5.x - API-Referenz -description: Access the API reference for Express.js 5.x, detailing all modules, methods, and properties for building web applications with this latest version. -menu: api -redirect_from: " " ---- - -
- -

5.x API

- -{% capture node-version %} - -Express 5.0 requires Node.js 18 or higher. - -{% endcapture %} - -{% include admonitions/note.html content=node-version %} - - -{% include api/en/5x/express.md %} -{% include api/en/5x/app.md %} -{% include api/en/5x/req.md %} -{% include api/en/5x/res.md %} -{% include api/en/5x/router.md %} - -
diff --git a/de/advanced/best-practice-performance.md b/de/advanced/best-practice-performance.md deleted file mode 100644 index 9ea397be26..0000000000 --- a/de/advanced/best-practice-performance.md +++ /dev/null @@ -1,311 +0,0 @@ ---- -layout: page -title: Leistungsspezifische Best Practices für Express-Anwendungen in Produktionsumgebungen -description: Discover performance and reliability best practices for Express apps in production, covering code optimizations and environment setups for optimal performance. -menu: advanced -order: 4 -redirect_from: " " ---- - -# Best Practices in Produktionsumgebungen: Leistung und Zuverlässigkeit - -In diesem Beitrag werden Best Practices in Bezug auf Leistung und Zuverlässigkeit für Express-Anwendungen behandelt, die in der Produktionsumgebung bereitgestellt werden. - -Dieses Thema gehört sicherlich zur "DevOps"-Welt und deckt traditionelle Entwicklungs- und Betriebsprozesse ab. Entsprechend sind die Informationen hier in zwei Teile unterteilt: - -- Things to do in your code (the dev part): - - Für statische Dateien Middleware verwenden - - Keine synchronen Funktionen verwenden - - [Do logging correctly](#do-logging-correctly) - - [Handle exceptions properly](#handle-exceptions-properly) -- Things to do in your environment / setup (the ops part): - - NODE_ENV auf "production" festlegen - - Automatischen Neustart Ihrer Anwendung sicherstellen - - Anwendung in einem Cluster ausführen - - Anforderungsergebnisse im Cache speichern - - Load Balancer verwenden - - Reverse Proxy verwenden - -## Things to do in your code {#in-code} - -Dies sind einige Beispiele für Maßnahmen, die Sie an Ihrem Code vornehmen können, um die Anwendungsleistung zu verbessern: - -- Für statische Dateien Middleware verwenden -- Keine synchronen Funktionen verwenden -- [Do logging correctly](#do-logging-correctly) -- [Handle exceptions properly](#handle-exceptions-properly) - -### GZIP-Komprimierung verwenden - -Mit der GZIP-Komprimierung lässt sich die Größe des Antworthauptteils deutlich verringern und somit die Geschwindigkeit der Webanwendung erhöhen. Verwenden Sie die Middleware [compression](https://www.npmjs.com/package/compression) für die GZIP-Komprimierung in Ihrer Express-Anwendung. Beispiel: - -```js -const compression = require('compression') -const express = require('express') -const app = express() - -app.use(compression()) -``` - -Bei Websites mit hohem Datenverkehr in Produktionsumgebungen lässt sich die Komprimierung am besten installieren, indem sie auf Reverse Proxy-Ebene implementiert wird (siehe [Reverse Proxy verwenden](#proxy)). In diesem Fall wird die Middleware "compression" nicht benötigt. Details zur Aktivierung der GZIP-Komprimierung in Nginx siehe [Modul ngx_http_gzip_module](http://nginx.org/en/docs/http/ngx_http_gzip_module.html) in der Nginx-Dokumentation. - -### Keine synchronen Funktionen verwenden - -Synchrone Funktionen und Methoden belasten den Ausführungsprozess, bis sie zurückgegeben werden. Ein einzelner Aufruf für eine synchrone Funktion kann in wenigen Mikrosekunden oder Millisekunden zurückgegeben werden. Bei Websites mit hohem Datenverkehr hingegen summieren sich diese Aufrufe und verringern die Leistung der Anwendung. Sie sollten also deren Verwendung in Produktionsumgebungen vermeiden. - -Auch wenn Node und viele andere Module synchrone und asynchrone Versionen ihrer Funktionen bieten, sollten Sie in Produktionsumgebungen immer die asynchrone Version verwenden. Nur beim ersten Systemstart ist die Verwendung einer synchronen Funktion begründet. - -You can use the `--trace-sync-io` command-line flag to print a warning and a stack trace whenever your application uses a synchronous API. Auch wenn Sie diese natürlich nicht in der Produktionsumgebung verwenden werden, soll dadurch trotzdem sichergestellt werden, dass Ihr Code in der Produktionsumgebung eingesetzt werden kann. Weitere Informationen hierzu siehe [Wöchentliches Update für io.js 2.1.0](https://nodejs.org/en/blog/weekly-updates/weekly-update.2015-05-22/#2-1-0). - -### Do logging correctly - -Im Allgemeinen gibt es für die Protokollierung Ihrer Anwendung zwei Gründe: 1) Debugging und 2) Protokollierung von Anwendungsaktivitäten (im Wesentlichen alles andere, außer Debugging). Die Verwendung von`console.log()` oder `console.err()` zur Ausgabe von Protokollnachrichten an das Terminal ist in der Entwicklung gängige Praxis. But [these functions are synchronous](https://nodejs.org/api/console.html#console) when the destination is a terminal or a file, so they are not suitable for production, unless you pipe the output to another program. - -#### Für Debuggingzwecke - -Wenn Sie die Protokollierung für Debuggingzwecke nutzen, sollten Sie statt `console.log()` besser ein spezielles Debuggingmodul wie [debug](https://www.npmjs.com/package/debug) verwenden. Mit einem solchen Modul können Sie über die Umgebungsvariable DEBUG steuern, welche Debugnachrichten an `console.err()` gesendet werden (falls vorhanden). Um Ihre Anwendung rein asynchron zu halten, können Sie trotzdem `console.err()` per Pipe zu einem anderen Programm umleiten. Sie nehmen dann aber kein Debugging in der Produktionsumgebung vor, richtig? - -#### Für Anwendungsaktivitäten - -If you're logging app activity (for example, tracking traffic or API calls), instead of using `console.log()`, use a logging library like [Pino](https://www.npmjs.com/package/pino), which is the fastest and most efficient option available. - -### Ausnahmebedingungen ordnungsgemäß handhaben - -Node-Anwendungen stürzen ab, wenn eine nicht abgefangene Ausnahmebedingung vorkommt. Wenn diese Ausnahmebedingungen nicht behandelt und entsprechende Maßnahmen eingeleitet werden, stürzt Ihre Express-Anwendung ab und geht offline. Wenn Sie dem nachfolgenden Rat in [Sicherstellen, dass Ihre Anwendung automatisch neu gestartet wird](#restart) folgen, wird Ihre Anwendung nach einem Absturz wiederhergestellt. Glücklicherweise haben Express-Anwendungen nur eine kurze Initialisierungszeit. Nevertheless, you want to avoid crashing in the first place, and to do that, you need to handle exceptions properly. - -Mit folgenden Verfahren stellen Sie sicher, dass alle Ausnahmebedingungen gehandhabt werden: - -- ["try-catch" verwenden](#try-catch) -- ["Promises" verwenden](#promises) - -Um näher auf diese Themen eingehen zu können, müssen Sie sich ein grundlegendes Verständnis der Fehlerbehandlung in Node und Express aneignen: Verwendung von Error-first-Callbacks und Propagieren von Fehlern in Middleware. Node verwendet die Konvention "Error-first-Callback" für die Rückgabe von Fehlern von asynchronen Funktionen, bei denen der erste Parameter zur Callback-Funktion das Fehlerobjekt ist, gefolgt von Ergebnisdaten in den nachfolgenden Parametern. Um anzugeben, dass kein Fehler vorliegt, müssen Sie "null" als ersten Parameter übergeben. Die Callback-Funktion muss der Konvention "Error-first-Callback" folgen, um den Fehler sinnvoll bearbeiten zu können. In Express hat sich bewährt, die Funktion "next()" zu verwenden, um Fehler über die Middleware-Chain zu propagieren. - -Weitere Informationen zu den Grundlagen der Fehlerbehandlung siehe: - -- [Fehlerbehandlung in Node.js](https://www.tritondatacenter.com/node-js/production/design/errors) - -#### "try-catch" verwenden - -"try-catch" ist ein JavaScript-Sprachkonstrukt, mit dem Sie Ausnahmebedingungen in synchronem Code abfangen können. Verwenden Sie "try-catch" beispielsweise, um JSON-Parsing-Fehler wie unten gezeigt zu bearbeiten. - -Dies ist ein Beispiel zur Verwendung von "try-catch", um eine potenzielle "process-crashing"-Ausnahmebedingung zu handhaben. -Diese Middlewarefunktion akzeptiert einen Abfragefeldparameter mit dem Namen "params", der ein JSON-Objekt ist. - -```js -app.get('/search', (req, res) => { - // Simulating async operation - setImmediate(() => { - const jsonStr = req.query.params - try { - const jsonObj = JSON.parse(jsonStr) - res.send('Success') - } catch (e) { - res.status(400).send('Invalid JSON string') - } - }) -}) -``` - -"try-catch" funktioniert jedoch nur in synchronem Code. Da die Node-Plattform primär asynchron ist (insbesondere in einer Produktionsumgebung), lassen sich mit "try-catch" nicht besonders viele Ausnahmebedingungen abfangen. - -#### "Promises" verwenden - -When an error is thrown in an `async` function or a rejected promise is awaited inside an `async` function, those errors will be passed to the error handler as if calling `next(err)` - -```js -app.get('/', async (req, res, next) => { - const data = await userData() // If this promise fails, it will automatically call `next(err)` to handle the error. - - res.send(data) -}) - -app.use((err, req, res, next) => { - res.status(err.status ?? 500).send({ error: err.message }) -}) -``` - -Also, you can use asynchronous functions for your middleware, and the router will handle errors if the promise fails, for example: - -```js -app.use(async (req, res, next) => { - req.locals.user = await getUser(req) - - next() // This will be called if the promise does not throw an error. -}) -``` - -Best practice is to handle errors as close to the site as possible. So while this is now handled in the router, it’s best to catch the error in the middleware and handle it without relying on separate error-handling middleware. - -#### What not to do - -Sie sollten _auf keinen_ Fall per Listener das Ereignis `uncaughtException` überwachen, das ausgegeben wird, wenn eine Ausnahmebedingung bis zurück zur Ereignisschleife bestehen bleibt. Durch das Hinzufügen eines Ereignislisteners für `uncaughtException` verändert sich das Standardverhalten des Prozesses, über das eine Ausnahmebedingung festgestellt wird. Das Ausführen einer Anwendung nach einer nicht abgefangenen Ausnahmebedingung ist aber eine durchaus riskante Vorgehensweise und wird nicht empfohlen, da der Prozessstatus störanfällig und unvorhersehbar wird. - -Außerdem wird die Verwendung von `uncaughtException` offiziell als [grobes Vorgehen](https://nodejs.org/api/process.html#process_event_uncaughtexception) angesehen, sodass es den [Vorschlag](https://github.com/nodejs/node-v0.x-archive/issues/2582) gibt, die Funktion aus dem Kern zu entfernen. Das Überwachen von `uncaughtException` per Listener ist also keine gute Idee. Daher empfehlen wir Dinge wie Mehrfachprozesse und Supervisoren: Ein Absturz und anschließender Neustart ist häufig die zuverlässigste Art der Fehlerbehebung. - -Zudem empfehlen wir, [domains](https://nodejs.org/api/domain.html) nicht zu verwenden. Mit diesem Modul, das zudem veraltet ist, lässt sich das Problem in der Regel nicht lösen. - -## Things to do in your environment / setup {#in-environment} - -Dies sind einige Beispiele für Maßnahmen, die Sie an Ihrer Systemumgebung vornehmen können, um die Anwendungsleistung zu verbessern: - -- NODE_ENV auf "production" festlegen -- Automatischen Neustart Ihrer Anwendung sicherstellen -- Anwendung in einem Cluster ausführen -- Anforderungsergebnisse im Cache speichern -- Load Balancer verwenden -- Reverse Proxy verwenden - -### NODE_ENV auf "production" festlegen - -In der Umgebungsvariablen NODE_ENV wird die Umgebung angegeben, in der eine Anwendung ausgeführt wird (in der Regel ist dies die Entwicklungs- oder Produktionsumgebung). One of the simplest things you can do to improve performance is to set NODE_ENV to `production`. - -Durch das Festlegen von NODE_ENV auf "production" führt Express Folgendes aus: - -- Speichern von Anzeigevorlagen im Cache. -- Speichern von CSS-Dateien, die aus CSS-Erweiterungen generiert wurden, im Cache. -- Generieren von weniger ausführlichen Fehlernachrichten. - -[Tests indicate](https://www.dynatrace.com/news/blog/the-drastic-effects-of-omitting-node-env-in-your-express-js-applications/) that just doing this can improve app performance by a factor of three! - -Wenn Sie umgebungsspezifischen Code schreiben müssen, können Sie den Wert von NODE_ENV mit `process.env.NODE_ENV` überprüfen. Beachten Sie, dass die Überprüfung des Werts seiner Umgebungsvariablen eine leistungsbezogene Penalisierung nach sich zieht. - -In einer Entwicklungsumgebung wird die Umgebungsvariable in der Regel in Ihrer interaktiven Shell festgelegt, indem Sie beispielsweise `export` oder Ihre Datei `.bash_profile` verwenden. But in general, you shouldn't do that on a production server; instead, use your OS's init system (systemd). Der nächste Abschnitt enthält weitere Details zur Verwendung des Init-Systems im Allgemeinen. Die Festlegung von NODE_ENV ist jedoch für das Leistungsverhalten so wichtig (und so einfach durchzuführen), dass hier besonders darauf eingegangen wird. - -Verwenden Sie bei systemd die Anweisung `Environment` in Ihrer Einheitendatei. Beispiel: - -```sh -# /etc/systemd/system/myservice.service -Environment=NODE_ENV=production -``` - -For more information, see [Using Environment Variables In systemd Units](https://www.flatcar.org/docs/latest/setup/systemd/environment-variables/). - -### Automatischen Neustart Ihrer Anwendung sicherstellen - -In der Produktionsumgebung sollte die Anwendung nie offline sein. Das bedeutet, dass Sie sicherstellen müssen, dass die Anwendung bei einem Absturz der Anwendung oder des Servers immer wieder neu gestartet wird. Auch wenn man hofft, das keines dieser Ereignisse jemals eintritt, muss man doch mit beiden Möglichkeiten rechnen und: - -- einen Prozessmanager verwenden, um die Anwendung (und Node) bei einem Absturz neu zu starten. -- das Init-System Ihres Betriebssystems verwenden, um den Prozessmanager bei einem Absturz des Betriebssystems neu zu starten. Außerdem kann das Init-System auch ohne einen Prozessmanager verwendet werden. - -Node-Anwendungen stürzen ab, wenn eine nicht abgefangene Ausnahmebedingung auftritt. Als Erstes müssen Sie in einem solchen Fall sicherstellen, dass Ihre Anwendung ausreichend getestet wurde und in der Lage ist, alle Ausnahmebedingungen zu handhaben (weitere Informationen siehe [Ausnahmebedingungen ordnungsgemäß handhaben](#exceptions)). Die sicherste Maßnahme ist jedoch, einen Mechanismus zu implementieren, über den bei einem Absturz der Anwendung ein automatischer Neustart der Anwendung ausgeführt wird. - -#### Prozessmanager verwenden - -In Entwicklungumgebungen wird die Anwendung einfach über die Befehlszeile mit `node server.js` oder einer vergleichbaren Datei gestartet. In der Produktionsumgebung hingegen ist durch diese Vorgehensweise die Katastrophe bereits vorprogrammiert. Wenn die Anwendung abstürzt, ist sie solange offline, bis Sie sie erneut starten. Um sicherzustellen, dass Ihre Anwendung nach einem Absturz neu gestartet wird, sollten Sie einen Prozessmanager verwenden. Ein Prozessmanager ist ein "Container" für Anwendungen, der die Bereitstellung erleichtert, eine hohe Verfügbarkeit sicherstellt und die Verwaltung der Anwendung zur Laufzeit ermöglicht. - -Neben einem Neustart der Anwendung nach einem Absturz bietet ein Prozessmanager noch weitere Möglichkeiten: - -- Einblicke in die Laufzeitleistung und die Ressourcennutzung -- Dynamische Änderung der Einstellungen zur Verbesserung des Leistungsverhaltens -- Control clustering (pm2). - -Historically, it was popular to use a Node.js process manager like [PM2](https://github.com/Unitech/pm2). See their documentation if you wish to do this. However, we recommend using your init system for process management. - -#### Init-System verwenden - -Als nächste Ebene der Zuverlässigkeit müssen Sie sicherstellen, dass Ihre Anwendung bei einem Serverneustart neu gestartet wird. Systeme können immer wieder aus verschiedenen Gründen abstürzen. Um sicherzustellen, dass Ihre Anwendung bei einem Serverabsturz neu gestartet wird, können Sie das in Ihr Betriebssystem integrierte Init-System verwenden. The main init system in use today is [systemd](https://wiki.debian.org/systemd). - -Es gibt zwei Möglichkeiten, Init-Systeme mit Ihrer Express-Anwendung zu verwenden: - -- Ausführung Ihrer Anwendung in einem Prozessmanager und Installation des Prozessmanagers als Service mit dem Init-System. Der Prozessmanager wird neu gestartet, wenn Ihre Anwendung abstürzt. Dies ist die empfohlene Vorgehensweise. -- Ausführung Ihrer Anwendung (und von Node) direkt mit dem Init-System. Diese Vorgehensweise ist zwar etwas einfacher, Sie profitieren jedoch nicht von den zusätzlichen Vorteilen des Einsatzes eines Prozessmanagers. - -##### systemd - -"systemd" ist ein Linux-System und Service-Manager. Die meisten wichtigen Linux-Distributionen haben "systemd" als Init-Standardsystem übernommen. - -Eine "systemd"-Servicekonfigurationsdatei wird als _Einheitendatei_ bezeichnet, die die Endung ".service" hat. Dies ist ein Beispiel für eine Einheitendatei zur direkten Verwaltung einer Node-Anwendung (ersetzen Sie den Text in Fettdruck durch Werte für Ihr System und Ihre Anwendung): Replace the values enclosed in `` for your system and app: - -```sh -[Unit] -Description= - -[Service] -Type=simple -ExecStart=/usr/local/bin/node -WorkingDirectory= - -User=nobody -Group=nogroup - -# Environment variables: -Environment=NODE_ENV=production - -# Allow many incoming connections -LimitNOFILE=infinity - -# Allow core dumps for debugging -LimitCORE=infinity - -StandardInput=null -StandardOutput=syslog -StandardError=syslog -Restart=always - -[Install] -WantedBy=multi-user.target -``` - -Weitere Informationen zu "systemd" siehe [systemd-Referenz (Man-Page)](http://www.freedesktop.org/software/systemd/man/systemd.unit.html). - -### Anwendung in einem Cluster ausführen - -In einem Multi-Core-System können Sie die Leistung einer Node-Anwendung mehrmals erhöhen, indem Sie einen Cluster von Prozessen starten. Ein Cluster führt mehrere Instanzen der Anwendung aus, idealerweise eine Instanz auf jedem CPU-Core. - -![Balancing between application instances using the cluster API](/images/clustering.png) - -Wichtig. Da die Anwendungsinstanzen als separate Prozesse ausgeführt werden, nutzen sie nicht dieselbe Hauptspeicherkapazität gemeinsam. Das heißt, Objekte befinden sich für jede Instanz der Anwendung auf lokaler Ebene. Daher kann der Status im Anwendungscode nicht beibehalten werden. Sie können jedoch einen speicherinternen Datenspeicher wie [Redis](http://redis.io/) verwenden, um sitzungsrelevante Daten und Statusinformationen zu speichern. Diese Einschränkung trifft im Wesentlichen auf alle Formen der horizontalen Skalierung zu, unabhängig davon, ob es sich um Clustering mit mehreren Prozessen oder mehreren physischen Servern handelt. - -Bei in Gruppen zusammengefassten Anwendungen (geclusterte Anwendungen) können Verarbeitungsprozesse einzeln ausfallen, ohne dass sich dies auf die restlichen Prozesse auswirkt. Neben den Leistungsvorteilen ist die Fehlerisolierung ein weiterer Grund, einen Cluster von Anwendungsprozessen auszuführen. Wenn ein Verarbeitungsprozess abstürzt, müssen Sie sicherstellen, dass das Ereignis protokolliert und ein neuer Prozess mithilfe von "cluster.fork()" gestartet wird. - -#### Clustermodule von Node verwenden - -Clustering is made possible with Node's [cluster module](https://nodejs.org/api/cluster.html). Dadurch wird ein Masterprozess eingeleitet, um Verarbeitungsprozesse zu starten und eingehende Verbindungen auf die Verarbeitungsprozesse zu verteilen. - -#### Using PM2 - -If you deploy your application with PM2, then you can take advantage of clustering _without_ modifying your application code. You should ensure your [application is stateless](https://pm2.keymetrics.io/docs/usage/specifics/#stateless-apps) first, meaning no local data is stored in the process (such as sessions, websocket connections and the like). - -When running an application with PM2, you can enable **cluster mode** to run it in a cluster with a number of instances of your choosing, such as the matching the number of available CPUs on the machine. You can manually change the number of processes in the cluster using the `pm2` command line tool without stopping the app. - -To enable cluster mode, start your application like so: - -```bash -# Start 4 worker processes -$ pm2 start npm --name my-app -i 4 -- start -# Auto-detect number of available CPUs and start that many worker processes -$ pm2 start npm --name my-app -i max -- start -``` - -This can also be configured within a PM2 process file (`ecosystem.config.js` or similar) by setting `exec_mode` to `cluster` and `instances` to the number of workers to start. - -Once running, the application can be scaled like so: - -```bash -# Add 3 more workers -$ pm2 scale my-app +3 -# Scale to a specific number of workers -$ pm2 scale my-app 2 -``` - -For more information on clustering with PM2, see [Cluster Mode](https://pm2.keymetrics.io/docs/usage/cluster-mode/) in the PM2 documentation. - -### Anforderungsergebnisse im Cache speichern - -Eine weitere Strategie zur Verbesserung des Leistungsverhaltens in Produktionsumgebungen ist das Speichern von Anforderungergebnissen im Cache. Ihre Anwendung muss also diese Operation nicht wiederholt ausführen, um dieselbe Anforderung wiederholt zu bedienen. - -Use a caching server like [Varnish](https://www.varnish-cache.org/) or [Nginx](https://blog.nginx.org/blog/nginx-caching-guide) (see also [Nginx Caching](https://serversforhackers.com/nginx-caching/)) to greatly improve the speed and performance of your app. - -### Load Balancer verwenden - -Unabhängig davon, wie gut eine Anwendung optimiert wurde, kann eine Einzelinstanz nur eine begrenzte Arbeitslast oder einen begrenzten Datenverkehr handhaben. Eine Möglichkeit, eine Anwendung zu skalieren, ist die Ausführung mehrerer Instanzen dieser Anwendung und die Verteilung des Datenverkehrs über eine Lastausgleichsfunktion (Load Balancer) vorzunehmen. Die Einrichtung eines solchen Load Balancer kann helfen, Leistung und Geschwindigkeit Ihrer Anwendung zu verbessern. - -Ein Load Balancer ist in der Regel ein Reverse Proxy, der den Datenverkehr zu und von mehreren Anwendungsinstanzen und Servern koordiniert. You can easily set up a load balancer for your app by using [Nginx](https://nginx.org/en/docs/http/load_balancing.html) or [HAProxy](https://www.digitalocean.com/community/tutorials/an-introduction-to-haproxy-and-load-balancing-concepts). - -Bei einer solchen Lastverteilung müssen Sie sicherstellen, dass Anforderungen, die einer bestimmten Sitzungs-ID zugeordnet sind, mit dem Prozess verbunden sind, von dem sie ursprünglich stammen. Dies wird auch als _Sitzungsaffinität_ oder _Affine Sitzungen_ bezeichnet und kann durch den obigen Vorschlag, einen Datenspeicher wie Redis für Sitzungsdaten zu verwenden (je nach Anwendung), umgesetzt werden. Eine Beschreibung hierzu siehe [Mehrere Knoten verwenden](https://socket.io/docs/v4/using-multiple-nodes/). - -### Reverse Proxy verwenden - -Ein Reverse Proxy befindet sich vor einer Webanwendung und führt Unterstützungsoperationen für die Anforderungen aus (außer das Weiterleiten von Anforderungen an die Anwendung). Fehlerseiten, Komprimierungen und Caching bearbeiten, Dateien bereitstellen und Lastverteilungen vornehmen. - -Durch die Übergabe von Tasks, die keine Kenntnis des Anwendungsstatus erfordern, an einen Reverse Proxy muss Express keine speziellen Anwendungstasks mehr ausführen. For this reason, it is recommended to run Express behind a reverse proxy like [Nginx](https://www.nginx.org/) or [HAProxy](https://www.haproxy.org/) in production. diff --git a/de/advanced/best-practice-security.md b/de/advanced/best-practice-security.md deleted file mode 100644 index 75d473657d..0000000000 --- a/de/advanced/best-practice-security.md +++ /dev/null @@ -1,282 +0,0 @@ ---- -layout: page -title: Sicherheitsspezifische Best Practices für Express-Anwendungen in Produktionsumgebungen -description: Discover crucial security best practices for Express apps in production, including using TLS, input validation, secure cookies, and preventing vulnerabilities. -menu: advanced -order: 3 -redirect_from: " " ---- - -# Best Practices in Produktionsumgebungen: Sicherheit - -## Überblick - -Der Begriff _"Produktion"_ bezieht sich auf die Phase im Softwarelebenszyklus, in der eine Anwendung oder API für Endbenutzer oder Verbraucher allgemein verfügbar ist. Im Gegensatz dazu wird in der Phase _"Entwicklung"_ noch aktiv Code geschrieben und getestet. Die Anwendung ist in dieser Phase noch nicht für externen Zugriff verfügbar. Die entsprechenden Systemumgebungen werden als _Produktionsumgebungen_ und _Entwicklungsumgebungen_ bezeichnet. - -Entwicklungs- und Produktionsumgebungen werden in der Regel unterschiedlich konfiguriert und weisen deutliche Unterschiede bei den Anforderungen auf. Was in der Entwicklung funktioniert, muss in der Produktion nicht unbedingt akzeptabel sein. Beispiel: In einer Entwicklungsumgebung ist eine ausführliche Protokollierung von Fehlern für Debuggingzwecke sinnvoll. Dieselbe Vorgehensweise kann in einer Produktionsumgebung jedoch zu einem Sicherheitsproblem führen. In einer Entwicklungsumgebung müssen Sie sich keine Gedanken zu Themen wie Skalierbarkeit, Zuverlässigkeit und Leistung machen, während dies in einer Produktionsumgebung kritische Faktoren sind. - -{% capture security-note %} - -If you believe you have discovered a security vulnerability in Express, please see -[Security Policies and Procedures](/en/resources/contributing.html#security-policies-and-procedures). - -{% endcapture %} - -{% include admonitions/note.html content=security-note %} - -In diesem Beitrag werden einige der Best Practices in Bezug auf das Thema Sicherheit für Express-Anwendungen behandelt, die in der Produktionsumgebung bereitgestellt werden. - -- [Production Best Practices: Security](#production-best-practices-security) - - [Overview](#overview) - - [Don't use deprecated or vulnerable versions of Express](#dont-use-deprecated-or-vulnerable-versions-of-express) - - Über [hsts](https://github.com/helmetjs/hsts) werden `Strict-Transport-Security`-Header festgelegt, über die sichere (HTTP over SSL/TLS) Verbindungen zum Server durchgesetzt werden. - - [Do not trust user input](#do-not-trust-user-input) - - [Prevent open redirects](#prevent-open-redirects) - - "Helmet" ist eine Ansammlung von neun kleineren Middlewarefunktionen, über die sicherheitsrelevante HTTP-Header festgelegt werden. - - [Reduce fingerprinting](#reduce-fingerprinting) - - Über [xssFilter](https://github.com/helmetjs/x-xss-protection) werden `X-XSS-Protection`-Header festgelegt, um XSS-Filter (Cross-site Scripting) in den meisten aktuellen Web-Browsern zu aktivieren. - - Über [noCache](https://github.com/helmetjs/nocache) werden `Cache-Control`- und Pragma-Header festgelegt, um clientseitiges Caching zu deaktivieren. - - Über [ieNoOpen](https://github.com/helmetjs/ienoopen) werden `X-Download-Options`-Header für IE8+ festgelegt. - - [Prevent brute-force attacks against authorization](#prevent-brute-force-attacks-against-authorization) - - [Ensure your dependencies are secure](#ensure-your-dependencies-are-secure) - - [Avoid other known vulnerabilities](#avoid-other-known-vulnerabilities) - - [Additional considerations](#additional-considerations) - -## Verwenden Sie keine veralteten oder anfälligen Versionen von Express - -Express 2.x und 3.x werden nicht mehr gepflegt. Sicherheits- und Leistungsprobleme in diesen Versionen werden nicht mehr behoben. Verwenden Sie diese Versionen nicht! Wenn Sie noch nicht auf Version 4 umgestellt haben, befolgen Sie die Anweisungen im [Migrationshandbuch](/{{ page.lang }}/guide/migrating-4.html). - -Stellen Sie außerdem sicher, dass Sie keine anfälligen Express-Versionen verwenden, die auf der [Seite mit den Sicherheitsupdates](/{{ page.lang }}/advanced/security-updates.html) aufgelistet sind. Falls doch, führen Sie ein Update auf eines der stabileren Releases durch, bevorzugt das aktuelle Release. - -## TLS verwenden - -Wenn über Ihre Anwendung vertrauliche Daten bearbeitet oder übertragen werden, sollten Sie [Transport Layer Security](https://en.wikipedia.org/wiki/Transport_Layer_Security) (TLS) verwenden, um die Verbindung und die Daten zu schützen. Diese Technologie verschlüsselt Daten, bevor sie vom Client zum Server gesendet werden. Dadurch lassen sich einige gängige (und einfache) Hackerattacken vermeiden. Auch wenn Ajax- und POST-Anforderungen nicht sofort offensichtlich und in Browsern "versteckt" zu sein scheinen, ist deren Netzverkehr anfällig für das [Ausspionieren von Paketen](https://en.wikipedia.org/wiki/Packet_analyzer) und [Man-in-the-Middle-Attacken](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). - -Möglicherweise sind Sie mit SSL-Verschlüsselung (Secure Socket Layer) bereits vertraut. [TLS ist einfach der nächste Entwicklungsschritt bei SSL](https://msdn.microsoft.com/en-us/library/windows/desktop/aa380515\(v=vs.85\).aspx). In anderen Worten: Wenn Sie bisher SSL verwendet haben, sollten Sie ein Upgrade auf TLS in Erwägung ziehen. Generell empfehlen wir für TLS den Nginx-Server. Eine gute Referenz zum Konfigurieren von TLS auf Nginx (und anderen Servern) ist [Empfohlene Serverkonfigurationen (Mozilla Wiki)](https://wiki.mozilla.org/Security/Server_Side_TLS#Recommended_Server_Configurations). - -Ein handliches Tool zum Abrufen eines kostenloses TLS-Zertifikats ist außerdem [Let's Encrypt](https://letsencrypt.org/about/), eine kostenlose, automatisierte und offene Zertifizierungsstelle der [Internet Security Research Group (ISRG)](https://letsencrypt.org/isrg/). - -## Do not trust user input - -For web applications, one of the most critical security requirements is proper user input validation and handling. This comes in many forms and we will not cover all of them here. -Ultimately, the responsibility for validating and correctly handling the types of user input your application accepts is yours. - -### Prevent open redirects - -An example of potentially dangerous user input is an _open redirect_, where an application accepts a URL as user input (often in the URL query, for example `?url=https://example.com`) and uses `res.redirect` to set the `location` header and -return a 3xx status. - -An application must validate that it supports redirecting to the incoming URL to avoid sending users to malicious links such as phishing websites, among other risks. - -Here is an example of checking URLs before using `res.redirect` or `res.location`: - -```js -app.use((req, res) => { - try { - if (new Url(req.query.url).host !== 'example.com') { - return res.status(400).end(`Unsupported redirect to host: ${req.query.url}`) - } - } catch (e) { - return res.status(400).end(`Invalid url: ${req.query.url}`) - } - res.redirect(req.query.url) -}) -``` - -## "Helmet" verwenden - -[Helmet](https://www.npmjs.com/package/helmet) kann beim Schutz Ihrer Anwendung gegen einige gängige Schwachstellen hilfreiche Dienste leisten, indem die HTTP-Header entsprechend konfiguriert werden. - -Helmet is a middleware function that sets security-related HTTP response headers. Helmet sets the following headers by default: - -- `Content-Security-Policy`: A powerful allow-list of what can happen on your page which mitigates many attacks -- `Cross-Origin-Opener-Policy`: Helps process-isolate your page -- `Cross-Origin-Resource-Policy`: Blocks others from loading your resources cross-origin -- `Origin-Agent-Cluster`: Changes process isolation to be origin-based -- `Referrer-Policy`: Controls the [`Referer`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer) header -- `Strict-Transport-Security`: Tells browsers to prefer HTTPS -- `X-Content-Type-Options`: Avoids [MIME sniffing](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#mime_sniffing) -- `X-DNS-Prefetch-Control`: Controls DNS prefetching -- `X-Download-Options`: Forces downloads to be saved (Internet Explorer only) -- `X-Frame-Options`: Legacy header that mitigates [Clickjacking](https://en.wikipedia.org/wiki/Clickjacking) attacks -- `X-Permitted-Cross-Domain-Policies`: Controls cross-domain behavior for Adobe products, like Acrobat -- `X-Powered-By`: Info about the web server. Removed because it could be used in simple attacks -- `X-XSS-Protection`: Legacy header that tries to mitigate [XSS attacks](https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting), but makes things worse, so Helmet disables it - -Each header can be configured or disabled. To read more about it please go to [its documentation website][helmet]. - -Installieren Sie "Helmet" wie alle anderen Module: - -```bash -$ npm install helmet -``` - -So verwenden Sie "Helmet" in Ihrem Code: - -```js -// ... - -const helmet = require('helmet') -app.use(helmet()) - -// ... -``` - -## Reduce fingerprinting - -It can help to provide an extra layer of security to reduce the ability of attackers to determine -the software that a server uses, known as "fingerprinting." Though not a security issue itself, -reducing the ability to fingerprint an application improves its overall security posture. -Server software can be fingerprinted by quirks in how it responds to specific requests, for example in -the HTTP response headers. - -Ein bewährtes Verfahren ist also, diesen Header mit der Methode `app.disable()` zu deaktivieren: - -```js -app.disable('x-powered-by') -``` - -{% capture powered-advisory %} - -Disabling the `X-Powered-By header` does not prevent -a sophisticated attacker from determining that an app is running Express. It may -discourage a casual exploit, but there are other ways to determine an app is running -Express. - -{% endcapture %} - -{% include admonitions/note.html content=powered-advisory %} - -Express also sends its own formatted "404 Not Found" messages and formatter error -response messages. These can be changed by -[adding your own not found handler](/en/starter/faq.html#how-do-i-handle-404-responses) -and -[writing your own error handler](/en/guide/error-handling.html#writing-error-handlers): - -```js -// last app.use calls right before app.listen(): - -// custom 404 -app.use((req, res, next) => { - res.status(404).send("Sorry can't find that!") -}) - -// custom error handler -app.use((err, req, res, next) => { - console.error(err.stack) - res.status(500).send('Something broke!') -}) -``` - -## Cookies sicher verwenden - -Um sicherzustellen, dass Cookies Ihre Anwendung nicht für Angriffsmöglichkeiten öffnen, sollten Sie den standardmäßigen Namen des Sitzungscookies nicht verwenden und die Cookie-Sicherheitsoptionen entsprechend festlegen. - -Es gibt zwei wesentliche Middleware-Cookie-Sitzungsmodule: - -- [express-session](https://www.npmjs.com/package/express-session), das in Express 3.x integrierte `express.session`-Middleware ersetzt. -- [cookie-session](https://www.npmjs.com/package/cookie-session), das in Express 3.x integrierte `express.cookieSession`-Middleware ersetzt. - -Der Hauptunterschied zwischen diesen beiden Modulen liegt darin, wie die Cookie-Sitzungsdaten gespeichert werden. Die [express-session](https://www.npmjs.com/package/express-session)-Middleware speichert Sitzungsdaten auf dem Server. Sie speichert nur die Sitzungs-ID im Cookie und nicht die Sitzungsdaten. Standardmäßig wird dabei der speicherinterne Speicher verwendet. Eine Verwendung der Middleware in der Produktionsumgebung ist nicht vorgesehen. In der Produktionsumgebung müssen Sie einen skalierbaren "Session-Store" einrichten. Siehe hierzu die Liste der [kompatiblen Session-Stores](https://github.com/expressjs/session#compatible-session-stores). - -Im Gegensatz dazu implementiert die [cookie-session](https://www.npmjs.com/package/cookie-session)-Middleware cookiegestützten Speicher: Sie serialisiert die gesamte Sitzung zum Cookie und nicht nur einen Sitzungsschlüssel. Diese Middleware sollten Sie nur verwenden, wenn Sitzungsdaten relativ klein sind und einfach als primitive Werte (und nicht als Objekte) codiert sind. Auch wenn Browser mindestens 4096 Byte pro Cookie unterstützen sollten, müssen Sie sicherstellen, dass dieses Limit nicht überschritten wird. Überschreiten Sie auf keinen Fall die Größe von 4093 Byte pro Domäne. Achten Sie zudem darauf, dass die Cookiedaten für den Client sichtbar sind. Wenn also ein Grund vorliegt, die Daten sicher oder unkenntlich zu machen, ist "express-session" möglicherweise die bessere Wahl. - -### Verwenden Sie nicht den standardmäßigen Namen des Sitzungscookies - -Die Verwendung des standardmäßigen Namens des Sitzungscookies kann Ihre Anwendung anfällig für Attacken machen. Das mögliche Sicherheitsproblem ist vergleichbar mit `X-Powered-By`: ein potenzieller Angreifer kann diesen Header verwenden, um einen elektronischen Fingerabdruck des Servers zu erstellen und Attacken entsprechend zu platzieren. - -Über [noSniff](https://github.com/helmetjs/dont-sniff-mimetype) werden `X-Content-Type-Options`-Header festgelegt, um bei Browsern MIME-Sniffing von Antworten weg vom deklarierten Inhaltstyp (declared content-type) vorzubeugen. - -```js -const session = require('express-session') -app.set('trust proxy', 1) // trust first proxy -app.use(session({ - secret: 's3Cur3', - name: 'sessionId' -})) -``` - -### Cookie-Sicherheitsoptionen festlegen - -Legen Sie die folgenden Cookieoptionen fest, um die Sicherheit zu erhöhen: - -- `secure` - Stellt sicher, dass der Browser das Cookie nur über HTTPS sendet. -- `httpOnly` - Stellt sicher, dass das Cookie nur über HTTP(S) und nicht über das Client-JavaScript gesendet wird und dadurch Schutz gegen Cross-Site Scripting-Attacken besteht. -- `domain` - Gibt die Domäne des Cookies an, die für den Vergleich mit der Domäne des Servers verwendet wird, in der die URL angefordert wird. Stimmen diese beiden überein, müssen Sie das Pfadattribut überprüfen. -- `path` - Gibt den Pfad des Cookies an, der für den Vergleich mit dem Anforderungspfad verwendet wird. Wenn dieser Pfad und die Domäne übereinstimmen, können Sie das Cookie in der Anforderung senden. -- `expires` - Wird verwendet, um das Ablaufdatum für persistente Cookies festzulegen. - -Dies ist ein Beispiel zur Verwendung der [cookie-session](https://www.npmjs.com/package/cookie-session)-Middleware: - -```js -const session = require('cookie-session') -const express = require('express') -const app = express() - -const expiryDate = new Date(Date.now() + 60 * 60 * 1000) // 1 hour -app.use(session({ - name: 'session', - keys: ['key1', 'key2'], - cookie: { - secure: true, - httpOnly: true, - domain: 'example.com', - path: 'foo/bar', - expires: expiryDate - } -})) -``` - -## Prevent brute-force attacks against authorization - -Make sure login endpoints are protected to make private data more secure. - -A simple and powerful technique is to block authorization attempts using two metrics: - -1. The number of consecutive failed attempts by the same user name and IP address. -2. The number of failed attempts from an IP address over some long period of time. For example, block an IP address if it makes 100 failed attempts in one day. - -[rate-limiter-flexible](https://github.com/animir/node-rate-limiter-flexible) package provides tools to make this technique easy and fast. You can find [an example of brute-force protection in the documentation](https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#login-endpoint-protection) - -## Ensure your dependencies are secure - -Using npm to manage your application's dependencies is powerful and convenient. But the packages that you use may contain critical security vulnerabilities that could also affect your application. The security of your app is only as strong as the "weakest link" in your dependencies. - -Since npm@6, npm automatically reviews every install request. Also, you can use `npm audit` to analyze your dependency tree. - -```bash -$ npm audit -``` - -If you want to stay more secure, consider [Snyk](https://snyk.io/). - -Snyk offers both a [command-line tool](https://www.npmjs.com/package/snyk) and a [Github integration](https://snyk.io/docs/github) that checks your application against [Snyk's open source vulnerability database](https://snyk.io/vuln/) for any known vulnerabilities in your dependencies. Install the CLI as follows: - -```bash -$ npm install -g snyk -$ cd your-app -``` - -Use this command to test your application for vulnerabilities: - -```bash -$ snyk test -``` - -### Vermeiden Sie andere Schwachstellen - -Achten Sie auf [Node Security Project](https://npmjs.com/advisories)-Empfehlungen, die Express oder andere Module, die Ihre Anwendung nutzt, beeinträchtigen können. Im Allgemeinen ist Node Security Project aber eine exzellente Ressource mit Wissen und Tools zur Sicherheit von Node. - -Letztendlich können Express-Anwendungen – wie viele andere Webanwendungen auch – anfällig für eine Vielzahl webbasierter Attacken sein. Machen Sie sich deshalb mit bekannten [webspezifischen Schwachstellen](https://www.owasp.org/www-project-top-ten/) vertraut und treffen Sie die geeigneten Vorkehrungen, um diese zu vermeiden. - -## Weitere Überlegungen - -Dies sind einige weitere Empfehlungen aus der hervorragenden [Node.js Security Checklist](https://blog.risingstack.com/node-js-security-checklist/). In diesem Blogbeitrag finden Sie alle Details zu diesen Empfehlungen: - -- Filtern und bereinigen Sie immer Benutzereingaben, um sich gegen XS-Angriffe (Cross-Site Scripting) und Befehlsinjektionsattacken zu schützen. -- Implementieren Sie Verteidungsmaßnahmen gegen SQL-Injection-Attacken, indem sie parametrisierte Abfragen oder vorbereitete Anweisungen einsetzen. -- Nutzen Sie das Open-Source-Tool [sqlmap](http://sqlmap.org/), um SQL-Injection-Schwachstellen in Ihrer Anwendung zu erkennen. -- Verwenden Sie die Tools [nmap](https://nmap.org/) und [sslyze](https://github.com/nabla-c0d3/sslyze), um die Konfiguration Ihrer SSL-Verschlüsselungen, -Schlüssel und Neuvereinbarungen sowie die Gültigkeit Ihres Zertifikats zu testen. -- Verwenden Sie [safe-regex](https://www.npmjs.com/package/safe-regex), um sicherzustellen, dass Ihre regulären Ausdrücke nicht für [Denial-of-Service-Attacken](https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS) anfällig sind. - -[helmet]: \ No newline at end of file diff --git a/de/advanced/developing-template-engines.md b/de/advanced/developing-template-engines.md deleted file mode 100644 index 82f5e3b8ed..0000000000 --- a/de/advanced/developing-template-engines.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -layout: page -title: Template-Engines für Express entwickeln -description: Learn how to develop custom template engines for Express.js using app.engine(), with examples on creating and integrating your own template rendering logic. -menu: advanced -order: 1 -redirect_from: " " ---- - -# Template-Engines für Express entwickeln - -Verwenden Sie die Methode `app.engine(ext, callback)`, um Ihre eigene Template-Engine zu erstellen. `ext` bezieht sich auf die Dateierweiterung, `callback` ist die Template-Engine-Funktion, die die folgenden Elemente als Parameter akzeptiert: die Position der Datei, das Optionsobjekt und die Callback-Funktion. - -Der folgende Code ist ein Beispiel für die Implementierung einer sehr einfachen Template-Engine für die Ausgabe von `.ntl`-Dateien. - -```js -const fs = require('fs') // this engine requires the fs module -app.engine('ntl', (filePath, options, callback) => { // define the template engine - fs.readFile(filePath, (err, content) => { - if (err) return callback(err) - // this is an extremely simple template engine - const rendered = content.toString() - .replace('#title#', `${options.title}`) - .replace('#message#', `

${options.message}

`) - return callback(null, rendered) - }) -}) -app.set('views', './views') // specify the views directory -app.set('view engine', 'ntl') // register the template engine -``` - -Ihre Anwendung ist jetzt in der Lage, `.ntl`-Dateien auszugeben. Erstellen Sie im Verzeichnis `views` eine Datei namens `index.ntl` mit dem folgenden Inhalt. - -```pug -#title# -#message# -``` - -Erstellen Sie dann in Ihrer Anwendung die folgende Route. - -```js -app.get('/', (req, res) => { - res.render('index', { title: 'Hey', message: 'Hello there!' }) -}) -``` - -Wenn Sie eine Anforderung zur Homepage einleiten, wird `index.ntl` im HTML-Format ausgegeben. \ No newline at end of file diff --git a/de/advanced/healthcheck-graceful-shutdown.md b/de/advanced/healthcheck-graceful-shutdown.md deleted file mode 100644 index 0f508986cc..0000000000 --- a/de/advanced/healthcheck-graceful-shutdown.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -layout: page -title: Health Checks and Graceful Shutdown -description: Learn how to implement health checks and graceful shutdown in Express apps to enhance reliability, manage deployments, and integrate with load balancers like Kubernetes. -menu: advanced -order: 5 -redirect_from: " " ---- - -# Health Checks and Graceful Shutdown - -## Graceful shutdown - -When you deploy a new version of your application, you must replace the previous version. The process manager you're using will first send a SIGTERM signal to the application to notify it that it will be killed. Once the application gets this signal, it should stop accepting new requests, finish all the ongoing requests, clean up the resources it used, including database connections and file locks then exit. - -### Beispiel - -```js -const server = app.listen(port) - -process.on('SIGTERM', () => { - debug('SIGTERM signal received: closing HTTP server') - server.close(() => { - debug('HTTP server closed') - }) -}) -``` - -## Health checks - -A load balancer uses health checks to determine if an application instance is healthy and can accept requests. For example, [Kubernetes has two health checks](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/): - -- `liveness`, that determines when to restart a container. -- `readiness`, that determines when a container is ready to start accepting traffic. When a pod is not ready, it is removed from the service load balancers. \ No newline at end of file diff --git a/de/advanced/security-updates.md b/de/advanced/security-updates.md deleted file mode 100644 index eb001048c0..0000000000 --- a/de/advanced/security-updates.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -layout: page -title: Express-Sicherheitsupdates -description: Review the latest security updates and patches for Express.js, including detailed vulnerability lists for different versions to help maintain a secure application. -menu: advanced -order: 2 -redirect_from: " " ---- - -# Sicherheitsupdates - -
-Schwachstellen bei Node.js wirken sich direkt auf Express aus. Daher sollten Sie [ein Auge auf Schwachstellen bei Node.js haben](https://nodejs.org -/en/blog/vulnerability/) und sicherstellen, dass Sie die aktuelle stabile Version von Node.js haben. -
- -Die folgende Liste enthält die Express-Schwachstellen, die im angegebenen Versionsupdate behoben wurden. - -{% capture security-policy %} -If you believe you have discovered a security vulnerability in Express, please see -[Security Policies and Procedures](/{{page.lang}}/resources/contributing.html#security-policies-and-procedures). -{% endcapture %} - -{% include admonitions/note.html content=security-policy %} - -## 4.x - -- 4.21.2 - - The dependency `path-to-regexp` has been updated to address a [vulnerability](https://github.com/pillarjs/path-to-regexp/security/advisories/GHSA-rhx6-c78j-4q9w). -- 4.21.1 - - The dependency `cookie` has been updated to address a [vulnerability](https://github.com/jshttp/cookie/security/advisories/GHSA-pxg6-pf52-xh8x), This may affect your application if you use `res.cookie`. -- 4.20.0 - - Fixed XSS vulnerability in `res.redirect` ([advisory](https://github.com/expressjs/express/security/advisories/GHSA-qw6h-vgh9-j6wx), [CVE-2024-43796](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-43796)). - - The dependency `serve-static` has been updated to address a [vulnerability](https://github.com/advisories/GHSA-cm22-4g7w-348p). - - The dependency `send` has been updated to address a [vulnerability](https://github.com/advisories/GHSA-m6fv-jmcg-4jfg). - - The dependency `path-to-regexp` has been updated to address a [vulnerability](https://github.com/pillarjs/path-to-regexp/security/advisories/GHSA-9wv6-86v2-598j). - - The dependency `body-parser` has been updated to addres a [vulnerability](https://github.com/advisories/GHSA-qwcr-r2fm-qrc7), This may affect your application if you had url enconding activated. -- 4.19.0, 4.19.1 - - Fixed open redirect vulnerability in `res.location` and `res.redirect` ([advisory](https://github.com/expressjs/express/security/advisories/GHSA-rv95-896h-c2vc), [CVE-2024-29041](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-29041)). -- 4.17.3 - - The dependency `qs` has been updated to address a [vulnerability](https://github.com/advisories/GHSA-hrpp-h998-j3pp). This may affect your application if the following APIs are used: `req.query`, `req.body`, `req.param`. -- 4.16.0 - - The dependency `forwarded` has been updated to address a [vulnerability](https://npmjs.com/advisories/527). This may affect your application if the following APIs are used: `req.host`, `req.hostname`, `req.ip`, `req.ips`, `req.protocol`. - - The dependency `mime` has been updated to address a [vulnerability](https://npmjs.com/advisories/535), but this issue does not impact Express. - - The dependency `send` has been updated to provide a protection against a [Node.js 8.5.0 vulnerability](https://nodejs.org/en/blog/vulnerability/september-2017-path-validation/). This only impacts running Express on the specific Node.js version 8.5.0. -- 4.15.5 - - The dependency `debug` has been updated to address a [vulnerability](https://snyk.io/vuln/npm:debug:20170905), but this issue does not impact Express. - - The dependency `fresh` has been updated to address a [vulnerability](https://npmjs.com/advisories/526). This will affect your application if the following APIs are used: `express.static`, `req.fresh`, `res.json`, `res.jsonp`, `res.send`, `res.sendfile` `res.sendFile`, `res.sendStatus`. -- 4.15.3 - - The dependency `ms` has been updated to address a [vulnerability](https://snyk.io/vuln/npm:ms:20170412). This may affect your application if untrusted string input is passed to the `maxAge` option in the following APIs: `express.static`, `res.sendfile`, and `res.sendFile`. -- 4.15.2 - - The dependency `qs` has been updated to address a [vulnerability](https://snyk.io/vuln/npm:qs:20170213), but this issue does not impact Express. Updating to 4.15.2 is a good practice, but not required to address the vulnerability. -- 4.11.1 - - Offenlegungsgefahr beim Rootpfad in `express.static`, `res.sendfile` und `res.sendFile` behoben. -- 4.10.7 - - Offene Umadressierungsschwachstelle in `express.static` ([Empfehlung](https://npmjs.com/advisories/35), [CVE-2015-1164](http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-1164)) behoben. -- 4.8.8 - - Schwachstellen durch Directory-Traversal-Technik in `express.static` ([Empfehlung](http://npmjs.com/advisories/32) , [CVE-2014-6394](http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-6394)) behoben. -- 4.8.4 - - Node.js 0.10 kann in bestimmten Situationen Lecks bei `fd` aufweisen, die sich auf `express.static` und `res.sendfile` auswirken. Böswillige Anforderungen können zu Lecks bei `fd` führen und letztendlich `EMFILE`-Fehler nach sich ziehen und bewirken, dass Server nicht antworten. -- 4.8.0 - - Sparse-Arrays mit extrem hohen Indizes in der Abfragezeichenfolge können bewirken, dass für die Prozessausführung nicht genügend Arbeitsspeicher zur Verfügung steht und es zu einem Serverabsturz kommt. - - Extrem verschachtelte Abfragezeichenfolgenobjekte können bewirken, dass der Prozess blockiert und der Server dadurch vorübergehend nicht antwortet. - -## 3.x - -
- **Express 3.x WIRD NICHT MEHR GEWARTET** - -Bekannte und unbekannte Probleme bei Sicherheit und Leistung in 3.x wurden seit dem letzten Update (1. August 2015) noch nicht behoben. Es wird dringend empfohlen, die aktuelle Version von Express zu verwenden. - -If you are unable to upgrade past 3.x, please consider [Commercial Support Options](/{{ page.lang }}/support#commercial-support-options). - -
- -- 3.19.1 - - Offenlegungsgefahr beim Rootpfad in `express.static`, `res.sendfile` und `res.sendFile` behoben. -- 3.19.0 - - Offene Umadressierungsschwachstelle in `express.static` ([Empfehlung](https://npmjs.com/advisories/35), [CVE-2015-1164](http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-1164)) behoben. -- 3.16.10 - - Schwachstellen durch Directory-Traversal-Technik in `express.static` behoben. -- 3.16.6 - - Node.js 0.10 kann in bestimmten Situationen Lecks bei `fd` aufweisen, die sich auf `express.static` und `res.sendfile` auswirken. Böswillige Anforderungen können zu Lecks bei `fd` führen und letztendlich `EMFILE`-Fehler nach sich ziehen und bewirken, dass Server nicht antworten. -- 3.16.0 - - Sparse-Arrays mit extrem hohen Indizes in der Abfragezeichenfolge können bewirken, dass für die Prozessausführung nicht genügend Arbeitsspeicher zur Verfügung steht und es zu einem Serverabsturz kommt. - - Extrem verschachtelte Abfragezeichenfolgenobjekte können bewirken, dass der Prozess blockiert und der Server dadurch vorübergehend nicht antwortet. -- 3.3.0 - - Die Antwort 404 bei einem nicht unterstützten Überschreibungsversuch war anfällig gegen Cross-Site Scripting-Attacken. \ No newline at end of file diff --git a/de/api.md b/de/api.md deleted file mode 100644 index 815162cbec..0000000000 --- a/de/api.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -layout: api -version: 5x -title: Express 5.x - API-Referenz -description: Access the API reference for Express.js detailing all modules, methods, and properties for building web applications with this version. -menu: api -redirect_from: " " ---- - -
-

5.x API

- {% include api/en/5x/express.md %} - - {% include api/en/5x/app.md %} - - {% include api/en/5x/req.md %} - - {% include api/en/5x/res.md %} - - {% include api/en/5x/router.md %} -
diff --git a/de/changelog/index.md b/de/changelog/index.md deleted file mode 100644 index c03940c3e7..0000000000 --- a/de/changelog/index.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -layout: page -title: Express changelog -description: Stay updated with the release changelog for Express.js, detailing new features, bug fixes, and important changes across versions. -sitemap: false -redirect_from: - - " " - - " " -redirect_to: https://github.com/expressjs/express/releases ---- diff --git a/de/guide/behind-proxies.md b/de/guide/behind-proxies.md deleted file mode 100644 index 3bf62362de..0000000000 --- a/de/guide/behind-proxies.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -layout: page -title: Express hinter Proxys -description: Learn how to configure Express.js applications to work correctly behind reverse proxies, including using the trust proxy setting to handle client IP addresses. -menu: guide -order: 8 -redirect_from: " " ---- - -# Express hinter Proxys - -When running an Express app behind a reverse proxy, some of the Express APIs may return different values than expected. In order to adjust for this, the `trust proxy` application setting may be used to expose information provided by the reverse proxy in the Express APIs. The most common issue is express APIs that expose the client's IP address may instead show an internal IP address of the reverse proxy. - -
-When configuring the `trust proxy` setting, it is important to understand the exact setup of the reverse proxy. Since this setting will trust values provided in the request, it is important that the combination of the setting in Express matches how the reverse proxy operates. -
- -Bei der Ausführung einer Express-Anwendung hinter einem Proxy legen Sie die Anwendungsvariable `trust proxy` (mithilfe von [app.set()](/{{ page.lang }}/4x/api.html#app.set)) auf einen der in der folgenden Tabelle enthaltenen Werte fest: - - - - - - - - - - - - - - - - - - - - - -
TypWert
BooleschWenn `true` angegeben wird, wird die IP-Adresse des Clients als der äußerst rechte Eintrag im Header `X-Forwarded-*` interpretiert. - -Wenn `false` angegeben wird, wird die Anwendung als direkte Verbindung zum Internet gesehen. Die IP-Adresse des Clients wird dann von `req.connection.remoteAddress` abgeleitet. Dies ist die Standardeinstellung. - -
-When setting to `true`, it is important to ensure that the last reverse proxy trusted is removing/overwriting all of the following HTTP headers: `X-Forwarded-For`, `X-Forwarded-Host`, and `X-Forwarded-Proto`, otherwise it may be possible for the client to provide any value. -
-
IP addresses -An IP address, subnet, or an array of IP addresses and subnets to trust as being a reverse proxy. The following list shows the pre-configured subnet names: - -- loopback - `127.0.0.1/8`, `::1/128` -- linklocal - `169.254.0.0/16`, `fe80::/10` -- uniquelocal - `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `fc00::/7` - -Sie können IP-Adressen wie folgt festlegen: - -```js -app.set('trust proxy', 'loopback') // specify a single subnet -app.set('trust proxy', 'loopback, 123.123.123.123') // specify a subnet and an address -app.set('trust proxy', 'loopback, linklocal, uniquelocal') // specify multiple subnets as CSV -app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']) // specify multiple subnets as an array -``` - -Sobald die Werte angegeben wurden, werden die betreffenden IP-Adressen und Teilnetze aus dem Adressfeststellungsprozess ausgeschlossen. Die nicht vertrauenswürdige IP-Adresse, die am nächsten zum Anwendungsserver liegt, wird als IP-Adresse des Clients festgelegt. This works by checking if `req.socket.remoteAddress` is trusted. If so, then each address in `X-Forwarded-For` is checked from right to left until the first non-trusted address. - -
Zahl -Use the address that is at most `n` number of hops away from the Express application. `req.socket.remoteAddress` is the first hop, and the rest are looked for in the `X-Forwarded-For` header from right to left. A value of `0` means that the first untrusted address would be `req.socket.remoteAddress`, i.e. there is no reverse proxy. - -
-When using this setting, it is important to ensure there are not multiple, different-length paths to the Express application such that the client can be less than the configured number of hops away, otherwise it may be possible for the client to provide any value. -
-
Function -Custom trust implementation. - -```js -app.set('trust proxy', (ip) => { - if (ip === '127.0.0.1' || ip === '123.123.123.123') return true // trusted IPs - else return false -}) -``` - -
- -Enabling `trust proxy` will have the following impact: - -
    -
  • Der Wert für [req.hostname](/{{ page.lang }}/api.html#req.hostname) wird vom Wert abgeleitet, der im Header `X-Forwarded-Host` festgelegt wurde. Dieser Wert kann vom Client oder Proxy festgelegt werden.
  • -
  • `X-Forwarded-Proto` kann vom Reverse Proxy festgelegt werden, um der Anwendung mitzuteilen, ob es sich um `https` oder `http` oder sogar um einen ungültigen Namen handelt. Dieser Wert wird durch [req.protocol](/{{ page.lang }}/api.html#req.protocol) abgebildet. -
  • -
  • Als Werte für [req.ip](/{{ page.lang }}/api.html#req.ip) und [req.ips](/{{ page.lang }}/api.html#req.ips) wird die Liste der Adressen aus `X-Forwarded-For` herangezogen. -
  • -
- -Die Einstellung für `trust proxy` wird mithilfe des [proxy-addr](https://www.npmjs.com/package/proxy-addr)-Pakets implementiert. Weitere Informationen finden Sie in der zugehörigen Dokumentation. diff --git a/de/guide/database-integration.md b/de/guide/database-integration.md deleted file mode 100644 index 0a39aab7ea..0000000000 --- a/de/guide/database-integration.md +++ /dev/null @@ -1,505 +0,0 @@ ---- -layout: page -title: Datenbankintegration in Express -description: Discover how to integrate various databases with Express.js applications, including setup examples for MongoDB, MySQL, PostgreSQL, and more. -menu: guide -order: 11 -redirect_from: " " ---- - -# Datenbankintegration - -Die Herstellung einer Verbindung zwischen Datenbanken und Express-Anwendungen erfolgt einfach durch Laden eines geeigneten Node.js-Treibers für die Datenbank in Ihre Anwendung. In diesem Dokument wird in Kurzform beschrieben, wie einige der gängigsten Node.js-Module für Datenbanksysteme Ihrer Express-Anwendung hinzugefügt und verwendet werden: - -- [Cassandra](#cassandra) -- [Couchbase](#couchbase) -- [CouchDB](#couchdb) -- [LevelDB](#leveldb) -- [MySQL](#mysql) -- [MongoDB](#mongo) -- [Neo4j](#neo4j) -- [Oracle](#oracle) -- [PostgreSQL](#postgres) -- [Redis](#redis) -- -- [SQLite](#sqlite) -- [ElasticSearch](#elasticsearch) - -
-Diese Datenbanktreiber sind neben zahlreichen anderen Treibern verfügbar. Weitere Optionen finden Sie auf der [npm](https://www.npmjs.com/)-Site. -
- -## Cassandra - -**Modul**: [cassandra-driver](https://github.com/datastax/nodejs-driver) -**Installation** - -### Installation - -```bash -$ npm install cassandra-driver -``` - -### Beispiel - -```js -const cassandra = require('cassandra-driver') -const client = new cassandra.Client({ contactPoints: ['localhost'] }) - -client.execute('select key from system.local', (err, result) => { - if (err) throw err - console.log(result.rows[0]) -}) -``` - -## Couchbase - -**Module**: [couchnode](https://github.com/couchbase/couchnode) - -### Installation - -```bash -$ npm install couchbase -``` - -### Beispiel - -```js -const couchbase = require('couchbase') -const bucket = (new couchbase.Cluster('http://localhost:8091')).openBucket('bucketName') - -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err) - } else { - console.log(result) - } -}) - -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1' -const query = N1qlQuery.fromString(n1ql) -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err) - } else { - console.log(result) - } -}) -``` - -## CouchDB - -**Modul**: [nano](https://github.com/dscape/nano) -**Installation** - -### Installation - -```bash -$ npm install nano -``` - -### Beispiel - -```js -const nano = require('nano')('http://localhost:5984') -nano.db.create('books') -const books = nano.db.use('books') - -// Insert a book document in the books database -books.insert({ name: 'The Art of war' }, null, (err, body) => { - if (err) { - console.log(err) - } else { - console.log(body) - } -}) - -// Get a list of all books -books.list((err, body) => { - if (err) { - console.log(err) - } else { - console.log(body.rows) - } -}) -``` - -## LevelDB - -**Modul**: [levelup](https://github.com/rvagg/node-levelup) -**Installation** - -### Installation - -```bash -$ npm install level levelup leveldown -``` - -### Beispiel - -```js -const levelup = require('levelup') -const db = levelup('./mydb') - -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err) - - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err) - - console.log(`name=${value}`) - }) -}) -``` - -## MySQL - -**Modul**: [mysql](https://github.com/felixge/node-mysql/) -**Installation** - -### Installation - -```bash -$ npm install mysql -``` - -### Beispiel - -```js -const mysql = require('mysql') -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db' -}) - -connection.connect() - -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err - - console.log('The solution is: ', rows[0].solution) -}) - -connection.end() -``` - -## MongoDB - -**Modul**: [mongodb](https://github.com/mongodb/node-mongodb-native) -**Installation** - -### Installation - -```bash -$ npm install mongodb -``` - -### Example (v2.\*) - -```js -const MongoClient = require('mongodb').MongoClient - -MongoClient.connect('mongodb://localhost:27017/animals', (err, db) => { - if (err) throw err - - db.collection('mammals').find().toArray((err, result) => { - if (err) throw err - - console.log(result) - }) -}) -``` - -### Example (v3.\*) - -```js -const MongoClient = require('mongodb').MongoClient - -MongoClient.connect('mongodb://localhost:27017/animals', (err, client) => { - if (err) throw err - - const db = client.db('animals') - - db.collection('mammals').find().toArray((err, result) => { - if (err) throw err - - console.log(result) - }) -}) -``` - -Wenn Sie nach einem Objektmodelltreiber für MongoDB suchen, schauen Sie unter [Mongoose](https://github.com/LearnBoost/mongoose) nach. - -## Neo4j - -**Module**: [neo4j-driver](https://github.com/neo4j/neo4j-javascript-driver) - -### Installation - -```bash -$ npm install neo4j-driver -``` - -### Beispiel - -```js -const neo4j = require('neo4j-driver') -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')) - -const session = driver.session() - -session.readTransaction((tx) => { - return tx.run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')) - }) - .catch((error) => { - console.log(error) - }) -}) -``` - -## Oracle - -**Modul**: [oracledb](https://github.com/oracle/node-oracledb) - -### Installation - -Anmerkung: [Siehe Installations-Voraussetzungen](https://github.com/oracle/node-oracledb#-installation). - -```bash -$ npm install oracledb -``` - -### Beispiel - -```js -const oracledb = require('oracledb') -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl' -} - -async function getEmployee (empId) { - let conn - - try { - conn = await oracledb.getConnection(config) - - const result = await conn.execute( - 'select * from employees where employee_id = :id', - [empId] - ) - - console.log(result.rows[0]) - } catch (err) { - console.log('Ouch!', err) - } finally { - if (conn) { // conn assignment worked, need to close - await conn.close() - } - } -} - -getEmployee(101) -``` - -## PostgreSQL - -**Modul**: [pg-promise](https://github.com/vitaly-t/pg-promise) -**Installation** - -### Installation - -```bash -$ npm install pg-promise -``` - -### Beispiel - -```js -const pgp = require('pg-promise')(/* options */) -const db = pgp('postgres://username:password@host:port/database') - -db.one('SELECT $1 AS value', 123) - .then((data) => { - console.log('DATA:', data.value) - }) - .catch((error) => { - console.log('ERROR:', error) - }) -``` - -## Redis - -**Modul**: [redis](https://github.com/mranney/node_redis) -**Installation** - -### Installation - -```bash -$ npm install redis -``` - -### Beispiel - -```js -const redis = require('redis') -const client = redis.createClient() - -client.on('error', (err) => { - console.log(`Error ${err}`) -}) - -client.set('string key', 'string val', redis.print) -client.hset('hash key', 'hashtest 1', 'some value', redis.print) -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print) - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`) - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`) - }) - - client.quit() -}) -``` - -## SQL Server - -**Module**: [tedious](https://github.com/tediousjs/tedious) - -### Installation - -```bash -$ npm install tedious -``` - -### Beispiel - -```js -const Connection = require('tedious').Connection -const Request = require('tedious').Request - -const config = { - server: 'localhost', - authentication: { - type: 'default', - options: { - userName: 'your_username', // update me - password: 'your_password' // update me - } - } -} - -const connection = new Connection(config) - -connection.on('connect', (err) => { - if (err) { - console.log(err) - } else { - executeStatement() - } -}) - -function executeStatement () { - request = new Request("select 123, 'hello world'", (err, rowCount) => { - if (err) { - console.log(err) - } else { - console.log(`${rowCount} rows`) - } - connection.close() - }) - - request.on('row', (columns) => { - columns.forEach((column) => { - if (column.value === null) { - console.log('NULL') - } else { - console.log(column.value) - } - }) - }) - - connection.execSql(request) -} -``` - -## SQLite - -**Modul**: [sqlite3](https://github.com/mapbox/node-sqlite3) -**Installation** - -### Installation - -```bash -$ npm install sqlite3 -``` - -### Beispiel - -```js -const sqlite3 = require('sqlite3').verbose() -const db = new sqlite3.Database(':memory:') - -db.serialize(() => { - db.run('CREATE TABLE lorem (info TEXT)') - const stmt = db.prepare('INSERT INTO lorem VALUES (?)') - - for (let i = 0; i < 10; i++) { - stmt.run(`Ipsum ${i}`) - } - - stmt.finalize() - - db.each('SELECT rowid AS id, info FROM lorem', (err, row) => { - console.log(`${row.id}: ${row.info}`) - }) -}) - -db.close() -``` - -## ElasticSearch - -**Modul**: [elasticsearch](https://github.com/elastic/elasticsearch-js) -**Installation** - -### Installation - -```bash -$ npm install elasticsearch -``` - -### Beispiel - -```js -const elasticsearch = require('elasticsearch') -const client = elasticsearch.Client({ - host: 'localhost:9200' -}) - -client.search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'] - } - } - } -}).then((response) => { - const hits = response.hits.hits -}, (error) => { - console.trace(error.message) -}) -``` diff --git a/de/guide/debugging.md b/de/guide/debugging.md deleted file mode 100644 index 570e26d6d7..0000000000 --- a/de/guide/debugging.md +++ /dev/null @@ -1,130 +0,0 @@ ---- -layout: page -title: Debugging bei Express -description: Learn how to enable and use debugging logs in Express.js applications by setting the DEBUG environment variable for enhanced troubleshooting. -menu: guide -order: 7 -redirect_from: " " ---- - -# Debugging bei Express - -Wenn Sie alle in Express verwendeten internen Protokolle anzeigen wollen, legen Sie beim Starten Ihrer Anwendung die Umgebungsvariable `DEBUG` auf `express:*` fest. - -```bash -$ DEBUG=express:* node index.js -``` - -Verwenden Sie unter Windows den entsprechenden Befehl. - -```bash -> $env:DEBUG = "express:*"; node index.js -``` - -Die Ausführung dieses Befehls für die durch [express generator](/{{ page.lang }}/starter/generator.html) generierte Standardanwendung resultiert in folgender Ausgabe: - -```bash -$ DEBUG=express:* node ./bin/www - express:router:route new / +0ms - express:router:layer new / +1ms - express:router:route get / +1ms - express:router:layer new / +0ms - express:router:route new / +1ms - express:router:layer new / +0ms - express:router:route get / +0ms - express:router:layer new / +0ms - express:application compile etag weak +1ms - express:application compile query parser extended +0ms - express:application compile trust proxy false +0ms - express:application booting in development mode +1ms - express:router use / query +0ms - express:router:layer new / +0ms - express:router use / expressInit +0ms - express:router:layer new / +0ms - express:router use / favicon +1ms - express:router:layer new / +0ms - express:router use / logger +0ms - express:router:layer new / +0ms - express:router use / jsonParser +0ms - express:router:layer new / +1ms - express:router use / urlencodedParser +0ms - express:router:layer new / +0ms - express:router use / cookieParser +0ms - express:router:layer new / +0ms - express:router use / stylus +90ms - express:router:layer new / +0ms - express:router use / serveStatic +0ms - express:router:layer new / +0ms - express:router use / router +0ms - express:router:layer new / +1ms - express:router use /users router +0ms - express:router:layer new /users +0ms - express:router use / &lt;anonymous&gt; +0ms - express:router:layer new / +0ms - express:router use / &lt;anonymous&gt; +0ms - express:router:layer new / +0ms - express:router use / &lt;anonymous&gt; +0ms - express:router:layer new / +0ms -``` - -Bei einer Anforderung an die Anwendung sind die Protokolle im Express-Code angegeben: - -```bash - express:router dispatching GET / +4h - express:router query : / +2ms - express:router expressInit : / +0ms - express:router favicon : / +0ms - express:router logger : / +1ms - express:router jsonParser : / +0ms - express:router urlencodedParser : / +1ms - express:router cookieParser : / +0ms - express:router stylus : / +0ms - express:router serveStatic : / +2ms - express:router router : / +2ms - express:router dispatching GET / +1ms - express:view lookup "index.pug" +338ms - express:view stat "/projects/example/views/index.pug" +0ms - express:view render "/projects/example/views/index.pug" +1ms -``` - -Wenn Sie nur die Protokolle von der Routerimplementierung sehen wollen, legen Sie den Wert für `DEBUG` auf `express:router` fest. Gleichermaßen gilt: Wenn Sie nur die Protokolle von der Anwendungsimplementierung sehen wollen, legen Sie den Wert für `DEBUG` auf `express:application` fest, usw. - -## Von `express` generierte Anwendungen - -An application generated by the `express` command uses the `debug` module and its debug namespace is scoped to the name of the application. - -Beispiel: Wenn Sie die Anwendung mit `$ express sample-app` generiert haben, können Sie die Debuganweisungen mit dem folgenden Befehl aktivieren: - -```bash -$ DEBUG=sample-app:* node ./bin/www -``` - -Sie können mehrere Debug-Namespaces in einer durch Kommas getrennten Namensliste angeben: - -```bash -$ DEBUG=http,mail,express:* node index.js -``` - -## Advanced options - -When running through Node.js, you can set a few environment variables that will change the behavior of the debug logging: - -| Name | Purpose | -| ------------------- | ----------------------------------------------------------------- | -| `DEBUG` | Enables/disables specific debugging namespaces. | -| `DEBUG_COLORS` | Whether or not to use colors in the debug output. | -| `DEBUG_DEPTH` | Object inspection depth. | -| `DEBUG_FD` | File descriptor to write debug output to. | -| `DEBUG_SHOW_HIDDEN` | Shows hidden properties on inspected objects. | - -{% capture debug-text %} - -The environment variables beginning with `DEBUG_` end up being -converted into an Options object that gets used with `%o`/`%O` formatters. -See the Node.js documentation for -[`util.inspect()`](https://nodejs.org/api/util.html#util_util_inspect_object_options) -for the complete list. - -{% endcapture %} - -{% include admonitions/note.html content=debug-text %} diff --git a/de/guide/error-handling.md b/de/guide/error-handling.md deleted file mode 100644 index 54c252ea71..0000000000 --- a/de/guide/error-handling.md +++ /dev/null @@ -1,284 +0,0 @@ ---- -layout: page -title: Fehlerbehandlung in Express -description: Understand how Express.js handles errors in synchronous and asynchronous code, and learn to implement custom error handling middleware for your applications. -menu: guide -order: 6 -redirect_from: " " ---- - -# Fehlerbehandlung - -_Error Handling_ refers to how Express catches and processes errors that -occur both synchronously and asynchronously. Express comes with a default error -handler so you don't need to write your own to get started. - -## Catching Errors - -It's important to ensure that Express catches all errors that occur while -running route handlers and middleware. - -Errors that occur in synchronous code inside route handlers and middleware -require no extra work. If synchronous code throws an error, then Express will -catch and process it. Beispiel: - -```js -app.get('/', (req, res) => { - throw new Error('BROKEN') // Express will catch this on its own. -}) -``` - -Middlewarefunktionen für die Fehlerbehandlung werden in derselben Weise definiert wie andere Middlewarefunktionen, nur, dass Fehlerbehandlungsfunktionen vier anstatt drei Argumente aufweisen: -`(err, req, res, next)`. Beispiel: - -```js -app.get('/', (req, res, next) => { - fs.readFile('/file-does-not-exist', (err, data) => { - if (err) { - next(err) // Pass errors to Express. - } else { - res.send(data) - } - }) -}) -``` - -Middleware für die Fehlerbehandlung wird ganz zuletzt nach allen anderen `app.use()`- und Weiterleitungsaufrufen definiert. -Beispiel: - -```js -app.get('/user/:id', async (req, res, next) => { - const user = await getUserById(req.params.id) - res.send(user) -}) -``` - -If `getUserById` throws an error or rejects, `next` will be called with either -the thrown error or the rejected value. If no rejected value is provided, `next` -will be called with a default Error object provided by the Express router. - -Wenn Sie Übergaben an die Funktion `next()` vornehmen (außer die Zeichenfolge `'route'`), sieht Express die aktuelle Anforderung als Fehler an und überspringt alle verbleibenden fehlerfreien Behandlungsroutinen und Middlewarefunktionen. - -If the callback in a sequence provides no data, only errors, you can simplify -this code as follows: - -```js -app.get('/', [ - function (req, res, next) { - fs.writeFile('/inaccessible-path', 'data', next) - }, - function (req, res) { - res.send('OK') - } -]) -``` - -In the above example, `next` is provided as the callback for `fs.writeFile`, -which is called with or without errors. If there is no error, the second -handler is executed, otherwise Express catches and processes the error. - -Bei einem Routenhandler mit mehreren Callback-Funktionen können Sie den Parameter `route` verwenden, um den nächsten Routenhandler zu überspringen. Beispiel: - -```js -app.get('/', (req, res, next) => { - setTimeout(() => { - try { - throw new Error('BROKEN') - } catch (err) { - next(err) - } - }, 100) -}) -``` - -The above example uses a `try...catch` block to catch errors in the -asynchronous code and pass them to Express. If the `try...catch` -block were omitted, Express would not catch the error since it is not part of the synchronous -handler code. - -Use promises to avoid the overhead of the `try...catch` block or when using functions -that return promises. Beispiel: - -```js -app.get('/', (req, res, next) => { - Promise.resolve().then(() => { - throw new Error('BROKEN') - }).catch(next) // Errors will be passed to Express. -}) -``` - -Since promises automatically catch both synchronous errors and rejected promises, -you can simply provide `next` as the final catch handler and Express will catch errors, -because the catch handler is given the error as the first argument. - -You could also use a chain of handlers to rely on synchronous error -catching, by reducing the asynchronous code to something trivial. Beispiel: - -```js -app.get('/', [ - function (req, res, next) { - fs.readFile('/maybe-valid-file', 'utf-8', (err, data) => { - res.locals.data = data - next(err) - }) - }, - function (req, res) { - res.locals.data = res.locals.data.split(',')[1] - res.send(res.locals.data) - } -]) -``` - -The above example has a couple of trivial statements from the `readFile` -call. If `readFile` causes an error, then it passes the error to Express, otherwise you -quickly return to the world of synchronous error handling in the next handler -in the chain. Then, the example above tries to process the data. If this fails, then the -synchronous error handler will catch it. If you had done this processing inside -the `readFile` callback, then the application might exit and the Express error -handlers would not run. - -Whichever method you use, if you want Express error handlers to be called in and the -application to survive, you must ensure that Express receives the error. - -## Die Standardfehlerbehandlungsroutine (Default Error Handler) - -Express ist bereits mit einer integrierten Fehlerbehandlungsroutine ausgestattet, mit der alle in der Anwendung festgestellten Fehler gehandhabt werden können. Diese Middleware für die Fehlerbehandlung wird am Ende des Middleware-Funktionsstack hinzugefügt. - -Wenn Sie einen Fehler an `next()` übergeben und diesen nicht mit einem Error-Handler bearbeiten, wird dieser über den integrierten Error-Handler bearbeitet. Der Fehler wird mit dem Stack-Trace zum Client geschrieben. Der Stack-Trace ist in der Produktionsumgebung nicht verfügbar. - -
-Legen Sie die Umgebungsvariable `NODE_ENV` auf `production` fest, um die Anwendung im Produktionsmodus auszuführen. -
- -When an error is written, the following information is added to the -response: - -- The `res.statusCode` is set from `err.status` (or `err.statusCode`). If - this value is outside the 4xx or 5xx range, it will be set to 500. -- The `res.statusMessage` is set according to the status code. -- The body will be the HTML of the status code message when in production - environment, otherwise will be `err.stack`. -- Any headers specified in an `err.headers` object. - -Wenn `next()` mit einem Fehler aufgerufen wird, nachdem Sie mit dem Schreiben der Antwort begonnen haben (z. B., wenn Sie beim Streamen der Antwort zum Client einen Fehler feststellen), schließt die Standardfehlerbehandlungsroutine in Express die Verbindung, und die Anforderung schlägt fehl. - -Wenn Sie also einen angepassten Error-Handler hinzufügen, empfiehlt es sich, eine Delegierung zur Standardfehlerbehandlungsroutine in Express vorzunehmen, wenn die Header bereits an den Client gesendet wurden: - -```js -function errorHandler (err, req, res, next) { - if (res.headersSent) { - return next(err) - } - res.status(500) - res.render('error', { error: err }) -} -``` - -Note that the default error handler can get triggered if you call `next()` with an error -in your code more than once, even if custom error handling middleware is in place. - -Other error handling middleware can be found at [Express middleware](/{{ page.lang }}/resources/middleware.html). - -## Writing error handlers - -Define error-handling middleware functions in the same way as other middleware functions, -except error-handling functions have four arguments instead of three: -`(err, req, res, next)`. Beispiel: - -```js -app.use((err, req, res, next) => { - console.error(err.stack) - res.status(500).send('Something broke!') -}) -``` - -You define error-handling middleware last, after other `app.use()` and routes calls; for example: - -```js -const bodyParser = require('body-parser') -const methodOverride = require('method-override') - -app.use(bodyParser.urlencoded({ - extended: true -})) -app.use(bodyParser.json()) -app.use(methodOverride()) -app.use((err, req, res, next) => { - // logic -}) -``` - -Antworten von der Middlewarefunktion können das von Ihnen gewünschte Format aufweisen wie beispielsweise eine Fehlerseite im HTML-Format, eine einfache Nachricht oder eine JSON-Zeichenfolge. - -Für organisatorische Zwecke (und Frameworks der höheren Ebene) können Sie mehrere Middlewarefunktionen für die Fehlerbehandlung definieren, wie Sie dies bei regulären Middlewarefunktionen auch tun würden. Wenn Sie beispielsweise eine Fehlerbehandlungsroutine (Error-Handler) für Anforderungen über `XHR` und andere Anforderungen definieren wollen, können Sie die folgenden Befehle verwenden: - -```js -const bodyParser = require('body-parser') -const methodOverride = require('method-override') - -app.use(bodyParser.urlencoded({ - extended: true -})) -app.use(bodyParser.json()) -app.use(methodOverride()) -app.use(logErrors) -app.use(clientErrorHandler) -app.use(errorHandler) -``` - -In diesem Beispiel kann die generische `logErrors`-Funktion Anforderungs- und Fehlerinformationen in `stderr` schreiben: - -```js -function logErrors (err, req, res, next) { - console.error(err.stack) - next(err) -} -``` - -In diesem Beispiel wird `clientErrorHandler` wie folgt definiert. In diesem Fall wird der Fehler explizit an den nächsten Error-Handler übergeben: - -Notice that when _not_ calling "next" in an error-handling function, you are responsible for writing (and ending) the response. Otherwise, those requests will "hang" and will not be eligible for garbage collection. - -```js -function clientErrorHandler (err, req, res, next) { - if (req.xhr) { - res.status(500).send({ error: 'Something failed!' }) - } else { - next(err) - } -} -``` - -Die `errorHandler`-Funktion "catch-all" kann wie folgt implementiert werden: - -```js -function errorHandler (err, req, res, next) { - res.status(500) - res.render('error', { error: err }) -} -``` - -If you have a route handler with multiple callback functions, you can use the `route` parameter to skip to the next route handler. Beispiel: - -```js -app.get('/a_route_behind_paywall', - (req, res, next) => { - if (!req.user.hasPaid) { - // continue handling this request - next('route') - } else { - next() - } - }, (req, res, next) => { - PaidContent.find((err, doc) => { - if (err) return next(err) - res.json(doc) - }) - }) -``` - -In diesem Beispiel wird der Handler `getPaidContent` übersprungen. Alle verbleibenden Handler in `app` für `/a_route_behind_paywall` werden jedoch weiter ausgeführt. - -
-Aufrufe zu `next()` und `next(err)` geben an, dass der aktuelle Handler abgeschlossen ist und welchen Status er aufweist. Durch `next(err)` werden alle verbleibenden Handler in der Kette übersprungen. Ausgenommen hiervor sind die Handler, die konfiguriert sind, um Fehler wie oben beschrieben zu behandeln. -
diff --git a/de/guide/migrating-4.md b/de/guide/migrating-4.md deleted file mode 100644 index 2292800f1a..0000000000 --- a/de/guide/migrating-4.md +++ /dev/null @@ -1,555 +0,0 @@ ---- -layout: page -title: Migration auf Express 4 -description: A guide to migrating your Express.js applications from version 3 to 4, covering changes in middleware, routing, and how to update your codebase effectively. -menu: guide -order: 9 -redirect_from: " " ---- - -# Wechsel zu Express 4 - -

Überblick

- -Express 4 bietet grundlegende Veränderungen im Vergleich zu Express 3. Das bedeutet, dass eine Express 3-Anwendung nicht funktioniert, wenn Sie die Express-Version in ihren Abhängigkeiten aktualisieren. - -In diesem Beitrag werden folgende Themen behandelt: - - - -

Änderungen in Express 4

- -In Express 4 wurden einige signifikante Änderungen vorgenommen: - - - -Siehe hierzu auch: - -- [Neue Features/Funktionen in 4.x.](https://github.com/expressjs/express/wiki/New-features-in-4.x) -- [Migration von 3.x auf 4.x.](https://github.com/expressjs/express/wiki/Migrating-from-3.x-to-4.x) - -

-Änderungen am Express-Core- und Middlewaresystem

- -In Express 4 bestehen keine Abhängigkeiten mehr zu Connect. Alle integrierten Middlewarefunktionen werden aus dem Core entfernt. Ausgenommen hiervon ist die Funktion `express.static`. Das bedeutet, dass Express nun ein unabhängiges Routing- und Middleware-Web-Framework ist und Express-Versionierung und -Releases von Middleware-Updates nicht betroffen sind. - -Ohne integrierte Middleware müssen Sie explizit alle Middlewarefunktionen hinzufügen, die für die Ausführung Ihrer Anwendung erforderlich sind. Befolgen Sie einfach diese Schritte: - -1. Installieren des Moduls: `npm install --save ` -2. Anfordern des Moduls in Ihrer Anwendung: `require('modulname')` -3. Verwendung des Moduls gemäß Dokumentation: `app.use( ... )` - -In der folgenden Tabelle sind Express 3-Middlewarefunktionen und deren Entsprechungen in Express 4 aufgelistet. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Express 3Express 4
express.bodyParserbody-parser + -multer
express.compresscompression
express.cookieSessioncookie-session
express.cookieParsercookie-parser
express.loggermorgan
express.sessionexpress-session
express.faviconserve-favicon
express.responseTimeresponse-time
express.errorHandlererrorhandler
express.methodOverridemethod-override
express.timeoutconnect-timeout
express.vhostvhost
express.csrfcsurf
express.directoryserve-index
express.staticserve-static
- -Hier finden Sie die [komplette Liste](https://github.com/senchalabs/connect#middleware) der Express 4-Middleware. - -In den meisten Fällen können Sie einfach nur die Middleware der alten Version 3 durch deren Entsprechung in Express 4 ersetzen. Details hierzu finden Sie in der modulspezifischen Dokumentation in GitHub. - -

app.use akzeptiert Parameter.

- -In Version 4 können Sie über einen Variablenparameter den Pfad definieren, in den Middlewarefunktionen geladen werden. Dann können Sie den Wert des Parameters aus dem Routenhandler laden. -Beispiel: - -```js -app.use('/book/:id', (req, res, next) => { - console.log('ID:', req.params.id) - next() -}) -``` - -

-Das Routingsystem -

- -Anwendungen laden nun implizit Routing-Middleware. Sie müssen sich also keine Gedanken mehr über die Reihenfolge machen, in der die Middleware in Bezug auf die `router`-Middleware geladen wird. - -Das Routingsystem verfügt jedoch über zwei neue Funktionen, die beim Organisieren Ihrer Weiterleitungen helfen: - -{: .doclist } - -- Die neue Methode `app.route()` zum Erstellen verkettbarer Routenhandler für einen Weiterleitungspfad -- Die neue Klasse `express.Router` zum Erstellen modular einbindbarer Routenhandler - -

Die Methode app.route()

- -Die neue Methode `app.route()` hilft beim Erstellen verkettbarer Routenhandler für einen Weiterleitungspfad. Da der Pfad an einer einzelnen Position angegeben wird, ist das Erstellen modularer Weiterleitungen hilfreich, da Redundanzen und Schreibfehler reduziert werden. Weitere Informationen zu Weiterleitungen finden Sie in der Dokumentation zu [`Router()`](/{{ page.lang }}/4x/api.html#router). - -Dies ist ein Beispiel für verkettete Routenhandler, die mit der Funktion `app.route()` definiert werden. - -```js -app.route('/book') - .get((req, res) => { - res.send('Get a random book') - }) - .post((req, res) => { - res.send('Add a book') - }) - .put((req, res) => { - res.send('Update the book') - }) -``` - -

Die Klasse express.Router

- -Eine weitere Funktion, die beim Organisieren von Weiterleitungen hilft, ist die neue Klasse `express.Router`. Über diese Klasse können Sie modular einbindbare Routenhandler erstellen. Eine `Router`-Instanz ist ein vollständiges Middleware- und Routingsystem. Aus diesem Grund wird diese Instanz oft auch als "Mini-App" bezeichnet. - -Im folgenden Beispiel wird ein Router als Modul erstellt, Middleware in das Modul geladen, es werden Weiterleitungen definiert und das Modul letztendlich in einen Pfad in der Hauptanwendung eingebunden. - -Beispiel: Erstellen Sie eine Routerdatei namens `birds.js` mit dem folgenden Inhalt im Anwendungsverzeichnis: - -```js -var express = require('express') -var router = express.Router() - -// middleware specific to this router -router.use((req, res, next) => { - console.log('Time: ', Date.now()) - next() -}) -// define the home page route -router.get('/', (req, res) => { - res.send('Birds home page') -}) -// define the about route -router.get('/about', (req, res) => { - res.send('About birds') -}) - -module.exports = router -``` - -Laden Sie dann das Routermodul in die Anwendung: - -```js -var birds = require('./birds') - -// ... - -app.use('/birds', birds) -``` - -Die Anwendung kann nun Anforderungen an die Pfade `/birds` und `/birds/about` bearbeiten und ruft die Middleware `timeLog` auf, die speziell für diese Weiterleitung bestimmt ist. - -

-Weitere Änderungen

- -In der folgenden Tabelle sind andere kleinere, aber trotzdem wichtige Änderungen in Express 4 aufgeführt: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ObjektBeschreibung
Node.js -Express 4 erfordert Node.js 0.10.x oder höher und unterstützt nicht mehr Node.js 0.8.x. -
-`http.createServer()` - -Das Modul `http` wird nicht mehr benötigt, es sei denn, Sie müssen direkt mit dem Modul arbeiten (socket.io/SPDY/HTTPS). Die Anwendung kann mithilfe der Funktion `app.listen()` gestartet werden. -
-`app.configure()` - -Die Funktion `app.configure()` wurde entfernt. Verwenden Sie die Funktion `process.env.NODE_ENV` oder `app.get('env')`, um die Umgebung zu erkennen und die Anwendung entsprechend zu konfigurieren. -
-`json spaces` - -Die Anwendungseigenschaft `json spaces` ist in Express 4 standardmäßig inaktiviert. -
-`req.accepted()` - -Verwenden Sie `req.accepts()`, `req.acceptsEncodings()`, `req.acceptsCharsets()` und `req.acceptsLanguages()`. -
-`res.location()` - -Löst keine relativen URLs mehr auf. -
-`req.params` - -War bisher ein Array, ist nun ein Objekt. -
-`res.locals` - -War bisher eine Funktion, ist nun ein Objekt. -
-`res.headerSent` - -Geändert in `res.headersSent`. -
-`app.route` - -Nun verfügbar als `app.mountpath`. -
-`res.on('header')` - -Entfernt. -
-`res.charset` - -Entfernt. -
-`res.setHeader('Set-Cookie', val)` - -Die Funktionalität ist nun auf die Einstellung des Basis-Cookiewerts begrenzt. Verwenden Sie `res.cookie()`, um weitere Funktionalität zu erhalten. -
- -

Beispiel für eine Anwendungsmigration

- -Dies ist ein Beispiel für die Migration einer Express 3-Anwendung auf Express 4. -Die dabei interessanten Dateien sind `app.js` und `package.json`. - -

-Anwendung der Version 3 -

- -

app.js

- -Es wird eine Express v.3-Anwendung mit der folgenden Datei `app.js` angenommen: - -```js -var express = require('express') -var routes = require('./routes') -var user = require('./routes/user') -var http = require('http') -var path = require('path') - -var app = express() - -// all environments -app.set('port', process.env.PORT || 3000) -app.set('views', path.join(__dirname, 'views')) -app.set('view engine', 'pug') -app.use(express.favicon()) -app.use(express.logger('dev')) -app.use(express.methodOverride()) -app.use(express.session({ secret: 'your secret here' })) -app.use(express.bodyParser()) -app.use(app.router) -app.use(express.static(path.join(__dirname, 'public'))) - -// development only -if (app.get('env') === 'development') { - app.use(express.errorHandler()) -} - -app.get('/', routes.index) -app.get('/users', user.list) - -http.createServer(app).listen(app.get('port'), () => { - console.log('Express server listening on port ' + app.get('port')) -}) -``` - -

package.json

- -Die zugehörige `package.json`-Datei der Version 3 sieht in etwa wie folgt aus: - -```json -{ - "name": "application-name", - "version": "0.0.1", - "private": true, - "scripts": { - "start": "node app.js" - }, - "dependencies": { - "express": "3.12.0", - "pug": "*" - } -} -``` - -

-Prozess -

- -Beginnen Sie den Migrationsprozess mit der Installation der erforderlichen Middleware für die Express 4-Anwendung und der Aktualisierung von Express und Pug auf die aktuellen Versionen. Verwenden Sie hierzu den folgenden Befehl: - -```bash -$ npm install serve-favicon morgan method-override express-session body-parser multer errorhandler express@latest pug@latest --save -``` - -Nehmen Sie an `app.js` die folgenden Änderungen vor: - -1. Die integrierten Express-Middlewarefunktionen `express.favicon`, - `express.logger`, `express.methodOverride`, - `express.session`, `express.bodyParser` und - `express.errorHandler` sind im Objekt `express` nicht mehr verfügbar. Sie müssen deren Alternativen manuell installieren und in die Anwendung laden. - -2. Sie müssen die Funktion `app.router` nicht mehr laden. - Sie ist kein gültiges Express 4-Anwendungsobjekt. Entfernen Sie also den Code `app.use(app.router);`. - -3. Stellen Sie sicher, dass die Middlewarefunktionen in der richtigen Reihenfolge geladen werden – laden Sie `errorHandler` nach dem Laden der Anwendungsweiterleitungen. - -

Anwendung der Version 4

- -

package.json

- -Durch Ausführung des Befehls `npm` wird `package.json` wie folgt aktualisiert: - -```json -{ - "name": "application-name", - "version": "0.0.1", - "private": true, - "scripts": { - "start": "node app.js" - }, - "dependencies": { - "body-parser": "^1.5.2", - "errorhandler": "^1.1.1", - "express": "^4.8.0", - "express-session": "^1.7.2", - "pug": "^2.0.0", - "method-override": "^2.1.2", - "morgan": "^1.2.2", - "multer": "^0.1.3", - "serve-favicon": "^2.0.1" - } -} -``` - -

app.js

- -Entfernen Sie dann ungültigen Code, laden Sie die erforderliche Middleware und nehmen Sie andere Änderungen nach Bedarf vor. Die Datei `app.js` sieht dann wie folgt aus: - -```js -var http = require('http') -var express = require('express') -var routes = require('./routes') -var user = require('./routes/user') -var path = require('path') - -var favicon = require('serve-favicon') -var logger = require('morgan') -var methodOverride = require('method-override') -var session = require('express-session') -var bodyParser = require('body-parser') -var multer = require('multer') -var errorHandler = require('errorhandler') - -var app = express() - -// all environments -app.set('port', process.env.PORT || 3000) -app.set('views', path.join(__dirname, 'views')) -app.set('view engine', 'pug') -app.use(favicon(path.join(__dirname, '/public/favicon.ico'))) -app.use(logger('dev')) -app.use(methodOverride()) -app.use(session({ - resave: true, - saveUninitialized: true, - secret: 'uwotm8' -})) -app.use(bodyParser.json()) -app.use(bodyParser.urlencoded({ extended: true })) -app.use(multer()) -app.use(express.static(path.join(__dirname, 'public'))) - -app.get('/', routes.index) -app.get('/users', user.list) - -// error handling middleware should be loaded after the loading the routes -if (app.get('env') === 'development') { - app.use(errorHandler()) -} - -var server = http.createServer(app) -server.listen(app.get('port'), () => { - console.log('Express server listening on port ' + app.get('port')) -}) -``` - -
Wenn Sie nicht direkt mit dem Modul `http` arbeiten müssen (socket.io/SPDY/HTTPS), ist das Laden des Moduls nicht erforderlich. Die Anwendung kann dann einfach wie folgt gestartet werden: - -```js -app.listen(app.get('port'), () => { - console.log('Express server listening on port ' + app.get('port')) -}) -``` - -
- -

Anwendung ausführen

- -Der Migrationsprozess ist abgeschlossen und die Anwendung ist nun eine Express 4-Anwendung. Zum Bestätigen starten Sie die Anwendung mit dem folgenden Befehl: - -```bash -$ node . -``` - -Laden Sie [http://localhost:3000](http://localhost:3000) und sehen Sie, wie die Homepage von Express 4 wiedergegeben wird. - -

Upgrade auf den Express 4 App Generator

- -Das Befehlszeilentool zum Generieren einer Express-Anwendung ist nach wie vor `express`. Für ein Upgrade auf die neue Version müssen Sie jedoch den Express 3 App Generator deinstallieren und dann den neuen Generator `express-generator` installieren. - -

Installation

- -Wenn der Express 3 App Generator bereits auf Ihrem System installiert ist, müssen Sie diesen deinstallieren: - -```bash -$ npm uninstall -g express -``` - -Abhängig davon, wie Ihre Datei- und Verzeichnissberechtigungen konfiguriert sind, müssen Sie diesen Befehl möglicherweise mit `sudo` ausführen. - -Installieren Sie nun den neuen Generator: - -```bash -$ npm install -g express-generator -``` - -Abhängig davon, wie Ihre Datei- und Verzeichnissberechtigungen konfiguriert sind, müssen Sie diesen Befehl möglicherweise mit `sudo` ausführen. - -Nun wird der Befehl `express` auf Ihrem System auf den Express 4 App Generator aktualisiert. - -

Änderungen am App Generator

- -Befehlsoptionen und -nutzung bleiben größtenteils unverändert. Es gelten jedoch folgende Ausnahmen: - -{: .doclist } - -- Option `--sessions` wurde entfernt. -- Option `--jshtml` wurde entfernt. -- Option `--hogan` wurde hinzugefügt, um [Hogan.js](http://twitter.github.io/hogan.js/) zu unterstützen. - -

Beispiel

- -Führen Sie den folgenden Befehl aus, um eine Express 4-Anwendung zu erstellen: - -```bash -$ express app4 -``` - -Wenn Sie sich den Inhalt der Datei `app4/app.js` ansehen, werden Sie feststellen, dass alle Middlewarefunktionen (außer `express.static`), die für die Anwendung erforderlich sind, als unabhängige Module geladen werden und die Middleware `router` nicht mehr explizit in die Anwendung geladen wird. - -Sie werden auch feststellen, dass die Datei `app.js` nun ein Node.js-Modul ist – im Gegensatz zur eigenständigen Anwendung, die vom bisherigen Generator generiert wurde. - -Starten Sie nach der Installation der Abhängigkeiten die Anwendung mit dem folgenden Befehl: - -```bash -$ npm start -``` - -Wenn Sie sich das npm-Startscript in der Datei `package.json` näher ansehen, werden Sie feststellen, dass der eigentliche Befehl, der die Anwendung startet, `node ./bin/www` heißt. Dieser Befehl lautete in Express 3 `node app.js`. - -Da die Datei `app.js`, die vom Express 4 Generator erstellt wurde, nun ein Node.js-Modul ist, kann dieses nicht mehr wie bisher unabhängig als Anwendung gestartet werden (es sei denn, Sie ändern den Code). Das Modul muss in eine Node.js-Datei geladen und über die Node.js-Datei gestartet werden. Die Node.js-Datei ist in diesem Fall `./bin/www`. - -Weder das Verzeichnis `bin` noch die erweiterungslose Datei `www` ist für das Erstellen einer Express-Anwendung oder das Starten der Anwendung zwingend erforderlich. Dies sind lediglich Vorschläge des Generators. Sie können diese also je nach Ihren Anforderungen ändern. - -Um das Verzeichnis `www` zu löschen und alles im "Express 3-Stil" zu belassen, löschen Sie die Zeile mit dem Eintrag `module.exports = app;` am Ende der Datei `app.js`. Fügen Sie dann stattdessen den folgenden Code an derselben Position ein: - -```js -app.set('port', process.env.PORT || 3000) - -var server = app.listen(app.get('port'), () => { - debug('Express server listening on port ' + server.address().port) -}) -``` - -Stellen Sie sicher, dass Sie das Modul `debug` am Anfang der Datei `app.js` laden. Verwenden Sie dazu den folgenden Code: - -```js -var debug = require('debug')('app4') -``` - -Ändern Sie als Nächstes `"start": "node ./bin/www"` in der Datei `package.json` in `"start": "node app.js"`. - -Sie haben nun die Funktionalität von `./bin/www` wieder in `app.js` verschoben. Diese Änderung wird nicht empfohlen. Die Übung hat Ihnen jedoch geholfen, zu verstehen, wie die Datei `./bin/www` funktioniert und warum die Datei `app.js` nicht mehr automatisch gestartet wird. diff --git a/de/guide/migrating-5.md b/de/guide/migrating-5.md deleted file mode 100644 index 35dd65e8b7..0000000000 --- a/de/guide/migrating-5.md +++ /dev/null @@ -1,618 +0,0 @@ ---- -layout: page -title: Migration auf Express 5 -description: A comprehensive guide to migrating your Express.js applications from version 4 to 5, detailing breaking changes, deprecated methods, and new improvements. -menu: guide -order: 10 -redirect_from: " " ---- - -# Wechsel zu Express 5 - -

Überblick

- -Express 5.0 befindet sich noch in der Beta-Release-Phase. Hier finden Sie jedoch bereits eine Vorschau zu den Änderungen in diesem Release und zur Migration Ihrer Express 4-Anwendung auf Express 5. - -To install this version, you need to have a Node.js version 18 or higher. Then, execute the following command in your application directory: - -```sh -npm install "express@5" -``` - -Sie können Ihre automatisierten Tests ausführen, um zu sehen, was fehlschlägt, und Probleme gemäß den folgenden Updates beheben. Nachdem Sie alle Testfehler behoben haben, führen Sie Ihre Anwendung aus, um zu sehen, welche Fehler noch auftreten. Sie werden sofort feststellen, ob die Anwendung Methoden oder Eigenschaften verwendet, die nicht unterstützt werden. - -## Express 5 Codemods - -To help you migrate your express server, we have created a set of codemods that will help you automatically update your code to the latest version of Express. - -Run the following command for run all the codemods available: - -```sh -npx codemod@latest @expressjs/v5-migration-recipe -``` - -If you want to run a specific codemod, you can run the following command: - -```sh -npx codemod@latest @expressjs/name-of-the-codemod -``` - -You can find the list of available codemods [here](https://codemod.link/express). - -

Änderungen in Express 5

- -**Entfernte Methoden und Eigenschaften** - - - -**Verbesserungen** - - - -**Geändert** - - - -## Entfernte Methoden und Eigenschaften - -Wenn Sie eine dieser Methoden oder Eigenschaften in Ihrer Anwendung verwenden, stürzt die Anwendung ab. Sie müssen also Ihre Anwendung ändern, wenn Sie auf Version 5 umgestellt haben. - -

app.del()

- -Express 5 unterstützt die Funktion `app.del()` nicht mehr. Wenn Sie diese Funktion verwenden, wird ein Fehler ausgelöst. Für die Registrierung von HTTP DELETE-Weiterleitungen verwenden Sie stattdessen die Funktion `app.delete()`. - -Anfänglich wurde `del` statt `delete` verwendet, weil `delete` in JavaScript ein reserviertes Schlüsselwort ist. Ab ECMAScript 6 jedoch können `delete` und andere reservierte Schlüsselwörter legal als Eigenschaftsnamen verwendet werden. - -{% capture codemod-route-del-to-delete %} -You can replace the deprecated signatures with the following command: - -```plain-text -npx codemod@latest @expressjs/route-del-to-delete -``` - -{% endcapture %} - -{% include admonitions/note.html content=codemod-route-del-to-delete %} - -```js -// v4 -app.del('/user/:id', (req, res) => { - res.send(`DELETE /user/${req.params.id}`) -}) - -// v5 -app.delete('/user/:id', (req, res) => { - res.send(`DELETE /user/${req.params.id}`) -}) -``` - -

app.param(fn)

- -Die Signatur `app.param(fn)` wurde für die Änderung der Verhaltensweise der Funktion `app.param(name, fn)` verwendet. Seit v4.11.0 wurde sie nicht mehr verwendet. In Express 5 wird sie überhaupt nicht mehr unterstützt. - -

Pluralisierte Methodennamen

- -Die folgenden Methodennamen wurden pluralisiert. In Express 4 wurde bei Verwendung der alten Methoden eine Warnung zur Einstellung der Unterstützung ausgegeben. Express 5 unterstützt diese Methoden nicht mehr. - -`req.acceptsLanguage()` wird durch `req.acceptsLanguages()` ersetzt. - -`req.acceptsCharset()` wird durch `req.acceptsCharsets()` ersetzt. - -`req.acceptsEncoding()` wird durch `req.acceptsEncodings()` ersetzt. - -{% capture codemod-pluralized-methods %} -You can replace the deprecated signatures with the following command: - -```plain-text -npx codemod@latest @expressjs/pluralize-method-names -``` - -{% endcapture %} - -{% include admonitions/note.html content=codemod-pluralized-methods %} - -```js -// v4 -app.all('/', (req, res) => { - req.acceptsCharset('utf-8') - req.acceptsEncoding('br') - req.acceptsLanguage('en') - - // ... -}) - -// v5 -app.all('/', (req, res) => { - req.acceptsCharsets('utf-8') - req.acceptsEncodings('br') - req.acceptsLanguages('en') - - // ... -}) -``` - -

Führender Doppelpunkt (:) im Namen für app.param(name, fn)

- -Ein führendes Doppelpunktzeichen (:) im Namen für die Funktion `app.param(name, fn)` ist ein Überbleibsel aus Express 3. Aus Gründen der Abwärtskompatibilität wurde dieser Name in Express 4 mit einem Hinweis zu veralteten Versionen weiter unterstützt. In Express 5 wird dieser Name stillschwiegend ignoriert und der Namensparameter ohne einen vorangestellten Doppelpunkt verwendet. - -Dies dürfte keine Auswirkungen auf Ihren Code haben, wenn Sie die Express 4-Dokumentation zu [app.param](/{{ page.lang }}/4x/api.html#app.param) befolgen, da dort der führende Doppelpunkt nicht erwähnt wird. - -

req.param(name)

- -Dieses potenziell verwirrende und durchaus riskante Verfahren des Abrufens von Formulardaten wurde entfernt. Sie müssen nun ganz speziell nach dem übergebenen Parameternamen im Objekt `req.params`, `req.body` oder `req.query` suchen. - -{% capture codemod-req-param %} -You can replace the deprecated signatures with the following command: - -```plain-text -npx codemod@latest @expressjs/explicit-request-params -``` - -{% endcapture %} - -{% include admonitions/note.html content=codemod-req-param %} - -```js -// v4 -app.post('/user', (req, res) => { - const id = req.param('id') - const body = req.param('body') - const query = req.param('query') - - // ... -}) - -// v5 -app.post('/user', (req, res) => { - const id = req.params.id - const body = req.body - const query = req.query - - // ... -}) -``` - -

res.json(obj, status)

- -Express 5 unterstützt die Signatur `res.json(obj, status)` nicht mehr. Stattdessen müssen Sie den Status festlegen und diesen dann mit `res.json()`-Methoden wie dieser verketten: `res.status(status).json(obj)`. - -{% capture codemod-status-send-order %} -You can replace the deprecated signatures with the following command: - -```plain-text -npx codemod@latest @expressjs/status-send-order -``` - -{% endcapture %} - -{% include admonitions/note.html content=codemod-status-send-order %} - -```js -// v4 -app.post('/user', (req, res) => { - res.json({ name: 'Ruben' }, 201) -}) - -// v5 -app.post('/user', (req, res) => { - res.status(201).json({ name: 'Ruben' }) -}) -``` - -

res.jsonp(obj, status)

- -Express 5 unterstützt die Signatur `res.jsonp(obj, status)` nicht mehr. Stattdessen müssen Sie den Status festlegen und diesen dann mit `res.jsonp()`-Methoden wie dieser verketten: `res.status(status).jsonp(obj)`. - -{% include admonitions/note.html content=codemod-status-send-order %} - -```js -// v4 -app.post('/user', (req, res) => { - res.jsonp({ name: 'Ruben' }, 201) -}) - -// v5 -app.post('/user', (req, res) => { - res.status(201).jsonp({ name: 'Ruben' }) -}) -``` - -

res.redirect(url, status)

- -Express 5 unterstützt die Signatur `res.send(obj, status)` nicht mehr. Stattdessen müssen Sie den Status festlegen und diesen dann mit `res.send()`-Methoden wie dieser verketten: `res.status(status).send(obj)`. - -{% capture codemod-redirect-arg-order %} -You can replace the deprecated signatures with the following command: - -```plain-text -npx codemod@latest @expressjs/redirect-arg-order -``` - -{% endcapture %} - -{% include admonitions/note.html content=codemod-redirect-arg-order %} - -```js -// v4 -app.get('/user', (req, res) => { - res.redirect('/users', 301) -}) - -// v5 -app.get('/user', (req, res) => { - res.redirect(301, '/users') -}) -``` - -

res.redirect('back') and res.location('back')

- -Express 5 no longer supports the magic string `back` in the `res.redirect()` and `res.location()` methods. Instead, use the `req.get('Referrer') || '/'` value to redirect back to the previous page. In Express 4, the `res.redirect('back')` and `res.location('back')` methods were deprecated. - -{% capture codemod-back-redirect-deprecated %} -You can replace the deprecated signatures with the following command: - -```plain-text -npx codemod@latest @expressjs/back-redirect-deprecated -``` - -{% endcapture %} - -{% include admonitions/note.html content=codemod-back-redirect-deprecated %} - -```js -// v4 -app.get('/user', (req, res) => { - res.redirect('back') -}) - -// v5 -app.get('/user', (req, res) => { - res.redirect(req.get('Referrer') || '/') -}) -``` - -

res.send(body, status)

- -Express 5 no longer supports the signature `res.send(obj, status)`. Instead, set the status and then chain it to the `res.send()` method like this: `res.status(status).send(obj)`. - -{% include admonitions/note.html content=codemod-status-send-order %} - -```js -// v4 -app.get('/user', (req, res) => { - res.send({ name: 'Ruben' }, 200) -}) - -// v5 -app.get('/user', (req, res) => { - res.status(200).send({ name: 'Ruben' }) -}) -``` - -

res.send(status)

- -Express 5 unterstützt die Signatur res.send(status), nicht mehr, wobei _`status`_ für eine Zahl steht. Verwenden Sie stattdessen die Funktion `res.sendStatus(statusCode)`, mit der der Statuscode für den HTTP-Antwort-Header festgelegt und die Textversion des Codes gesendet wird: "Not Found" (Nicht gefunden), "Internal Server Error" (Interner Serverfehler) usw. -Wenn Sie eine Zahl senden und hierfür die Funktion `res.send()` verwenden müssen, müssen Sie die Zahl in Anführungszeichen setzen, um diese in eine Zeichenfolge zu konvertieren. Dadurch interpretiert Express diese Zahl nicht als Versuch, die nicht mehr unterstützte alte Signatur zu verwenden. - -{% include admonitions/note.html content=codemod-status-send-order %} - -```js -// v4 -app.get('/user', (req, res) => { - res.send(200) -}) - -// v5 -app.get('/user', (req, res) => { - res.sendStatus(200) -}) -``` - -

res.sendfile()

- -Die Funktion `res.sendfile()` wurde durch eine Version in Camel-Schreibweise von `res.sendFile()` in Express 5 ersetzt. - -**Note:** In Express 5, `res.sendFile()` uses the `mime-types` package for MIME type detection, which returns different Content-Type values than Express 4 for several common file types: - -- JavaScript files (.js): now "text/javascript" instead of "application/javascript" -- JSON files (.json): now "application/json" instead of "text/json" -- CSS files (.css): now "text/css" instead of "text/plain" -- XML files (.xml): now "application/xml" instead of "text/xml" -- Font files (.woff): now "font/woff" instead of "application/font-woff" -- SVG files (.svg): now "image/svg+xml" instead of "application/svg+xml" - -{% capture codemod-camelcase-sendfile %} -You can replace the deprecated signatures with the following command: - -```plain-text -npx codemod@latest @expressjs/camelcase-sendfile -``` - -{% endcapture %} - -{% include admonitions/note.html content=codemod-camelcase-sendfile %} - -```js -// v4 -app.get('/user', (req, res) => { - res.sendfile('/path/to/file') -}) - -// v5 -app.get('/user', (req, res) => { - res.sendFile('/path/to/file') -}) -``` - -

router.param(fn)

- -The `router.param(fn)` signature was used for modifying the behavior of the `router.param(name, fn)` function. Seit v4.11.0 wurde sie nicht mehr verwendet. In Express 5 wird sie überhaupt nicht mehr unterstützt. - -

express.static.mime

- -In Express 5, `mime` is no longer an exported property of the `static` field. -Use the [`mime-types` package](https://github.com/jshttp/mime-types) to work with MIME type values. - -**Important:** This change affects not only direct usage of `express.static.mime` but also other Express methods that rely on MIME type detection, such as `res.sendFile()`. The following MIME types have changed from Express 4: - -- JavaScript files (.js): now served as "text/javascript" instead of "application/javascript" -- JSON files (.json): now served as "application/json" instead of "text/json" -- CSS files (.css): now served as "text/css" instead of "text/plain" -- HTML files (.html): now served as "text/html; charset=utf-8" instead of just "text/html" -- XML files (.xml): now served as "application/xml" instead of "text/xml" -- Font files (.woff): now served as "font/woff" instead of "application/font-woff" - -```js -// v4 -express.static.mime.lookup('json') - -// v5 -const mime = require('mime-types') -mime.lookup('json') -``` - -

express:router debug logs

- -In Express 5, router handling logic is performed by a dependency. Therefore, the -debug logs for the router are no longer available under the `express:` namespace. -In v4, the logs were available under the namespaces `express:router`, `express:router:layer`, -and `express:router:route`. All of these were included under the namespace `express:*`. -In v5.1+, the logs are available under the namespaces `router`, `router:layer`, and `router:route`. -The logs from `router:layer` and `router:route` are included in the namespace `router:*`. -To achieve the same detail of debug logging when using `express:*` in v4, use a conjunction of -`express:*`, `router`, and `router:*`. - -```sh -# v4 -DEBUG=express:* node index.js - -# v5 -DEBUG=express:*,router,router:* node index.js -``` - -## Geändert - -

Path route matching syntax

- -Path route matching syntax is when a string is supplied as the first parameter to the `app.all()`, `app.use()`, `app.METHOD()`, `router.all()`, `router.METHOD()`, and `router.use()` APIs. The following changes have been made to how the path string is matched to an incoming request: - -- The wildcard `*` must have a name, matching the behavior of parameters `:`, use `/*splat` instead of `/*` - -```js -// v4 -app.get('/*', async (req, res) => { - res.send('ok') -}) - -// v5 -app.get('/*splat', async (req, res) => { - res.send('ok') -}) -``` - -{% capture note_wildcard %} -`*splat` matches any path without the root path. If you need to match the root path as well `/`, you can use `/{*splat}`, wrapping the wildcard in braces. - -```js -// v5 -app.get('/{*splat}', async (req, res) => { - res.send('ok') -}) -``` - -{% endcapture %} -{% include admonitions/note.html content=note_wildcard %} - -- The optional character `?` is no longer supported, use braces instead. - -```js -// v4 -app.get('/:file.:ext?', async (req, res) => { - res.send('ok') -}) - -// v5 -app.get('/:file{.:ext}', async (req, res) => { - res.send('ok') -}) -``` - -- Regexp characters are not supported. Beispiel: - -```js -app.get('/[discussion|page]/:slug', async (req, res) => { - res.status(200).send('ok') -}) -``` - -should be changed to: - -```js -app.get(['/discussion/:slug', '/page/:slug'], async (req, res) => { - res.status(200).send('ok') -}) -``` - -- Some characters have been reserved to avoid confusion during upgrade (`()[]?+!`), use `\` to escape them. -- Parameter names now support valid JavaScript identifiers, or quoted like `:"this"`. - -

Rejected promises handled from middleware and handlers

- -Request middleware and handlers that return rejected promises are now handled by forwarding the rejected value as an `Error` to the error handling middleware. This means that using `async` functions as middleware and handlers are easier than ever. When an error is thrown in an `async` function or a rejected promise is `await`ed inside an async function, those errors will be passed to the error handler as if calling `next(err)`. - -Details of how Express handles errors is covered in the [error handling documentation](/en/guide/error-handling.html). - -

express.urlencoded

- -The `express.urlencoded` method makes the `extended` option `false` by default. - -

express.static dotfiles

- -In Express 5, the `express.static` middleware's `dotfiles` option now defaults to `"ignore"`. This is a change from Express 4, where dotfiles were served by default. As a result, files inside a directory that starts with a dot (`.`), such as `.well-known`, will no longer be accessible and will return a **404 Not Found** error. This can break functionality that depends on serving dot-directories, such as Android App Links, and Apple Universal Links. - -Example of breaking code: - -```js -// v4 -app.use(express.static('public')) -``` - -After migrating to Express 5, a request to `/.well-known/assetlinks.json` will result in a **404 Not Found**. - -To fix this, serve specific dot-directories explicitly using the `dotfiles: "allow"` option: - -```js -// v5 -app.use('/.well-known', express.static('public/.well-known', { dotfiles: 'allow' })) -app.use(express.static('public')) -``` - -This approach allows you to safely serve only the intended dot-directories while keeping the default secure behavior for other dotfiles, which remain inaccessible. - -

app.listen

- -In Express 5, the `app.listen` method will invoke the user-provided callback function (if provided) when the server receives an error event. In Express 4, such errors would be thrown. This change shifts error-handling responsibility to the callback function in Express 5. If there is an error, it will be passed to the callback as an argument. -Beispiel: - -```js -const server = app.listen(8080, '0.0.0.0', (error) => { - if (error) { - throw error // e.g. EADDRINUSE - } - console.log(`Listening on ${JSON.stringify(server.address())}`) -}) -``` - -

app.router

- -Das Objekt `app.router`, das in Express 4 entfernt wurde, ist in Express 5 wieder verfügbar. In der neuen Version fungiert dieses Objekt nur als Referenz zum Express-Basisrouter – im Gegensatz zu Express 3, wo die Anwendung dieses Objekt explizit laden musste. - -

req.body

- -The `req.body` property returns `undefined` when the body has not been parsed. In Express 4, it returns `{}` by default. - -

req.host

- -In Express 4 übergab die Funktion `req.host` nicht ordnungsgemäß eine eventuell vorhandene Portnummer. In Express 5 wird die Portnummer beibehalten. - -

req.params

- -The `req.params` object now has a **null prototype** when using string paths. However, if the path is defined with a regular expression, `req.params` remains a standard object with a normal prototype. Additionally, there are two important behavioral changes: - -**Wildcard parameters are now arrays:** - -Wildcards (e.g., `/*splat`) capture path segments as an array instead of a single string. - -```js -app.get('/*splat', (req, res) => { - // GET /foo/bar - console.dir(req.params) - // => [Object: null prototype] { splat: [ 'foo', 'bar' ] } -}) -``` - -**Unmatched parameters are omitted:** - -In Express 4, unmatched wildcards were empty strings (`''`) and optional `:` parameters (using `?`) had a key with value `undefined`. In Express 5, unmatched parameters are completely omitted from `req.params`. - -```js -// v4: unmatched wildcard is empty string -app.get('/*', (req, res) => { - // GET / - console.dir(req.params) - // => { '0': '' } -}) - -// v4: unmatched optional param is undefined -app.get('/:file.:ext?', (req, res) => { - // GET /image - console.dir(req.params) - // => { file: 'image', ext: undefined } -}) - -// v5: unmatched optional param is omitted -app.get('/:file{.:ext}', (req, res) => { - // GET /image - console.dir(req.params) - // => [Object: null prototype] { file: 'image' } -}) -``` - -

req.query

- -The `req.query` property is no longer a writable property and is instead a getter. The default query parser has been changed from "extended" to "simple". - -

res.clearCookie

- -The `res.clearCookie` method ignores the `maxAge` and `expires` options provided by the user. - -

res.status

- -The `res.status` method only accepts integers in the range of `100` to `999`, following the behavior defined by Node.js, and it returns an error when the status code is not an integer. - -

res.vary

- -The `res.vary` throws an error when the `field` argument is missing. In Express 4, if the argument was omitted, it gave a warning in the console - -## Verbesserungen - -

res.render()

- -Diese Methode erzwingt nun asynchrones Verhalten für alle View-Engines, sodass durch View-Engines mit synchroner Implementierung verursachte Fehler vermieden werden, durch die die empfohlene Schnittstelle nicht verwendet werden konnte. - -

Brotli encoding support

- -Express 5 supports Brotli encoding for requests received from clients that support it. diff --git a/de/guide/overriding-express-api.md b/de/guide/overriding-express-api.md deleted file mode 100644 index 5210622ff2..0000000000 --- a/de/guide/overriding-express-api.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -layout: page -title: Overriding the Express API -description: Discover how to customize and extend the Express.js API by overriding methods and properties on the request and response objects using prototypes. -menu: guide -order: 4 ---- - -# Overriding the Express API - -The Express API consists of various methods and properties on the request and response objects. These are inherited by prototype. There are two extension points for the Express API: - -1. The global prototypes at `express.request` and `express.response`. -2. App-specific prototypes at `app.request` and `app.response`. - -Altering the global prototypes will affect all loaded Express apps in the same process. If desired, alterations can be made app-specific by only altering the app-specific prototypes after creating a new app. - -## Methods - -You can override the signature and behavior of existing methods with your own, by assigning a custom function. - -Following is an example of overriding the behavior of [res.sendStatus](/4x/api.html#res.sendStatus). - -```js -app.response.sendStatus = function (statusCode, type, message) { - // code is intentionally kept simple for demonstration purpose - return this.contentType(type) - .status(statusCode) - .send(message) -} -``` - -The above implementation completely changes the original signature of `res.sendStatus`. It now accepts a status code, encoding type, and the message to be sent to the client. - -The overridden method may now be used this way: - -```js -res.sendStatus(404, 'application/json', '{"error":"resource not found"}') -``` - -## Properties - -Properties in the Express API are either: - -1. Assigned properties (ex: `req.baseUrl`, `req.originalUrl`) -2. Defined as getters (ex: `req.secure`, `req.ip`) - -Since properties under category 1 are dynamically assigned on the `request` and `response` objects in the context of the current request-response cycle, their behavior cannot be overridden. - -Properties under category 2 can be overwritten using the Express API extensions API. - -The following code rewrites how the value of `req.ip` is to be derived. Now, it simply returns the value of the `Client-IP` request header. - -```js -Object.defineProperty(app.request, 'ip', { - configurable: true, - enumerable: true, - get () { return this.get('Client-IP') } -}) -``` - -## Prototype - -In order to provide the Express API, the request/response objects passed to Express (via `app(req, res)`, for example) need to inherit from the same prototype chain. By default, this is `http.IncomingRequest.prototype` for the request and `http.ServerResponse.prototype` for the response. - -Unless necessary, it is recommended that this be done only at the application level, rather than globally. Also, take care that the prototype that is being used matches the functionality as closely as possible to the default prototypes. - -```js -// Use FakeRequest and FakeResponse in place of http.IncomingRequest and http.ServerResponse -// for the given app reference -Object.setPrototypeOf(Object.getPrototypeOf(app.request), FakeRequest.prototype) -Object.setPrototypeOf(Object.getPrototypeOf(app.response), FakeResponse.prototype) -``` diff --git a/de/guide/routing.md b/de/guide/routing.md deleted file mode 100644 index 8d76504d18..0000000000 --- a/de/guide/routing.md +++ /dev/null @@ -1,411 +0,0 @@ ---- -layout: page -title: Weiterleitung in Express -description: Learn how to define and use routes in Express.js applications, including route methods, route paths, parameters, and using Router for modular routing. -menu: guide -order: 1 -redirect_from: " " ---- - -# Weiterleitung (Routing) - -Der Begriff _Weiterleitung_ (Routing) bezieht sich auf die Definition von Anwendungsendpunkten (URIs) und deren Antworten auf Clientanforderungen. -Eine Einführung in dieses Routing siehe [Basisrouting](/{{ page.lang }}/starter/basic-routing.html). - -You define routing using methods of the Express `app` object that correspond to HTTP methods; -for example, `app.get()` to handle GET requests and `app.post` to handle POST requests. For a full list, -see [app.METHOD](/{{ page.lang }}/5x/api.html#app.METHOD). You can also use [app.all()](/{{ page.lang }}/5x/api.html#app.all) to handle all HTTP methods and [app.use()](/{{ page.lang }}/5x/api.html#app.use) to -specify middleware as the callback function (See [Using middleware](/{{ page.lang }}/guide/using-middleware.html) for details). - -These routing methods specify a callback function (sometimes called "handler functions") called when the application receives a request to the specified route (endpoint) and HTTP method. In other words, the application "listens" for requests that match the specified route(s) and method(s), and when it detects a match, it calls the specified callback function. - -In fact, the routing methods can have more than one callback function as arguments. -With multiple callback functions, it is important to provide `next` as an argument to the callback function and then call `next()` within the body of the function to hand off control -to the next callback. - -Der folgende Code ist ein Beispiel für ein sehr einfaches Basisrouting. - -```js -const express = require('express') -const app = express() - -// respond with "hello world" when a GET request is made to the homepage -app.get('/', (req, res) => { - res.send('hello world') -}) -``` - -

Weiterleitungsmethoden

- -Eine Weiterleitungsmethode wird von einer HTTP-Methode abgeleitet und an eine Instanz der Klasse `express` angehängt. - -Der folgende Code ist ein Beispiel für Weiterleitungen, die für die Methoden GET und POST zum Stamm (Root) der Anwendung definiert werden. - -```js -// GET method route -app.get('/', (req, res) => { - res.send('GET request to the homepage') -}) - -// POST method route -app.post('/', (req, res) => { - res.send('POST request to the homepage') -}) -``` - -Express supports methods that correspond to all HTTP request methods: `get`, `post`, and so on. -For a full list, see [app.METHOD](/{{ page.lang }}/5x/api.html#app.METHOD). - -Es gibt eine spezielle Weiterleitungsmethode, `app.all()`, die nicht von einer HTTP-Methode abgeleitet wird. Diese Methode wird zum Laden von Middlewarefunktionen bei einem Pfad für alle Anforderungsmethoden verwendet. Im folgenden Beispiel wird der Handler für Anforderungen zur Weiterleitung "/secret" ausgeführt, um herauszufinden, ob Sie GET-, POST-, PUT-, DELETE- oder andere HTTP-Anforderungsmethoden verwenden, die im [HTTP-Modul](https://nodejs.org/api/http.html#http_http_methods) unterstützt werden. - -```js -app.all('/secret', (req, res, next) => { - console.log('Accessing the secret section ...') - next() // pass control to the next handler -}) -``` - -

Weiterleitungspfade

- -Über Weiterleitungspfade werden in Kombination mit einer Anforderungsmethode die Endpunkte definiert, bei denen Anforderungen erfolgen können. Weiterleitungspfade können Zeichenfolgen, Zeichenfolgemuster oder reguläre Ausdrücke sein. - -{% capture caution-character %} In express 5, the characters `?`, `+`, `*`, `[]`, and `()` are handled differently than in version 4, please review the [migration guide](/{{ page.lang }}/guide/migrating-5.html#path-syntax) for more information.{% endcapture %} - -{% include admonitions/caution.html content=caution-character %} - -{% capture note-dollar-character %}In express 4, regular expression characters such as `$` need to be escaped with a `\`. -{% endcapture %} - -{% include admonitions/caution.html content=note-dollar-character %} - -{% capture note-path-to-regexp %} - -Express uses [path-to-regexp](https://www.npmjs.com/package/path-to-regexp) for matching the route paths; see the path-to-regexp documentation for all the possibilities in defining route paths. [Express Route Tester](http://forbeslindesay.github.io/express-route-tester/) ist ein handliches Tool zum Testen von Express-Basisweiterleitungen, auch wenn dieses Tool keine Musterabgleiche unterstützt. - -{% endcapture %} - -{% include admonitions/note.html content=note-path-to-regexp %} - -{% capture query-string-note %} - -Query strings are not part of the route path. - -{% endcapture %} - -{% include admonitions/warning.html content=query-string-note %} - -### Route paths based on strings - -Dieser Weiterleitungspfad gleicht Weiterleitungsanforderungen zum Stammverzeichnis (`/`) ab. - -```js -app.get('/', (req, res) => { - res.send('root') -}) -``` - -Dieser Weiterleitungspfad gleicht Anforderungen mit `/about` ab. - -```js -app.get('/about', (req, res) => { - res.send('about') -}) -``` - -Dieser Weiterleitungspfad gleicht Anforderungen mit `/random.text` ab. - -```js -app.get('/random.text', (req, res) => { - res.send('random.text') -}) -``` - -### Route paths based on string patterns - -{% capture caution-string-patterns %} The string patterns in Express 5 no longer work. Please refer to the [migration guide](/{{ page.lang }}/guide/migrating-5.html#path-syntax) for more information.{% endcapture %} - -{% include admonitions/caution.html content=caution-string-patterns %} - -Dieser Weiterleitungspfad gleicht `acd` und `abcd` ab. - -```js -app.get('/ab?cd', (req, res) => { - res.send('ab?cd') -}) -``` - -Dies sind einige Beispiele für Weiterleitungspfade auf Basis von Zeichenfolgemustern. - -```js -app.get('/ab+cd', (req, res) => { - res.send('ab+cd') -}) -``` - -Dies sind einige Beispiele für Weiterleitungspfade auf Basis von Zeichenfolgen. - -```js -app.get('/ab*cd', (req, res) => { - res.send('ab*cd') -}) -``` - -Dieser Weiterleitungspfad gleicht `/abe` und `/abcde` ab. - -```js -app.get('/ab(cd)?e', (req, res) => { - res.send('ab(cd)?e') -}) -``` - -### Route paths based on regular expressions - -Dieser Weiterleitungspfad gleicht alle Weiterleitungsnamen ab, die den Buchstaben "a" enthalten. - -```js -app.get(/a/, (req, res) => { - res.send('/a/') -}) -``` - -Dieser Weiterleitungspfad gleicht `butterfly` und `dragonfly`, jedoch nicht `butterflyman`, `dragonfly man` usw. - -```js -app.get(/.*fly$/, (req, res) => { - res.send('/.*fly$/') -}) -``` - -

Route parameters

- -Route parameters are named URL segments that are used to capture the values specified at their position in the URL. The captured values are populated in the `req.params` object, with the name of the route parameter specified in the path as their respective keys. - -``` -Route path: /users/:userId/books/:bookId -Request URL: http://localhost:3000/users/34/books/8989 -req.params: { "userId": "34", "bookId": "8989" } -``` - -To define routes with route parameters, simply specify the route parameters in the path of the route as shown below. - -```js -app.get('/users/:userId/books/:bookId', (req, res) => { - res.send(req.params) -}) -``` - -
-The name of route parameters must be made up of "word characters" ([A-Za-z0-9_]). -
- -Since the hyphen (`-`) and the dot (`.`) are interpreted literally, they can be used along with route parameters for useful purposes. - -``` -Route path: /flights/:from-:to -Request URL: http://localhost:3000/flights/LAX-SFO -req.params: { "from": "LAX", "to": "SFO" } -``` - -``` -Route path: /plantae/:genus.:species -Request URL: http://localhost:3000/plantae/Prunus.persica -req.params: { "genus": "Prunus", "species": "persica" } -``` - -{% capture warning-regexp %} -In express 5, Regexp characters are not supported in route paths, for more information please refer to the [migration guide](/{{ page.lang }}/guide/migrating-5.html#path-syntax).{% endcapture %} - -{% include admonitions/caution.html content=warning-regexp %} - -To have more control over the exact string that can be matched by a route parameter, you can append a regular expression in parentheses (`()`): - -``` -Route path: /user/:userId(\d+) -Request URL: http://localhost:3000/user/42 -req.params: {"userId": "42"} -``` - -{% capture escape-advisory %} - -Because the regular expression is usually part of a literal string, be sure to escape any `\` characters with an additional backslash, for example `\\d+`. - -{% endcapture %} - -{% include admonitions/warning.html content=escape-advisory %} - -{% capture warning-version %} - -In Express 4.x, the `*` character in regular expressions is not interpreted in the usual way. As a workaround, use `{0,}` instead of `*`. This will likely be fixed in Express 5. - -{% endcapture %} - -{% include admonitions/warning.html content=warning-version %} - -

Routenhandler (Weiterleitungsroutinen)

- -Sie können mehrere Callback-Funktionen angeben, die sich wie [Middleware](/{{ page.lang }}/guide/using-middleware.html) verhalten, um eine Anforderung zu verarbeiten. Die einzige Ausnahme hierbei ist, dass diese Callbacks möglicherweise `next('route')` aufrufen, um die verbleibenden Weiterleitungs-Callbacks zu umgehen. Mit diesem Verfahren können Sie Vorabbedingungen für eine Weiterleitung festlegen und dann die Steuerung an nachfolgende Weiterleitungen übergeben, wenn kein Grund vorliegt, mit der aktuellen Weiterleitung fortzufahren. - -```js -app.get('/user/:id', (req, res, next) => { - if (req.params.id === '0') { - return next('route') - } - res.send(`User ${req.params.id}`) -}) - -app.get('/user/:id', (req, res) => { - res.send('Special handler for user ID 0') -}) -``` - -In this example: - -- `GET /user/5` → handled by first route → sends "User 5" -- `GET /user/0` → first route calls `next('route')`, skipping to the next matching `/user/:id` route - -Routenhandler können eine Funktion und/oder ein Funktionsarray sein, wie in den folgenden Beispielen zu sehen ist. - -Eine einzelne Callback-Funktion kann eine Weiterleitung verarbeiten. Beispiel: - -```js -app.get('/example/a', (req, res) => { - res.send('Hello from A!') -}) -``` - -Mehrere Callback-Funktionen können eine Weiterleitung verarbeiten (achten Sie darauf, dass Sie das Objekt `next` angeben). Beispiel: - -```js -app.get('/example/b', (req, res, next) => { - console.log('the response will be sent by the next function ...') - next() -}, (req, res) => { - res.send('Hello from B!') -}) -``` - -Ein Array von Callback-Funktionen kann eine Weiterleitung verarbeiten. Beispiel: - -```js -const cb0 = function (req, res, next) { - console.log('CB0') - next() -} - -const cb1 = function (req, res, next) { - console.log('CB1') - next() -} - -const cb2 = function (req, res) { - res.send('Hello from C!') -} - -app.get('/example/c', [cb0, cb1, cb2]) -``` - -Eine Kombination aus unabhängigen Funktionen und Funktionsarrays kann eine Weiterleitung verarbeiten. Beispiel: - -```js -const cb0 = function (req, res, next) { - console.log('CB0') - next() -} - -const cb1 = function (req, res, next) { - console.log('CB1') - next() -} - -app.get('/example/d', [cb0, cb1], (req, res, next) => { - console.log('the response will be sent by the next function ...') - next() -}, (req, res) => { - res.send('Hello from D!') -}) -``` - -

Antwortmethoden

- -Über die Methoden für das Antwortobjekt (`res`) in der folgenden Tabelle kann eine Antwort an den Client gesendet und der Anforderung/Antwort-Zyklus beendet werden. Wenn keine dieser Methoden über einen Routenhandler aufgerufen wird, bleibt die Clientanforderung im Status "blockiert". - -| Methode | Beschreibung | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | -| [res.download()](/{{ page.lang }}/4x/api.html#res.download) | Gibt eine Eingabeaufforderung zum Herunterladen einer Datei aus. | -| [res.end()](/{{ page.lang }}/4x/api.html#res.end) | End the response process. | -| [res.json()](/{{ page.lang }}/4x/api.html#res.json) | Sendet eine JSON-Antwort. | -| [res.jsonp()](/{{ page.lang }}/4x/api.html#res.jsonp) | Sendet eine JSON-Antwort mit JSONP-Unterstützung. | -| [res.redirect()](/{{ page.lang }}/4x/api.html#res.redirect) | Leitet eine Anforderung um. | -| [res.render()](/{{ page.lang }}/4x/api.html#res.render) | Render a view template. | -| [res.send()](/{{ page.lang }}/4x/api.html#res.send) | Sendet eine Antwort mit unterschiedlichen Typen. | -| [res.sendFile](/{{ page.lang }}/4x/api.html#res.sendFile) | Sendet eine Datei als Oktett-Stream. | -| [res.sendStatus()](/{{ page.lang }}/4x/api.html#res.sendStatus) | Legt den Antwortstatuscode fest und sendet dessen Zeichenfolgedarstellung als Antworthauptteil. | - -

app.route()

- -Sie können mithilfe von `app.route()` verkettbare Routenhandler für einen Weiterleitungspfad erstellen. -Da der Pfad an einer einzelnen Position angegeben wird, ist das Erstellen modularer Weiterleitungen hilfreich, da Redundanzen und Schreibfehler reduziert werden. Weitere Informationen zu Weiterleitungen finden Sie in der Dokumentation zu [Router()](/{{ page.lang }}/4x/api.html#router). - -Dies ist ein Beispiel für verkettete Routenhandler, die mit der Funktion `app.route()` definiert werden. - -```js -app.route('/book') - .get((req, res) => { - res.send('Get a random book') - }) - .post((req, res) => { - res.send('Add a book') - }) - .put((req, res) => { - res.send('Update the book') - }) -``` - -

express.Router

- -Mit der Klasse `express.Router` lassen sich modular einbindbare Routenhandler erstellen. Eine `Router`-Instanz ist ein vollständiges Middleware- und Routingsystem. Aus diesem Grund wird diese Instanz oft auch als "Mini-App" bezeichnet. - -Im folgenden Beispiel wird ein Router als Modul erstellt, eine Middlewarefunktion in das Modul geladen, es werden Weiterleitungen definiert und das Modul letztendlich in einen Pfad in der Hauptanwendung eingebunden. - -Erstellen Sie eine Routerdatei namens `birds.js` mit dem folgenden Inhalt im Anwendungsverzeichnis: - -```js -const express = require('express') -const router = express.Router() - -// middleware that is specific to this router -const timeLog = (req, res, next) => { - console.log('Time: ', Date.now()) - next() -} -router.use(timeLog) - -// define the home page route -router.get('/', (req, res) => { - res.send('Birds home page') -}) -// define the about route -router.get('/about', (req, res) => { - res.send('About birds') -}) - -module.exports = router -``` - -Laden Sie dann das Routermodul in die Anwendung: - -```js -const birds = require('./birds') - -// ... - -app.use('/birds', birds) -``` - -Die Anwendung kann nun Anforderungen an die Pfade `/birds` und `/birds/about` bearbeiten und ruft die Middlewarefunktion `timeLog` auf, die speziell für diese Weiterleitung bestimmt ist. - -But if the parent route `/birds` has path parameters, it will not be accessible by default from the sub-routes. To make it accessible, you will need to pass the `mergeParams` option to the Router constructor [reference](/{{ page.lang }}/5x/api.html#app.use). - -```js -const router = express.Router({ mergeParams: true }) -``` diff --git a/de/guide/using-middleware.md b/de/guide/using-middleware.md deleted file mode 100644 index 4e67b123c3..0000000000 --- a/de/guide/using-middleware.md +++ /dev/null @@ -1,277 +0,0 @@ ---- -layout: page -title: Express-Middleware verwenden -description: Learn how to use middleware in Express.js applications, including application-level and router-level middleware, error handling, and integrating third-party middleware. -menu: guide -order: 3 -redirect_from: " " ---- - -# Middleware verwenden - -Express ist ein Weiterleitungs- und Middleware-Web-Framework, das selbst nur minimale Funktionalität aufweist: Eine Express-Anwendung besteht im Wesentlichen aus einer Reihe von Middlewarefunktionsaufrufen. - -_Middleware_ functions are functions that have access to the [request object](/{{ page.lang }}/5x/api.html#req) (`req`), the [response object](/{{ page.lang }}/5x/api.html#res) (`res`), and the next middleware function in the application's request-response cycle. Die nächste Middlewarefunktion wird im Allgemeinen durch die Variable `next` bezeichnet. - -Über Middlewarefunktionen lassen sich die folgenden Tasks ausführen: - -- Ausführen von Code -- Vornehmen von Änderungen an der Anforderung und an Antwortobjekten -- End the request-response cycle. -- Aufrufen der nächsten Middlewarefunktion im Stack - -Wenn über die aktuelle Middlewarefunktion der Anforderung/Antwort-Zyklus nicht beendet werden kann, muss `next()` aufgerufen werden, um die Steuerung an die nächste Middlewarefunktion zu übergeben. Andernfalls geht die Anforderung in den Status "Blockiert" über. - -Eine Express-Anwendung kann die folgenden Middlewaretypen verwenden: - -- [Middleware auf Anwendungsebene](#middleware.application) -- [Middleware auf Routerebene](#middleware.router) -- [Middleware für die Fehlerbehandlung](#middleware.error-handling) -- [Integrierte Middleware](#middleware.built-in) -- [Middleware anderer Anbieter](#middleware.third-party) - -Sie können Middleware auf Anwendungsebene und Routerebene mit einem optionalen Mountpfad laden. -Sie können auch eine Reihe von Middlewarefunktionen zusammen laden. Dadurch wird ein Sub-Stack des Middlewaresystems am Mountpunkt erstellt. - -

Middleware auf Anwendungsebene

- -Bind application-level middleware to an instance of the [app object](/{{ page.lang }}/5x/api.html#app) by using the `app.use()` and `app.METHOD()` functions, where `METHOD` is the HTTP method of the request that the middleware function handles (such as GET, PUT, or POST) in lowercase. - -Dieses Beispiel zeigt eine Middlewarefunktion ohne Mountpfad. Die Funktion wird immer dann ausgeführt, wenn die Anwendung eine Anforderung erhält. - -```js -const express = require('express') -const app = express() - -app.use((req, res, next) => { - console.log('Time:', Date.now()) - next() -}) -``` - -Dieses Beispiel zeigt eine Middlewarefunktion mit dem Mountpfad `/user/:id`. Die Funktion wird für jede Art von HTTP-Anforderung auf dem Pfad `/user/:id` ausgeführt. - -```js -app.use('/user/:id', (req, res, next) => { - console.log('Request Type:', req.method) - next() -}) -``` - -Dieses Beispiel zeigt eine Weiterleitung und deren Handlerfunktion (Middlewaresystem). Die Funktion verarbeitet GET-Anforderungen zum Pfad `/user/:id`. - -```js -app.get('/user/:id', (req, res, next) => { - res.send('USER') -}) -``` - -Dies ist ein Beispiel zum Laden einer Reihe von Middlewarefunktionen an einem Mountpunkt mit einem Mountpfad. -Das Beispiel veranschaulicht einen Middleware-Stack, über den Anforderungsinformationen zu einer HTTP-Anforderung zum Pfad `/user/:id` ausgegeben werden. - -```js -app.use('/user/:id', (req, res, next) => { - console.log('Request URL:', req.originalUrl) - next() -}, (req, res, next) => { - console.log('Request Type:', req.method) - next() -}) -``` - -Mit einem Routenhandler können Sie mehrere Weiterleitungen für einen Pfad definieren. Im folgenden Beispiel werden zwei Weiterleitungen für GET-Anforderungen zum Pfad `/user/:id` definiert. Die zweite Weiterleitung verursacht zwar keine Probleme, wird jedoch nie aufgerufen, da durch die erste Weiterleitung der Anforderung/Antwort-Zyklus beendet wird. - -Dieses Beispiel zeigt einen Middleware-Sub-Stack, über den GET-Anforderungen zum Pfad `/user/:id` verarbeitet werden. - -```js -app.get('/user/:id', (req, res, next) => { - console.log('ID:', req.params.id) - next() -}, (req, res, next) => { - res.send('User Info') -}) - -// handler for the /user/:id path, which prints the user ID -app.get('/user/:id', (req, res, next) => { - res.send(req.params.id) -}) -``` - -Wenn Sie den Rest der Middlewarefunktionen eines Weiterleitungs-Middleware-Stack überspringen wollen, rufen Sie `next('route')` auf, um die Steuerung an die nächste Weiterleitung zu übergeben. - -{% capture next-function %} - -`next('route')` will work only in middleware functions that were loaded by using the `app.METHOD()` or `router.METHOD()` functions. - -{% endcapture %} - -{% include admonitions/note.html content=next-function %} - -Dieses Beispiel zeigt einen Middleware-Sub-Stack, über den GET-Anforderungen zum Pfad `/user/:id` verarbeitet werden. - -```js -app.get('/user/:id', (req, res, next) => { - // if the user ID is 0, skip to the next route - if (req.params.id === '0') next('route') - // otherwise pass the control to the next middleware function in this stack - else next() -}, (req, res, next) => { - // send a regular response - res.send('regular') -}) - -// handler for the /user/:id path, which sends a special response -app.get('/user/:id', (req, res, next) => { - res.send('special') -}) -``` - -Middleware can also be declared in an array for reusability. - -Dies ist ein Beispiel zur Verwendung der Middlewarefunktion `express.static` mit einem ausführlich dargestellten Optionsobjekt: - -```js -function logOriginalUrl (req, res, next) { - console.log('Request URL:', req.originalUrl) - next() -} - -function logMethod (req, res, next) { - console.log('Request Type:', req.method) - next() -} - -const logStuff = [logOriginalUrl, logMethod] -app.get('/user/:id', logStuff, (req, res, next) => { - res.send('User Info') -}) -``` - -

Middleware auf Routerebene

- -Middleware auf Routerebene funktioniert in der gleichen Weise wie Middleware auf Anwendungsebene, mit dem einzigen Unterschied, dass sie an eine Instanz von `express.Router()` gebunden ist. - -```js -const router = express.Router() -``` - -Laden Sie Middleware auf Routerebene über die Funktionen `router.use()` und `router.METHOD()`. - -Der folgende Beispielcode repliziert das Middlewaresystem, das oben für die Middleware auf Anwendungsebene gezeigt wird, durch Verwendung von Middleware auf Routerebene. - -```js -const express = require('express') -const app = express() -const router = express.Router() - -// a middleware function with no mount path. This code is executed for every request to the router -router.use((req, res, next) => { - console.log('Time:', Date.now()) - next() -}) - -// a middleware sub-stack shows request info for any type of HTTP request to the /user/:id path -router.use('/user/:id', (req, res, next) => { - console.log('Request URL:', req.originalUrl) - next() -}, (req, res, next) => { - console.log('Request Type:', req.method) - next() -}) - -// a middleware sub-stack that handles GET requests to the /user/:id path -router.get('/user/:id', (req, res, next) => { - // if the user ID is 0, skip to the next router - if (req.params.id === '0') next('route') - // otherwise pass control to the next middleware function in this stack - else next() -}, (req, res, next) => { - // render a regular page - res.render('regular') -}) - -// handler for the /user/:id path, which renders a special page -router.get('/user/:id', (req, res, next) => { - console.log(req.params.id) - res.render('special') -}) - -// mount the router on the app -app.use('/', router) -``` - -To skip the rest of the router's middleware functions, call `next('router')` -to pass control back out of the router instance. - -Dieses Beispiel zeigt einen Middleware-Sub-Stack, über den GET-Anforderungen zum Pfad `/user/:id` verarbeitet werden. - -```js -const express = require('express') -const app = express() -const router = express.Router() - -// predicate the router with a check and bail out when needed -router.use((req, res, next) => { - if (!req.headers['x-auth']) return next('router') - next() -}) - -router.get('/user/:id', (req, res) => { - res.send('hello, user!') -}) - -// use the router and 401 anything falling through -app.use('/admin', router, (req, res) => { - res.sendStatus(401) -}) -``` - -

Middleware für die Fehlerbehandlung

- -
-Middleware für die Fehlerbehandlung benötigt immer *vier* Argumente. Sie müssen vier Argumente angeben, um die Erkennung als Middlewarefunktion für die Fehlerbehandlung zu ermöglichen. Selbst wenn das Objekt `next` nicht verwenden müssen, müssen Sie dies angeben, um die Signatur beizubehalten. Andernfalls wird das Objekt `next` als reguläre Middleware interpretiert, sodass keine Fehlerbehandlung möglich ist. -
- -Middlewarefunktionen für die Fehlerbehandlung werden in derselben Weise definiert wie andere Middlewarefunktionen, außer dass Fehlerbehandlungsfunktionen speziell bei Signaturen vier anstatt drei Argumente aufweisen `(err, req, res, next)`: - -```js -app.use((err, req, res, next) => { - console.error(err.stack) - res.status(500).send('Something broke!') -}) -``` - -Details zu Middleware für die Fehlerbehandlung siehe [Fehlerbehandlung](/{{ page.lang }}/guide/error-handling.html). - -

Integrierte Middleware

- -Seit Version 4.x bestehen bei Express keine Abhängigkeiten zu [Connect](https://github.com/senchalabs/connect) mehr. Mit Ausnahme von `express.static` befinden sich nun alle Middlewarefunktionen, die bisher in Express enthalten waren, in separaten Modulen. Sehen Sie sich hierzu auch die [Liste der Middlewarefunktionen](https://github.com/senchalabs/connect#middleware) an. - -Die einzige integrierte Middlewarefunktion in Express ist `express.static`. - -- [express.static](/en/5x/api.html#express.static) serves static assets such as HTML files, images, and so on. -- [express.json](/en/5x/api.html#express.json) parses incoming requests with JSON payloads. **NOTE: Available with Express 4.16.0+** -- [express.urlencoded](/en/5x/api.html#express.urlencoded) parses incoming requests with URL-encoded payloads. **NOTE: Available with Express 4.16.0+** - -

Middleware anderer Anbieter

- -Mit Middleware anderer Anbieter können Sie Express-Anwendungen um neue Funktionalität erweitern. - -Installieren Sie das Modul Node.js für die erforderliche Funktionalität. Laden Sie das Modul dann in Ihre Anwendung auf Anwendungsebene oder auf Routerebene. - -Das folgende Beispiel veranschaulicht das Installieren und Laden der Middlewarefunktion `cookie-parser` für das Cookie-Parsing. - -```bash -$ npm install cookie-parser -``` - -```js -const express = require('express') -const app = express() -const cookieParser = require('cookie-parser') - -// load the cookie-parsing middleware -app.use(cookieParser()) -``` - -Eine nicht vollständige Liste zu den Middlewarefunktionen anderer Anbieter, die im Allgemeinen mit Express verwendet werden, finden Sie unter [Middleware anderer Anbieter](../resources/middleware.html). diff --git a/de/guide/using-template-engines.md b/de/guide/using-template-engines.md deleted file mode 100644 index 7dd34574e1..0000000000 --- a/de/guide/using-template-engines.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -layout: page -title: Template-Engines in Express verwenden -description: Discover how to integrate and use template engines like Pug, Handlebars, and EJS with Express.js to render dynamic HTML pages efficiently. -menu: guide -order: 5 -redirect_from: " " ---- - -# Template-Engines in Express verwenden - -A _template engine_ enables you to use static template files in your application. At runtime, the template engine replaces -variables in a template file with actual values, and transforms the template into an HTML file sent to the client. -This approach makes it easier to design an HTML page. - -The [Express application generator](/{{ page.lang }}/starter/generator.html) uses [Pug](https://pugjs.org/api/getting-started.html) as its default, but it also supports [Handlebars](https://www.npmjs.com/package/handlebars), and [EJS](https://www.npmjs.com/package/ejs), among others. - -To render template files, set the following [application setting properties](/{{ page.lang }}/4x/api.html#app.set), in the default `app.js` created by the generator: - -- `views`, das Verzeichnis, in dem sich die Vorlagendateien befinden. Beispiel: `app.set('views', './views')` - This defaults to the `views` directory in the application root directory. -- `view engine`, die zu verwendende Template-Engine. Beispiel: `app.set('view engine', 'pug')` - -Installieren Sie dann das entsprechende npm-Paket für die Template-Engine: - -```bash -$ npm install pug --save -``` - -
Express-konforme Template-Engines wie Pug exportieren eine Funktion namens `__express(filePath, options, callback)`, die über die Funktion `res.render()` aufgerufen wird, um den Vorlagencode ausgeben zu können. - -Einige Template-Engines folgen dieser Konvention nicht. Die Bibliothek [Consolidate.js](https://www.npmjs.org/package/consolidate) folgt dieser Konvention, indem alle gängigen Node.js-Template-Engines zugeordnet werden. Daher ist eine reibungslose Funktion in Express gewährleistet. - -
- -Nach der Festlegung der View-Engine muss die Engine nicht angegeben oder das Template-Engine-Modul nicht in Ihre Anwendung geladen werden. Express lädt das Modul intern (wie unten für das obige Beispiel gezeigt). - -```js -app.set('view engine', 'pug') -``` - -Erstellen Sie eine Pug-Vorlagendatei namens `index.pug` im Verzeichnis `views` mit dem folgenden Inhalt: - -```pug -html - head - title= title - body - h1= message -``` - -Dann erstellen Sie eine Weiterleitung, um die Datei `index.pug` auszugeben. Wenn die Eigenschaft `view engine` nicht festgelegt wurde, müssen Sie die Erweiterung der Datei `view` angeben. Andernfalls müssen Sie diese Erweiterung nicht angeben. - -```js -app.get('/', (req, res) => { - res.render('index', { title: 'Hey', message: 'Hello there!' }) -}) -``` - -Wenn Sie eine Anforderung zur Homepage ausführen, wird die Datei `index.pug` im HTML-Format ausgegeben. - -The view engine cache does not cache the contents of the template's output, only the underlying template itself. The view is still re-rendered with every request even when the cache is on. diff --git a/de/guide/writing-middleware.md b/de/guide/writing-middleware.md deleted file mode 100644 index b6fd011d01..0000000000 --- a/de/guide/writing-middleware.md +++ /dev/null @@ -1,221 +0,0 @@ ---- -layout: page -title: Middleware für die Verwendung in Express-Anwendungen schreiben -description: Learn how to write custom middleware functions for Express.js applications, including examples and best practices for enhancing request and response handling. -menu: guide -order: 2 -redirect_from: " " ---- - -# Middleware für die Verwendung in Express-Anwendungen schreiben - -

Überblick

- -_Middleware_ functions are functions that have access to the [request object](/{{ page.lang }}/5x/api.html#req) (`req`), the [response object](/{{ page.lang }}/5x/api.html#res) (`res`), and the `next` function in the application's request-response cycle. Die nächste Middlewarefunktion wird im Allgemeinen durch die Variable `next` bezeichnet. - -Über Middlewarefunktionen lassen sich die folgenden Tasks ausführen: - -- Ausführen von Code -- Vornehmen von Änderungen an der Anforderung und an Antwortobjekten -- End the request-response cycle. -- Aufrufen der nächsten Middleware im Stack - -Wenn über die aktuelle Middlewarefunktion der Anforderung/Antwort-Zyklus nicht beendet werden kann, muss `next()` aufgerufen werden, um die Steuerung an die nächste Middlewarefunktion zu übergeben. Andernfalls geht die Anforderung in den Status "Blockiert" über. - -Das folgende Beispiel zeigt die Elemente eines Middlewarefunktionsaufrufs: - -
- - - - -
Pfad (Weiterleitung), für den die Middlewarefunktion angewendet wird.
- -
Die Middlewarefunktion.
- -
Callback-Argument zur Middlewarefunktion, die nach der geltenden Konvention als "next" bezeichnet wird.
- -
HTTP response argument to the middleware function, called "res" by convention.
- -
HTTP request argument to the middleware function, called "req" by convention.
- -
-Elements of a middleware function call - -
HTTP-Methode, für die die Middlewarefunktion angewendet wird.
-
- -Starting with Express 5, middleware functions that return a Promise will call `next(value)` when they reject or throw an error. `next` will be called with either the rejected value or the thrown Error. - -

Beispiel

- -Here is an example of a simple "Hello World" Express application. -The remainder of this article will define and add three middleware functions to the application: -one called `myLogger` that prints a simple log message, one called `requestTime` that -displays the timestamp of the HTTP request, and one called `validateCookies` that validates incoming cookies. - -```js -const express = require('express') -const app = express() - -app.get('/', (req, res) => { - res.send('Hello World!') -}) - -app.listen(3000) -``` - -

Dies ist ein Beispiel einer einfachen Express-Anwendung namens "Hello World", für die Sie zwei Middlewarefunktionen definieren:

-Dies ist ein einfaches Beispiel einer Middlewarefunktion namens "myLogger". Diese Funktion gibt lediglich "LOGGED" aus, wenn eine Anforderung zur Anwendung über diese Funktion läuft. Die Middlewarefunktion ist der Variablen `myLogger` zugeordnet. - -```js -const myLogger = function (req, res, next) { - console.log('LOGGED') - next() -} -``` - -
-Beachten Sie den Aufruf oben zu `next()`. Durch den Aufruf dieser Funktion wird die nächste Middlewarefunktion in der Anwendung aufgerufen. -Die Funktion `next()` ist nicht Teil der Node.js- oder Express-API, sondern das dritte Argument, das an die Middlewarefunktion übergeben wird. Die Funktion `next()` kann jeden beliebigen Namen haben, per Konvention erhält sie jedoch immer den Namen "next". -Um Unklarheiten zu vermeiden, sollten Sie immer diese Konvention verwenden. -
- -Zum Laden der Middlewarefunktion rufen Sie `app.use()` auf und geben die Middlewarefunktion an. -Beispiel: Durch den folgenden Code wird die Middlewarefunktion `myLogger` vor der Weiterleitung zum Stammverzeichnispfad (/) geladen. - -```js -const express = require('express') -const app = express() - -const myLogger = function (req, res, next) { - console.log('LOGGED') - next() -} - -app.use(myLogger) - -app.get('/', (req, res) => { - res.send('Hello World!') -}) - -app.listen(3000) -``` - -Sobald die Anwendung eine Anforderung erhält, gibt sie die Nachricht "LOGGED" an das Terminal aus. - -Die Reihenfolge beim Laden der Middleware ist wichtig: Middlewarefunktionen, die zuerst geladen werden, werden auch zuerst ausgeführt. - -Wenn `myLogger` nach der Weiterleitung zum Stammverzeichnispfad geladen wird, erreicht die Weiterleitung die Middlewarefunktion nicht. Die Anwendung gibt "LOGGED" nicht aus, weil der Routenhandler für den Stammverzeichnispfad den Anforderung/Antwort-Zyklus beendet. - -Die Middlewarefunktion `myLogger` gibt einfach eine Nachricht aus und übergibt dann die Anforderung zur nächsten Middlewarefunktion im Stack durch Aufruf der Funktion `next()`. - -

Middleware function requestTime

- -Im nächsten Beispiel wird die Eigenschaft `requestTime` zum Anforderungsobjekt hinzugefügt. Diese Middlewarefunktion erhält den Namen "requestTime". - -```js -const requestTime = function (req, res, next) { - req.requestTime = Date.now() - next() -} -``` - -Die Anwendung verwendet nun die Middlewarefunktion `requestTime`. Außerdem verwendet die Callback-Funktion der Weiterleitung zum Stammverzeichnispfad die Eigenschaft, die die Middlewarefunktion zu `req` (dem Anforderungsobjekt) hinzufügt. - -```js -const express = require('express') -const app = express() - -const requestTime = function (req, res, next) { - req.requestTime = Date.now() - next() -} - -app.use(requestTime) - -app.get('/', (req, res) => { - let responseText = 'Hello World!
' - responseText += `Requested at: ${req.requestTime}` - res.send(responseText) -}) - -app.listen(3000) -``` - -Wenn Sie eine Anforderung zum Stammverzeichnis der Anwendung einleiten, zeigt die Anwendung nun die Zeitmarke Ihrer Anforderung im Browser an. - -

Middleware function validateCookies

- -Finally, we'll create a middleware function that validates incoming cookies and sends a 400 response if cookies are invalid. - -Here's an example function that validates cookies with an external async service. - -```js -async function cookieValidator (cookies) { - try { - await externallyValidateCookie(cookies.testCookie) - } catch { - throw new Error('Invalid cookies') - } -} -``` - -Here, we use the [`cookie-parser`](/resources/middleware/cookie-parser.html) middleware to parse incoming cookies off the `req` object and pass them to our `cookieValidator` function. The `validateCookies` middleware returns a Promise that upon rejection will automatically trigger our error handler. - -```js -const express = require('express') -const cookieParser = require('cookie-parser') -const cookieValidator = require('./cookieValidator') - -const app = express() - -async function validateCookies (req, res, next) { - await cookieValidator(req.cookies) - next() -} - -app.use(cookieParser()) - -app.use(validateCookies) - -// error handler -app.use((err, req, res, next) => { - res.status(400).send(err.message) -}) - -app.listen(3000) -``` - -
-Note how `next()` is called after `await cookieValidator(req.cookies)`. This ensures that if `cookieValidator` resolves, the next middleware in the stack will get called. If you pass anything to the `next()` function (except the string `'route'` or `'router'`), Express regards the current request as being an error and will skip any remaining non-error handling routing and middleware functions. -
- -Da Sie Zugriff auf das Anforderungsobjekt, das Antwortobjekt, die nächste Middlewarefunktion im Stack und die gesamte Node.js-API haben, sind die Möglichkeiten, die Sie mit Middlewarefunktionen haben, nahezu unendlich. - -Weitere Informationen zur Verwendung von Middleware in Express siehe [ Express-Middleware verwenden](/{{ page.lang }}/guide/using-middleware.html). - -

Configurable middleware

- -If you need your middleware to be configurable, export a function which accepts an options object or other parameters, which, then returns the middleware implementation based on the input parameters. - -File: `my-middleware.js` - -```js -module.exports = function (options) { - return function (req, res, next) { - // Implement the middleware function based on the options object - next() - } -} -``` - -The middleware can now be used as shown below. - -```js -const mw = require('./my-middleware.js') - -app.use(mw({ option1: '1', option2: '2' })) -``` - -Refer to [cookie-session](https://github.com/expressjs/cookie-session) and [compression](https://github.com/expressjs/compression) for examples of configurable middleware. diff --git a/de/index.md b/de/index.md deleted file mode 100644 index 00be6c05be..0000000000 --- a/de/index.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -layout: home -title: Express - Node.js Web Application Framework -description: "Express is a fast, unopinionated, minimalist web framework for Node.js, providing a robust set of features for web and mobile applications." -menu: home -redirect_from: " " ---- - -
-
-
- -

Fast, unopinionated, minimalist web framework for Node.js

-
-
$ npm install express --save
-
- -
- -```javascript -const express = require('express') -const app = express() -const port = 3000 - -app.get('/', (req, res) => { - res.send('Hello World!') -}) - -app.listen(port, () => { - console.log(`Example app listening on port ${port}`) -}) -``` - -
-
- -{% if site.announcement %} - -
- {% include announcement.html %} -
-{% endif %} - -
-
-
-

Web Applications

Express ist ein minimalistisches und flexibles Node.js Web Application Framework, das eine robustes Reihe an Funktionen für Web- und mobile Anwendungen bereitstellt. -
-
-

APIs

With a myriad of HTTP utility methods and middleware at your disposal, creating a robust API is quick and easy. -
-
-

Performance

Express provides a thin layer of fundamental web application features, without obscuring Node.js features that you know and love. -
-
-

Middleware

- Express is a lightweight and flexible routing framework with minimal core features - meant to be augmented through the use of Express middleware modules. -
-
-
diff --git a/de/resources/community.md b/de/resources/community.md deleted file mode 100644 index 1fc9cecffd..0000000000 --- a/de/resources/community.md +++ /dev/null @@ -1,89 +0,0 @@ ---- -layout: page -title: Express-Community -description: Connect with the Express.js community, learn about the technical committee, find resources, explore community-contributed modules, and get involved in discussions. -menu: resources -order: 1 -redirect_from: " " ---- - -# Community - -## Technical committee - -The Express technical committee meets online every two weeks (as needed) to discuss development and maintenance of Express, -and other issues relevant to the Express project. Each meeting is typically announced in an -[expressjs/discussions issue](https://github.com/expressjs/discussions/issues) with a link to join or view the meeting, which is -open to all observers. - -The meetings are recorded; for a list of the recordings, see the [Express.js YouTube channel](https://www.youtube.com/channel/UCYjxjAeH6TRik9Iwy5nXw7g). - -Members of the Express technical committee are: - -**Active:** - -- [@blakeembrey](https://github.com/blakeembrey) - Blake Embrey -- [@crandmck](https://github.com/crandmck) - Rand McKinney -- [@LinusU](https://github.com/LinusU) - Linus Unnebäck -- [@ulisesgascon](https://github.com/ulisesGascon) - Ulises Gascón -- [@sheplu](https://github.com/sheplu) - Jean Burellier -- [@wesleytodd](https://github.com/wesleytodd) - Wes Todd -- [@jonchurch](https://github.com/jonchurch) - Jon Church -- [@ctcpip](https://github.com/ctcpip/) - Chris de Almeida - -**Inactive:** - -- [@dougwilson](https://github.com/dougwilson) - Douglas Wilson -- [@hacksparrow](https://github.com/hacksparrow) - Hage Yaapa -- [@jonathanong](https://github.com/jonathanong) - jongleberry -- [@niftylettuce](https://github.com/niftylettuce) - niftylettuce -- [@troygoode](https://github.com/troygoode) - Troy Goode - -## Express is made of many modules - -Unsere lebhafte Community hat zu einer Vielzahl von Erweiterungen, [Middlewaremodulen](/{{ page.lang }}/resources/middleware.html) und Frameworks der höheren Ebene geführt. - -Additionally, the Express community maintains modules in these two GitHub orgs: - -- [jshttp](https://github.com/jshttp) modules providing useful utility functions; see [Utility modules](/{{ page.lang }}/resources/utils.html). -- [pillarjs](https://github.com/pillarjs): low-level modules that Express uses internally. - -To keep up with what is going on in the whole community, check out the [ExpressJS StatusBoard](https://expressjs.github.io/statusboard/). - -## Probleme - -Wenn Sie Ihrer Meinung nach einen Fehler festgestellt haben oder lediglich ein Feature/eine Funktion anfordern wollen, können Sie in der [Issue Queue](https://github.com/expressjs/express/issues) ein Ticket öffnen. - -## Beispiele - -Zeigen Sie Dutzende von [Beispielen](https://github.com/expressjs/express/tree/master/examples) zu Express-Anwendungen im Repository an, das alles abdeckt – von API-Design und Authentifizierung bis zur Einbindung von Template-Engines. - -## Github Discussions - -The [GitHub Discussions](https://github.com/expressjs/discussions) section is an excellent space to engage in conversations about the development and maintenance of Express, as well as to share ideas and discuss topics related to its usage. - -# Branding of Express.js - -## Express.js Logo - -Express is a project of the OpenJS Foundation. Please review the [trademark policy](https://trademark-policy.openjsf.org/) for information about permissible use of Express.js logos and marks. - -
-
-

Logotype

- Express.js logo - - - Express.js logo - -
-
-

Logomark

- Express.js mark - - - Express.js mark - -
-
-
diff --git a/de/resources/contributing.md b/de/resources/contributing.md deleted file mode 100644 index 24bdb8dd2a..0000000000 --- a/de/resources/contributing.md +++ /dev/null @@ -1,516 +0,0 @@ ---- -layout: page -title: Contributing to Express -description: Find out how to contribute to Express.js, including guidelines for reporting issues, submitting pull requests, becoming a collaborator, and understanding security policies. -menu: resources -order: 5 -redirect_from: " " ---- - -# Contributing to Express - -### Looking to contribute to Expressjs.com? Click [here](#expressjs-website-contributing). - -Express and the other projects in the [expressjs organization on GitHub](https://github.com/expressjs) are projects of the [OpenJs Foundation](https://openjsf.org/). -These projects are governed under the general policies and guidelines of the Node.js Foundation along with the additional guidelines below. - -- [Technical committee](#technical-committee) -- [Community contributing guide](#community-contributing-guide) -- [Collaborator's guide](#collaborators-guide) -- [Security policies and procedures](#security-policies-and-procedures) - -## Technical committee - -The Express technical committee consists of active project members, and guides development and maintenance of the Express project. For more information, see [Express Community - Technical committee](community.html#technical-committee). - -## Community contributing guide - - - -The goal of this document is to create a contribution process that: - -- Encourages new contributions. -- Encourages contributors to remain involved. -- Avoids unnecessary processes and bureaucracy whenever possible. -- Creates a transparent decision making process that makes it clear how - contributors can be involved in decision making. - -### Vocabulary - -- A **Contributor** is any individual creating or commenting on an issue or pull request. -- A **Committer** is a subset of contributors who have been given write access to the repository. -- A **Project Captain** is the lead maintainer of a repository. -- A **TC (Technical Committee)** is a group of committers representing the required technical - expertise to resolve rare disputes. -- A **Triager** is a subset of contributors who have been given triage access to the repository. - -### Logging Issues - -Log an issue for any question or problem you might have. When in doubt, log an issue, and -any additional policies about what to include will be provided in the responses. The only -exception is security disclosures which should be sent privately. - -Committers may direct you to another repository, ask for additional clarifications, and -add appropriate metadata before the issue is addressed. - -Please be courteous and respectful. Every participant is expected to follow the -project's Code of Conduct. - -### Contributions - -Any change to resources in this repository must be through pull requests. This applies to all changes -to documentation, code, binary files, etc. Even long term committers and TC members must use -pull requests. - -No pull request can be merged without being reviewed. - -For non-trivial contributions, pull requests should sit for at least 36 hours to ensure that -contributors in other timezones have time to review. Consideration should also be given to -weekends and other holiday periods to ensure active committers all have reasonable time to -become involved in the discussion and review process if they wish. - -The default for each contribution is that it is accepted once no committer has an objection. -During a review, committers may also request that a specific contributor who is most versed in a -particular area gives a "LGTM" before the PR can be merged. There is no additional "sign off" -process for contributions to land. Once all issues brought by committers are addressed it can -be landed by any committer. - -In the case of an objection being raised in a pull request by another committer, all involved -committers should seek to arrive at a consensus by way of addressing concerns being expressed -by discussion, compromise on the proposed change, or withdrawal of the proposed change. - -If a contribution is controversial and committers cannot agree about how to get it to land -or if it should land then it should be escalated to the TC. TC members should regularly -discuss pending contributions in order to find a resolution. It is expected that only a -small minority of issues be brought to the TC for resolution and that discussion and -compromise among committers be the default resolution mechanism. - -### Becoming a Triager - -Anyone can become a triager! Read more about the process of being a triager in -[the triage process document](https://github.com/expressjs/discussions/blob/master/Triager-Guide.md). - -Currently, any existing [organization member](https://github.com/orgs/expressjs/people) can nominate -a new triager. If you are interested in becoming a triager, our best advice is to actively participate -in the community by helping triaging issues and pull requests. As well we recommend -to engage in other community activities like attending the TC meetings, and participating in the Slack -discussions. If you feel ready and have been helping triage some issues, reach out to an active member of the organization to ask if they'd -be willing to support you. If they agree, they can create a pull request to formalize your nomination. In the case of an objection to the nomination, the triage team is responsible for working with the individuals involved and finding a resolution. - -You can also reach out to any of the [organization members](https://github.com/orgs/expressjs/people) -if you have questions or need guidance. - -### Becoming a Committer - -All contributors who have landed significant and valuable contributions should be onboarded in a timely manner, -and added as a committer, and be given write access to the repository. - -Committers are expected to follow this policy and continue to send pull requests, go through -proper review, and have other committers merge their pull requests. - -### TC Process - -The TC uses a "consensus seeking" process for issues that are escalated to the TC. -The group tries to find a resolution that has no open objections among TC members. -If a consensus cannot be reached that has no objections then a majority wins vote -is called. It is also expected that the majority of decisions made by the TC are via -a consensus seeking process and that voting is only used as a last-resort. - -Resolution may involve returning the issue to project captains with suggestions on -how to move forward towards a consensus. It is not expected that a meeting of the TC -will resolve all issues on its agenda during that meeting and may prefer to continue -the discussion happening among the project captains. - -Members can be added to the TC at any time. Any TC member can nominate another committer -to the TC and the TC uses its standard consensus seeking process to evaluate whether or -not to add this new member. The TC will consist of a minimum of 3 active members and a -maximum of 10. If the TC should drop below 5 members the active TC members should nominate -someone new. If a TC member is stepping down, they are encouraged (but not required) to -nominate someone to take their place. - -TC members will be added as admin's on the Github orgs, npm orgs, and other resources as -necessary to be effective in the role. - -To remain "active" a TC member should have participation within the last 12 months and miss -no more than six consecutive TC meetings. Our goal is to increase participation, not punish -people for any lack of participation, this guideline should be only be used as such -(replace an inactive member with a new active one, for example). Members who do not meet this -are expected to step down. If A TC member does not step down, an issue can be opened in the -discussions repo to move them to inactive status. TC members who step down or are removed due -to inactivity will be moved into inactive status. - -Inactive status members can become active members by self nomination if the TC is not already -larger than the maximum of 10. They will also be given preference if, while at max size, an -active member steps down. - -### Project Captains - -The Express TC can designate captains for individual projects/repos in the -organizations. These captains are responsible for being the primary -day-to-day maintainers of the repo on a technical and community front. -Repo captains are empowered with repo ownership and package publication rights. -When there are conflicts, especially on topics that effect the Express project -at large, captains are responsible to raise it up to the TC and drive -those conflicts to resolution. Captains are also responsible for making sure -community members follow the community guidelines, maintaining the repo -and the published package, as well as in providing user support. - -Like TC members, Repo captains are a subset of committers. - -To become a captain for a project the candidate is expected to participate in that -project for at least 6 months as a committer prior to the request. They should have -helped with code contributions as well as triaging issues. They are also required to -have 2FA enabled on both their GitHub and npm accounts. - -Any TC member or an existing captain on the **same** repo can nominate another committer -to the captain role. To do so, they should submit a PR to this document, updating the -**Active Project Captains** section (while maintaining the sort order) with the project -name, the nominee's GitHub handle, and their npm username (if different). - -- Repos can have as many captains as make sense for the scope of work. -- A TC member or an existing repo captain **on the same project** can nominate a new captain. - Repo captains from other projects should not nominate captains for a different project. - -The PR will require at least 2 approvals from TC members and 2 weeks hold time to allow -for comment and/or dissent. When the PR is merged, a TC member will add them to the -proper GitHub/npm groups. - -#### Active Projects and Captains - -The list can be found at [https://github.com/expressjs/discussions/blob/HEAD/docs/contributing/captains_and_committers.md#active-projects-and-members](https://github.com/expressjs/discussions/blob/HEAD/docs/contributing/captains_and_committers.md#active-projects-and-members) - -#### Current Initiative Captains - -The list can be found at [https://github.com/expressjs/discussions/blob/HEAD/docs/contributing/captains_and_committers.md#current-initiative-captains](https://github.com/expressjs/discussions/blob/HEAD/docs/contributing/captains_and_committers.md#current-initiative-captains) - -### Inactivity and Emeritus Policy for Any Role - -To support the health and continuity of the project, all individuals holding a role within the community (such as Triager, Committer, WG member, Project Captain, or TC member) are encouraged to maintain active participation. - -Inactivity is defined as the absence of meaningful involvement in the project—such as contributions, code reviews, triage, meeting attendance, or discussion participation—for a continuous period of 6 months. - -#### Exceptions - -Anyone may request a temporary leave from active participation due to personal or professional reasons. In such cases, the individual should inform the relevant team or the Technical Committee (TC). During this time, the inactivity policy is paused, and the individual will not be flagged as inactive. - -#### Inactivity Process - -- If someone is deemed inactive, the individual may be transitioned to an emeritus role that reflects their past contributions. A best effort will be made to inform them that this has occurred. They may request to be reinstated when they are ready to be active again. -- The emeritus status helps preserve a clear record of contributors who have meaningfully shaped the project over time. - -#### Accountability - -- The Technical Committee (TC) and the respective captains of each package/team are responsible for assessing activity levels and enacting this policy fairly and transparently, in coordination with other relevant teams. -- In case of disagreement, the situation can be discussed and resolved by consensus within the TC or appropriate team. - -### Developer's Certificate of Origin 1.1 - -```text -By making a contribution to this project, I certify that: - - (a) The contribution was created in whole or in part by me and I - have the right to submit it under the open source license - indicated in the file; or - - (b) The contribution is based upon previous work that, to the best - of my knowledge, is covered under an appropriate open source - license and I have the right under that license to submit that - work with modifications, whether created in whole or in part - by me, under the same open source license (unless I am - permitted to submit under a different license), as indicated - in the file; or - - (c) The contribution was provided directly to me by some other - person who certified (a), (b) or (c) and I have not modified - it. - - (d) I understand and agree that this project and the contribution - are public and that a record of the contribution (including all - personal information I submit with it, including my sign-off) is - maintained indefinitely and may be redistributed consistent with - this project or the open source license(s) involved. -``` - -## Collaborator's guide - - - -### Website Issues - -Open issues for the expressjs.com website in https://github.com/expressjs/expressjs.com. - -For issues in other Express managed repos (everything in `expressjs`, `pillarjs` or `jshttp` other than `expressjs/express`), be sure to check their contributing guide and open issues and PRs in the appropriate repository. - -### PRs and Code contributions - -- Tests must pass. -- Follow the [JavaScript Standard Style](https://standardjs.com/) and `npm run lint`. -- If you fix a bug, add a test. - -### Branches - -Use the `master` branch for bug fixes or minor work that is intended for the -current release stream. - -Use the correspondingly named branch, e.g. `6.x`, for anything intended for -a future release of Express. - -### Steps for contributing - -1. Create an issue for the - bug you want to fix or the feature that you want to add. -2. Create your own fork on GitHub, then - checkout your fork. -3. Write your code in your local copy. It's good practice to create a branch for - each new issue you work on, although not compulsory. -4. To run the test suite, first install the dependencies by running `npm install`, - then run `npm test`. -5. Ensure your code is linted by running `npm run lint` -- fix any issue you - see listed. -6. If the tests pass, you can commit your changes to your fork and then create - a pull request from there. Make sure to reference your issue from the pull - request comments by including the issue number e.g. `#123`. - -### Issues which are questions - -We will typically close any vague issues or questions that are specific to some -app you are writing. Please double check the docs and other references before -being trigger happy with posting a question issue. - -Things that will help get your question issue looked at: - -- Full and runnable JS code. -- Clear description of the problem or unexpected behavior. -- Clear description of the expected result. -- Steps you have taken to debug it yourself. - -If you post a question and do not outline the above items or make it easy for -us to understand and reproduce your issue, it will be closed. - -If your question meets all of the above requirements but you do not believe it needs to be looked at -by the maintainers -(for example, if you are just looking for community input) please open it as a discussion topic instead -of an issue. If you -are unsure and open an issue, we may move it to discussions if we triage them and decide they do -not need high -visibility or maintainer input. - -## Security Policies and Procedures - - - -This document outlines security procedures and general policies for the Express -project. - -- [Reporting a Bug or Security Vulnerability](#reporting-a-bug-or-security-vulnerability) -- [Disclosure Policy](#disclosure-policy) -- [Comments on this Policy](#comments-on-this-policy) -- [The Express Threat Model](#the-express-threat-model) - -### Reporting a Bug or Security Vulnerability - -> [!IMPORTANT] -> Before reporting a vulnerability, please review the [Express Threat Model](#the-express-threat-model) to check if the issue falls within Express's security scope. - -The Express team and community take all security vulnerabilities seriously. -Thank you for improving the security of Express and related projects. -We appreciate your efforts in responsible disclosure and will make every effort -to acknowledge your contributions. - -A [Security triage team member](https://github.com/expressjs/security-wg#security-triage-team-expressjssecurity-triage) -or [the repo captain](https://github.com/expressjs/discussions/blob/master/docs/contributing/captains_and_committers.md) -will acknowledge your report as soon as possible. -These timelines may extend when our triage -volunteers are away on holiday, particularly at the end of the year. - -After the initial reply to your report, the security team will -endeavor to keep you informed of the progress towards a fix and full -announcement, and may ask for additional information or guidance. - -> [!NOTE] -> You can find more information about our process in [this guide](https://github.com/expressjs/security-wg/blob/main/docs/incident_response_plan.md) - -#### Reporting Security Bugs via GitHub Security Advisory (Preferred) - -The preferred way to report security vulnerabilities is through -[GitHub Security Advisories](https://github.com/advisories). -This allows us to collaborate on a fix while maintaining the -confidentiality of the report. - -To report a vulnerability -([docs](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability)): - -1. Visit the **Security** tab of the affected repository on GitHub. -2. Click **Report a vulnerability** and follow the provided steps. - -This process applies to any repositories within the Express ecosystem. -If you are unsure whether a repository falls under this policy, -feel free to reach out via email. - -#### Reporting via Email - -If you prefer, you can also report security issues by emailing `express-security@lists.openjsf.org`. - -To ensure a timely response, please include all relevant details directly in the email body rather than linking to external sources or attaching files. - -The lead maintainer will acknowledge your email within 48 hours and provide an initial response outlining the next steps. The security team will keep you updated on the progress and may request additional details. - -#### Third-Party Modules - -If the security issue pertains to a third-party module that is not directly maintained within the Express ecosystem, please report it to the maintainers of that module. - -### Disclosure Policy - -When the security team receives a security bug report, they will assign it to a -primary handler. This person will coordinate the fix and release process, -involving the following steps: - -- Confirm the problem and determine the affected versions. -- Audit code to find any potential similar problems. -- Prepare fixes for all releases still under maintenance. These fixes will be - released as fast as possible to npm. - -### Comments on this Policy - -If you have suggestions on how this process could be improved please submit a -pull request. - -### The Express Threat Model - -The Express threat model defines the boundaries of what the framework considers its security responsibility. It establishes which elements are trusted (such as the developer, the runtime environment, and application code) versus untrusted (such as data from network connections). Issues arising from trusted elements are considered out of scope, while Express is responsible for safely handling untrusted data. - -Many commonly reported concerns fall outside Express's security scope and are the responsibility of the application developer. Such as prototype pollution from unsanitized user input, misconfigured static file serving, or issues in third-party dependencies. - -For complete details, see the [Express Threat Model](https://github.com/expressjs/security-wg/blob/main/docs/ThreatModel.md). - ----- - -# Contributing to Expressjs.com {#expressjs-website-contributing} - - - -### The Official Documentation of the Express.js Framework - -This is the contribution documentation for the [expressjs.com](https://github.com/expressjs/expressjs.com) website. - -#### Need some ideas? These are some typical issues. - -1. **Website issues**: If you see anything on the site that could use a tune-up, think about how to fix it. - - Display or screen sizing problems - - Mobile responsiveness issues - - Missing or broken accessibility features - - Website outages - - Broken links - - Page structure or user interface enhancements - -2. **Content Issues**: Fix anything related to site content or typos. - - Spelling errors - - Incorrect/outdated Express.js documentation - - Missing content - -3. **Translation Issues**: Fix any translation errors or contribute new content. - - Fix spelling errors - - Fix incorrect/poorly translated words - - Check out the [Contributing translations](#contributing-translations) section below for a contributing guide. - -#### Want to work on a backlog issue? - -We often have bugs or enhancements that need work. You can find these under our repo's [Issues tab](https://github.com/expressjs/expressjs.com/issues). Check out the tags to find something that's a good match for you. - -#### Have an idea? Found a bug? - -If you've found a bug or a typo, or if you have an idea for an enhancement, you can: - -- Submit a [new issue](https://github.com/expressjs/expressjs.com/issues/new/choose) on our repo. Do this for larger proposals, or if you'd like to discuss or get feedback first. - -- Make a [GitHub pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request). If you have already done work, and it's ready to go, feel free to send it our way. - -## Getting Started - -The steps below will guide you through the Expressjs.com contribution process. - -#### Step 1: (OPTIONAL) Open a New Issue - -So you've found a problem that you want to fix, or have a site enhancement you want to make. - -1. If you want to get feedback or discuss, open a discussion [issue](https://github.com/expressjs/expressjs.com/issues/new/choose) prior to starting work. This is not required, but encouraged for larger proposals. - - While we highly encourage this step, it is only for submissions proposing significant change. It helps us to clarify and focus the work, and ensure it aligns with overall project priorities. - - For submissions proposing minor improvements or corrections, this is not needed. You can skip this step. - - When opening an issue please give it a title and fill in the description section. The more details you provide, the more feedback we can give. - -2. After receiving your issue the Express.js documentation team will respond with feedback. We read every submission and always try to respond quickly with feedback. - - For submissions proposing significant change, we encourage you to follow the review process before starting work. - -#### Step 2: Get the Application Code Base - -Clone the repo and get the code: - -```sh -git clone https://github.com/expressjs/expressjs.com.git -``` - -After you've got the code you're ready to start making your changes! - -But just in case you need a little extra explanation, this section below outlines the main sections of the code base, where most changes are likely to be made. - -**Markdown Page Files**: - -- These files render to html and make up the individual pages of the site. Most of the site's documentation text content is written in `md` files. -- Change these to make changes to individual pages' content/text or markup. -- Each language has its own complete set of pages, located under their respective language directories - all the Spanish markdown content is found in the `es` directory, for example. - -**Includes Partials and Layout Templates** - -- `_includes` are partials that are imported and reused across multiple pages. - - These are used to import text content for reuse across pages, such as the API documentation, e.g., `_includes > api > en > 5x`, which is included in every language. - - These are used to include the page components that make up site-wide user interface and periphery structure, e.g., Header, Footer, etc. -- `_layouts` are the templates used to wrap the site's individual pages. - - These are used to display the structure of the site's periphery, such as the header and footer, and for injecting and displaying individual markdown pages inside the `content` tag. - -**Blog Markdown Files** - -- These files make up the individual blog posts. If you want to contribute a blog post please - follow the specific instructions for [How to write a blog post.](https://expressjs.com/en/blog/write-post.html) -- Located under the `_posts` directory. - -**CSS or Javascript** - -- All css and js files are kept in `css` and `js` folders on the project root. - -The Express.js website is built using [Jekyll](https://jekyllrb.com/) and is hosted on [GitHub Pages](https://pages.github.com/). - -#### Step 3: Running the Application - -Now you'll need a way to see your changes, which means you'll need a running version of the application. You have two options. - -1. **Run Locally**: This gets the local version of the application up and running on your machine. Follow our [Local Setup Guide](https://github.com/expressjs/expressjs.com?tab=readme-ov-file#build-the-website-locally) to use this option. - - This is the recommended option for moderate to complex work. - -2. **Run using Deploy Preview**: Use this option if you don't want to bother with a local installation. Part of our continuous integration pipeline includes [Netlify Deploy Preview](https://docs.netlify.com/deploy/deploy-types/deploy-previews/). - 1. To use this you'll need to get your changes online - after you've made your first commit on your feature branch, make a _draft_ pull request. - 2. After the build steps are complete, you'll have access to a **Deploy Preview** tab that will run your changes on the web, rebuilding after each commit is pushed. - 3. After you are completely done your work, and it's ready for review, remove the draft status on your pull request and submit your work. - -## Contributing translations - -We use Crowdin to manage our translations in multiple languages and achieve automatic translation with artificial intelligence. Since these translations can be inefficient in some cases, we need help from the community to provide accurate and helpful translations. - -The documentation is translated into these languages: - -- Chinese Simplified (`zh-cn`) -- Chinese Traditional (`zh-tw`) -- English (`en`) -- French (`fr`) -- German (`de`) -- Italian (`it`) -- Japanese (`ja`) -- Korean (`ko`) -- Brazilian Portuguese (`pt-br`) -- Spanish (`es`) - -### How to translate - -1. Request to join the Express.js Website project on [Crowdin](https://express.crowdin.com/website) -2. [Select the language you want to translate](https://support.crowdin.com/for-translators/#starting-translation) -3. [Start translating](https://support.crowdin.com/online-editor/) - diff --git a/de/resources/glossary.md b/de/resources/glossary.md deleted file mode 100644 index bbbd9f6eca..0000000000 --- a/de/resources/glossary.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -layout: page -title: Express-Glossar -description: A comprehensive glossary of terms related to Express.js, Node.js, middleware, routing, and other key concepts to help you understand and use Express effectively. -menu: resources -order: 2 -redirect_from: " " ---- - -# Glossar - -### Anwendung - -Im Allgemeinen besteht eine Anwendung aus einem oder mehreren Programmen, über die Operationen für bestimmte Zwecke ausgeführt werden. Im Zusammenhang mit Express ist eine Anwendung ein Programm, das die auf der Node.js-Plattform laufende Express-API nutzt. Wird auch als [Anwendungsobjekt](/{{ page.lang }}/api.html#express) bezeichnet. - -### API - -Anwendungsprogrammierschnittstelle (Application Programming Interface). Abkürzung bei der ersten Verwendung ausschreiben. - -### Express - -Schnelles, offenes, unkompliziertes Web-Framework für Node.js-Anwendungen. Im Allgemeinen wird der Name "Express" eher verwendet als "Express.js", auch wenn "Express.js" ebenfalls verwendet werden kann. - -### libuv - -Eine plattformübergreifende Unterstützungsbibliothek, bei der die asynchrone Ein-/Ausgabe im Mittelpunkt steht. Sie wurde in erster Linie für die Verwendung in Node.js entwickelt. - -### middleware - -Eine Funktion, die über die Weiterleitungsebene in Express vor dem letzten Anforderungshandler aufgerufen wird. Deshalb befindet sich diese Funktion in der Mitte zwischen einer unformatierten Anforderung und der endgültigen beabsichtigten Weiterleitung. Nachfolgend finden Sie einige Details zur Middlewareterminologie: - -- `var foo = require('middleware')` bedeutet, dass ein Node.js-Modul _benötigt_ oder _verwendet_ wird. Dann gibt die Anweisung `var mw = foo()` in der Regel die Middleware zurück. -- `app.use(mw)` bedeutet, dass die _Middleware dem globalen Verarbeitungsstack hinzugefügt wird_. -- `app.get('/foo', mw, function (req, res) { ... })` bedeutet, dass die _Middleware dem "GET /foo"-Verarbeitungsstack hinzugefügt wird_. - -### Node.js - -Eine Softwareplattform, die für die Erstellung skalierbarer Netzanwendungen verwendet wird. Node.js verwendet JavaScript als Scripting-Sprache und erzielt den hohen Durchsatz durch nicht blockierende Ein-/Ausgabe und eine Ereignisschleife mit einem Thread. Siehe auch [nodejs.org](http://nodejs.org/). **Hinweis**: Der ursprüngliche Name lautet "Node.js". - -### Open-Source - -Bei Verwendung als Adjektiv muss dieser Begriff mit Bindestrichen gekoppelt werden: Beispiel: "Dies ist eine Open-Source-Software." Siehe auch [Open-Source-Software in Wikipedia](http://en.wikipedia.org/wiki/Open-source_software). - -{% capture english-rules %} - -Although it is common not to hyphenate this term, we are using the standard English rules for hyphenating a compound adjective. - -{% endcapture %} - -{% include admonitions/note.html content=english-rules %} - -### Anforderung - -Eine HTTP-Antwort. Ein Server gibt eine HTTP-Antwortnachricht an den Client zurück. Die Antwort enthält Informationen zum Beendigungsstatus in Bezug auf die Anforderung und kann im Nachrichtenhauptteil auch angeforderten Inhalt enthalten. - -### Antwort - -Eine HTTP-Anforderung. Ein Client übergibt eine HTTP-Anforderungsnachricht an einen Server, der wiederum eine Antwort zurückgibt. The response contains completion status information about the request and might also contain requested content in its message body. - -### Weiterleitung (Route) - -Teil einer URL, die eine Ressource angibt. Beispiel: In `http://foo.com/products/id` ist "/products/id" die Weiterleitung. - -### Router - -Siehe [Router](/{{ page.lang }}/4x/api.html#router) in der API-Referenz. diff --git a/de/resources/middleware.md b/de/resources/middleware.md deleted file mode 100644 index 3657f5b2ba..0000000000 --- a/de/resources/middleware.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -layout: middleware -title: Express-Middleware -description: Explore a list of Express.js middleware modules maintained by the Express team and the community, including built-in middleware and popular third-party modules. -menu: resources -order: 3 -redirect_from: " " -module: mw-home ---- - -## Express-Middleware - -Nachfolgend sind einige Express-Middlewaremodule aufgeführt: - -| [express-slash](https://github.com/ericf/express-slash): Express-Middlewaremodul für Benutzer, die hohen Wert auf abschließende Schrägstriche legen. | Beschreibung | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | -| [body-parser](/{{page.lang}}/resources/middleware/body-parser.html) | Parse HTTP request body. | -| [compression](/{{page.lang}}/resources/middleware/compression.html) | Compress HTTP responses. | -| [connect-rid](/{{page.lang}}/resources/middleware/connect-rid.html) | Generate unique request ID. | -| [cookie-parser](/{{page.lang}}/resources/middleware/cookie-parser.html) | Parse cookie header and populate `req.cookies`. See also [cookies](https://github.com/jed/cookies). | -| [cookie-session](/{{page.lang}}/resources/middleware/cookie-session.html) | Establish cookie-based sessions. | -| [cors](/{{page.lang}}/resources/middleware/cors.html) | Enable cross-origin resource sharing (CORS) with various options. | -| [errorhandler](/{{page.lang}}/resources/middleware/errorhandler.html) | Development error-handling/debugging. | -| [method-override](/{{page.lang}}/resources/middleware/method-override.html) | Override HTTP methods using header. | -| [morgan](/{{page.lang}}/resources/middleware/morgan.html) | HTTP request logger. | -| [multer](/{{page.lang}}/resources/middleware/multer.html) | Handle multi-part form data. | -| [response-time](/{{page.lang}}/resources/middleware/response-time.html) | Record HTTP response time. | -| [serve-favicon](/{{page.lang}}/resources/middleware/serve-favicon.html) | Serve a favicon. | -| [serve-index](/{{page.lang}}/resources/middleware/serve-index.html) | Serve directory listing for a given path. | -| [serve-static](/{{page.lang}}/resources/middleware/serve-static.html) | Serve static files. | -| [session](/{{page.lang}}/resources/middleware/session.html) | Establish server-based sessions (development only). | -| [timeout](/{{page.lang}}/resources/middleware/timeout.html) | Set a timeout perioHTTP request processing. | -| [vhost](/{{page.lang}}/resources/middleware/vhost.html) | Create virtual domains. | - -## Informationen zu weiteren Middlewaremodulen siehe: - -These are some additional popular middleware modules. - -{% include community-caveat.html %} - -| [express-slash](https://github.com/ericf/express-slash): Express-Middlewaremodul für Benutzer, die hohen Wert auf abschließende Schrägstriche legen. | Beschreibung | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [helmet](https://github.com/helmetjs/helmet): Modul zur Sicherung Ihrer Anwendungen durch Festlegung verschiedener HTTP-Header. | Helps secure your apps by setting various HTTP headers. | -| [passport](https://github.com/jaredhanson/passport): Express-Middlewaremodul für die Authentifizierung. | Authentication using "strategies" such as OAuth, OpenID and many others. See [passportjs.org](https://passportjs.org/) for more information. | diff --git a/de/resources/middleware/body-parser.md b/de/resources/middleware/body-parser.md deleted file mode 100644 index 9c3e7e0276..0000000000 --- a/de/resources/middleware/body-parser.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: middleware -title: Express body-parser middleware -menu: resources -redirect_from: ' ' -module: body-parser ---- diff --git a/de/resources/middleware/compression.md b/de/resources/middleware/compression.md deleted file mode 100644 index d288542a65..0000000000 --- a/de/resources/middleware/compression.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: middleware -title: Express compression middleware -menu: resources -redirect_from: ' ' -module: compression ---- diff --git a/de/resources/middleware/connect-rid.md b/de/resources/middleware/connect-rid.md deleted file mode 100644 index 95b2689265..0000000000 --- a/de/resources/middleware/connect-rid.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: middleware -title: Express connect-rid middleware -menu: resources -redirect_from: ' ' -module: connect-rid ---- diff --git a/de/resources/middleware/cookie-parser.md b/de/resources/middleware/cookie-parser.md deleted file mode 100644 index 55100372e3..0000000000 --- a/de/resources/middleware/cookie-parser.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: middleware -title: Express cookie-parser middleware -menu: resources -redirect_from: ' ' -module: cookie-parser ---- diff --git a/de/resources/middleware/cookie-session.md b/de/resources/middleware/cookie-session.md deleted file mode 100644 index c6308fa81c..0000000000 --- a/de/resources/middleware/cookie-session.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: middleware -title: Express cookie-session middleware -menu: resources -redirect_from: ' ' -module: cookie-session ---- diff --git a/de/resources/middleware/cors.md b/de/resources/middleware/cors.md deleted file mode 100644 index 4692141929..0000000000 --- a/de/resources/middleware/cors.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: middleware -title: Express cors middleware -menu: resources -redirect_from: ' ' -module: cors ---- diff --git a/de/resources/middleware/errorhandler.md b/de/resources/middleware/errorhandler.md deleted file mode 100644 index 3b11a1208e..0000000000 --- a/de/resources/middleware/errorhandler.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: middleware -title: Express errorhandler middleware -menu: resources -redirect_from: ' ' -module: errorhandler ---- diff --git a/de/resources/middleware/method-override.md b/de/resources/middleware/method-override.md deleted file mode 100644 index 8dff029cd4..0000000000 --- a/de/resources/middleware/method-override.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: middleware -title: Express method-override middleware -menu: resources -redirect_from: ' ' -module: method-override ---- diff --git a/de/resources/middleware/morgan.md b/de/resources/middleware/morgan.md deleted file mode 100644 index 99c4c80fa9..0000000000 --- a/de/resources/middleware/morgan.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: middleware -title: Express morgan middleware -menu: resources -redirect_from: ' ' -module: morgan ---- diff --git a/de/resources/middleware/multer.md b/de/resources/middleware/multer.md deleted file mode 100644 index 2fffab3b73..0000000000 --- a/de/resources/middleware/multer.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: middleware -title: Express multer middleware -menu: resources -redirect_from: ' ' -module: multer ---- diff --git a/de/resources/middleware/response-time.md b/de/resources/middleware/response-time.md deleted file mode 100644 index 78ee67cd32..0000000000 --- a/de/resources/middleware/response-time.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: middleware -title: Express response-time middleware -menu: resources -redirect_from: ' ' -module: response-time ---- diff --git a/de/resources/middleware/serve-favicon.md b/de/resources/middleware/serve-favicon.md deleted file mode 100644 index 7a1c8e6f5c..0000000000 --- a/de/resources/middleware/serve-favicon.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: middleware -title: Express serve-favicon middleware -menu: resources -redirect_from: ' ' -module: serve-favicon ---- diff --git a/de/resources/middleware/serve-index.md b/de/resources/middleware/serve-index.md deleted file mode 100644 index deeb963c8f..0000000000 --- a/de/resources/middleware/serve-index.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: middleware -title: Express serve-index middleware -menu: resources -redirect_from: ' ' -module: serve-index ---- diff --git a/de/resources/middleware/serve-static.md b/de/resources/middleware/serve-static.md deleted file mode 100644 index ff9bcc313e..0000000000 --- a/de/resources/middleware/serve-static.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: middleware -title: Express serve-static middleware -menu: resources -redirect_from: ' ' -module: serve-static ---- diff --git a/de/resources/middleware/session.md b/de/resources/middleware/session.md deleted file mode 100644 index 22cf354ae7..0000000000 --- a/de/resources/middleware/session.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: middleware -title: Express session middleware -menu: resources -redirect_from: ' ' -module: session ---- diff --git a/de/resources/middleware/timeout.md b/de/resources/middleware/timeout.md deleted file mode 100644 index 1afa2c5dd3..0000000000 --- a/de/resources/middleware/timeout.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: middleware -title: Express timeout middleware -menu: resources -redirect_from: ' ' -module: timeout ---- diff --git a/de/resources/middleware/vhost.md b/de/resources/middleware/vhost.md deleted file mode 100644 index 2bad4c964f..0000000000 --- a/de/resources/middleware/vhost.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: middleware -title: Express vhost middleware -menu: resources -redirect_from: ' ' -module: vhost ---- diff --git a/de/resources/utils.md b/de/resources/utils.md deleted file mode 100644 index f5ba489b99..0000000000 --- a/de/resources/utils.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -layout: page -title: Express utilities -description: Discover utility modules related to Express.js and Node.js, including tools for cookies, CSRF protection, URL parsing, routing, and more to enhance your applications. -menu: resources -order: 4 -redirect_from: " " ---- - -## Express utility functions - -The [pillarjs](https://github.com/pillarjs) GitHub organization contains a number of modules -for utility functions that may be generally useful. - -| Utility modules | Beschreibung | -| -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [cookies](https://www.npmjs.com/package/cookies) | Get and set HTTP(S) cookies that can be signed to prevent tampering, using Keygrip. Can be used with the Node.js HTTP library or as Express middleware. | -| [csrf](https://www.npmjs.com/package/csrf) | Contains the logic behind CSRF token creation and verification. Use this module to create custom CSRF middleware. | -| [finalhandler](https://www.npmjs.com/package/finalhandler) | Function to invoke as the final step to respond to HTTP request. | -| [parseurl](https://www.npmjs.com/package/parseurl) | Parse a URL with caching. | -| [path-to-regexp](https://www.npmjs.com/package/path-to-regexp) | Turn an Express-style path string such as \`\`/user/:name\` into a regular expression. | -| [resolve-path](https://www.npmjs.com/package/resolve-path) | Resolves a relative path against a root path with validation. | -| [router](https://www.npmjs.com/package/router) | Simple middleware-style router. | -| [send](https://www.npmjs.com/package/send) | Library for streaming files as a HTTP response, with support for partial responses (ranges), conditional-GET negotiation, and granular events. | - -For additional low-level HTTP-related modules, see [jshttp](http://jshttp.github.io/). diff --git a/de/starter/basic-routing.md b/de/starter/basic-routing.md deleted file mode 100644 index e264e27975..0000000000 --- a/de/starter/basic-routing.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -layout: page -title: Basisrouting in Express -description: Learn the fundamentals of routing in Express.js applications, including how to define routes, handle HTTP methods, and create route handlers for your web server. -menu: starter -order: 4 -redirect_from: " " ---- - -# Basisrouting - -Per _Routing_ wird bestimmt, wie eine Antwort auf eine Clientanforderung an einem bestimmten Endpunkt antwortet. Dies ist eine URI (oder ein Pfad) und eine bestimmte HTTP-Anforderungsmethode (GET, POST usw.). - -Jede Weiterleitung (Route) kann eine oder mehrere Handlerfunktionen haben, die ausgeführt werden, wenn die Weiterleitung abgeglichen wird. - -Weiterleitungsdefinitionen haben die folgende Struktur: - -```js -app.METHOD(PATH, HANDLER) -``` - -Where: - -- `app` ist eine Instanz von `express`. -- `METHOD` is an [HTTP request method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods), in lowercase. -- `PATH` ist ein Pfad auf dem Server. -- `HANDLER` ist die Funktion, die ausgeführt wird, wenn die Weiterleitung abgeglichen wird. - -
-In diesem Lernprogramm wird vorausgesetzt, dass eine Instanz von `express` namens `app` erstellt und der Server ausgeführt wird. Wenn Sie mit dem Erstellen und Starten von Anwendungen nicht vertraut sind, spielen Sie das [Beispiel "Hello World"](/{{ page.lang }}/starter/hello-world.html) durch. -
- -Die folgenden Beispiele veranschaulichen das Definieren einfacher Weiterleitungen. - -Antworten Sie mit `Hello World!` auf der Homepage: - -```js -app.get('/', (req, res) => { - res.send('Hello World!') -}) -``` - -Respond to a POST request on the root route (`/`), the application's home page: - -```js -app.post('/', (req, res) => { - res.send('Got a POST request') -}) -``` - -Antworten Sie auf eine PUT-Anforderung zur Weiterleitung `/user`: - -```js -app.put('/user', (req, res) => { - res.send('Got a PUT request at /user') -}) -``` - -Antworten Sie auf eine DELETE-Anforderung zur Weiterleitung `/user`: - -```js -app.delete('/user', (req, res) => { - res.send('Got a DELETE request at /user') -}) -``` - -Details zum Thema Routing finden Sie in der entsprechenden [Routinganleitung](/{{ page.lang }}/guide/routing.html). - diff --git a/de/starter/examples.md b/de/starter/examples.md deleted file mode 100644 index 86700f94c3..0000000000 --- a/de/starter/examples.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -layout: page -title: Express examples -description: Explore a collection of Express.js application examples covering various use cases, integrations, and advanced configurations to help you learn and build your projects. -menu: starter -order: 6 -redirect_from: " " ---- - -{% capture examples %}{% include readmes/express-master/examples.md %}{% endcapture %} -{{ examples | replace: "](.", "](https://github.com/expressjs/express/tree/master/examples" }} - -## Additional examples - -These are some additional examples with more extensive integrations. - -{% include community-caveat.html %} - -- [prisma-fullstack](https://github.com/prisma/prisma-examples/tree/latest/pulse/fullstack-simple-chat) - Fullstack app with Express and Next.js using [Prisma](https://www.npmjs.com/package/prisma) as an ORM -- [prisma-rest-api-ts](https://github.com/prisma/prisma-examples/tree/latest/orm/express) - REST API with Express in TypeScript using [Prisma](https://www.npmjs.com/package/prisma) as an ORM \ No newline at end of file diff --git a/de/starter/faq.md b/de/starter/faq.md deleted file mode 100644 index 3769257e13..0000000000 --- a/de/starter/faq.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -layout: page -title: Häufig gestellte Fragen zu Express -description: Finden Sie Antworten auf häufig gestellte Fragen zu Express.js, darunter Themen wie Anwendungsstruktur, Models, Authentifizierung, Template-Engines, Fehlerbehandlung und mehr. -menu: starter -order: 7 -redirect_from: " " ---- - -# Häufig gestellte Fragen - -## Wie muss ich meine Anwendung strukturieren? - -Auf diese Frage gibt es keine verbindliche Antwort. Die Antwort hängt vom Umfang Ihrer Anwendung und dem eingebundenen Team ab. Um so flexibel wie möglich zu sein, gibt es bei Express keine Voraussetzungen hinsichtlich der Struktur. - -Weiterleitungen und andere anwendungsspezifische Logik können in einer beliebigen Anzahl von Dateien und in jeder von Ihnen bevorzugten Verzeichnisstruktur vorkommen. Die folgenden Beispiele sollen als Entscheidungshilfe dienen: - -- [Weiterleitungslisten](https://github.com/expressjs/express/blob/4.13.1/examples/route-separation/index.js#L32-47) -- [Weiterleitungszuordnung](https://github.com/expressjs/express/blob/4.13.1/examples/route-map/index.js#L52-L66) -- [Controller im MVC-Stil](https://github.com/expressjs/express/tree/master/examples/mvc) - -Darüber hinaus gibt es Erweiterungen anderer Anbieter für Express, die zur Vereinfachung einiger dieser Muster beitragen: - -- [Weiterleitung mit "express-resource"](https://github.com/expressjs/express-resource) - -## Wie definiere ich Modelle? - -Express hat keine Vorstellungen von einer Datenbank. Dieses Konzept bleibt Node-Modulen anderer Anbieter überlassen, wodurch Schnittstellen zu allen Datenbank möglich sind. - -[LoopBack](http://loopback.io) zeigt ein Express-basiertes Framework, um das Modelle angeordnet sind. - -## Wie kann ich Benutzer authentifizieren? - -Die Authentifizierung ist ein weiterer meinungsstarker Bereich, in den Express nicht eingreift. Sie können ein Authentifizierungsschema nach Ihren Vorstellungen verwenden. -Ein einfaches Benutzername/Kennwort-Schema können Sie in [diesem Beispiel](https://github.com/expressjs/express/tree/master/examples/auth) sehen. - -## Welche Template-Engines unterstützt Express? - -Express unterstützt jede Template-Engine, die der `(path, locals, callback)`-Signatur entspricht. -Informationen zur Normalisierung von Template-Engine-Schnittstellen und -Caching siehe das Projekt [consolidate.js](https://github.com/visionmedia/consolidate.js). Nicht aufgelistete Template-Engines können trotzdem die Express-Signatur unterstützen. - -For more information, see [Using template engines with Express](/{{page.lang}}/guide/using-template-engines.html). - -## Wie handhabe ich 404-Antworten? - -In Express sind 404-Antworten nicht das Ergebnis eines Fehlers, sodass diese Antworten von der Fehlerbehandlungsroutine nicht erfasst werden. Dieses Verhalten ist damit zu erklären, dass eine 404-Antwort einfach angibt, dass keine weiteren Arbeiten auszuführen sind. In anderen Worten: Express hat alle Middlewarefunktionen und Weiterleitungen ausgeführt und festgestellt, dass keine Funktion eine Antwort zurückgegeben hat. Sie müssen also bei der Handhabung der 404-Antwort nur eine Middlewarefunktion am Ende des Stacks (unterhalb von allen anderen Funktionen) hinzufügen: - -```js -app.use((req, res, next) => { - res.status(404).send("Sorry can't find that!") -}) -``` - -Add routes dynamically at runtime on an instance of `express.Router()` -so the routes are not superseded by a middleware function. - -## Wie richte ich eine Fehlerbehandlungsroutine ein? - -Middleware für die Fehlerbehandlung wird in derselben Weise definiert wie andere Middleware; außer dass sie vier anstatt drei Argumente aufweist. Dies gilt speziell bei der Signatur `(err, req, res, next)`: - -```js -app.use((err, req, res, next) => { - console.error(err.stack) - res.status(500).send('Something broke!') -}) -``` - -Weitere Informationen siehe [Fehlerbehandlung](/{{ page.lang }}/guide/error-handling.html). - -## Wie gebe ich normales HTML-Format aus? - -Das ist nicht Ihre Aufgabe! Sie müssen kein HTML-Format mit der Funktion `res.render()` ausgeben. -Verwenden Sie die Funktion `res.sendFile()`, wenn Sie es mit einer bestimmten Datei zu tun haben. -Wenn Sie viele Assets aus einem Verzeichnis bedienen müssen, verwenden Sie die Middlewarefunktion `express.static()`. - -## Welche Version von Node.js benötigt Express? - -- [Express 4.x](/{{ page.lang }}/4x/api.html) requires Node.js 0.10 or higher. -- [Express 5.x](/{{ page.lang }}/5x/api.html) requires Node.js 18 or higher. \ No newline at end of file diff --git a/de/starter/generator.md b/de/starter/generator.md deleted file mode 100644 index e0c0a551ac..0000000000 --- a/de/starter/generator.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -layout: page -title: Express-Anwendungsgenerator -description: Learn how to use the Express application generator tool to quickly create a skeleton for your Express.js applications, streamlining setup and configuration. -menu: starter -order: 3 -redirect_from: " " ---- - -# Express-Anwendungsgenerator - -Mit dem Application Generator Tool `express` können Sie innerhalb kürzester Zeit ein Anwendungsgerüst erstellen. - -You can run the application generator with the `npx` command (available in Node.js 8.2.0). - -```bash -$ npx express-generator -``` - -For earlier Node versions, install the application generator as a global npm package and then launch it: - -```bash -$ npm install -g express-generator -$ express -``` - -Zeigen Sie die Befehlsoptionen mit der Option `-h` an: - -```bash -$ express -h - - Usage: express [options] [dir] - - Options: - - -h, --help output usage information - --version output the version number - -e, --ejs add ejs engine support - --hbs add handlebars engine support - --pug add pug engine support - -H, --hogan add hogan.js engine support - --no-view generate without view engine - -v, --view add view support (ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade) - -c, --css add stylesheet support (less|stylus|compass|sass) (defaults to plain css) - --git add .gitignore - -f, --force force on non-empty directory -``` - -Im folgenden Beispiel wird eine Express-Anwendung mit dem Namen _myapp_ im aktuellen Arbeitsverzeichnis erstellt: The app will be created in a folder named _myapp_ in the current working directory and the view engine will be set to Pug: - -```bash -$ express --view=pug myapp - - create : myapp - create : myapp/package.json - create : myapp/app.js - create : myapp/public - create : myapp/public/javascripts - create : myapp/public/images - create : myapp/routes - create : myapp/routes/index.js - create : myapp/routes/users.js - create : myapp/public/stylesheets - create : myapp/public/stylesheets/style.css - create : myapp/views - create : myapp/views/index.pug - create : myapp/views/layout.pug - create : myapp/views/error.pug - create : myapp/bin - create : myapp/bin/www -``` - -Installieren Sie dann Abhängigkeiten: - -```bash -$ cd myapp -$ npm install -``` - -Verwenden Sie unter Windows diesen Befehl: - -```bash -$ DEBUG=myapp:* npm start -``` - -Führen Sie unter MacOS oder Linux die Anwendung mit diesem Befehl aus: - -```bash -> set DEBUG=myapp:* & npm start -``` - -On Windows PowerShell, use this command: - -```bash -PS> $env:DEBUG='myapp:*'; npm start -``` - -Laden Sie dann `http://localhost:3000/` in Ihren Browser, um auf die Anwendung zuzugreifen. - -Die erstellte Anwendung hat die folgende Verzeichnisstruktur: - -```bash -. -├── app.js -├── bin -│ └── www -├── package.json -├── public -│ ├── images -│ ├── javascripts -│ └── stylesheets -│ └── style.css -├── routes -│ ├── index.js -│ └── users.js -└── views - ├── error.pug - ├── index.pug - └── layout.pug - -7 directories, 9 files -``` - -
-Die vom Generator erstellte Anwendungsstruktur ist nur eine der vielen Möglichkeiten, Express-Anwendungen zu strukturieren. Sie können diese Struktur verwenden oder sie an Ihre Anforderungen anpassen. -
\ No newline at end of file diff --git a/de/starter/hello-world.md b/de/starter/hello-world.md deleted file mode 100644 index ea21ea36ff..0000000000 --- a/de/starter/hello-world.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -layout: page -title: Beispiel "Hello World" in Express -description: Get started with Express.js by building a simple 'Hello World' application, demonstrating the basic setup and server creation for beginners. -menu: starter -order: 2 -redirect_from: " " ---- - -# Beispiel "Hello World" - -
-Dies ist wohl die einfachste Express-Anwendung, die Sie erstellen können. Es handelt sich um eine Anwendung mit nur einer Datei und — *nicht* das, was Sie mit dem [Express Generator](/{{ page.lang }}/starter/generator.html) erhalten würden. Mit dem Generator würde das Gerüst für eine vollständige Anwendung mit zahlreichen JavaScript-Dateien, Jade-Vorlagen und Unterverzeichnissen für verschiedene Zwecke erstellt werden. -
- -```js -const express = require('express') -const app = express() -const port = 3000 - -app.get('/', (req, res) => { - res.send('Hello World!') -}) - -app.listen(port, () => { - console.log(`Example app listening on port ${port}`) -}) -``` - -Die Anwendung startet einen Server und ist an Port 3000 empfangsbereit für Verbindungen. Die Anwendung antwortet mit "Hello World!" auf Anforderungen zur Stamm-URL (`/`) oder zu _route_. Bei jedem anderen Pfad lautet die Antwort **404 Not Found**. - -### Running Locally - -Erstellen Sie zunächst ein Verzeichnis namens `myapp`, wechseln Sie in das Verzeichnis und führen Sie `npm init` aus. Installieren Sie dann `express` als Abhängigkeit, wie im [Installationshandbuch](/{{ page.lang }}/starter/installing.html) beschrieben. - -Erstellen Sie im Verzeichnis `myapp` eine Datei namens `app.js` und fügen Sie den folgenden Code hinzu: - -
`req` (Anforderung) und `res` (Antwort) sind genau dieselben Objekte, die Node bereitstellt. Sie können also `req.pipe()`, `req.on('data', callback)` und alle anderen Tasks, die Sie ausführen wollen, ohne Express ausführen.
- -Führen Sie die Anwendung mit dem folgenden Befehl aus: - -```bash -$ node app.js -``` - -Laden Sie dann [http://localhost:3000/](http://localhost:3000/) in einen Browser, um die Ausgabe zu sehen. \ No newline at end of file diff --git a/de/starter/installing.md b/de/starter/installing.md deleted file mode 100644 index 7e2b55fc4b..0000000000 --- a/de/starter/installing.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -layout: page -title: Express installieren -description: Erfahren Sie, wie Sie Express.js in Ihrer Node.js-Umgebung installieren, wie Sie Ihr Projektverzeichnis aufsetzen und Abhängigkeiten mit npm verwalten. -menu: starter -order: 1 -redirect_from: " " ---- - -# Installation - -Angenommen, Sie haben [Node.js](https://nodejs.org/) bereits installiert. Erstellen Sie ein Verzeichnis für Ihre Anwendung und definieren Sie dieses Verzeichnis als Ihr Arbeitsverzeichnis. - -- [Express 4.x](/{{ page.lang }}/4x/api.html) requires Node.js 0.10 or higher. -- [Express 5.x](/{{ page.lang }}/5x/api.html) requires Node.js 18 or higher. - -```bash -$ mkdir myapp -$ cd myapp -``` - -Erstellen Sie mit dem Befehl `npm init` eine Datei namens `package.json` für Ihre Anwendung. -Weitere Informationen zur Funktionsweise von `package.json` finden Sie in den [Angaben zur Handhabung der npm-Datei package.json](https://docs.npmjs.com/files/package.json). - -```bash -$ npm init -``` - -Dieser Befehl fordert Sie zur Eingabe verschiedener Angaben wie Name und Version Ihrer Anwendung auf. -For now, you can simply hit RETURN to accept the defaults for most of them, with the following exception: - -``` -entry point: (index.js) -``` - -Geben Sie `app.js` oder einen Namen Ihrer Vorstellung als Namen für die Hauptdatei ein. Wenn dieser Name `index.js` lauten soll, drücken Sie die Eingabetaste, um den vorgeschlagenen Standarddateinamen zu akzeptieren. - -Installieren Sie jetzt Express im Verzeichnis `myapp` und speichern Sie es in der Abhängigkeitsliste. Beispiel: - -```bash -$ npm install express -``` - -Wenn Sie Express vorübergehend installieren und nicht zur Abhängigkeitsliste hinzufügen wollen, geben Sie die Option `--save` nicht an: - -```bash -$ npm install express --no-save -``` - -
-Node-Module, die mit der Option `--save` installiert werden, werden zur `Abhängigkeitsliste` in der Datei `package.json` hinzugefügt. Danach werden bei der Ausführung von `npm install` im Verzeichnis `app` automatisch alle Module in der Abhängigkeitsliste installiert. -
\ No newline at end of file diff --git a/de/starter/static-files.md b/de/starter/static-files.md deleted file mode 100644 index 79258d1360..0000000000 --- a/de/starter/static-files.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -layout: page -title: Statische Dateien in Express bereitstellen -description: Understand how to serve static files like images, CSS, and JavaScript in Express.js applications using the built-in 'static' middleware. -menu: starter -order: 5 -redirect_from: " " ---- - -# Statische Dateien in Express bereitstellen - -Wenn Sie statische Dateien wie Bilder, CSS-Dateien und JavaScript-Dateien bereitstellen wollen, verwenden Sie die in Express integrierte Middlewarefunktion `express.static`. - -The function signature is: - -```js -express.static(root, [options]) -``` - -The `root` argument specifies the root directory from which to serve static assets. -For more information on the `options` argument, see [express.static](/{{page.lang}}/5x/api.html#express.static). - -Beispiel: Verwenden Sie den folgenden Code, um Bilder, CSS-Dateien und JavaScript-Dateien in einem Verzeichnis namens `public` bereitzustellen: - -```js -app.use(express.static('public')) -``` - -Jetzt können Sie die Dateien laden, die sich im Verzeichnis `public` befinden: - -```text -http://localhost:3000/images/kitten.jpg -http://localhost:3000/css/style.css -http://localhost:3000/js/app.js -http://localhost:3000/images/bg.png -http://localhost:3000/hello.html -``` - -
Express sucht nach den Dateien, die sich auf das Verzeichnis mit den statischen Assets beziehen. Der Name dieses Verzeichnisses ist also nicht Teil der URL.
- -Wenn Sie mehrere Verzeichnisse mit statischen Assets verwenden wollen, rufen Sie die Middlewarefunktion `express.static` mehrmals auf: - -```js -app.use(express.static('public')) -app.use(express.static('files')) -``` - -Express sucht in der Reihenfolge nach den Dateien, in der sie die Verzeichnisse mit den statischen Assets über die Middlewarefunktion `express.static` festgelegt haben. - -{% capture alert_content %} -For best results, [use a reverse proxy](/{{page.lang}}/advanced/best-practice-performance.html#use-a-reverse-proxy) cache to improve performance of serving static assets. -{% endcapture %} -{% include admonitions/note.html content=alert_content %} - -To create a virtual path prefix (where the path does not actually exist in the file system) for files that are served by the `express.static` function, [specify a mount path](/{{ page.lang }}/5x/api.html#app.use) for the static directory, as shown below: - -```js -app.use('/static', express.static('public')) -``` - -Jetzt können Sie die Dateien, die sich im Verzeichnis `public` befinden, aus dem Pfadpräfix `/static` laden. - -```text -http://localhost:3000/static/images/kitten.jpg -http://localhost:3000/static/css/style.css -http://localhost:3000/static/js/app.js -http://localhost:3000/static/images/bg.png -http://localhost:3000/static/hello.html -``` - -Der Pfad, den Sie für die Funktion `express.static` angeben, ist jedoch relativ zum Verzeichnis, aus dem Sie Ihren Prozess `node` starten. Wenn Sie die Express-Anwendung aus einem anderen Verzeichnis ausführen, ist es sicherer, den absoluten Pfad des Verzeichnisses zu verwenden, das Sie bereitstellen wollen: - -```js -const path = require('path') -app.use('/static', express.static(path.join(__dirname, 'public'))) -``` - -For more details about the `serve-static` function and its options, see [serve-static](/resources/middleware/serve-static.html). \ No newline at end of file diff --git a/de/support/index.md b/de/support/index.md deleted file mode 100644 index 3b0c7ad325..0000000000 --- a/de/support/index.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -layout: page -title: Version-Support -description: Finden Sie Informationen über den Support-Zeitplan für verschiedene Express.js Versionen, einschließlich der aktuellen Versionen und der End-of-Life-Richtlinien. -menu: support ---- - -# Version-Support - -Es wird nur die neueste Version einer bestimmten Hauptversionslinie unterstützt. - -Versionen, die EOL (End-of-Life) sind, _können_ Updates für kritische Sicherheitslücken erhalten, aber das Express-Team bietet keine Garantie und plant nicht, Fixes für gefundene Fehler zu entwickeln oder zu veröffentlichen. - -| Major Version | Minimale Node.js Version | Support Startdatum | Support-Enddatum | -| ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | ------------------ | ---------------------------------------------------------------- | -| [**v5.x**{: .supported }](/{{page.lang}}/5x/api.html){: .ignore-underline} | 18 | September 2024 | **läuft derzeit**{: .supported } | -| [**v4.x**{: .supported }](/{{page.lang}}/4x/api.html){: .ignore-underline} | 0.10.0 | April 2014 | **läuft derzeit**{: .supported } | -| [**v3.x**{: .eol }](/{{page.lang}}/3x/api.html){: .ignore-underline} | 0.8.0 | Oktober 2012 | Juli 2015 | -| [**v2.x**{: .eol }](/2x/){: .ignore-underline} | 0.4.1 | März 2011 | Juli 2012 | -| **v1.x**{: .eol } | 0.2.0 | Dezember 2010 | März 2011 | -| **v0.14.x**{: .eol } | 0.1.98 | Dezember 2010 | Dezember 2010 | - -## Kommerzielle Support Optionen - -Wenn Sie nicht auf eine unterstützte Version von Express aktualisieren können, wenden Sie sich bitte an einen unserer Partner, um Sicherheitsaktualisierungen zu erhalten: - -- [HeroDevs Never-Ending Support](http://www.herodevs.com/support/express-nes?utm_source=expressjs&utm_medium=link&utm_campaign=express_eol_page) diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 0000000000..e3d5d0bda6 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,182 @@ +# Configuration + +## Menu + +The navigation menus are configured in `src/config/menu/`. The type definitions are in `src/config/types.ts`. + +### Menu Structure + +Menus are composed of **sections** and **items**: + +``` +Menu +├── sections[] # Groups of menu items +│ ├── title? # Optional section header +│ ├── basePath? # Base path prepended to all item hrefs +│ ├── omitFrom? # Versions to exclude this section from +│ └── items[] # Menu items in this section +└── items[] # Alternative: flat list of items (no sections) +``` + +### Configuration Files + +| File | Purpose | +| --------------- | ------------------------------------------- | +| `main.ts` | Top-level navigation (Docs, API, Resources) | +| `docs.ts` | Documentation sidebar menu | +| `api.ts` | API reference sidebar menu | +| `resources.ts` | Resources section menu | +| `middleware.ts` | Middleware submenu | + +### Adding Menu Items + +Basic menu item with a link: + +```typescript +{ href: `/guide/routing`, label: 'Routing', ariaLabel: 'Routing guide' } +``` + +Menu item with a submenu: + +```typescript +{ + label: 'Properties', + ariaLabel: 'Application properties', + submenu: { + items: [ + { href: `/api/application/app-locals`, label: 'app.locals' }, + { href: `/api/application/app-mountpath`, label: 'app.mountpath' }, + ], + }, +} +``` + +### Adding Sections + +Sections group related menu items with an optional title: + +```typescript +{ + title: 'Getting started', + items: [ + { href: `/starter/installing`, label: 'Installing' }, + { href: `/starter/hello-world`, label: 'Hello world' }, + ], +} +``` + +### Version Specific Menus + +**Enabling versioning on a submenu** — Use `versioned` to specify which versions the menu supports. The version prefix will be automatically prepended to URLs: + +```typescript +{ + label: 'API Reference', + submenu: { + versioned: ['5x', '4x', '3x'], // Supports all three versions + sections: apiMenu.sections, + }, +} +``` + +**Omitting items from specific versions** — Use `omitFrom` to hide an item in certain versions: + +```typescript +{ + href: `/api/application/app-mountpath`, + label: 'app.mountpath', + omitFrom: ['3x'], // Not available in Express 3.x +} +``` + +**Omitting entire sections from specific versions**: + +```typescript +{ + title: 'Router', + omitFrom: ['3x'], // Router section doesn't exist in 3.x + items: [ + { href: `/api/router/overview`, label: 'Overview' }, + ], +} +``` + +## Announcement Bar + +The announcement bar is a banner displayed at the top of the homepage. It supports two modes: **automatic** (latest blog post) and **custom** (manual configuration). + +The announcement bar is configured via `src/config/announcement.json`: + +```json +{ + "title": "", + "description": "", + "link": "", + "variant": "info", + "startDate": "", + "endDate": "" +} +``` + +### Fields + +| Field | Type | Description | +| ------------- | -------- | ---------------------------------------------------------------------------------------------------- | +| `title` | `string` | Banner title. If set, the custom announcement is used instead of the blog. | +| `description` | `string` | Body text displayed below the title. | +| `link` | `string` | Optional URL for a call-to-action link. The link text is managed via i18n (`announcement.readMore`). | +| `variant` | `string` | Visual style: `"info"` (blue) or `"warning"` (orange/red). Defaults to `"info"`. | +| `startDate` | `string` | ISO date (`YYYY-MM-DD`). Banner is hidden before this date. If empty, shows immediately. | +| `endDate` | `string` | ISO date (`YYYY-MM-DD`). Banner is hidden after this date. If empty, shows indefinitely. | + +### Custom Announcement + +When `title` has a value, the banner displays the custom announcement with the configured fields. This takes priority over the blog fallback. + +**Example — time-limited announcement:** + +```json +{ + "title": "Express@5.1.0: Now the Default on npm", + "description": "Express 5.1.0 is now the default on npm with an official LTS schedule.", + "link": "/en/blog/v5-1-latest-release", + "variant": "info", + "startDate": "2026-03-01", + "endDate": "2026-05-31" +} +``` + +**Example — persistent warning:** + +```json +{ + "title": "Critical Security Update", + "description": "Please upgrade path-to-regexp to the latest version.", + "link": "/en/blog/2026-03-30-security-releases", + "variant": "warning", + "startDate": "", + "endDate": "" +} +``` + +### Latest Blog (Default) + +When `title` is empty, the banner automatically displays the most recent blog post. In this mode: + +- The **title** and **description** are pulled from the blog post's frontmatter. +- A **"Read more"** link points to the full blog post. +- The banner is **visible for 1 month** after the blog's publication date (derived from the filename, e.g., `2026-03-30-security-releases.mdx` → starts `2026-03-30`, ends `2026-04-30`). +- If the blog post has the `security` tag, the banner uses the `warning` variant. Otherwise, it uses `info`. + +To reset the announcement bar to this default behavior, clear the `title` field: + +```json +{ + "title": "", + "description": "", + "link": "", + "variant": "info", + "startDate": "", + "endDate": "" +} +``` diff --git a/docs/content.md b/docs/content.md new file mode 100644 index 0000000000..9be6e6cb65 --- /dev/null +++ b/docs/content.md @@ -0,0 +1,210 @@ +# Content + +The site uses Astro's [Content Collections](https://docs.astro.build/en/guides/content-collections/) to manage documentation and resources. The configuration is defined in `src/content.config.ts`. + +## Collections + +| Collection | Location | Description | +| ---------- | -------------------- | -------------------------------------------------------------- | +| `docs` | `src/content/docs/` | Versioned documentation pages (guides) | +| `api` | `src/content/api/` | Versioned API reference documentation | +| `pages` | `src/content/pages/` | Global pages: non-versioned docs, resources, support, and more | +| `blog` | `src/content/blog/` | Blog posts | + +> [!NOTE] +> For a guide on writing and publishing blog posts, see [How to write a blog post](https://expressjs.com/en/blog/write-post). + +## Frontmatter Schema + +Each content file requires frontmatter with the following properties: + +```yaml +--- +title: 'Page Title' # Required: The page title +description: 'Description' # Optional: Page description for SEO +--- +``` + +## Using Components in Content (MDX) + +Content files that need to use interactive components must use the `.mdx` extension instead of `.md`. MDX allows you to import and use Astro components directly inside your markdown content. + +### Available Components + +| Component | Import | Description | +| --------- | -------------------------------------------------------------- | ----------------------------------------------------------- | +| `Alert` | `import Alert from '@components/primitives/Alert/Alert.astro'` | Contextual alerts with `info`, `warning`, and `alert` types | + +### Example + +```mdx +--- +title: My page title +description: A page with alerts +--- + +import Alert from '@components/primitives/Alert/Alert.astro'; + +Regular markdown content here. + +This is an informational note. + +This is a warning message. + +This is a critical alert. +``` + +> Component imports must be placed **after** the frontmatter block and **before** the content where they are used. Do not place imports inside the frontmatter (`---`) block. + +## API Reference + +The API reference lives in its own collection at `src/content/api/`, organized by version. Unlike `docs`, the API collection is not language-scoped — it is only available in English. + +### Structure + +``` +src/content/api/ +├── 3x/ +│ ├── api.mdx # API overview +│ └── api/ +│ ├── application/ # express() and app methods +│ ├── middleware/ # Built-in middleware +│ ├── request/ # req object +│ └── response/ # res object +├── 4x/ +│ ├── api.mdx +│ └── api/ +│ ├── application/ +│ ├── express/ # express module-level methods +│ ├── middleware/ +│ ├── request/ +│ ├── response/ +│ └── router/ # Router (added in 4.x) +└── 5x/ + ├── api.mdx + └── api/ + ├── application/ + ├── middleware/ + ├── request/ + ├── response/ + └── router/ +``` + +### URL Patterns + +- Versioned: `/en/5x/api/application` → Express 5.x application API +- Non-versioned: `/en/api/application` → Defaults to Express 5.x + +## Versioning + +The Express.js documentation supports multiple versions. Content is organized by version in both the `docs` and `api` collections. + +### Version Structure + +``` +src/content/docs/ +└── en/ + ├── 3x/ # Express 3.x documentation + ├── 4x/ # Express 4.x documentation + └── 5x/ # Express 5.x documentation (default) +``` + +### How Versioning Works + +- **Default Version**: `5x` is the current default version +- **Supported Versions**: `5x`, `4x`, `3x` +- **URL Patterns**: + - Versioned: `/en/5x/api/` → Express 5.x API docs + - Non-versioned: `/en/api/` → Defaults to Express 5.x + +### Adding Version Specific Content + +1. Create your content file in the appropriate version directory: + + ``` + src/content/docs/en/5x/guide/my-new-page.md + ``` + +2. The versioning system (configured in `src/pages/[lang]/[...slug].astro`) will automatically: + - Serve the page at `/en/5x/guide/my-new-page` + - Create non-versioned aliases for default version content + +### Adding a New Version + +To add support for a new Express version (e.g., `6x`): + +1. **Create content directories:** + - `src/content/docs/en/6x/` — Guides and documentation + - `src/content/api/6x/` — API reference + +2. **Update the version type** in `src/config/types.ts`: + + ```typescript + export type VersionPrefix = '6x' | '5x' | '4x' | '3x'; + ``` + +3. **Update default version and prefixes** in `src/pages/[lang]/[...slug].astro`: + + ```typescript + const DEFAULT_VERSION = '6x'; + const VERSION_PREFIXES = ['6x', '5x', '4x', '3x']; + ``` + +4. **Update the sidebar** in `src/components/patterns/Sidebar/Sidebar.astro`: + - Add the new version to the `versions` array + - Update `defaultVersion` + +5. **Update menu versioning** in `src/config/menu/main.ts`: + - Add `'6x'` to the `versioned` arrays + +6. **Review `omitFrom` entries** in `src/config/menu/api.ts`: + - Determine which API features exist or are deprecated in the new version + +### Global Pages (Non-Versioned) + +Some documentation pages are shared across all versions and don't belong to a specific Express version (e.g., security updates, migration guides, performance best practices). These pages live in the `pages` collection instead of `docs`. + +#### Structure + +``` +src/content/pages/ +└── en/ + ├── advanced/ + │ ├── best-practice-performance.md + │ ├── best-practice-security.mdx + │ ├── healthcheck-graceful-shutdown.md + │ └── security-updates.mdx + ├── guide/ + │ ├── database-integration.mdx + │ ├── migrating-4.mdx + │ └── migrating-5.mdx + ├── resources/ + │ ├── community.md + │ ├── contributing.md + │ ├── glossary.mdx + │ ├── utils.md + └── support.md +``` + +Pages with a slug starting with `resources/` are treated as resource pages and won't show "Docs" in the breadcrumbs. + +#### Adding a Global Page + +1. Create your content file in `src/content/pages/`: + + ``` + src/content/pages/en/guide/my-global-page.md + ``` + +2. Add the menu item in `src/config/menu/docs.ts` with `global: true`: + + ```typescript + { + href: `/guide/my-global-page`, + label: 'menu.myGlobalPage', + ariaLabel: 'menu.myGlobalPageAria', + global: true, + } + ``` + + The `global: true` flag tells the sidebar to generate the link without a version prefix and prevents the version switcher from modifying its URL. diff --git a/docs/design-system.md b/docs/design-system.md new file mode 100644 index 0000000000..ea6d7bde81 --- /dev/null +++ b/docs/design-system.md @@ -0,0 +1,93 @@ +# Design System + +Primitive components, tokens, and patterns used across the Express.js website. + +All components are imported from `@components/primitives`. + +## Typography + +**Fonts:** Helvetica Neue for body and headings, JetBrains Mono for code, Inter (bold) for OG images. + +**Headings:** `H1` (page titles), `H2` (sections), `H3` (subsections), `H4` (minor), `H5` (lead text). + +**Body:** `Body` (default), `BodyMd` (secondary content), `BodySm` (captions/metadata), `BodyXs` (fine print). + +**Code:** `Code` for inline code blocks. + +**Weights:** `light` (300), `normal` (400), `medium` (500), `semibold` (600), `bold` (700). + +All typography components support `as` prop for polymorphic rendering (e.g., `

`). + +## Iconography + +Uses `astro-icon` with [Fluent Icons](https://icon-sets.iconify.design/fluent/) (prefix `fluent:`). + +```astro +import {Icon} from "astro-icon/components"; + +``` + +## Color Tokens + +**Primitives:** `--white`, `--black`, `--gray-{50-950}`, `--sky-{50,500,700,800}`, `--green-{50,300,500,700,900}`, `--amber-{50,300,500,600,700,900}`, `--red-{50,300,500,600,700,900}`. + +**Semantic (light / dark):** + +| Token | Light | Dark | +| -------------------------- | --------------------- | --------------------- | +| `--color-bg-primary` | `--white` #FFFFFF | `--black` #000000 | +| `--color-bg-secondary` | `--gray-50` #FAFAFA | `--gray-900` #141414 | +| `--color-bg-tertiary` | `--gray-150` #EEEEEE | `--gray-800` #262626 | +| `--color-bg-inverse` | `--gray-900` #141414 | `--gray-100` #F6F6F6 | +| `--color-bg-mute` | `--gray-200` #E5E5E5 | `--gray-600` #525252 | +| `--color-bg-success` | `--green-50` #F0FDF4 | `--green-950` #052E16 | +| `--color-bg-warning` | `--amber-50` #FFFBEB | `--amber-950` #451A03 | +| `--color-bg-error` | `--red-50` #FEF2F2 | `--red-950` #450A0A | +| `--color-text-primary` | `--gray-900` #141414 | `--gray-100` #F6F6F6 | +| `--color-text-secondary` | `--gray-600` #525252 | `--gray-150` #EEEEEE | +| `--color-text-tertiary` | `--gray-500` #737373 | `--gray-500` #737373 | +| `--color-text-inverse` | `--gray-100` #F6F6F6 | `--gray-900` #141414 | +| `--color-text-mute` | `--gray-500` #737373 | `--gray-500` #737373 | +| `--color-text-success` | `--green-700` #15803D | `--green-400` #4ADE80 | +| `--color-text-warning` | `--amber-700` #B45309 | `--amber-400` #FBBF24 | +| `--color-text-error` | `--red-700` #B91C1C | `--red-400` #F87171 | +| `--color-border-primary` | `--gray-300` #D4D4D4 | `--gray-500` #737373 | +| `--color-border-secondary` | `--gray-200` #E5E5E5 | `--gray-800` #262626 | +| `--color-border-tertiary` | `--gray-400` #A3A3A3 | `--gray-400` #A3A3A3 | +| `--color-border-inverse` | `--gray-700` #404040 | `--gray-200` #E5E5E5 | +| `--color-border-mute` | `--gray-300` #D4D4D4 | `--gray-700` #404040 | +| `--color-border-success` | `--green-200` #BBF7D0 | `--green-800` #166534 | +| `--color-border-warning` | `--amber-200` #FDE68A | `--amber-800` #92400E | +| `--color-border-error` | `--red-200` #FECACA | `--red-800` #991B1B | + +Colors are defined using OKLCH color space with `light-dark()` for theme switching. + +## Spacing + +`--space-{0.5,1,2,3,4,6,8,12,16}` mapping to `2px` through `64px`. + +## Type Scale + +`--font-size-{xs,sm,base,lg,xl,2xl,3xl,4xl,5xl,6xl}` mapping to `12px` through `54px`. + +## Buttons + +` +

+
+
+ {`${alt} +
+ +
+ diff --git a/src/components/patterns/LogoDisplay/LogoDisplay.css b/src/components/patterns/LogoDisplay/LogoDisplay.css new file mode 100644 index 0000000000..553ab0f327 --- /dev/null +++ b/src/components/patterns/LogoDisplay/LogoDisplay.css @@ -0,0 +1,42 @@ +.logo-display { + display: flex; + gap: 1.5rem; + flex-wrap: wrap; +} + +.logo-display__card { + display: flex; + flex-direction: column; + align-items: stretch; + border: 1px solid var(--color-border-primary); + border-radius: 8px; + overflow: hidden; +} + +.logo-display__preview { + display: flex; + align-items: center; + justify-content: center; + padding: 2rem; +} + +.logo-display__preview--light-bg { + background: #fff; +} + +.logo-display__preview--dark-bg { + background: #1a1a1a; +} + +.logo-display__img { + width: 200px; + aspect-ratio: 200 / 65; + object-fit: contain; +} + +.logo-display__card .btn { + border: none; + border-radius: 0; + text-align: center; + justify-content: center; +} diff --git a/src/components/patterns/MiddlewareInfo/MiddlewareInfo.astro b/src/components/patterns/MiddlewareInfo/MiddlewareInfo.astro new file mode 100644 index 0000000000..d1282d9678 --- /dev/null +++ b/src/components/patterns/MiddlewareInfo/MiddlewareInfo.astro @@ -0,0 +1,37 @@ +--- +/** + * MiddlewareInfo Component + * + * Displays version, links to GitHub, changelog, and npm for a middleware package. + */ +import './MiddlewareInfo.css'; +import { Icon } from 'astro-icon/components'; + +interface Props { + version: string; + github: string; + npm: string; +} + +const { version, github, npm } = Astro.props; +const changelog = `${github}/releases`; +--- + + diff --git a/src/components/patterns/MiddlewareInfo/MiddlewareInfo.css b/src/components/patterns/MiddlewareInfo/MiddlewareInfo.css new file mode 100644 index 0000000000..1965e393fd --- /dev/null +++ b/src/components/patterns/MiddlewareInfo/MiddlewareInfo.css @@ -0,0 +1,27 @@ +.middleware-info { + display: flex; + flex-wrap: wrap; + gap: 1.5rem; + padding: 0.75rem 0 1.5rem; + border-bottom: 1px solid var(--color-border-primary); + margin-bottom: 1.5rem; +} + +.middleware-info__item { + display: inline-flex; + align-items: center; + gap: 0.375rem; + font-size: var(--text-sm); + line-height: 1; + color: var(--color-text-secondary); + text-decoration: none; +} + +.middleware-info__item svg { + flex-shrink: 0; + vertical-align: middle; +} + +a.middleware-info__item:hover { + color: var(--color-text-primary); +} diff --git a/src/components/patterns/PageHead/PageHead.astro b/src/components/patterns/PageHead/PageHead.astro new file mode 100644 index 0000000000..c94fd21d11 --- /dev/null +++ b/src/components/patterns/PageHead/PageHead.astro @@ -0,0 +1,22 @@ +--- +import './PageHead.css'; +import { Flex, H1, Body } from '@components/primitives'; + +interface Props { + title: string; + description?: string; +} + +const { title, description } = Astro.props; +--- + + +

{title}

+ { + description && ( + + {description} + + ) + } +
diff --git a/src/components/patterns/PageHead/PageHead.css b/src/components/patterns/PageHead/PageHead.css new file mode 100644 index 0000000000..052913de1b --- /dev/null +++ b/src/components/patterns/PageHead/PageHead.css @@ -0,0 +1,14 @@ +@layer patterns { + .page-head { + margin: var(--space-10) 0; + + @media (--md-up) { + margin: var(--space-16) 0 var(--space-12); + } + } + + .page-head__description { + max-width: 68rem; + text-align: center; + } +} diff --git a/src/components/patterns/PageHead/index.ts b/src/components/patterns/PageHead/index.ts new file mode 100644 index 0000000000..364713a857 --- /dev/null +++ b/src/components/patterns/PageHead/index.ts @@ -0,0 +1,7 @@ +/** + * PageHead Component Index + * + * Export PageHead component + */ + +export { default } from './PageHead.astro'; diff --git a/src/components/patterns/PageTopbar/PageTopbar.astro b/src/components/patterns/PageTopbar/PageTopbar.astro new file mode 100644 index 0000000000..2f44597118 --- /dev/null +++ b/src/components/patterns/PageTopbar/PageTopbar.astro @@ -0,0 +1,32 @@ +--- +/** + * PageTopbar Pattern Component + * + * Renders a page-level topbar with breadcrumb navigation and an optional action (via slot). + * + * @example + * + * + * + */ +import Breadcrumbs from '@components/patterns/Breadcrumbs/Breadcrumbs.astro'; +import type { BreadcrumbItem } from '@utils/content'; +import './PageTopbar.css'; + +interface Props { + breadcrumbs: BreadcrumbItem[]; +} + +const { breadcrumbs } = Astro.props; +--- + +
+ + +
diff --git a/src/components/patterns/PageTopbar/PageTopbar.css b/src/components/patterns/PageTopbar/PageTopbar.css new file mode 100644 index 0000000000..205d8345aa --- /dev/null +++ b/src/components/patterns/PageTopbar/PageTopbar.css @@ -0,0 +1,15 @@ +@layer patterns { + .page-topbar { + padding: var(--space-6) 0; + display: flex; + justify-content: space-between; + align-items: center; + gap: var(--space-4); + + @media (--xs-only) { + flex-direction: column; + align-items: flex-start; + gap: var(--space-2); + } + } +} diff --git a/src/components/patterns/Pagination/Pagination.astro b/src/components/patterns/Pagination/Pagination.astro new file mode 100644 index 0000000000..c247315eaf --- /dev/null +++ b/src/components/patterns/Pagination/Pagination.astro @@ -0,0 +1,60 @@ +--- +/** + * Pagination Pattern Component + * + * Renders previous/next navigation with a current page indicator. + * + * @example + * + */ +import './Pagination.css'; +import { Icon } from 'astro-icon/components'; +import { Flex, Button, Body } from '@components/primitives'; +import type { HTMLAttributes } from 'astro/types'; + +interface Props extends HTMLAttributes<'div'> { + prevUrl?: string; + nextUrl?: string; + currentPage: number; + lastPage: number; +} + +const { prevUrl, nextUrl, currentPage, lastPage, ...rest } = Astro.props; +--- + + + { + prevUrl ? ( + + ) : ( + + ) + } + + Page {currentPage} of {lastPage} + + { + nextUrl ? ( + + ) : ( + + ) + } + diff --git a/src/components/patterns/Pagination/Pagination.css b/src/components/patterns/Pagination/Pagination.css new file mode 100644 index 0000000000..481094494d --- /dev/null +++ b/src/components/patterns/Pagination/Pagination.css @@ -0,0 +1,11 @@ +@layer patterns { + .pagination { + margin-top: var(--space-8); + } + + .pagination a[aria-disabled='true'] { + opacity: 0.6; + pointer-events: none; + cursor: not-allowed; + } +} diff --git a/src/components/patterns/PostCard/PostCard.astro b/src/components/patterns/PostCard/PostCard.astro new file mode 100644 index 0000000000..c764d893d6 --- /dev/null +++ b/src/components/patterns/PostCard/PostCard.astro @@ -0,0 +1,88 @@ +--- +/** + * PostCard Pattern Component + * + * An article card with a label, title, and author avatar(s). + * + * @example — Single author + * + * + * @example — Multiple authors + * + */ +import './PostCard.css'; +import type { HTMLAttributes } from 'astro/types'; +import { Image } from 'astro:assets'; +import { H3, Avatar, Tag } from '@components/primitives'; + +interface Author { + src: string; + alt?: string; + name: string; +} + +interface Props extends HTMLAttributes<'article'> { + href: string; + labels: string[]; + title: string; + coverSrc?: string; + coverAlt?: string; + authors: Author[]; + avatarCaption?: string; +} + +const { + href, + labels, + title, + coverSrc, + coverAlt = '', + authors, + avatarCaption, + class: className, + ...rest +} = Astro.props; +--- + + diff --git a/src/components/patterns/PostCard/PostCard.css b/src/components/patterns/PostCard/PostCard.css new file mode 100644 index 0000000000..42998d26dd --- /dev/null +++ b/src/components/patterns/PostCard/PostCard.css @@ -0,0 +1,51 @@ +@layer patterns { + .post-card { + display: flex; + flex-direction: column; + border: var(--border-width-1) solid var(--color-border-secondary); + border-radius: var(--radius-base); + gap: var(--space-4); + overflow: hidden; + padding: var(--space-4); + cursor: pointer; + transition: border-color 0.15s ease-in-out; + + &:hover { + border: var(--color-focus-ring) solid var(--border-width-1); + } + } + + .post-card__labels { + display: flex; + flex-wrap: wrap; + gap: var(--space-2); + margin-bottom: var(--space-4); + } + + .post-card__link { + display: flex; + flex-direction: column; + justify-content: space-between; + gap: inherit; + color: inherit; + text-decoration: none; + height: 100%; + + &:focus { + outline: none; + } + } + + .post-card:focus-within { + border: var(--color-focus-ring) solid var(--border-width-1); + } + + .post-card__cover { + margin-bottom: var(--space-3); + display: block; + width: 100%; + aspect-ratio: 1200 / 630; + object-fit: cover; + border-radius: var(--radius-base); + } +} diff --git a/src/components/patterns/PostHeader/PostHeader.astro b/src/components/patterns/PostHeader/PostHeader.astro new file mode 100644 index 0000000000..d274e90375 --- /dev/null +++ b/src/components/patterns/PostHeader/PostHeader.astro @@ -0,0 +1,165 @@ +--- +/** + * PostHeader Pattern Component + * + * A blog post header section with tags, title, author avatar, and share icons. + * + * @example — Single author + * + * + * @example — Multiple authors, no tags + * + */ +import './PostHeader.css'; +import type { HTMLAttributes } from 'astro/types'; +import { Image } from 'astro:assets'; +import { H1, Avatar, Tag, Flex } from '@components/primitives'; +import { Icon } from 'astro-icon/components'; +import { getLangFromUrl, useTranslations } from '@/i18n/utils'; + +interface Author { + src: string; + alt?: string; + name: string; +} + +interface Props extends HTMLAttributes<'div'> { + labels?: string[]; + title: string; + authors: Author[]; + date?: string; + cover?: string; +} + +const { labels = [], title, authors, date, cover, class: className, ...rest } = Astro.props; + +const lang = getLangFromUrl(Astro.url); +const t = useTranslations(lang); + +const pageUrl = Astro.url.href; +const encodedUrl = encodeURIComponent(pageUrl); +const encodedTitle = encodeURIComponent(title); + +const xShareUrl = `https://x.com/intent/tweet?text=${encodedTitle}&url=${encodedUrl}`; +const bskyShareUrl = `https://bsky.app/intent/compose?text=${encodedTitle}%20${encodedUrl}`; +const linkedinShareUrl = `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`; +const telegramShareUrl = `https://t.me/share/url?url=${encodedUrl}&text=${encodedTitle}`; +const whatsappShareUrl = `https://wa.me/?text=${encodedTitle}%20${encodedUrl}`; +--- + +
+ { + labels.length > 0 && ( + + {labels.map((label) => ( + {label} + ))} + + ) + } +

{title}

+ {authors.length > 0 && } + + + { + cover && ( +
+ +
+ ) + } +
+ + diff --git a/src/components/patterns/PostHeader/PostHeader.css b/src/components/patterns/PostHeader/PostHeader.css new file mode 100644 index 0000000000..a72562b14a --- /dev/null +++ b/src/components/patterns/PostHeader/PostHeader.css @@ -0,0 +1,78 @@ +@layer patterns { + .post-header { + padding: 0; + margin: var(--space-4) 0; + display: flex; + flex-direction: column; + gap: var(--space-6); + justify-content: center; + align-items: center; + + @media (--md-up) { + margin: var(--space-10) 0; + } + } + + .post-header__title { + text-align: center; + @media (--md-only) { + padding: 0 var(--space-8); + } + + @media (--lg-up) { + padding: 0 var(--space-16); + } + } + + .post-header__share { + display: flex; + align-items: center; + gap: var(--space-1); + } + + .post-header__share-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: var(--space-9); + height: var(--space-9); + border-radius: var(--radius-round); + color: var(--color-icon-primary); + background: none; + border: none; + cursor: pointer; + text-decoration: none; + transition: background-color 0.15s ease; + + &:hover { + background-color: var(--color-bg-secondary); + } + + &:focus-visible { + outline: var(--color-focus-ring) solid var(--border-width-2); + outline-offset: var(--border-width-1); + } + + & svg { + width: var(--space-4); + height: var(--space-4); + } + } + + .post-header__copy-btn--copied { + color: var(--color-success); + } + + .post-header__cover { + width: 100%; + max-width: 720px; + overflow: hidden; + border-radius: var(--radius-lg); + } + + .post-header__cover img { + width: 100%; + aspect-ratio: 1200 / 630; + object-fit: cover; + } +} diff --git a/src/components/patterns/RelatedContent/RelatedContent.astro b/src/components/patterns/RelatedContent/RelatedContent.astro new file mode 100644 index 0000000000..f8c931d41c --- /dev/null +++ b/src/components/patterns/RelatedContent/RelatedContent.astro @@ -0,0 +1,46 @@ +--- +import type { CollectionEntry } from 'astro:content'; +import './RelatedContent.css'; +import { H2 } from '@/components/primitives'; +import { PostCard } from '@/components/patterns'; + +interface Props { + posts: CollectionEntry<'blog'>[]; + lang: string; + title: string; +} + +const { posts, lang, title } = Astro.props; +--- + +{ + posts.length > 0 && ( + + ) +} diff --git a/src/components/patterns/RelatedContent/RelatedContent.css b/src/components/patterns/RelatedContent/RelatedContent.css new file mode 100644 index 0000000000..2ad1f57d27 --- /dev/null +++ b/src/components/patterns/RelatedContent/RelatedContent.css @@ -0,0 +1,48 @@ +@layer patterns { + .related-content { + margin-top: var(--space-24); + position: relative; + + h2 { + margin-bottom: var(--space-6); + } + + @media (--xs-only) { + margin-top: var(--space-16); + + &::after { + content: ''; + position: absolute; + inset: 0; + left: auto; + width: var(--space-16); + background: linear-gradient(to right, transparent, var(--color-bg-primary)); + pointer-events: none; + } + } + } + + .related-content__grid { + display: flex; + gap: var(--space-6); + + & > .post-card { + flex: 1 1 33.33%; + } + + @media (--xs-only) { + overflow-x: auto; + scroll-snap-type: x mandatory; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; + &::-webkit-scrollbar { + display: none; + } + + & > .post-card { + flex: 1 0 var(--size-60); + scroll-snap-align: start; + } + } + } +} diff --git a/src/components/patterns/Searchbox/Chat.css b/src/components/patterns/Searchbox/Chat.css new file mode 100644 index 0000000000..29319ca0e3 --- /dev/null +++ b/src/components/patterns/Searchbox/Chat.css @@ -0,0 +1,228 @@ +@layer patterns { + .chat-container { + display: flex; + flex-direction: column; + flex-grow: 1; + overflow: hidden; + justify-content: space-between; + position: relative; + height: 100%; + } + + .chat-container-inner { + overflow: auto; + position: relative; + display: flex; + flex: 1; + flex-direction: column; + height: 100%; + + /* Firefox */ + scrollbar-width: thin; + scrollbar-color: var(--color-border-primary) transparent; + + /* Chrome / Safari / Edge */ + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-track { + background: transparent; + } + + &::-webkit-scrollbar-thumb { + background-color: var(--color-border-primary); + border-radius: 9999px; + } + } + + /* ============================================ + Chat Interactions (messages area) + ============================================ */ + + .chat-interactions { + flex: 1; + } + + .chat-interaction { + display: flex; + flex-direction: column; + padding: var(--space-6); + } + + .chat-user-prompt { + align-self: flex-end; + background-color: var(--color-bg-tertiary); + border: var(--border-width-1) solid var(--color-border-secondary); + border-radius: var(--radius-lg); + padding: var(--space-3) var(--space-4); + font-size: var(--font-size-base); + color: var(--color-text-primary); + max-width: 80%; + } + + .chat-assistant-message { + ol { + list-style: decimal; + } + + ul { + list-style: disc; + } + } + + .chat-title { + font-size: var(--font-size-2xl); + margin: var(--space-4) 0; + } + + .chat-code-block { + padding: 0; + } + + .chat-loading { + display: flex; + align-items: center; + } + + .chat-typing-dots { + display: flex; + align-items: flex-end; + gap: var(--space-1); + padding: var(--space-1) 0; + + & span { + margin: 0; + } + + & .chat-typing-label { + display: block; + color: var(--color-text-tertiary); + font-size: var(--font-size-sm); + margin-right: var(--space-0-5); + line-height: 1; + } + + & span:not(.chat-typing-label) { + display: block; + width: var(--size-1); + height: var(--size-1); + border-radius: var(--radius-full); + background-color: var(--color-text-tertiary); + animation: chat-dot-bounce 1.2s ease-in-out infinite; + + &:nth-child(3) { + animation-delay: 0.2s; + } + &:nth-child(4) { + animation-delay: 0.4s; + } + } + } + + @keyframes chat-dot-bounce { + 0%, + 60%, + 100% { + transform: translateY(0); + opacity: 0.4; + } + 30% { + transform: translateY(-4px); + opacity: 1; + } + } + + .chat-error { + color: var(--color-text-danger, var(--color-text-secondary)); + font-size: var(--font-size-sm); + } + + /* ============================================ + Chat Input + ============================================ */ + + .chat-input-wrapper { + padding: var(--space-3) var(--space-4); + border-top: var(--border-width-1) solid var(--color-border-secondary); + background-color: var(--color-bg-secondary); + flex-shrink: 0; + display: flex; + align-items: center; + gap: var(--space-3); + position: sticky; + bottom: 0; + } + + .chat-input-icon { + color: var(--color-icon-primary); + flex-shrink: 0; + } + + .chat-input-field { + flex: 1; + background: none; + border: none; + outline: none; + font-size: var(--font-size-md); + color: var(--color-text-primary); + font-family: inherit; + resize: none; + line-height: var(--line-height-snug); + + &::placeholder { + color: var(--color-text-tertiary); + } + } + + .chat-input-button { + display: flex; + align-items: center; + justify-content: center; + width: var(--size-8); + height: var(--size-8); + border-radius: var(--radius-base); + border: none; + background-color: var(--color-bg-tertiary); + color: var(--color-text-primary); + cursor: pointer; + flex-shrink: 0; + transition: + background-color var(--duration-150) var(--ease-in-out), + color var(--duration-150) var(--ease-in-out); + + &:hover:not(:disabled) { + background-color: var(--color-bg-mute); + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + &:focus-visible { + outline: 1px solid var(--color-focus-ring); + } + } + + .chat-go-to-bottom { + position: absolute; + bottom: var(--space-16); + left: 50%; + transform: translateX(-50%); + display: inline-flex; + align-items: center; + justify-content: center; + padding: var(--space-2); + margin-bottom: var(--space-2); + background-color: var(--color-bg-mute); + color: var(--color-text-primary); + border-radius: var(--radius-full); + cursor: pointer; + transition: background-color var(--duration-300) var(--ease-in-out); + + &:hover { + background-color: var(--color-bg-secondary); + } + } +} diff --git a/src/components/patterns/Searchbox/Chat.tsx b/src/components/patterns/Searchbox/Chat.tsx new file mode 100644 index 0000000000..dd849be1ee --- /dev/null +++ b/src/components/patterns/Searchbox/Chat.tsx @@ -0,0 +1,91 @@ +import { ChatInteractions, PromptTextArea } from '@orama/ui/components'; +import { useChat } from '@orama/ui/hooks/useChat'; +import { useScrollableContainer } from '@orama/ui/hooks/useScrollableContainer'; +import ArrowDown16Filled from '~icons/fluent/arrow-down-16-filled'; +import Sparkle16Filled from '~icons/fluent/sparkle-16-filled'; +import PauseCircle48Regular from '~icons/fluent/pause-circle-48-regular'; +import Send16Filled from '~icons/fluent/send-16-filled'; +import ChatActions from './ChatActions'; +import './Chat.css'; +import { ChatSources } from './ChatSources'; + +export default function Chat() { + const { loading } = useChat({ throttle_delay: 50 }); + const { containerRef, scrollToBottom, recalculateGoToBottomButton, showGoToBottomButton } = + useScrollableContainer(); + + return ( +
+
+ scrollToBottom({ animated: true })} + > + {(interaction) => ( +
+ + {interaction.query} + + +
+ Thinking + + + +
+
+ + Something went wrong. Please try again. + + + + {interaction.response} + + +
+ )} +
+
+ + {showGoToBottomButton && ( + + )} + + + + + } + aria-label="Send message" + > + + + +
+ ); +} diff --git a/src/components/patterns/Searchbox/ChatActions.css b/src/components/patterns/Searchbox/ChatActions.css new file mode 100644 index 0000000000..ff55cbfcbf --- /dev/null +++ b/src/components/patterns/Searchbox/ChatActions.css @@ -0,0 +1,24 @@ +@layer patterns { + .chat-actions { + display: flex; + gap: var(--space-2); + margin: 0; + padding-left: 0; + justify-content: flex-end; + list-style: none; + } + + .chat-action { + cursor: pointer; + padding: var(--space-2); + border-radius: var(--radius-base); + + &:hover { + background-color: var(--color-bg-tertiary); + } + } + + .chat-action--disliked { + color: var(--color-text-mute); + } +} diff --git a/src/components/patterns/Searchbox/ChatActions.tsx b/src/components/patterns/Searchbox/ChatActions.tsx new file mode 100644 index 0000000000..997e13f9c5 --- /dev/null +++ b/src/components/patterns/Searchbox/ChatActions.tsx @@ -0,0 +1,60 @@ +import { ChatInteractions } from '@orama/ui/components'; +import ArrowSync20Regular from '~icons/fluent/arrow-sync-20-regular'; +import Checkmark16Regular from '~icons/fluent/checkmark-16-regular'; +import Copy16Regular from '~icons/fluent/copy-16-regular'; +import ThumbDislike16Regular from '~icons/fluent/thumb-dislike-16-regular'; +import './ChatActions.css'; +import { useState } from 'react'; + +interface ChatActionsProps { + interaction: Parameters[0]['children']>[0]; +} + +export default function ChatActions({ interaction }: ChatActionsProps) { + const [disliked, setDisliked] = useState(false); + + const dislikeMessage = () => { + setDisliked(true); + }; + + if (interaction.loading) { + return null; + } + + return ( +
    +
  • + + + +
  • +
  • + + {(copied: boolean) => + copied ? ( + <> + + Copied + + ) : ( + <> + + Copy + + ) + } + +
  • +
  • + +
  • +
+ ); +} diff --git a/src/components/patterns/Searchbox/ChatSources.css b/src/components/patterns/Searchbox/ChatSources.css new file mode 100644 index 0000000000..843e276c64 --- /dev/null +++ b/src/components/patterns/Searchbox/ChatSources.css @@ -0,0 +1,102 @@ +@layer patterns { + .chat-sources-scroll-container { + position: relative; + overflow: hidden; + + &::after { + content: ''; + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: var(--space-16); + pointer-events: none; + background: linear-gradient( + to right, + transparent, + color-mix(in oklch, var(--color-bg-primary) 15%, transparent) 20%, + color-mix(in oklch, var(--color-bg-primary) 40%, transparent) 40%, + color-mix(in oklch, var(--color-bg-primary) 70%, transparent) 65%, + var(--color-bg-primary) 90% + ); + } + } + + .chat-sources { + display: flex; + flex-wrap: nowrap; + align-items: stretch; + gap: var(--space-3); + margin-top: var(--space-8); + margin-bottom: 0; + overflow-x: scroll; + scroll-behavior: smooth; + -ms-overflow-style: none; + scrollbar-width: none; + padding-left: 0; + list-style: none; + + &::-webkit-scrollbar { + display: none; + } + } + + .chat-sources-item { + display: flex; + max-width: 100%; + height: 100%; + align-items: center; + gap: var(--space-2); + font-size: var(--font-size-base); + } + + .chat-sources-link { + display: block; + width: var(--space-64); + height: 100%; + border-radius: var(--radius-xl); + background-color: var(--color-bg-tertiary); + padding: var(--space-3); + color: var(--color-text-primary); + text-decoration: none; + cursor: pointer; + transition: var(--transition-colors); + + &:hover { + background-color: var(--color-bg-secondary); + } + + &:focus { + background-color: var(--color-bg-mute); + outline: none; + } + } + + .chat-sources-title { + display: flex; + align-items: center; + gap: var(--space-2); + font-size: var(--font-size-sm); + font-weight: var(--font-weight-semibold); + color: var(--color-text-secondary); + overflow: hidden; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; + margin: 0; + + svg { + flex-shrink: 0; + } + } + + .chat-sources-excerpt { + margin-top: var(--space-2); + margin-bottom: 0; + font-size: var(--font-size-sm); + color: var(--color-text-tertiary); + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + } +} diff --git a/src/components/patterns/Searchbox/ChatSources.tsx b/src/components/patterns/Searchbox/ChatSources.tsx new file mode 100644 index 0000000000..0aee5577e4 --- /dev/null +++ b/src/components/patterns/Searchbox/ChatSources.tsx @@ -0,0 +1,43 @@ +import type { Interaction, AnyObject } from '@orama/core'; +import { ChatInteractions } from '@orama/ui/components'; +import type { FC } from 'react'; +import Link20Regular from '~icons/fluent/link-20-regular'; +import './ChatSources.css'; + +type ChatSourcesProps = { + interaction: Interaction; +}; + +export const ChatSources: FC = ({ interaction }) => { + if (!interaction?.sources) { + return null; + } + + return ( +
+ + {(document: AnyObject, index: number) => ( + + )} + +
+ ); +}; diff --git a/src/components/patterns/Searchbox/Search.css b/src/components/patterns/Searchbox/Search.css new file mode 100644 index 0000000000..b91b58756d --- /dev/null +++ b/src/components/patterns/Searchbox/Search.css @@ -0,0 +1,324 @@ +@layer patterns { + /* ============================================ + Search Tab + ============================================ */ + + .search-tab-panel { + display: flex; + flex-direction: column; + overflow: hidden; + flex: 1; + min-height: 0; + } + + .search-tabs-scroll-container { + position: relative; + overflow: hidden; + + &::after { + content: ''; + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: var(--space-10); + pointer-events: none; + background: linear-gradient(to right, transparent, var(--color-bg-primary)); + } + } + + .search-tabs { + display: flex; + gap: var(--space-1); + overflow-x: auto; + scrollbar-width: none; + list-style: none; + + &::-webkit-scrollbar { + display: none; + } + } + + .search-tab { + font-size: var(--font-size-base); + color: var(--color-text-secondary); + background: none; + border-radius: var(--radius-base); + padding: var(--space-1-5) var(--space-3); + cursor: pointer; + transition: + color var(--duration-150) var(--ease-in-out), + background-color var(--duration-150) var(--ease-in-out); + border: var(--border-width-1) solid transparent; + display: flex; + align-items: center; + gap: var(--space-2); + + &:hover:not(.search-tab--selected) { + color: var(--color-text-primary); + background-color: var(--color-bg-secondary); + } + + &:focus-visible { + outline: 1px solid var(--color-focus-ring); + } + + &--selected, + &[data-state='active'] { + font-weight: var(--font-weight-semibold); + color: var(--color-text-primary); + background-color: var(--color-bg-tertiary); + border: var(--border-width-1) solid var(--color-border-tertiary); + } + } + + .search-tab-count { + margin: 0; + } + + .search-input-wrapper { + padding: var(--space-3) var(--space-4); + border-top: var(--border-width-1) solid var(--color-border-secondary); + background-color: var(--color-bg-secondary); + flex-shrink: 0; + display: flex; + align-items: center; + gap: var(--space-3); + width: 100%; + } + + .search-input-form { + display: flex; + align-items: center; + gap: var(--space-2); + } + + .search-ask-button { + display: flex; + align-items: center; + gap: var(--space-1-5); + padding: var(--space-1) var(--space-2-5); + border-radius: var(--radius-base); + border: var(--border-width-1) solid var(--color-border-secondary); + background-color: var(--color-bg-tertiary); + color: var(--color-text-secondary); + font-size: var(--font-size-sm); + font-family: inherit; + cursor: pointer; + flex-shrink: 0; + transition: + background-color var(--duration-150) var(--ease-in-out), + color var(--duration-150) var(--ease-in-out), + border-color var(--duration-150) var(--ease-in-out); + + svg { + color: var(--color-icon-primary); + } + + &:hover { + background-color: var(--color-bg-mute); + color: var(--color-text-primary); + } + + &:focus-visible { + outline: 1px solid var(--color-focus-ring); + } + + &:disabled { + opacity: 0.8; + cursor: not-allowed; + background-color: var(--color-bg-secondary); + color: var(--color-text-tertiary); + border-color: var(--color-border-secondary); + + svg { + color: var(--color-icon-secondary); + } + } + } + + .search-input-field { + width: 100%; + font-size: var(--font-size-md); + color: var(--color-text-primary); + flex-grow: 1; + background: none; + border: none; + outline: none; + font-family: inherit; + + &::placeholder { + color: var(--color-text-tertiary); + } + } + + .search-results-wrapper { + overflow-y: auto; + flex: 1; + min-height: 0; + padding: var(--space-6); + padding-top: var(--space-3); + + /* Firefox */ + scrollbar-width: thin; + scrollbar-color: var(--color-border-primary) transparent; + + /* Chrome / Safari / Edge */ + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-track { + background: transparent; + } + + &::-webkit-scrollbar-thumb { + background-color: var(--color-border-primary); + border-radius: 9999px; + } + } + + .search-no-results { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: var(--space-8) var(--space-4); + color: var(--color-text-secondary); + font-size: var(--font-size-sm); + } + + .search-loading { + display: flex; + flex-direction: column; + gap: var(--space-1); + padding: var(--space-6); + padding-top: var(--space-3); + } + + .search-skeleton-item { + display: flex; + align-items: center; + gap: var(--space-3); + padding: var(--space-4) var(--space-2); + } + + .search-skeleton-icon { + flex-shrink: 0; + width: 1.25rem; + height: 1.25rem; + border-radius: var(--radius-base); + background: linear-gradient( + 90deg, + var(--color-bg-tertiary) 25%, + var(--color-bg-secondary) 50%, + var(--color-bg-tertiary) 75% + ); + background-size: 400% 100%; + animation: search-shimmer 1.4s ease-in-out infinite; + } + + .search-skeleton-text { + display: flex; + flex-direction: column; + gap: var(--space-2); + flex: 1; + } + + .search-skeleton-line { + border-radius: var(--radius-full); + background: linear-gradient( + 90deg, + var(--color-bg-tertiary) 25%, + var(--color-bg-secondary) 50%, + var(--color-bg-tertiary) 75% + ); + background-size: 400% 100%; + animation: search-shimmer 1.4s ease-in-out infinite; + + &--title { + height: 0.75rem; + } + &--desc { + height: 0.625rem; + } + } + + @keyframes search-shimmer { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } + } + + .search-results-group-title { + font-size: var(--font-size-base); + color: var(--color-text-secondary); + text-transform: uppercase; + letter-spacing: var(--letter-spacing-wide); + margin: var(--space-4) 0; + } + + .search-results-list { + list-style: none; + margin: var(--space-4) 0 var(--space-6) 0; + padding: 0; + } + + .search-result-item { + border-radius: var(--radius-base); + overflow: hidden; + } + + .search-result-link { + display: flex; + align-items: center; + gap: var(--space-3); + padding: var(--space-4) var(--space-2); + border-radius: var(--radius-base); + color: var(--color-text-primary); + text-decoration: none; + transition: background-color var(--duration-150) var(--ease-in-out); + + svg { + flex-shrink: 0; + } + + &:hover, + &:focus-visible { + background-color: var(--color-bg-secondary); + outline: none; + } + + &:focus-visible { + outline: 1px solid var(--color-focus-ring); + } + } + + .search-result-description { + font-size: var(--font-size-base); + color: color-mix(in oklch, var(--color-text-primary) 80%, transparent); + letter-spacing: var(--letter-spacing-wide); + margin: var(--space-1) 0 0 0; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + } + + .search-result-breadcrumb { + font-size: var(--font-size-base); + font-weight: var(--font-weight-medium); + color: var(--color-text-tertiary); + margin: 0 0 var(--space-2) 0; + } + + .search-result-title { + font-size: var(--font-size-base); + color: var(--color-text-primary); + font-weight: var(--font-weight-medium); + margin: 0; + } +} diff --git a/src/components/patterns/Searchbox/Search.tsx b/src/components/patterns/Searchbox/Search.tsx new file mode 100644 index 0000000000..0e4f59d8a4 --- /dev/null +++ b/src/components/patterns/Searchbox/Search.tsx @@ -0,0 +1,186 @@ +import { useEffect, useRef, useState } from 'react'; +import { FacetTabs, SearchInput, SearchResults } from '@orama/ui/components'; +import type { Hit } from '@orama/core'; +import SearchNoResults from './SearchNoResults'; +import './Search.css'; +import Document20Regular from '~icons/fluent/document-20-regular'; +import Search16Regular from '~icons/fluent/search-16-regular'; +import Sparkle16Filled from '~icons/fluent/sparkle-16-filled'; +import { useSearch } from '@orama/ui/hooks/useSearch'; +import { useTranslations } from '@/i18n/utils'; +import type { ui } from '@/i18n/locales'; +import { useChat } from '@orama/ui/hooks/useChat'; + +type DocDocument = { + title: string; + description?: string; + category?: string; + path?: string; + content?: string; + version?: string; +}; + +function getPathBreadcrumb(path: string, category: string, t: (key: string) => string): string { + const rootLabel = t(category); + const categorySlug = rootLabel.toLowerCase(); + + const segments = path + .split('/') + .filter(Boolean) + .slice(1) // remove language prefix (e.g., 'en') + .filter((s) => s !== categorySlug) // remove redundant category segment + .slice(0, -1) // remove last segment (the document page itself) + .map((s) => + s + .split('-') + .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) + .join(' ') + ); + + return [rootLabel, ...segments].join(' > '); +} + +interface SearchProps { + lang: string; + placeholder: string; + mode: 'search' | 'chat'; + onModeChange: (mode: 'search' | 'chat') => void; +} + +export default function Search({ lang, placeholder, mode, onModeChange }: SearchProps) { + const t = useTranslations(lang as keyof typeof ui); + const { + context: { searchTerm, selectedFacet }, + } = useSearch(); + const { ask } = useChat(); + const [lastChatTerm, setLastChatTerm] = useState(); + + const inputWrapperRef = useRef(null); + + useEffect(() => { + if (mode === 'search') { + const id = setTimeout(() => { + inputWrapperRef.current?.querySelector('input')?.focus(); + }, 50); + return () => clearTimeout(id); + } + }, [mode]); + + const enterChat = () => { + onModeChange('chat'); + setLastChatTerm(searchTerm); + ask({ query: searchTerm ?? '' }); + }; + + return ( + <> + + {[65, 80, 50].map((w) => ( +
+
+
+
+
+
+
+ ))} + + + +
+ + + {(group, isSelected) => ( + <> + + {t(group.name)} + ({group.count}) + + + )} + + +
+ + + {(group) => ( +
+

{t(group.name)}

+ + {(result: Hit) => { + const document = result.document as DocDocument; + return ( + + +
+

+ {getPathBreadcrumb(document?.path ?? '', document?.category ?? '', t)} +

+

{document?.title}

+ {document?.description && ( +

{document.description}

+ )} +
+
+ ); + }} +
+
+ )} +
+ + + + + + + + + + + + ); +} diff --git a/src/components/patterns/Searchbox/SearchFooter.css b/src/components/patterns/Searchbox/SearchFooter.css new file mode 100644 index 0000000000..3151386ebc --- /dev/null +++ b/src/components/patterns/Searchbox/SearchFooter.css @@ -0,0 +1,40 @@ +@layer patterns { + .search-footer-shortcuts { + display: none; + align-items: center; + gap: var(--space-3); + + span { + margin: 0; + } + + @media (--md-up) { + display: flex; + } + } + + .search-footer-shortcut { + display: flex; + align-items: center; + gap: var(--space-1-5); + font-size: var(--font-size-sm); + color: var(--color-text-primary); + } + + .search-footer-kbd { + display: inline-flex; + align-items: center; + justify-content: center; + font-family: var(--font-mono); + font-size: var(--font-size-sm); + background-color: var(--color-bg-secondary); + border-radius: var(--radius-md); + padding: var(--space-1); + } + + .search-footer-branding { + display: flex; + align-items: center; + gap: var(--space-2); + } +} diff --git a/src/components/patterns/Searchbox/SearchFooter.tsx b/src/components/patterns/Searchbox/SearchFooter.tsx new file mode 100644 index 0000000000..951f39d540 --- /dev/null +++ b/src/components/patterns/Searchbox/SearchFooter.tsx @@ -0,0 +1,41 @@ +import ArrowTurnDownRight48Regular from '~icons/fluent/arrow-turn-down-right-48-regular'; +import ArrowDown16Regular from '~icons/fluent/arrow-down-16-regular'; +import ArrowUp16Regular from '~icons/fluent/arrow-up-16-regular'; +import OramaLogoLight from '../../../icons/orama-logo-light.svg?react'; +import OramaLogoDark from '../../../icons/orama-logo-dark.svg?react'; +import './SearchFooter.css'; + +export default function SearchFooter() { + return ( + <> +
+
+ + + + select +
+
+ + + + + + + navigate +
+
+ esc + close +
+
+
+ Powered by + + + + +
+ + ); +} diff --git a/src/components/patterns/Searchbox/SearchNoResults.css b/src/components/patterns/Searchbox/SearchNoResults.css new file mode 100644 index 0000000000..b9960b535e --- /dev/null +++ b/src/components/patterns/Searchbox/SearchNoResults.css @@ -0,0 +1,55 @@ +@layer patterns { + .search-empty-image { + width: var(--size-36); + } + + :root[data-theme='light'] .search-empty-image--dark, + :root[data-theme='dark'] .search-empty-image--light { + display: none; + } + + .search-image-wrapper { + display: block; + text-align: center; + } + + .search-suggestions { + margin-top: var(--space-3); + + ul { + display: flex; + align-items: center; + justify-content: center; + gap: var(--space-3); + padding-left: 0; + flex-wrap: wrap; + list-style: none; + } + + li { + } + } + + .search-suggestion__item { + display: flex; + align-items: center; + gap: var(--space-2); + padding: var(--space-1-5) var(--space-3); + border-radius: var(--radius-base); + border: var(--border-width-1) solid var(--color-border-primary); + color: var(--color-text-primary); + cursor: pointer; + transition: + background-color 0.2s, + color 0.2s; + + &:hover { + background-color: var(--color-bg-tertiary); + color: var(--color-text-primary); + } + + svg { + color: var(--color-icon-primary); + } + } +} diff --git a/src/components/patterns/Searchbox/SearchNoResults.tsx b/src/components/patterns/Searchbox/SearchNoResults.tsx new file mode 100644 index 0000000000..6df01296f1 --- /dev/null +++ b/src/components/patterns/Searchbox/SearchNoResults.tsx @@ -0,0 +1,60 @@ +import { SearchResults, Suggestions } from '@orama/ui/components'; +import Sparkle20Filled from '~icons/fluent/sparkle-20-filled'; +import ExpressLogoWhite from '../../../icons/logo-express-white.svg?react'; +import ExpressLogoBlack from '../../../icons/logo-express-black.svg?react'; +import './SearchNoResults.css'; + +const SUGGESTIONS = [ + 'Can you give me a guide to install Express.js', + 'What are the advantages of version 5', + 'What are Express Superpowers', +]; + +interface SearchNoResultsProps { + onSuggestionClick?: (text: string) => void; +} + +export default function SearchNoResults({ onSuggestionClick }: SearchNoResultsProps) { + return ( + + {(searchTerm) => ( + <> + {searchTerm ? ( + + No results for “{searchTerm}” + + ) : ( + <> +
+ + +
+

What would you like to know about Express and its features?

+ +
    + {SUGGESTIONS.map((text) => ( +
  • + onSuggestionClick?.(text)} + className="search-suggestion__item" + > + + {text} + +
  • + ))} +
+
+ + )} + + )} +
+ ); +} diff --git a/src/components/patterns/Searchbox/Searchbox.css b/src/components/patterns/Searchbox/Searchbox.css new file mode 100644 index 0000000000..2db415419a --- /dev/null +++ b/src/components/patterns/Searchbox/Searchbox.css @@ -0,0 +1,280 @@ +@layer patterns { + .search-trigger { + display: flex; + align-items: center; + gap: var(--space-6); + background-color: transparent; + border-radius: var(--radius-base); + border: 0; + color: var(--color-text-tertiary); + cursor: pointer; + transition: + background-color 0.2s, + border-color 0.2s; + + &:hover { + background-color: var(--color-bg-secondary); + border-color: var(--color-border-secondary); + outline: none; + } + + &:focus-visible { + outline: 1px solid var(--color-focus-ring); + } + + span { + line-height: var(--line-height-snug); + + @media (--xs-only) { + display: none; + } + } + + svg { + color: var(--color-icon-primary); + } + + @media (--md-up) { + border: 1px solid var(--color-border-secondary); + padding: var(--space-1-5) var(--space-1-5) var(--space-1-5) var(--space-2); + } + } + + .search-trigger-content { + display: flex; + align-items: center; + gap: var(--space-2); + font-size: var(--font-size-sm); + + span { + margin: 0; + } + } + + .search-trigger-icon { + border-radius: var(--radius-base); + transition: background-color var(--duration-200) var(--ease-in-out); + flex-shrink: 0; + + @media (--xs-only) { + background-color: var(--color-bg-secondary); + padding: var(--space-2); + + &:hover { + background-color: var(--color-bg-mute); + } + } + } + + .search-trigger-shortcut { + margin-left: auto; + font-size: var(--font-size-xs); + color: var(--color-text-secondary); + background-color: var(--color-bg-secondary); + padding: var(--space-1) var(--space-2); + border-radius: var(--radius-base); + + @media (--xs-only) { + display: none; + } + } + + .search-modal-overlay { + background-color: var(--color-overlay); + z-index: var(--z-index-modal-overlay); + + @media (--xs-only) { + margin-top: 6rem; + height: calc(100vh - 6rem); + } + } + + .search-modal-inner { + background-color: color-mix(in oklch, var(--color-bg-primary) 90%, transparent); + width: 100%; + display: flex; + flex-direction: column; + overflow: hidden; + backdrop-filter: blur(2px); + margin: 0 auto; + + > section { + height: 100%; + } + + @media (--xs-only) { + max-width: 100%; + border-radius: 0; + } + + @media (--md-up) { + border-radius: var(--radius-lg); + margin-top: var(--size-14); + width: 80%; + max-width: 80rem; + height: 80vh; + max-height: 70rem; + } + } + + /* ============================================ + Modal Content + ============================================ */ + + .search-modal-content { + display: flex; + flex-direction: column; + height: 100%; + position: relative; + overflow: hidden; + } + + /* ============================================ + Modal Header (tabs + close button) + ============================================ */ + + .search-modal-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-2) var(--space-4); + flex-shrink: 0; + border-bottom: var(--border-width-1) solid var(--color-border-secondary); + } + + .search-modal-header-left { + display: flex; + align-items: center; + gap: var(--space-2); + } + + .search-modal-header-right { + display: flex; + align-items: center; + gap: var(--space-3); + } + + .search-modal-footer { + padding: var(--space-5) var(--space-6); + background-color: var(--color-bg-tertiary); + display: flex; + gap: var(--space-3); + align-items: center; + justify-content: flex-end; + + @media (--md-up) { + justify-content: space-between; + } + + small { + margin: 0; + } + + .orama-logo-light, + .orama-logo-dark { + width: var(--size-16); + height: auto; + } + + :root[data-theme='dark'] & .orama-logo-light, + :root[data-theme='light'] & .orama-logo-dark { + display: none; + } + } + + .search-modal-back { + display: flex; + align-items: center; + gap: var(--space-1-5); + padding: var(--space-1) var(--space-2); + border-radius: var(--radius-base); + border: none; + background: none; + font-size: var(--font-size-sm); + cursor: pointer; + transition: + color var(--duration-150) var(--ease-in-out), + background-color var(--duration-150) var(--ease-in-out); + + &:hover { + color: var(--color-text-primary); + background-color: var(--color-bg-secondary); + } + + &:focus-visible { + outline: 1px solid var(--color-focus-ring); + } + } + + .search-modal-chat { + display: flex; + align-items: center; + gap: var(--space-1-5); + border-radius: var(--radius-base); + border: none; + background: none; + color: var(--color-text-secondary); + font-size: var(--font-size-sm); + cursor: pointer; + position: relative; + transition: + color var(--duration-150) var(--ease-in-out), + background-color var(--duration-150) var(--ease-in-out); + + span { + margin: 0; + } + + .search-modal-back + & { + &::before { + content: '|'; + position: absolute; + left: calc(-1 * var(--space-3)); + opacity: 0.8; + color: var(--color-border-primary); + } + } + } + + .chat-interactions-count { + background-color: var(--color-bg-inverse); + color: var(--color-text-inverse); + padding: 0 var(--space-2); + border-radius: var(--radius-base); + font-size: var(--font-size-sm); + } + + .search-modal-body { + flex-grow: 1; + display: flex; + flex-direction: column; + justify-content: space-between; + overflow-y: auto; + } + + .search-modal-close { + display: flex; + align-items: center; + justify-content: center; + width: var(--size-8); + height: var(--size-8); + border-radius: var(--radius-base); + border: none; + background: none; + color: var(--color-text-secondary); + cursor: pointer; + transition: + color var(--duration-150) var(--ease-in-out), + background-color var(--duration-150) var(--ease-in-out); + flex-shrink: 0; + + &:hover { + color: var(--color-text-primary); + background-color: var(--color-bg-secondary); + } + + &:focus-visible { + outline: 1px solid var(--color-focus-ring); + } + } +} diff --git a/src/components/patterns/Searchbox/Searchbox.tsx b/src/components/patterns/Searchbox/Searchbox.tsx new file mode 100644 index 0000000000..94f67454c3 --- /dev/null +++ b/src/components/patterns/Searchbox/Searchbox.tsx @@ -0,0 +1,149 @@ +import { useEffect, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { OramaCloud } from '@orama/core'; +import { ChatRoot, Modal, SearchRoot } from '@orama/ui/components'; +import { useSearch } from '@orama/ui/hooks/useSearch'; +import Search from './Search'; +import SearchSparkle16Regular from '~icons/fluent/search-sparkle-16-regular'; +import ArrowLeft24Regular from '~icons/fluent/arrow-left-24-regular'; +import Dismiss16Regular from '~icons/fluent/dismiss-16-regular'; +import Chat from './Chat'; +import SearchFooter from './SearchFooter'; +import './orama-styles.css'; +import './Searchbox.css'; +import { useChat } from '@orama/ui/hooks/index'; + +const orama = import.meta.env.PUBLIC_ORAMA_PROJECT_ID + ? new OramaCloud({ + projectId: import.meta.env.PUBLIC_ORAMA_PROJECT_ID, + apiKey: import.meta.env.PUBLIC_ORAMA_API_KEY, + }) + : null; + +interface SearchboxProps { + lang: string; + placeholder: string; + ariaLabel: string; +} + +interface SearchModalHeaderProps { + mode: 'search' | 'chat'; + onModeChange: (mode: 'search' | 'chat') => void; +} + +function SearchModalHeader({ mode, onModeChange }: SearchModalHeaderProps) { + const { + context: { searchTerm }, + reset, + } = useSearch(); + const { + context: { interactions }, + } = useChat(); + + function handleBackToSearch() { + if (mode === 'chat') { + onModeChange('search'); + return; + } + + reset(); + } + + return ( +
+
+ {(mode === 'chat' || searchTerm) && ( + + )} + {interactions && interactions.length > 0 && ( + + )} +
+
+ + + +
+
+ ); +} + +export default function Searchbox({ lang, placeholder, ariaLabel }: SearchboxProps) { + const [shortcutKey, setShortcutKey] = useState('⌘ K'); + const [mounted, setMounted] = useState(false); + const [mode, setMode] = useState<'search' | 'chat'>('search'); + + useEffect(() => { + setMounted(true); + const nav = navigator as Navigator & { userAgentData?: { platform: string } }; + const isMac = nav.userAgentData?.platform + ? /mac/i.test(nav.userAgentData.platform) + : /Mac|iPhone|iPod|iPad/i.test(navigator.userAgent); + setShortcutKey(isMac ? '⌘ K' : 'Ctrl K'); + }, []); + + return ( + + + + +
+
+ +
+ {placeholder} +
+
+ {shortcutKey} +
+
+ + {orama && + mounted && + createPortal( + + + + +
+
+ +
+ {mode === 'chat' && } +
+
+ +
+
+
+
, + document.body + )} +
+
+
+ ); +} diff --git a/src/components/patterns/Searchbox/orama-styles.css b/src/components/patterns/Searchbox/orama-styles.css new file mode 100644 index 0000000000..743d58e7aa --- /dev/null +++ b/src/components/patterns/Searchbox/orama-styles.css @@ -0,0 +1 @@ +@import '@orama/ui/styles.css' layer(orama); diff --git a/src/components/patterns/Sidebar/Sidebar.astro b/src/components/patterns/Sidebar/Sidebar.astro new file mode 100644 index 0000000000..3d7c4c3d55 --- /dev/null +++ b/src/components/patterns/Sidebar/Sidebar.astro @@ -0,0 +1,90 @@ +--- +import './Sidebar.css'; +import type { HTMLAttributes } from 'astro/types'; +import { getLangFromUrl, useTranslations } from '@/i18n/utils'; +import { mainMenu } from '@/config/menu/main'; +import type { VersionConfig } from '@/components/patterns/VersionSwitcher/types'; +import type { Menu } from '@/config/types'; +import SidebarMenu from './SidebarMenu.astro'; +import { + detectVersionFromUrl, + collectAllSubmenus, + calculateInitialActiveLevel, + type SubmenuData, +} from './utils'; +import { Icon } from 'astro-icon/components'; + +type Props = HTMLAttributes<'div'> & { + menu?: Menu; + versions?: VersionConfig[]; + defaultVersion?: string; +}; + +const { + menu = mainMenu, + versions = [ + { id: '5x', label: 'v5.x', isDefault: true }, + { id: '4x', label: 'v4.x' }, + { id: '3x', label: 'v3.x (deprecated)' }, + ], + defaultVersion = '5x', + ...rest +} = Astro.props; + +const lang = getLangFromUrl(Astro.url); +const t = useTranslations(lang); +const currentPath = Astro.url.pathname; +const currentVersion = detectVersionFromUrl(currentPath, versions, defaultVersion); + +const submenus: SubmenuData[] = []; +collectAllSubmenus(menu, 0, 'root', '', [], currentVersion, submenus); +const initialActiveLevel = calculateInitialActiveLevel(submenus, currentPath, lang, currentVersion); +--- + + + + diff --git a/src/components/patterns/Sidebar/Sidebar.css b/src/components/patterns/Sidebar/Sidebar.css new file mode 100644 index 0000000000..850c79b609 --- /dev/null +++ b/src/components/patterns/Sidebar/Sidebar.css @@ -0,0 +1,459 @@ +@layer patterns { + .sidebar-backdrop { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: color-mix(in oklch, var(--color-bg-primary) 50%, transparent); + opacity: 0; + visibility: hidden; + transition: + opacity var(--duration-300) var(--ease-in-out), + visibility var(--duration-300) var(--ease-in-out); + z-index: var(--z-index-sidebar-backdrop); + + @media (--md-up) { + display: none; + } + } + + .sidebar-backdrop--visible { + opacity: 1; + visibility: visible; + } + + .sidebar { + background-color: var(--color-bg-secondary); + transition: transform var(--duration-300) var(--ease-in-out); + z-index: var(--z-index-sidebar); + top: var(--space-14); + overscroll-behavior: contain; + + @media (--xs-only) { + visibility: hidden; + transform: translateX(-100%); + overflow: hidden; + top: var(--space-14); + position: fixed; + left: 0; + bottom: 0; + width: 80%; + max-width: var(--size-80); + + .sidebar-logo-item { + display: none; + } + } + + @media (--md-up) { + position: sticky; + align-self: flex-start; + height: 100vh; + top: 0; + flex: 0 0 auto; + } + } + + .sidebar--open { + transform: translateX(0); + visibility: visible; + } + + .sidebar-toggle-nested { + position: absolute; + top: var(--space-4); + right: 0; + transform: translate3d(100%, 0, 0); + z-index: 30; + display: flex; + align-items: center; + justify-content: center; + width: var(--space-8); + height: var(--space-8); + padding: 0; + background-color: var(--color-bg-tertiary); + border: 0; + border-radius: 0; + cursor: pointer; + color: var(--color-text-primary); + border-radius: 0 var(--radius-base) var(--radius-base) 0; + opacity: 1; + visibility: visible; + transition: + background-color var(--duration-200) var(--ease-in-out), + transform var(--duration-200) var(--ease-in-out), + opacity var(--duration-200) var(--ease-in-out), + visibility var(--duration-200) var(--ease-in-out); + + &:hover { + color: var(--color-text-primary); + } + + &:focus-visible { + outline: 2px solid var(--color-border-focus); + outline-offset: 2px; + } + + &[aria-expanded='false'] { + transform: translate3d(100%, 0, 0) scale(-1); + background-color: var(--color-bg-secondary); + border-radius: var(--radius-base) 0 0 var(--radius-base) 0; + } + + &[data-visible='false'] { + opacity: 0; + visibility: hidden; + pointer-events: none; + } + + @media (--xs-only) { + display: none; + } + } + + .sidebar-container { + display: flex; + flex-direction: row; + position: relative; + flex-grow: 1; + height: 100%; + transform: translateX(0); + transition: transform var(--duration-300) var(--ease-in-out); + + &.sidebar-container--interactive { + transition: transform var(--duration-300) var(--ease-in-out); + } + + @media (--xs-only) { + &:not([data-current-nav-level='0']) { + transform: translateX(-100%); + } + + &[data-current-nav-level='2'] .sidebar-nested { + transform: translateX(-100%); + } + + &[data-current-nav-level='3'] .sidebar-nested { + transform: translateX(-200%); + } + + &[data-current-nav-level='4'] .sidebar-nested { + transform: translateX(-300%); + } + + &[data-current-nav-level='5'] .sidebar-nested { + transform: translateX(-400%); + } + } + + @media (--md-up) { + &[data-current-nav-level='0'] .sidebar-nested { + width: 0; + overflow: hidden; + } + + &[data-current-nav-level='0'] .sidebar-nested-inner { + transform: translateX(-100%); + } + + &[data-current-nav-level='2'] .sidebar-nested-inner { + transform: translateX(-100%); + } + + &[data-current-nav-level='3'] .sidebar-nested-inner { + transform: translateX(-200%); + } + + &[data-current-nav-level='4'] .sidebar-nested-inner { + transform: translateX(-300%); + } + + &[data-current-nav-level='5'] .sidebar-nested-inner { + transform: translateX(-400%); + } + } + } + + .sidebar-nested { + display: flex; + flex: 1 0 100%; + transition: transform var(--duration-300) var(--ease-in-out); + + .sidebar-container--interactive & { + transition: transform var(--duration-300) var(--ease-in-out); + } + + @media (--md-up) { + flex-basis: auto; + overflow: hidden; + width: var(--size-64); + z-index: 10; + position: relative; + + .sidebar-container--interactive & { + transition: width var(--duration-300) var(--ease-in-out); + } + } + } + + .sidebar-nested-inner { + display: flex; + flex: 1 0 100%; + + .sidebar-container--interactive & { + transition: transform var(--duration-300) var(--ease-in-out); + } + + @media (--md-up) { + position: absolute; + width: var(--size-64); + height: 100%; + left: 0; + top: 0; + } + } + + .sidebar-column { + display: flex; + flex-direction: column; + flex: 1 0 100%; + background-color: var(--color-bg-secondary); + padding: var(--space-4); + transition: transform var(--duration-300) var(--ease-in-out); + overflow: auto; + } + + .sidebar-root-column { + @media (--md-up) { + flex: 0 0 auto; + padding: var(--space-5) var(--space-3) var(--space-4) var(--space-3); + z-index: 20; + + .sidebar-nav-item { + flex-direction: column; + } + + .sidebar-nav-item--root-submenu .sidebar-nav-arrow { + display: none; + } + + .sidebar-nav-item--selected { + .sidebar-nav-icon { + background-color: var(--color-bg-tertiary); + } + } + } + } + + .sidebar-nav { + overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: var(--color-border-primary) transparent; + scrollbar-gutter: stable; + flex-grow: 1; + display: flex; + flex-direction: column; + } + + .sidebar-nav-list { + list-style: none; + margin: 0; + padding: 0; + flex: 1; + + & li { + margin: 0; + } + } + + .sidebar-nav-content { + overflow-y: auto; + padding-right: var(--space-2); + } + + .sidebar-nav-back { + display: flex; + align-items: center; + gap: var(--space-2); + padding: var(--space-2) 0; + padding-bottom: var(--space-4); + margin-bottom: var(--space-2); + background: none; + border: none; + border-bottom: 1px solid var(--color-border-mute); + cursor: pointer; + color: var(--color-text-primary); + transition: color var(--duration-200) var(--ease-in-out); + width: 100%; + text-align: left; + } + + .sidebar-section { + margin-bottom: var(--space-6); + + &:last-child { + margin-bottom: 0; + } + } + + .sidebar-section-list { + list-style: none; + margin: 0; + margin-top: var(--space-3); + padding: 0; + + li { + margin: 0; + } + + .sidebar-nav-content & { + margin-left: var(--space-3); + border-left: 1px solid var(--color-border-mute); + } + + h3 + & { + margin-top: 0; + } + } + + .sidebar-nav-item { + display: flex; + align-items: center; + gap: var(--space-3); + width: 100%; + padding: var(--space-3) 0; + text-decoration: none; + border: 0; + cursor: pointer; + position: relative; + transition: background-color var(--duration-200) var(--ease-in-out); + background-color: transparent; + border-radius: 0 var(--radius-base) var(--radius-base) 0; + + @media (--md-up) { + gap: var(--space-1-5); + } + } + + .sidebar-nav-label { + flex-grow: 1; + text-align: left; + } + + .sidebar-nav-item--nested { + padding: var(--space-3); + font-size: var(--font-size-sm); + font-weight: var(--font-weight-normal); + color: var(--color-text-secondary); + border-radius: var(--radius-base); + } + + .sidebar-nav-item--nested:hover, + .sidebar-nav-item--nested.sidebar-nav-item--active { + background-color: var(--color-bg-secondary); + } + + .sidebar-nav-item--nested:hover { + color: var(--color-text-primary); + } + + .sidebar-nav-item--nested:focus-visible { + outline-offset: -2px; + background-color: var(--color-bg-secondary); + } + + .sidebar-nav-item--nested.sidebar-nav-item--active { + font-weight: var(--font-weight-semibold); + position: relative; + } + + .sidebar-nav-item--nested.sidebar-nav-item--active::before { + content: ''; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: var(--size-0-5); + background-color: var(--color-bg-inverse); + } + + .sidebar-nav-icon { + padding: var(--space-2); + border-radius: var(--radius-base); + background-color: var(--color-bg-secondary); + transition: background-color var(--duration-200) var(--ease-in-out); + flex-shrink: 0; + } + + .sidebar-nav-item:hover .sidebar-nav-icon, + .sidebar-nav-item--active .sidebar-nav-icon, + .sidebar-nav-item--active.sidebar-nav-item--selected .sidebar-nav-icon { + background-color: var(--color-bg-mute); + } + + .sidebar-nav-arrow { + flex-shrink: 0; + opacity: 0.6; + transform: translateX(0); + transition: opacity var(--duration-200) var(--ease-in-out); + + .sidebar-nav-item:hover & { + opacity: 1; + } + } + + .sidebar-nav-item--submenu { + display: flex; + align-items: center; + justify-content: space-between; + text-align: left; + } + + .sidebar-nested-column { + background-color: var(--color-bg-tertiary); + position: relative; + } + + .sidebar-nav-panel { + position: absolute; + top: 0; + left: 0; + width: 100%; + z-index: 0; + opacity: 0; + padding: var(--space-4) var(--space-4) var(--space-8) var(--space-4); + + @media (--md-up) { + padding-top: var(--space-3); + } + + &:not(.sidebar-nav-panel--active) { + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip-path: inset(50%); + border: 0; + white-space: nowrap; + } + + &--active { + z-index: 10; + opacity: 1; + height: 100%; + display: flex; + flex-direction: column; + } + } + + @media (prefers-reduced-motion: reduce) { + .sidebar, + .sidebar-backdrop, + .sidebar-nav, + .sidebar-nav-item { + transition: none; + } + } +} diff --git a/src/components/patterns/Sidebar/SidebarController.ts b/src/components/patterns/Sidebar/SidebarController.ts new file mode 100644 index 0000000000..fbe533902d --- /dev/null +++ b/src/components/patterns/Sidebar/SidebarController.ts @@ -0,0 +1,455 @@ +import { SidebarVersionManager } from './SidebarVersionManager'; +import { SidebarFocusTrap } from './SidebarFocusTrap'; + +// CSS transition duration in milliseconds (must match Sidebar.css --duration-300) +const TRANSITION_DURATION = 300; + +export class SidebarController { + private static readonly FOCUSABLE_SELECTOR = + 'a:not([tabindex="-1"]), button:not([tabindex="-1"]), input:not([tabindex="-1"]), select:not([tabindex="-1"]), textarea:not([tabindex="-1"]), [tabindex]:not([tabindex="-1"])'; + private static readonly INTERACTIVE_SELECTOR = 'a, button, input, select, textarea'; + + private sidebar: HTMLElement | null; + private backdrop: HTMLElement | null; + private navContainer: HTMLElement | null; + private toggleNestedButton: HTMLElement | null = null; + private versionManager: SidebarVersionManager | null = null; + private focusTrap: SidebarFocusTrap | null = null; + + private isOpen = false; + private lastFocusedElement: HTMLElement | null = null; + private activeLevel = 0; + private activeSubmenuPath: string[] = ['root']; + private selectedItem: HTMLElement | null = null; + private isNestedCollapsed = false; + + private initialActiveLevel = 0; + private initialActiveSubmenuPath: string[] = ['root']; + + constructor() { + this.sidebar = document.querySelector('[data-sidebar]'); + this.backdrop = document.querySelector('[data-sidebar-backdrop]'); + this.navContainer = document.querySelector('[data-nav-container]'); + this.toggleNestedButton = document.querySelector('[data-toggle-nested-button]'); + + if (this.sidebar && this.backdrop) { + const currentVersion = this.sidebar.dataset.currentVersion || '5x'; + this.versionManager = new SidebarVersionManager( + this.sidebar, + currentVersion, + this.activeSubmenuPath + ); + this.focusTrap = new SidebarFocusTrap(this.sidebar, () => this.activeSubmenuPath); + this.init(); + } + } + + private init(): void { + this.backdrop?.addEventListener('click', () => this.close()); + document.addEventListener('keydown', (e) => this.handleKeyDown(e)); + document.addEventListener('sidebar:toggle', () => this.toggle()); + + this.setupSubmenuTriggers(); + this.setupBackButtons(); + this.setupNavItemSelection(); + this.versionManager?.setup(); + this.initializeFromActiveState(); + this.updateActiveColumns(); + this.setupToggleNestedButton(); + } + + private initializeFromActiveState(): void { + const rootColumn = this.sidebar?.querySelector('[data-initial-active-level]') as HTMLElement; + const initialActiveLevel = parseInt(rootColumn?.dataset.initialActiveLevel || '0', 10); + + if (initialActiveLevel > 0) { + this.activeSubmenuPath = ['root']; + + for (let level = 1; level <= initialActiveLevel; level++) { + const levelColumn = this.sidebar?.querySelector(`[data-nav-level="${level}"]`); + const activePanelInLevel = levelColumn?.querySelector( + '.sidebar-nav-panel.sidebar-nav-panel--active' + ) as HTMLElement; + + if (activePanelInLevel?.dataset.parentId) { + this.activeSubmenuPath.push(activePanelInLevel.dataset.parentId); + } + } + + this.activeLevel = initialActiveLevel; + this.initialActiveLevel = initialActiveLevel; + this.initialActiveSubmenuPath = [...this.activeSubmenuPath]; + this.versionManager?.updatePath(this.activeSubmenuPath); + + if (this.navContainer) { + this.navContainer.dataset.currentNavLevel = String(initialActiveLevel); + } + } + } + + private focusActiveLevel(): void { + const currentSubmenuId = this.activeSubmenuPath[this.activeSubmenuPath.length - 1]; + const targetContainer = this.sidebar?.querySelector( + `[data-parent-id="${currentSubmenuId}"]` + ) as HTMLElement; + + if (!targetContainer) { + console.warn(`Focus target not found for submenu: ${currentSubmenuId}`); + return; + } + + const firstFocusable = targetContainer.querySelector( + SidebarController.FOCUSABLE_SELECTOR + ) as HTMLElement; + + if (!firstFocusable) { + console.warn(`No focusable elements found in: ${currentSubmenuId}`); + return; + } + + firstFocusable.focus(); + } + + private setupSubmenuTriggers(): void { + this.sidebar?.querySelectorAll('[data-submenu-trigger]').forEach((trigger) => { + trigger.addEventListener('click', (e) => { + e.preventDefault(); + const button = e.currentTarget as HTMLButtonElement; + const targetId = button.dataset.targetId; + const targetLevel = parseInt(button.dataset.targetLevel || '1', 10); + + if (targetId) { + this.navigateToSubmenu(targetId, targetLevel); + button.setAttribute('aria-expanded', 'true'); + } + }); + }); + } + + private setupBackButtons(): void { + this.sidebar?.querySelectorAll('[data-back-button]').forEach((button) => { + button.addEventListener('click', (e) => { + e.preventDefault(); + this.navigateBack(); + }); + }); + } + + private setupNavItemSelection(): void { + this.sidebar?.querySelectorAll('.sidebar-nav-item').forEach((item) => { + item.addEventListener('click', () => { + // Only track selection for root-level items (level 0) + const isRootLevel = item.closest('[data-nav-level="0"]') !== null; + if (isRootLevel) { + this.setSelectedItem(item as HTMLElement); + } + }); + }); + } + + private setupToggleNestedButton(): void { + this.toggleNestedButton?.addEventListener('click', () => { + this.toggleNestedColumns(); + }); + this.updateToggleButtonVisibility(); + } + + private updateToggleButtonVisibility(): void { + if (!this.toggleNestedButton) return; + + const shouldBeVisible = this.activeLevel > 0 || this.initialActiveLevel > 0; + this.toggleNestedButton.setAttribute('data-visible', String(shouldBeVisible)); + } + + private setSelectedItem(item: HTMLElement): void { + if (this.selectedItem) { + this.selectedItem.classList.remove('sidebar-nav-item--selected'); + } + + item.classList.add('sidebar-nav-item--selected'); + this.selectedItem = item; + } + + private toggleNestedColumns(): void { + this.isNestedCollapsed = !this.isNestedCollapsed; + this.enableTransitions(); + + if (this.isNestedCollapsed) { + if (this.selectedItem) { + this.selectedItem.classList.remove('sidebar-nav-item--selected'); + this.selectedItem = null; + } + + this.activeLevel = 0; + if (this.navContainer) { + this.navContainer.dataset.currentNavLevel = '0'; + } + } else { + this.activeSubmenuPath = [...this.initialActiveSubmenuPath]; + this.activeLevel = this.initialActiveLevel; + this.versionManager?.updatePath(this.activeSubmenuPath); + if (this.navContainer) { + this.navContainer.dataset.currentNavLevel = String(this.initialActiveLevel); + } + } + + if (this.toggleNestedButton) { + this.toggleNestedButton.setAttribute('aria-expanded', String(!this.isNestedCollapsed)); + } + + this.updateActiveColumns(); + this.updateToggleButtonVisibility(); + } + + private navigateToSubmenu(submenuId: string, level: number): void { + const submenuColumn = this.sidebar?.querySelector( + `[data-parent-id="${submenuId}"]` + ) as HTMLElement; + + if (!submenuColumn) { + console.warn(`Submenu column not found: ${submenuId}`); + return; + } + + this.enableTransitions(); + + // Check if this submenu is part of the initial path (contains current page) + // If so, restore to the initial navigation state instead of just going to target level + const isInInitialPath = this.initialActiveSubmenuPath.includes(submenuId); + const shouldRestoreInitialState = isInInitialPath && this.initialActiveLevel > level; + + if (shouldRestoreInitialState) { + // Restore to the initial deep navigation state + this.activeSubmenuPath = [...this.initialActiveSubmenuPath]; + this.activeLevel = this.initialActiveLevel; + } else { + this.activeSubmenuPath = this.activeSubmenuPath.slice(0, level); + this.activeSubmenuPath.push(submenuId); + this.activeLevel = level; + } + + // If navigating to a submenu, ensure nested columns are expanded + if (this.isNestedCollapsed && level > 0) { + this.isNestedCollapsed = false; + if (this.toggleNestedButton) { + this.toggleNestedButton.setAttribute('aria-expanded', 'true'); + } + } + + this.versionManager?.updatePath(this.activeSubmenuPath); + + this.updateActiveColumns(); + this.updateToggleButtonVisibility(); + + if (this.navContainer) { + this.navContainer.dataset.currentNavLevel = String(this.activeLevel); + } + + this.afterTransition(() => this.focusActiveLevel()); + } + + private navigateBack(): void { + if (this.activeSubmenuPath.length <= 1) return; + + this.enableTransitions(); + + // Clear selected item state when navigating back + if (this.selectedItem) { + this.selectedItem.classList.remove('sidebar-nav-item--selected'); + this.selectedItem = null; + } + + const currentSubmenuId = this.activeSubmenuPath[this.activeSubmenuPath.length - 1]; + let triggerToFocus: HTMLElement | null = null; + + if (currentSubmenuId) { + const trigger = this.sidebar?.querySelector( + `[data-target-id="${currentSubmenuId}"]` + ) as HTMLElement; + trigger?.setAttribute('aria-expanded', 'false'); + triggerToFocus = trigger; + } + + this.activeSubmenuPath.pop(); + this.activeLevel = this.activeSubmenuPath.length - 1; + this.versionManager?.updatePath(this.activeSubmenuPath); + + // If navigating back to level 0, collapse nested columns and update toggle button + if (this.activeLevel === 0) { + this.isNestedCollapsed = true; + if (this.toggleNestedButton) { + this.toggleNestedButton.setAttribute('aria-expanded', 'false'); + } + } + + this.updateActiveColumns(); + this.updateToggleButtonVisibility(); + + if (this.navContainer) { + this.navContainer.dataset.currentNavLevel = String(this.activeLevel); + } + + // Restore focus to the trigger button after the transition + this.afterTransition(() => triggerToFocus?.focus()); + } + + private updateActiveColumns(): void { + const allLevels = this.sidebar?.querySelectorAll('[data-nav-level]'); + + allLevels?.forEach((column) => { + const columnLevel = parseInt((column as HTMLElement).dataset.navLevel || '0', 10); + const isCurrentLevel = columnLevel === this.activeLevel; + + if (columnLevel === 0) { + column.setAttribute('aria-hidden', 'false'); + this.updateFocusableElements(column as HTMLElement, true); + } else { + const panels = column.querySelectorAll('[data-parent-id]'); + panels.forEach((panel) => { + const parentId = (panel as HTMLElement).dataset.parentId || ''; + const isInActivePath = this.activeSubmenuPath.includes(parentId); + + panel.setAttribute('aria-hidden', isInActivePath ? 'false' : 'true'); + panel.classList.toggle('sidebar-nav-panel--active', isInActivePath); + + const shouldBeFocusable = isCurrentLevel && isInActivePath; + this.updateFocusableElements(panel as HTMLElement, shouldBeFocusable); + }); + } + }); + + this.versionManager?.updateVisibility(this.activeLevel); + } + + private updateFocusableElements(container: HTMLElement, isVisible: boolean): void { + const scrollableContainers = container.querySelectorAll('.sidebar-nav-content, .sidebar-nav'); + scrollableContainers.forEach((scrollContainer) => { + if (isVisible) { + scrollContainer.removeAttribute('tabindex'); + } else { + scrollContainer.setAttribute('tabindex', '-1'); + } + }); + + const focusableElements = container.querySelectorAll(SidebarController.INTERACTIVE_SELECTOR); + + focusableElements.forEach((element) => { + if (isVisible) { + const originalTabindex = element.getAttribute('data-original-tabindex'); + if (originalTabindex !== null) { + if (originalTabindex === '') { + element.removeAttribute('tabindex'); + } else { + element.setAttribute('tabindex', originalTabindex); + } + element.removeAttribute('data-original-tabindex'); + } + } else { + const currentTabindex = element.getAttribute('tabindex'); + const hasOriginalTabindex = element.hasAttribute('data-original-tabindex'); + + if (!hasOriginalTabindex && currentTabindex !== '-1') { + element.setAttribute('data-original-tabindex', currentTabindex || ''); + element.setAttribute('tabindex', '-1'); + } + } + }); + } + + private enableTransitions(): void { + this.navContainer?.classList.add('sidebar-container--interactive'); + } + + private afterTransition(callback: () => void): void { + setTimeout(callback, TRANSITION_DURATION); + } + + private handleKeyDown(e: KeyboardEvent): void { + if (e.key === 'Escape' && this.isOpen) { + if (this.activeLevel > 0) { + this.navigateBack(); + } else { + this.close(); + } + } + this.focusTrap?.trap(e, this.isOpen); + } + + public open(): void { + if (this.isOpen || !this.sidebar || !this.backdrop) return; + + this.lastFocusedElement = document.activeElement as HTMLElement; + this.isOpen = true; + + this.sidebar.setAttribute('aria-hidden', 'false'); + this.backdrop.setAttribute('aria-hidden', 'false'); + this.sidebar.classList.add('sidebar--open'); + this.backdrop.classList.add('sidebar-backdrop--visible'); + document.body.style.overflow = 'hidden'; + + this.afterTransition(() => this.focusActiveLevel()); + } + + public close(): void { + if (!this.isOpen || !this.sidebar || !this.backdrop) return; + + document.dispatchEvent(new CustomEvent('sidebar:closed')); + this.isOpen = false; + + // Clear selected item state immediately + if (this.selectedItem) { + this.selectedItem.classList.remove('sidebar-nav-item--selected'); + this.selectedItem = null; + } + + this.sidebar.setAttribute('aria-hidden', 'true'); + this.backdrop.setAttribute('aria-hidden', 'true'); + this.sidebar.classList.remove('sidebar--open'); + this.backdrop.classList.remove('sidebar-backdrop--visible'); + + this.afterTransition(() => this.resetToInitialState()); + + document.body.style.overflow = ''; + this.lastFocusedElement?.focus(); + this.lastFocusedElement = null; + } + + private resetToInitialState(): void { + this.sidebar?.querySelectorAll('[data-submenu-trigger]').forEach((trigger) => { + trigger.setAttribute('aria-expanded', 'false'); + }); + + this.activeSubmenuPath = [...this.initialActiveSubmenuPath]; + this.activeLevel = this.initialActiveLevel; + this.isNestedCollapsed = false; + this.versionManager?.updatePath(this.activeSubmenuPath); + + // Reset toggle button state + if (this.toggleNestedButton) { + this.toggleNestedButton.setAttribute('aria-expanded', 'true'); + } + + this.updateActiveColumns(); + this.updateToggleButtonVisibility(); + + if (this.navContainer) { + this.navContainer.dataset.currentNavLevel = String(this.activeLevel); + } + } + + public toggle(): void { + if (this.isOpen) { + this.close(); + } else { + this.open(); + } + } +} + +declare global { + interface Window { + sidebarController?: SidebarController; + } +} + +(window as Window).sidebarController = new SidebarController(); diff --git a/src/components/patterns/Sidebar/SidebarFocusTrap.ts b/src/components/patterns/Sidebar/SidebarFocusTrap.ts new file mode 100644 index 0000000000..665b2333ba --- /dev/null +++ b/src/components/patterns/Sidebar/SidebarFocusTrap.ts @@ -0,0 +1,65 @@ +export class SidebarFocusTrap { + private sidebar: HTMLElement; + private getActiveSubmenuPath: () => string[]; + + constructor(sidebar: HTMLElement, getActiveSubmenuPath: () => string[]) { + this.sidebar = sidebar; + this.getActiveSubmenuPath = getActiveSubmenuPath; + } + + getFocusableElements(): HTMLElement[] { + const activeSubmenuPath = this.getActiveSubmenuPath(); + const activeSubmenuId = activeSubmenuPath[activeSubmenuPath.length - 1]; + const activeColumn = this.sidebar.querySelector(`[data-parent-id="${activeSubmenuId}"]`); + if (!activeColumn) return []; + + const selectors = + 'a[href], button:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'; + return Array.from(activeColumn.querySelectorAll(selectors)); + } + + trap(e: KeyboardEvent, isOpen: boolean): void { + if (!isOpen) return; + + if (e.key === 'Tab') { + this.handleTabNavigation(e); + } else if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { + this.handleArrowNavigation(e); + } + } + + private handleTabNavigation(e: KeyboardEvent): void { + const focusableElements = this.getFocusableElements(); + if (focusableElements.length === 0) return; + + const first = focusableElements[0]; + const last = focusableElements[focusableElements.length - 1]; + + if (e.shiftKey && document.activeElement === first) { + e.preventDefault(); + last?.focus(); + } else if (!e.shiftKey && document.activeElement === last) { + e.preventDefault(); + first?.focus(); + } + } + + private handleArrowNavigation(e: KeyboardEvent): void { + const focusableElements = this.getFocusableElements(); + if (focusableElements.length === 0) return; + + const currentIndex = focusableElements.indexOf(document.activeElement as HTMLElement); + if (currentIndex === -1) return; + + e.preventDefault(); + + let nextIndex: number; + if (e.key === 'ArrowDown') { + nextIndex = currentIndex + 1 >= focusableElements.length ? 0 : currentIndex + 1; + } else { + nextIndex = currentIndex - 1 < 0 ? focusableElements.length - 1 : currentIndex - 1; + } + + focusableElements[nextIndex]?.focus(); + } +} diff --git a/src/components/patterns/Sidebar/SidebarItemsList.astro b/src/components/patterns/Sidebar/SidebarItemsList.astro new file mode 100644 index 0000000000..bd0c92cbe1 --- /dev/null +++ b/src/components/patterns/Sidebar/SidebarItemsList.astro @@ -0,0 +1,102 @@ +--- +import type { MenuItem, VersionPrefix } from '@/config/types'; +import type { LanguageCode } from '@/i18n/locales'; +import SidebarNavItem from './SidebarNavItem.astro'; +import { + isLink, + hasSubmenu, + filterItems, + getItemId, + resolveHref, + normalizePath, + submenuContainsCurrentPath, +} from './utils'; + +interface Props { + items: MenuItem[]; + parentId: string; + sectionIndex: number | null; + currentPath: string; + lang: LanguageCode; + basePath: string; + versioned: VersionPrefix[]; + version: string; + variant: 'root' | 'nested'; + showIcon?: boolean; + targetLevel: number; +} + +const { + items, + parentId, + sectionIndex, + currentPath, + lang, + basePath, + versioned, + version, + variant, + showIcon = false, + targetLevel, +} = Astro.props; + +const filteredItems = filterItems(items, version); +--- + +{ + filteredItems + .filter((item) => isLink(item) || hasSubmenu(item)) + .map((item, itemIndex) => { + const itemId = getItemId(parentId, sectionIndex, itemIndex); + const itemHref = item.href; + + const itemVersioned = item.submenu?.versioned ?? versioned; + const itemBasePath = item.submenu?.basePath ?? basePath; + + const href = itemHref + ? resolveHref(itemHref, lang, itemBasePath, itemVersioned, version, item.global) + : ''; + + const isActive = + hasSubmenu(item) && variant === 'root' + ? submenuContainsCurrentPath( + item.submenu, + itemBasePath, + itemVersioned, + currentPath, + lang, + version + ) + : href + ? normalizePath(currentPath) === normalizePath(href) + : false; + + if (hasSubmenu(item)) { + return ( + + ); + } + + return ( + + ); + }) +} diff --git a/src/components/patterns/Sidebar/SidebarMenu.astro b/src/components/patterns/Sidebar/SidebarMenu.astro new file mode 100644 index 0000000000..370b5603cf --- /dev/null +++ b/src/components/patterns/Sidebar/SidebarMenu.astro @@ -0,0 +1,239 @@ +--- +import type { Menu, VersionPrefix } from '@/config/types'; +import type { VersionConfig } from '@/components/patterns/VersionSwitcher/types'; +import type { LanguageCode } from '@/i18n/locales'; +import { Body } from '@/components/primitives'; +import { Image } from 'astro:assets'; +import { Icon } from 'astro-icon/components'; +import { VersionSwitcher } from '@/components/patterns'; +import { useTranslations } from '@/i18n/utils'; +import SidebarItemsList from './SidebarItemsList.astro'; +import { + shouldOmitSection, + collectAllSubmenus, + submenuContainsCurrentPath, + calculateInitialActiveLevel, + groupSubmenusByLevel, + type SubmenuData, +} from './utils'; + +interface Props { + menu: Menu; + level: number; + title?: string; + lang: LanguageCode; + currentPath: string; + basePath?: string; + versioned?: VersionPrefix[]; + version?: string; + versions?: VersionConfig[]; + defaultVersion?: string; + parentId?: string; +} + +const { + menu, + level, + lang, + currentPath, + basePath = '', + versioned = [], + version = '5x', + versions = [], + defaultVersion = '5x', + parentId = 'root', +} = Astro.props; + +const t = useTranslations(lang); + +if (level !== 0) return null; + +const submenus: SubmenuData[] = []; +collectAllSubmenus(menu, level, parentId, basePath, versioned, version, submenus); + +const submenusByLevel = groupSubmenusByLevel(submenus); +const sortedLevels = [...submenusByLevel.keys()].sort((a, b) => a - b); +const initialActiveLevel = calculateInitialActiveLevel(submenus, currentPath, lang, version); + +const filteredSections = + menu.sections?.filter((section) => !shouldOmitSection(section, version)) ?? []; + +const commonItemsListProps = { + currentPath, + lang, + basePath, + versioned, + version, +}; +--- + + + +{ + sortedLevels.length > 0 && ( + + ) +} diff --git a/src/components/patterns/Sidebar/SidebarNavItem.astro b/src/components/patterns/Sidebar/SidebarNavItem.astro new file mode 100644 index 0000000000..4529fd36ad --- /dev/null +++ b/src/components/patterns/Sidebar/SidebarNavItem.astro @@ -0,0 +1,117 @@ +--- +import type { MenuItem } from '@/config/types'; +import { BodyMd } from '@/components/primitives'; +import { Icon } from 'astro-icon/components'; +import { useTranslations } from '@/i18n/utils'; +import type { LanguageCode } from '@/i18n/locales'; + +interface Props { + item: MenuItem & { href?: string }; + href: string; + isActive: boolean; + variant: 'root' | 'nested'; + type: 'link' | 'submenu'; + submenuId?: string; + targetLevel?: number; + showIcon?: boolean; + lang: LanguageCode; +} + +const { + item, + href, + isActive, + variant, + type, + submenuId, + targetLevel, + showIcon = false, + lang, +} = Astro.props; + +const t = useTranslations(lang); +const isNested = variant === 'nested'; +const isRootSubmenu = variant === 'root'; +const isExternal = href.startsWith('http'); + +// show overview instead of specific section names for top-level items that link to an overview page +const overviewAliases = new Set([ + 'menu.main.api', + 'menu.sections.express', + 'menu.sections.request', + 'menu.sections.response', + 'menu.sections.router', + 'menu.sections.application', +]); + +function getLabel() { + return overviewAliases.has(item.label) ? t('menu.items.overview') : t(item.label); +} +--- + +{ + type === 'link' ? ( +
  • + + {showIcon && item.icon && ( + + )} + + + {getLabel()} + + +
  • + ) : ( +
  • + +
  • + ) +} diff --git a/src/components/patterns/Sidebar/SidebarVersionManager.ts b/src/components/patterns/Sidebar/SidebarVersionManager.ts new file mode 100644 index 0000000000..2ce610820c --- /dev/null +++ b/src/components/patterns/Sidebar/SidebarVersionManager.ts @@ -0,0 +1,145 @@ +export class SidebarVersionManager { + private sidebar: HTMLElement; + private currentVersion: string; + private activeSubmenuPath: string[]; + + constructor(sidebar: HTMLElement, currentVersion: string, activeSubmenuPath: string[]) { + this.sidebar = sidebar; + this.currentVersion = currentVersion; + this.activeSubmenuPath = activeSubmenuPath; + } + + setup(): void { + const versionSwitchers = this.sidebar.querySelectorAll( + '[data-version-switcher] select, [data-version-select]' + ); + + versionSwitchers.forEach((switcher) => { + switcher.addEventListener('change', (e) => { + const select = e.target as HTMLSelectElement; + this.handleVersionChange(select.value); + }); + }); + } + + handleVersionChange(newVersion: string): void { + const previousVersion = this.currentVersion; + this.currentVersion = newVersion; + + document.dispatchEvent( + new CustomEvent('sidebar:versionChange', { + detail: { previousVersion, newVersion }, + }) + ); + + const currentPath = this.sidebar.dataset.currentPath || ''; + + if (currentPath.includes(`/${previousVersion}/`)) { + if (this.isPathOmittedForVersion(currentPath, newVersion)) { + const fallbackPath = this.getFirstAvailableLinkForVersion(newVersion, previousVersion); + if (fallbackPath) { + window.location.href = fallbackPath; + return; + } + } + + window.location.href = currentPath.replace(`/${previousVersion}/`, `/${newVersion}/`); + return; + } + + if (this.isInVersionedSubmenu()) { + const fallbackPath = this.getFirstAvailableLinkForVersion(newVersion, previousVersion); + if (fallbackPath) { + window.location.href = fallbackPath; + } + } + } + + private isInVersionedSubmenu(): boolean { + const activeSubmenuId = this.activeSubmenuPath[this.activeSubmenuPath.length - 1]; + if (activeSubmenuId === 'root') return false; + + const activePanel = this.sidebar.querySelector( + `[data-parent-id="${activeSubmenuId}"]` + ) as HTMLElement; + + if (!activePanel) return false; + + // Check if the active panel contains versioned links + const versionedLink = activePanel.querySelector('a[href*="/4x/"], a[href*="/5x/"]'); + return versionedLink !== null; + } + + private isPathOmittedForVersion(currentPath: string, targetVersion: string): boolean { + const activeLink = this.sidebar.querySelector( + 'a.sidebar-nav-item--active[data-omit-from]' + ) as HTMLAnchorElement; + + if (!activeLink) return false; + + const omitFrom = activeLink.dataset.omitFrom?.split(',') || []; + return omitFrom.includes(targetVersion); + } + + private getFirstAvailableLinkForVersion( + targetVersion: string, + previousVersion: string + ): string | null { + const activeSubmenuId = this.activeSubmenuPath[this.activeSubmenuPath.length - 1]; + const activePanel = this.sidebar.querySelector( + `[data-parent-id="${activeSubmenuId}"]` + ) as HTMLElement; + + if (!activePanel) return null; + + const links = activePanel.querySelectorAll( + 'a.sidebar-nav-item[href]' + ) as NodeListOf; + + for (const link of links) { + const omitFrom = link.dataset.omitFrom?.split(',') || []; + if (!omitFrom.includes(targetVersion)) { + const href = link.getAttribute('href') || ''; + if (href.includes(`/${previousVersion}/`)) { + return href.replace(`/${previousVersion}/`, `/${targetVersion}/`); + } + return href; + } + } + + return null; + } + + updateVisibility(activeLevel: number): void { + const versionSwitchers = this.sidebar.querySelectorAll('[data-version-switcher]'); + + versionSwitchers.forEach((switcher) => { + const column = switcher.closest('[data-parent-id]') as HTMLElement; + const parentId = column?.dataset.parentId || ''; + const isActiveColumn = parentId === this.activeSubmenuPath[this.activeSubmenuPath.length - 1]; + + if (activeLevel > 0 && isActiveColumn) { + switcher.classList.remove('sidebar-version-switcher--hidden'); + } else { + switcher.classList.add('sidebar-version-switcher--hidden'); + } + }); + } + + updatePath(path: string[]): void { + this.activeSubmenuPath = path; + } + + getVersion(): string { + return this.currentVersion; + } + + setVersion(version: string): void { + const versionSelects = + this.sidebar.querySelectorAll('[data-version-select]'); + versionSelects.forEach((select) => { + select.value = version; + }); + this.handleVersionChange(version); + } +} diff --git a/src/components/patterns/Sidebar/utils.ts b/src/components/patterns/Sidebar/utils.ts new file mode 100644 index 0000000000..973e41120f --- /dev/null +++ b/src/components/patterns/Sidebar/utils.ts @@ -0,0 +1,220 @@ +import type { Menu, MenuItem, VersionPrefix } from '@/config/types'; +import type { VersionConfig } from '@/components/patterns/VersionSwitcher/types'; +import type { LanguageCode } from '@/i18n/locales'; + +export type SubmenuData = { + menu: Menu; + title: string; + id: string; + basePath: string; + versioned: VersionPrefix[]; + level: number; +}; + +export function normalizePath(path: string): string { + return path.replace(/\/$/, ''); +} + +export function isVersioned(versioned: VersionPrefix[] | undefined, version: string): boolean { + return versioned?.includes(version as VersionPrefix) ?? false; +} + +export function resolveHref( + href: string, + lang: LanguageCode, + basePath: string, + versioned: VersionPrefix[] | undefined, + version: string, + global?: boolean +): string { + if (href.startsWith('http')) return href; + const useVersion = !global && isVersioned(versioned, version); + const versionPath = useVersion ? `/${version}` : ''; + return `/${lang}${basePath}${versionPath}${href}`; +} + +export function isLink(item: MenuItem): item is MenuItem & { href: string } { + return 'href' in item && item.href !== undefined; +} + +export function hasSubmenu(item: MenuItem): item is MenuItem & { submenu: Menu } { + return 'submenu' in item && item.submenu !== undefined; +} + +function shouldOmit(entity: { omitFrom?: VersionPrefix[] }, version: string): boolean { + return entity.omitFrom?.includes(version as VersionPrefix) ?? false; +} + +const shouldOmitItem = (item: MenuItem, version: string): boolean => shouldOmit(item, version); + +export const shouldOmitSection = ( + section: { omitFrom?: VersionPrefix[] }, + version: string +): boolean => shouldOmit(section, version); + +export function filterItems(items: MenuItem[], version: string): MenuItem[] { + return items.filter((item) => !shouldOmitItem(item, version)); +} + +export function getItemId( + parentId: string, + sectionIndex: number | null, + itemIndex: number +): string { + return sectionIndex !== null + ? `${parentId}-s${sectionIndex}-i${itemIndex}` + : `${parentId}-i${itemIndex}`; +} + +export function collectAllSubmenus( + menuToScan: Menu, + currentLevel: number, + currentParentId: string, + currentBasePath: string, + currentVersioned: VersionPrefix[] | undefined, + version: string, + submenus: SubmenuData[] +): void { + const processItems = (items: MenuItem[], sectionIndex: number | null): void => { + filterItems(items, version).forEach((item, itemIndex) => { + if (!hasSubmenu(item)) return; + + const itemId = getItemId(currentParentId, sectionIndex, itemIndex); + const submenuId = `submenu-${itemId}`; + const submenuBasePath = item.submenu.basePath || currentBasePath; + const submenuVersioned = item.submenu.versioned ?? currentVersioned ?? []; + + submenus.push({ + menu: item.submenu, + title: item.label, + id: submenuId, + basePath: submenuBasePath, + versioned: submenuVersioned, + level: currentLevel + 1, + }); + + collectAllSubmenus( + item.submenu, + currentLevel + 1, + submenuId, + submenuBasePath, + submenuVersioned, + version, + submenus + ); + }); + }; + + menuToScan.sections + ?.filter((section) => !shouldOmitSection(section, version)) + .forEach((section, sectionIndex) => processItems(section.items, sectionIndex)); + + if (menuToScan.items) { + processItems(menuToScan.items, null); + } +} + +function checkItemsForPath( + items: MenuItem[], + normalizedCurrentPath: string, + basePath: string, + versioned: VersionPrefix[] | undefined, + lang: LanguageCode, + version: string +): boolean { + return filterItems(items, version).some((item) => { + if (isLink(item)) { + const href = resolveHref(item.href, lang, basePath, versioned, version, item.global); + const hrefWithoutHash = href.split('#')[0]; + return normalizedCurrentPath === normalizePath(hrefWithoutHash); + } + if (hasSubmenu(item)) { + const nestedBasePath = item.submenu.basePath || basePath; + const nestedVersioned = item.submenu.versioned ?? versioned; + return submenuContainsCurrentPath( + item.submenu, + nestedBasePath, + nestedVersioned, + normalizedCurrentPath, + lang, + version + ); + } + return false; + }); +} + +export function submenuContainsCurrentPath( + submenuMenu: Menu, + submenuBasePath: string, + submenuVersioned: VersionPrefix[] | undefined, + currentPath: string, + lang: LanguageCode, + version: string +): boolean { + const normalizedCurrentPath = normalizePath(currentPath); + const filteredSections = + submenuMenu.sections?.filter((s) => !shouldOmitSection(s, version)) ?? []; + + return ( + filteredSections.some((section) => + checkItemsForPath( + section.items, + normalizedCurrentPath, + submenuBasePath, + submenuVersioned, + lang, + version + ) + ) || + (submenuMenu.items + ? checkItemsForPath( + submenuMenu.items, + normalizedCurrentPath, + submenuBasePath, + submenuVersioned, + lang, + version + ) + : false) + ); +} + +export function calculateInitialActiveLevel( + submenus: SubmenuData[], + currentPath: string, + lang: LanguageCode, + version: string +): number { + return submenus.reduce((maxLevel, submenu) => { + const containsPath = submenuContainsCurrentPath( + submenu.menu, + submenu.basePath, + submenu.versioned, + currentPath, + lang, + version + ); + return containsPath && submenu.level > maxLevel ? submenu.level : maxLevel; + }, 0); +} + +export function groupSubmenusByLevel(submenus: SubmenuData[]): Map { + return submenus.reduce((map, submenu) => { + const levelSubmenus = map.get(submenu.level); + if (levelSubmenus) { + levelSubmenus.push(submenu); + } else { + map.set(submenu.level, [submenu]); + } + return map; + }, new Map()); +} + +export function detectVersionFromUrl( + currentPath: string, + versions: VersionConfig[], + defaultVersion: string +): string { + return versions.find((v) => currentPath.includes(`/${v.id}/`))?.id ?? defaultVersion; +} diff --git a/src/components/patterns/TableOfContents/TableOfContents.astro b/src/components/patterns/TableOfContents/TableOfContents.astro new file mode 100644 index 0000000000..cd7092d151 --- /dev/null +++ b/src/components/patterns/TableOfContents/TableOfContents.astro @@ -0,0 +1,152 @@ +--- +/** + * TableOfContents Pattern Component + * + * Displays a table of contents for blog posts. + * - Desktop/tablet: sticky lateral sidebar + * - Mobile: collapsible dropdown + * + * @example + * + */ +import './TableOfContents.css'; +import type { HTMLAttributes } from 'astro/types'; +import { getLangFromUrl, useTranslations } from '@/i18n/utils'; +import { Icon } from 'astro-icon/components'; + +interface Heading { + depth: number; + slug: string; + text: string; +} + +interface Props extends HTMLAttributes<'nav'> { + headings: Heading[]; +} + +const { headings, class: className, ...rest } = Astro.props; + +const lang = getLangFromUrl(Astro.url); +const t = useTranslations(lang); + +const filteredHeadings = headings.filter((h) => h.depth === 2 || h.depth === 3); +const tocHeadings = + filteredHeadings.length > 0 + ? [{ depth: 2, slug: 'overview', text: t('toc.overview') }, ...filteredHeadings] + : []; +--- + +{ + tocHeadings.length > 0 && ( + + ) +} + + diff --git a/src/components/patterns/TableOfContents/TableOfContents.css b/src/components/patterns/TableOfContents/TableOfContents.css new file mode 100644 index 0000000000..430658e752 --- /dev/null +++ b/src/components/patterns/TableOfContents/TableOfContents.css @@ -0,0 +1,129 @@ +@layer patterns { + .toc { + width: 100%; + z-index: var(--z-index-sidebar-backdrop); + } + + .toc__heading { + display: none; + + @media (--md-up) { + display: block; + font-size: var(--font-size-xs); + font-weight: var(--font-weight-semibold); + color: var(--color-text-secondary); + text-transform: uppercase; + letter-spacing: var(--letter-spacing-wider); + margin: 0 0 var(--space-3) 0; + } + } + + .toc__details { + border: var(--border-width-1) solid var(--color-border-mute); + border-radius: var(--radius-lg); + overflow: hidden; + + @media (--md-up) { + border: none; + border-radius: 0; + overflow: visible; + } + } + + .toc__summary { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-3) var(--space-4); + font-size: var(--font-size-sm); + font-weight: var(--font-weight-semibold); + color: var(--color-text-primary); + cursor: pointer; + list-style: none; + user-select: none; + + &::-webkit-details-marker { + display: none; + } + + &:focus-visible { + outline: var(--color-focus-ring) solid var(--border-width-2); + outline-offset: var(--border-width-1); + } + + @media (--md-up) { + display: none; + } + } + + .toc__chevron { + width: var(--space-5); + height: var(--space-5); + color: var(--color-icon-secondary); + transition: transform 0.2s ease; + flex-shrink: 0; + + details[open] & { + transform: rotate(180deg); + } + } + + .toc__list { + list-style: none; + margin: 0; + padding: var(--space-3) var(--space-4) var(--space-3); + display: flex; + flex-direction: column; + gap: var(--space-0-5); + border-top: var(--border-width-1) solid var(--color-border-mute); + + @media (--md-up) { + padding: 0; + border-top: none; + border-left: var(--border-width-1) solid var(--color-border-mute); + } + } + + .toc__item--h3 { + padding-left: var(--space-3); + + @media (--md-up) { + padding-left: var(--space-4); + } + } + + .toc__link { + display: block; + font-size: var(--font-size-sm); + color: var(--color-text-secondary); + text-decoration: none; + padding: var(--space-1) 0; + line-height: var(--line-height-snug); + transition: color 0.15s ease; + + @media (--md-up) { + padding: var(--space-1) var(--space-3); + border-left: var(--border-width-2) solid transparent; + margin-left: calc(var(--border-width-1) * -1); + } + + &:hover { + color: var(--color-text-primary); + } + + &:focus-visible { + outline: var(--color-focus-ring) solid var(--border-width-2); + outline-offset: var(--border-width-1); + border-radius: var(--radius-sm); + } + } + + .toc__link--active { + color: var(--color-text-primary); + font-weight: var(--font-weight-medium); + + @media (--md-up) { + border-left-color: var(--color-text-primary); + } + } +} diff --git a/src/components/patterns/ThemeSwitcher/ThemeSwitcher.astro b/src/components/patterns/ThemeSwitcher/ThemeSwitcher.astro new file mode 100644 index 0000000000..df5d5b81b7 --- /dev/null +++ b/src/components/patterns/ThemeSwitcher/ThemeSwitcher.astro @@ -0,0 +1,79 @@ +--- +/** + * Theme Switcher Component + * Allows users to toggle between light and dark themes + * Persists theme preference to localStorage + */ +import './ThemeSwitcher.css'; +import { BodyMd } from '@/components/primitives'; +import { Icon } from 'astro-icon/components'; +import { getLangFromUrl, useTranslations } from '@/i18n/utils'; + +interface Props { + showLabels?: boolean; +} + +const { showLabels = true } = Astro.props; +const lang = getLangFromUrl(Astro.url); +const t = useTranslations(lang); +--- + + + + diff --git a/src/components/patterns/ThemeSwitcher/ThemeSwitcher.css b/src/components/patterns/ThemeSwitcher/ThemeSwitcher.css new file mode 100644 index 0000000000..cfaaa5524b --- /dev/null +++ b/src/components/patterns/ThemeSwitcher/ThemeSwitcher.css @@ -0,0 +1,44 @@ +@layer patterns { + .toggle { + border: 0; + background: none; + cursor: pointer; + display: flex; + align-items: center; + border-radius: var(--radius-base); + transition: var(--transition-colors); + gap: var(--space-3); + } + + .toggle:hover { + .toggle-icon { + background-color: var(--color-bg-mute); + } + } + + .toggle-icon { + padding: var(--space-2); + border-radius: var(--radius-base); + background-color: var(--color-bg-secondary); + transition: background-color var(--duration-200) var(--ease-in-out); + flex-shrink: 0; + } + + :root[data-theme='light'] .sun, + :root:not([data-theme]) .sun { + display: none; + } + + :root[data-theme='light'] .label-dark, + :root:not([data-theme]) .label-dark { + display: none; + } + + :root[data-theme='dark'] .label-light { + display: none; + } + + :root[data-theme='dark'] .moon { + display: none; + } +} diff --git a/src/components/patterns/VersionSwitcher/VersionSwitcher.astro b/src/components/patterns/VersionSwitcher/VersionSwitcher.astro new file mode 100644 index 0000000000..21a2c4bdd8 --- /dev/null +++ b/src/components/patterns/VersionSwitcher/VersionSwitcher.astro @@ -0,0 +1,113 @@ +--- +/** + * VersionSwitcher Component + * + * A dropdown to switch between different versions of documentation. + * Detects the current version from the URL or fallback to the default version. + * + * @example + * + */ + +import './VersionSwitcher.css'; +import { Icon } from 'astro-icon/components'; +import { Flex, Select } from '@/components/primitives'; +import type { SelectOption } from '@/components/primitives'; +import type { VersionConfig, VersionSwitcherProps } from './types'; +import { getLangFromUrl, useTranslations } from '@/i18n/utils'; + +const { + versions, + defaultVersion, + currentPath = '', + class: className, + ...rest +} = Astro.props as VersionSwitcherProps; +const lang = getLangFromUrl(Astro.url); +const t = useTranslations(lang); + +function getVersionFromPath(path: string, availableVersions: VersionConfig[]): string | null { + for (const version of availableVersions) { + if (path.includes(`/${version.id}/`)) { + return version.id; + } + } + return null; +} + +const urlVersion = getVersionFromPath(currentPath, versions); +const selectedVersion = urlVersion || defaultVersion; + +const selectOptions: SelectOption[] = versions.map((version) => ({ + value: version.id, + label: version.label, + selected: version.id === selectedVersion, +})); +--- + + +