diff --git a/package-lock.json b/package-lock.json index 29c75a3..702fbe5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1582,6 +1582,11 @@ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" + }, "@zeit/node-file-trace": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/@zeit/node-file-trace/-/node-file-trace-0.5.1.tgz", @@ -1666,11 +1671,11 @@ "integrity": "sha512-WT9LheDC4/d/sD/jgC6L5UMq4U9X3KNMy0JrXp/MdJL83ZqcuPQuMkj50beOX0dMub8IoZUYycfN7bIVZuU5zg==" }, "acorn-class-fields": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/acorn-class-fields/-/acorn-class-fields-0.3.4.tgz", - "integrity": "sha512-yqUCIu0UJHFmCVhH3Cq29UR+3OJo1CtNWf1ncxTf3KfdEDt7aD0iVYmX7UN+RvIHyOsgukzplQhNkgePtASLUw==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/acorn-class-fields/-/acorn-class-fields-0.3.6.tgz", + "integrity": "sha512-nOzMl1byCFAJLgxNUG7QorpzRHWlkBKVSSOMKUu+bVbVZG5lU4NZkOp/uA7CnE+NAsWhmxTsMgQdHsQXUO8Ulg==", "requires": { - "acorn-private-class-elements": "^0.2.5" + "acorn-private-class-elements": "^0.2.6" } }, "acorn-dynamic-import": { @@ -1689,16 +1694,16 @@ "integrity": "sha512-pshgiVR5mhpjFVdizKTN+kAGRqjJFUOEB3TvpQ6kiAutb1lvHrIVVcGoe5xzMpJkVNifCeymMG7/tsDkWn8CdQ==" }, "acorn-private-class-elements": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/acorn-private-class-elements/-/acorn-private-class-elements-0.2.5.tgz", - "integrity": "sha512-3eApRrJmPjaxWB3XidP8YMeVq9pcswPFE0KsSWVuhceCU68ZS8fkcf0fTXGhCmnNd7n48NWWV27EKMFPeCoJLg==" + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/acorn-private-class-elements/-/acorn-private-class-elements-0.2.6.tgz", + "integrity": "sha512-PV+AhOU1/vCx5zIBgGYLB5+OoT8IPKZUcWEGdLBTQgFBMMzPM9S5SKSG4EdiuULqoq3pV3C07rGuSC1Y5gbi/g==" }, "acorn-private-methods": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/acorn-private-methods/-/acorn-private-methods-0.3.1.tgz", - "integrity": "sha512-IV5XZInFQaQK5ucjJy/HAk2UYvt+Buax00evzwo8NSuo8zhOBhW5v6VOjAljYUhAzQ/Hosi+Kaz6xJmvPiSM4Q==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/acorn-private-methods/-/acorn-private-methods-0.3.2.tgz", + "integrity": "sha512-jTgRNDbEkbtxOIPUmDZ4u4oDGCO4tNPDNeW+jJrkbLl/Hzl9EVLva+kGQ289irSPhxi7FI9TjuXmIiqjnJcj9w==", "requires": { - "acorn-private-class-elements": "^0.2.4" + "acorn-private-class-elements": "^0.2.6" } }, "adjust-sourcemap-loader": { @@ -2246,6 +2251,12 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, + "buffer-loader": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/buffer-loader/-/buffer-loader-0.1.0.tgz", + "integrity": "sha512-ucbiQL7IicJm1EHuXC4Oheu2oGjz6qKRmyqAXYTtWe4iC49743AU2DHlWpF51qKF+g/t62Jk8Yn+3FaCz5jJEA==", + "dev": true + }, "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", @@ -2391,6 +2402,11 @@ "tslib": "^1.9.0" } }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -3295,9 +3311,9 @@ } }, "execa": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.2.tgz", - "integrity": "sha512-QI2zLa6CjGWdiQsmSkZoGtDx2N+cQIGb3yNolGTdjSQzydzLgYYf8LRuagp7S7fPimjcrzUDSUFd/MgzELMi4Q==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", + "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", "requires": { "cross-spawn": "^7.0.0", "get-stream": "^5.0.0", @@ -3507,6 +3523,40 @@ "locate-path": "^2.0.0" } }, + "find-yarn-workspace-root": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-1.2.1.tgz", + "integrity": "sha512-dVtfb0WuQG+8Ag2uWkbG79hOUzEsRrhBzgfn86g2sJPkzmcpGdghbNTfUKGTxymFrY/tLIodDzLoW9nOJ4FY8Q==", + "requires": { + "fs-extra": "^4.0.3", + "micromatch": "^3.1.4" + }, + "dependencies": { + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + } + } + }, "flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", @@ -4080,6 +4130,14 @@ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "requires": { + "ci-info": "^2.0.0" + } + }, "is-color-stop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", @@ -4319,6 +4377,14 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, + "klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "requires": { + "graceful-fs": "^4.1.11" + } + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -4813,14 +4879,14 @@ } }, "next-aws-lambda": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/next-aws-lambda/-/next-aws-lambda-2.4.1.tgz", - "integrity": "sha512-GdRUJdCMuBaMhn4PdCd07+rB2NCkw8S9OM5I9Pag6XUgyKpuTFOc+wmCR9nIvM3yPina7uuoBdLnP6am6gJQsA==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/next-aws-lambda/-/next-aws-lambda-2.5.0.tgz", + "integrity": "sha512-TKI0e+RFOuevQnkliE73VCzx9VRF8qpXuL18uxOJvPhCe09pxLwfaDQMuNKf3b2d9PS/Ajn+Y/O41bBuJMu4aA==" }, "next-on-netlify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/next-on-netlify/-/next-on-netlify-2.0.0.tgz", - "integrity": "sha512-Z6NJZFlfMckyTjsYfnduPe9+hP6rYdhyTffjGRcxEOkf2bQL8T+qrvxP2O789GddVhZFZVciQ75jgjOmX009Jg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/next-on-netlify/-/next-on-netlify-2.3.0.tgz", + "integrity": "sha512-zTQT5sKIXKrK0ghRU/PgM82H2qV1yTtrhFt+4EYLAc2T/DdYxqZ3lEJvO9yCZUxEQ/2VYKYM0he98UPdSn/Dlg==", "requires": { "@sls-next/lambda-at-edge": "1.2.0-alpha.3", "fs-extra": "^9.0.0", @@ -4832,6 +4898,11 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, "node-fetch": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", @@ -5194,6 +5265,88 @@ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" }, + "patch-package": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.2.2.tgz", + "integrity": "sha512-YqScVYkVcClUY0v8fF0kWOjDYopzIM8e3bj/RU1DPeEF14+dCGm6UeOYm4jvCyxqIEQ5/eJzmbWfDWnUleFNMg==", + "requires": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^2.4.2", + "cross-spawn": "^6.0.5", + "find-yarn-workspace-root": "^1.2.1", + "fs-extra": "^7.0.1", + "is-ci": "^2.0.0", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.0", + "rimraf": "^2.6.3", + "semver": "^5.6.0", + "slash": "^2.0.0", + "tmp": "^0.0.33" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "path-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", @@ -6439,6 +6592,11 @@ } } }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -7103,6 +7261,14 @@ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", diff --git a/package.json b/package.json index 5941b60..c900647 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,19 @@ { "name": "next-on-netlify-demo", "scripts": { + "postinstall": "patch-package", "dev": "next", "build": "next build", "postbuild": "next-on-netlify" }, "dependencies": { "next": "^9.4.4", - "next-on-netlify": "^2.0.0", + "next-on-netlify": "^2.3.0", + "patch-package": "^6.2.2", "react": "^16.13.1", "react-dom": "^16.13.1" + }, + "devDependencies": { + "buffer-loader": "^0.1.0" } } diff --git a/pages/api/[...slug].js b/pages/api/[...slug].js new file mode 100644 index 0000000..b5b459e --- /dev/null +++ b/pages/api/[...slug].js @@ -0,0 +1,41 @@ +export default async (req, res) => { + // Get the ID to show + const { query } = req + const { slug } = query + const id = slug[0] + + // Get the data + const fetchRes = await fetch(`https://api.tvmaze.com/shows/${id}`); + const show = await fetchRes.json(); + + // Show could not be found + if(fetchRes.status > 200) { + res.status(404) + res.json({ + error: 'Show could not be found :(' + }) + return + } + + res.status(200) + res.json({ + title: 'API route: catch-all endpoint', + description: 'This endpoint fetches a TV show from an external API. ' + + 'It is a catch-all endpoint. ' + + 'The first URL parameter determines the ID of the show to fetch. ' + + 'You can change the URL to anything else, such as /api/1871/whatever/path/you/want', + slug: slug, + viewCode: 'https://github.com/FinnWoelm/next-on-netlify-demo/tree/master/pages/api/[...slug].js', + goHome: 'https://next-on.netlify.app', + show: { + id: show.id, + name: show.name, + type: show.type, + language: show.language, + status: show.status, + premiered: show.premiered, + officialSite: show.officialSite, + averageRating: show.rating?.average + } + }) +} diff --git a/pages/api/[id].js b/pages/api/[id].js new file mode 100644 index 0000000..48ed937 --- /dev/null +++ b/pages/api/[id].js @@ -0,0 +1,38 @@ +export default async (req, res) => { + // Get the ID to show + const { query } = req + const { id } = query + + // Get the data + const fetchRes = await fetch(`https://api.tvmaze.com/shows/${id}`); + const show = await fetchRes.json(); + + // Show could not be found + if(fetchRes.status > 200) { + res.status(404) + res.json({ + error: 'Show could not be found :(' + }) + return + } + + res.status(200) + res.json({ + title: 'API route: dynamic endpoint', + description: 'This endpoint fetches a TV show from an external API. ' + + 'The ID is set in the URL: /api/:id. ' + + 'You can change the ID to any number between 1-10000. Try it!', + viewCode: 'https://github.com/FinnWoelm/next-on-netlify-demo/tree/master/pages/api/[id].js', + goHome: 'https://next-on.netlify.app', + show: { + id: show.id, + name: show.name, + type: show.type, + language: show.language, + status: show.status, + premiered: show.premiered, + officialSite: show.officialSite, + averageRating: show.rating?.average + } + }) +} diff --git a/pages/api/enterPreview/[id].js b/pages/api/enterPreview/[id].js new file mode 100644 index 0000000..0fbca07 --- /dev/null +++ b/pages/api/enterPreview/[id].js @@ -0,0 +1,12 @@ +export default (req, res) => { + // Get the ID to redirect to + const { query } = req + const { id } = query + + // Enable Preview Mode by setting preview mode cookies + res.setPreviewData({}) + + // Redirect to a page with support for preview mode + res.writeHead(307, { Location: `/previewMode/${id}` }) + res.end() +} diff --git a/pages/api/exitPreview/[id].js b/pages/api/exitPreview/[id].js new file mode 100644 index 0000000..c8f8b06 --- /dev/null +++ b/pages/api/exitPreview/[id].js @@ -0,0 +1,13 @@ +export default (req, res) => { + // Get the ID to redirect to + const { query } = req + const { id } = query + + // Clear the preview mode cookies. + // This function accepts no arguments. + res.clearPreviewData() + + // Redirect to a page with support for preview mode + res.writeHead(307, { Location: `/previewMode/${id}` }) + res.end() +} diff --git a/pages/api/image.js b/pages/api/image.js new file mode 100644 index 0000000..5a210f1 --- /dev/null +++ b/pages/api/image.js @@ -0,0 +1,10 @@ +import roll from 'buffer-loader!../../roll.jpg' + +export default (req, res) => { + res.status(200) + res.setHeader('Content-Type', 'image/jpeg') + // Send the image buffer. There are many other ways to send images and other + // files. For example, you can create a buffer from a base64-encoded string + // of an image: https://stackoverflow.com/a/28440633/6451879 + res.send(roll) +} diff --git a/pages/api/redirect.js b/pages/api/redirect.js new file mode 100644 index 0000000..e799276 --- /dev/null +++ b/pages/api/redirect.js @@ -0,0 +1,4 @@ +export default (req, res) => { + res.writeHead(307, { Location: '/you-have-been-redirected' }) + res.end() +} diff --git a/pages/api/show.js b/pages/api/show.js new file mode 100644 index 0000000..89ef06c --- /dev/null +++ b/pages/api/show.js @@ -0,0 +1,11 @@ +export default (req, res) => { + res.status(200) + res.json({ + title: 'API route: basic endpoint', + description: 'API endpoints are handled by Netlify Functions. ' + + 'You can run all sorts of code and return all sorts of responses. ' + + 'This one simply returns some JSON.', + viewCode: 'https://github.com/FinnWoelm/next-on-netlify-demo/tree/master/pages/api/show.js', + goHome: 'https://next-on.netlify.app' + }) +} diff --git a/pages/api/xml.js b/pages/api/xml.js new file mode 100644 index 0000000..8672e2d --- /dev/null +++ b/pages/api/xml.js @@ -0,0 +1,13 @@ +export default (req, res) => { + res.status(200) + res.setHeader('Content-Type', 'application/xml') + res.send( + ` + + API route: with content-type XML + API endpoints are handled by Netlify Functions. This one returns XML. + https://github.com/FinnWoelm/next-on-netlify-demo/tree/master/pages/api/xml.js + https://next-on.netlify.app + ` + ) +} diff --git a/pages/getInitialProps/[...slug].js b/pages/getInitialProps/[...slug].js new file mode 100644 index 0000000..8170486 --- /dev/null +++ b/pages/getInitialProps/[...slug].js @@ -0,0 +1,82 @@ +import Error from 'next/error' +import Link from 'next/link' + +const CatchAllShow = ({ errorCode, show, slug }) => { + + // If show was not found, render 404 page + if (errorCode) { + return + } + + // Otherwise, render the page + return ( + <> +

getInitialProps: with catch-all routing

+

+ This page uses getInitialProps() to fetch a TV show from an API.
+ It uses catch-all routing. +

+

+ URL parameters are made available in getInitialProps: +
+ {slug.map((item, index) => ( + + [{index}]: {item}
+
+ ))} +

+

+ The first URL parameter determines the ID of the TV show to fetch. +

+

+ You can change the URL to anything else, such as /getInitialProps/1871/whatever/path/you/want. Try it! +

+ + View code on GitHub + + +
+ +

Show #{show.id}: {show.name}

+ + https://api.tvmaze.com/shows/{show.id} + + +

+ Type: {show.type}
+ Language: {show.language}
+ Status: {show.status}
+ Premiered: {show.premiered}
+ Official Site: {show.officialSite}
+ Rating (average): {show.rating?.average} +

+ +
+ + + Go back home + + + ) +} + +CatchAllShow.getInitialProps = async ({ query }) => { + // Get the ID to render + const { slug } = query + const id = slug[0] + + // Get the show + const res = await fetch(`https://api.tvmaze.com/shows/${id}`); + const show = await res.json(); + + // Set error code if show could not be found + const errorCode = res.status > 200 ? res.status : false + + return { errorCode, show, slug } +} + +export default CatchAllShow diff --git a/pages/getInitialProps/[id].js b/pages/getInitialProps/[id].js new file mode 100644 index 0000000..95844bf --- /dev/null +++ b/pages/getInitialProps/[id].js @@ -0,0 +1,69 @@ +import Error from 'next/error' +import Link from 'next/link' + +const DynamicShow = ({ errorCode, show }) => { + + // If show was not found, render 404 page + if (errorCode) { + return + } + + // Otherwise, render show + return ( + <> +

getInitialProps: with dynamic routing

+

+ This page uses getInitialProps() to fetch a TV show from an API.
+ The ID is set in the URL: /getInitialProps/:id +

+

+ You can change the ID to any number between 1-10000. Try it! +

+ + View code on GitHub + + +
+ +

Show #{show.id}: {show.name}

+ + https://api.tvmaze.com/shows/{show.id} + + +

+ Type: {show.type}
+ Language: {show.language}
+ Status: {show.status}
+ Premiered: {show.premiered}
+ Official Site: {show.officialSite}
+ Rating (average): {show.rating?.average} +

+ +
+ + + Go back home + + + ) +} + +DynamicShow.getInitialProps = async ({ query }) => { + // Get the ID to render + const { id } = query + + // Get the data + const res = await fetch(`https://api.tvmaze.com/shows/${id}`); + const show = await res.json(); + + // Set error code if show could not be found + const errorCode = res.status > 200 ? res.status : false + + return { errorCode, show } +} + +export default DynamicShow diff --git a/pages/getInitialProps/show.js b/pages/getInitialProps/show.js new file mode 100644 index 0000000..0f7ddf7 --- /dev/null +++ b/pages/getInitialProps/show.js @@ -0,0 +1,54 @@ +import Link from 'next/link' + +const Show = ({ show }) => ( + <> +

getInitialProps: basic page

+

+ This page uses getInitialProps() to fetch a TV show from an API. +

+

+ When navigating client-side, getInitialProps() runs on the client. +
+ When requesting this page directly, the page is rendered server-side by a Netlify Function. +

+ + View code on GitHub + + +
+ +

Show #{show.id}: {show.name}

+ + https://api.tvmaze.com/shows/{show.id} + + +

+ Type: {show.type}
+ Language: {show.language}
+ Status: {show.status}
+ Premiered: {show.premiered}
+ Official Site: {show.officialSite}
+ Rating (average): {show.rating?.average} +

+ +
+ + + Go back home + + +) + +Show.getInitialProps = async () => { + // Get the show + const res = await fetch('https://api.tvmaze.com/shows/768'); + const show = await res.json(); + + return { show } +} + +export default Show diff --git a/pages/getServerSideProps/[...slug].js b/pages/getServerSideProps/[...slug].js new file mode 100644 index 0000000..480fe0e --- /dev/null +++ b/pages/getServerSideProps/[...slug].js @@ -0,0 +1,81 @@ +import Error from 'next/error' +import Link from 'next/link' + +const CatchAllShow = ({ errorCode, show, slug }) => { + + // If show was not found, render 404 page + if (errorCode) { + return + } + + return ( + <> +

getServerSideProps: with catch-all routing

+

+ This page uses getServerSideProps() to fetch a TV show from an API.
+ It uses catch-all routing. +

+

+ URL parameters are made available in getServerSideProps: +
+ {slug.map((item, index) => ( + + [{index}]: {item}
+
+ ))} +

+

+ The first URL parameter determines the ID of the TV show to fetch. +

+

+ You can change the URL to anything else, such as /getServerSideProps/112/whatever/path/you/want. Try it! +

+ + View code on GitHub + + +
+ +

Show #{show.id}: {show.name}

+ + https://api.tvmaze.com/shows/{show.id} + + +

+ Type: {show.type}
+ Language: {show.language}
+ Status: {show.status}
+ Premiered: {show.premiered}
+ Official Site: {show.officialSite}
+ Rating (average): {show.rating?.average} +

+ +
+ + + Go back home + + + ) +} + +export async function getServerSideProps({ params }) { + // Get the ID to render + const { slug } = params + const id = slug[0] + + // Get the data + const res = await fetch(`https://api.tvmaze.com/shows/${id}`); + const show = await res.json(); + + // Set error code if show could not be found + const errorCode = res.status > 200 ? res.status : false + + return { props: { errorCode, show, slug } } +} + +export default CatchAllShow diff --git a/pages/getServerSideProps/[id].js b/pages/getServerSideProps/[id].js new file mode 100644 index 0000000..9f47c7d --- /dev/null +++ b/pages/getServerSideProps/[id].js @@ -0,0 +1,68 @@ +import Error from 'next/error' +import Link from 'next/link' + +const DynamicShow = ({ errorCode, show }) => { + + // If show was not found, render 404 page + if (errorCode) { + return + } + + return ( + <> +

getServerSideProps: with dynamic routing

+

+ This page uses getServerSideProps() to fetch a TV show from an API.
+ The ID is set in the URL: /getServerSideProps/:id +

+

+ You can change the ID to any number between 1-10000. Try it! +

+ + View code on GitHub + + +
+ +

Show #{show.id}: {show.name}

+ + https://api.tvmaze.com/shows/{show.id} + + +

+ Type: {show.type}
+ Language: {show.language}
+ Status: {show.status}
+ Premiered: {show.premiered}
+ Official Site: {show.officialSite}
+ Rating (average): {show.rating?.average} +

+ +
+ + + Go back home + + + ) +} + +export async function getServerSideProps({ params }) { + // Get the ID to render + const { id } = params + + // Get the data + const res = await fetch(`https://api.tvmaze.com/shows/${id}`); + const show = await res.json(); + + // Set error code if show could not be found + const errorCode = res.status > 200 ? res.status : false + + return { props: { errorCode, show } } +} + +export default DynamicShow diff --git a/pages/getServerSideProps/show.js b/pages/getServerSideProps/show.js new file mode 100644 index 0000000..9b4abab --- /dev/null +++ b/pages/getServerSideProps/show.js @@ -0,0 +1,58 @@ +import Link from 'next/link' + +const Show = ({ show }) => ( + <> +

getServerSideProps: basic page

+

+ This page uses getServerSideProps() to fetch a TV show from an API. +

+

+ getServerSideProps() never runs on the client. +
+ When navigating client-side, getServerSideProps() runs inside a Netlify + Function. The resulting JSON data is used to render this page. +
+ When requesting this page directly, the entire page is rendered + server-side by a Netlify Function. +

+ + View code on GitHub + + +
+ +

Show #{show.id}: {show.name}

+ + https://api.tvmaze.com/shows/{show.id} + + +

+ Type: {show.type}
+ Language: {show.language}
+ Status: {show.status}
+ Premiered: {show.premiered}
+ Official Site: {show.officialSite}
+ Rating (average): {show.rating?.average} +

+ +
+ + + Go back home + + +) + +export async function getServerSideProps() { + // Get the show + const res = await fetch('https://api.tvmaze.com/shows/263'); + const show = await res.json(); + + return { props: { show } } +} + +export default Show diff --git a/pages/getStaticProps/[...slug].js b/pages/getStaticProps/[...slug].js new file mode 100644 index 0000000..92a0934 --- /dev/null +++ b/pages/getStaticProps/[...slug].js @@ -0,0 +1,109 @@ +import Link from 'next/link' + +const CatchAllShow = ({ show, slug }) => ( + <> +

getStaticProps: with catch-all routing

+

+ This page uses getStaticProps() to fetch a TV show from an API.
+ It uses catch-all routing. +

+

+ Because the page uses catch-all routing, the list of paths are + pre-defined in getStaticPaths. +
+ For this page, you can choose between one of the following pre-defined + paths: +

+ + + View code on GitHub + + +
+ +

Show #{show.id}: {show.name}

+ + https://api.tvmaze.com/shows/{show.id} + + +

+ Type: {show.type}
+ Language: {show.language}
+ Status: {show.status}
+ Premiered: {show.premiered}
+ Official Site: {show.officialSite}
+ Rating (average): {show.rating?.average} +

+ +
+ + + Go back home + + +) + +export async function getStaticPaths() { + // Define the paths we want to prerender + const paths = [ + { params: { slug: ['1719', 'mr', 'bean' ] } }, + { params: { slug: ['143', 'silicon', 'valley' ] } }, + { params: { slug: ['490', 'star', 'trek' ] } }, + { params: { slug: ['431', 'friends' ] } }, + { params: { slug: ['361', 'saturday', 'night', 'live' ] } }, + { params: { slug: ['6544', 'sesame', 'street' ] } }, + ] + + // We'll pre-render only these paths at build time. + // { fallback: false } means other routes should 404. + return { paths, fallback: false } +} + +export async function getStaticProps({ params }) { + // Get the ID to render + const { slug } = params + const id = slug[0] + + // Get the data + const res = await fetch(`https://api.tvmaze.com/shows/${id}`); + const show = await res.json(); + + return { props: { show, slug } } +} + +export default CatchAllShow diff --git a/pages/getStaticProps/[id].js b/pages/getStaticProps/[id].js new file mode 100644 index 0000000..057924d --- /dev/null +++ b/pages/getStaticProps/[id].js @@ -0,0 +1,71 @@ +import Link from 'next/link' + +const DynamicShow = ({ show }) => ( + <> +

getStaticProps: with dynamic routing

+

+ This page uses getStaticProps() to fetch a TV show from an API.
+ The ID is set in the URL: /getStaticProps/:id +

+

+ Because the page uses dynamic routing, the list of paths are pre-defined + in getStaticPaths. +
+ For this page, you can change the ID to any number between 1-100. Try it! +

+ + View code on GitHub + + +
+ +

Show #{show.id}: {show.name}

+ + https://api.tvmaze.com/shows/{show.id} + + +

+ Type: {show.type}
+ Language: {show.language}
+ Status: {show.status}
+ Premiered: {show.premiered}
+ Official Site: {show.officialSite}
+ Rating (average): {show.rating?.average} +

+ +
+ + + Go back home + + +) + +export async function getStaticPaths() { + // Define the paths we want to prerender + const paths = [] + for(let id = 1; id <= 100; id++) { + paths.push({ params: { id: String(id) }}) + } + + // We'll pre-render only these paths at build time. + // { fallback: false } means other routes should 404. + return { paths, fallback: false } +} + +export async function getStaticProps({ params }) { + // Get the ID to render + const { id } = params + + // Get the data + const res = await fetch(`https://api.tvmaze.com/shows/${id}`); + const show = await res.json(); + + return { props: { show } } +} + +export default DynamicShow diff --git a/pages/getStaticProps/show.js b/pages/getStaticProps/show.js new file mode 100644 index 0000000..dabc114 --- /dev/null +++ b/pages/getStaticProps/show.js @@ -0,0 +1,57 @@ +import Link from 'next/link' + +const Show = ({ show }) => ( + <> +

getStaticProps: basic page

+

+ This page uses getStaticProps() to fetch a TV show from an API. +

+

+ The page and the page's data are pre-rendered at build time. + Both are served from Netlify's super fast CDN. +
+ When navigating client-side, the page's JSON data is loaded. +
+ When requesting this page directly, the pre-rendered page is loaded. +

+ + View code on GitHub + + +
+ +

Show #{show.id}: {show.name}

+ + https://api.tvmaze.com/shows/{show.id} + + +

+ Type: {show.type}
+ Language: {show.language}
+ Status: {show.status}
+ Premiered: {show.premiered}
+ Official Site: {show.officialSite}
+ Rating (average): {show.rating?.average} +

+ +
+ + + Go back home + + +) + +export async function getStaticProps() { + // Get the show + const res = await fetch('https://api.tvmaze.com/shows/151'); + const show = await res.json(); + + return { props: { show } } +} + +export default Show diff --git a/pages/getStaticProps/withFallback/[id].js b/pages/getStaticProps/withFallback/[id].js new file mode 100644 index 0000000..10a941b --- /dev/null +++ b/pages/getStaticProps/withFallback/[id].js @@ -0,0 +1,96 @@ +import Error from 'next/error' +import Link from 'next/link' +import { useRouter } from 'next/router' + +const DynamicShow = ({ errorCode, show }) => { + const router = useRouter() + + // On Netlify, isFallback is NEVER true. Pages for any undefined paths are + // always server-side-rendered. + // See: https://github.com/FinnWoelm/next-on-netlify#fallbacks-for-pages-with-getstaticpaths + if (router.isFallback) { + return
Loading... (This only renders when using next dev)
+ } + + // If show was not found, render 404 page + if (errorCode) { + return + } + + return ( + <> +

getStaticProps: with fallback: true

+

+ This page uses getStaticProps() to fetch a TV show from an API.
+ The ID is set in the URL: /getStaticProps/withFallback/:id +

+

+ The page uses dynamic routing and some paths are pre-defined in + getStaticPaths (IDs 1-10). +
+ If you request one of those pages, you will see the pre-rendered HTML + page. +
+ If you load this page with any other ID (e.g., 32), the page will be + rendered server-side by a Netlify Function. +

+ + View code on GitHub + + +
+ +

Show #{show.id}: {show.name}

+ + https://api.tvmaze.com/shows/{show.id} + + +

+ Type: {show.type}
+ Language: {show.language}
+ Status: {show.status}
+ Premiered: {show.premiered}
+ Official Site: {show.officialSite}
+ Rating (average): {show.rating?.average} +

+ +
+ + + Go back home + + + ) +} + +export async function getStaticPaths() { + // Define the paths we want to prerender + const paths = [] + for(let id = 1; id <= 10; id++) { + paths.push({ params: { id: String(id) }}) + } + + // We'll pre-render only these paths at build time. + // { fallback: true } means other routes will be server-side rendered. + return { paths, fallback: true } +} + +export async function getStaticProps({ params }) { + // Get the ID to render + const { id } = params + + // Get the data + const res = await fetch(`https://api.tvmaze.com/shows/${id}`); + const show = await res.json(); + + // Set error code if show could not be found + const errorCode = res.status > 200 ? res.status : false + + return { props: { errorCode, show } } +} + +export default DynamicShow diff --git a/pages/index.js b/pages/index.js index 784e088..8d810e3 100644 --- a/pages/index.js +++ b/pages/index.js @@ -1,12 +1,31 @@ import Link from 'next/link' -const Index = ({ shows }) => ( -
- NextJS on Netlify Banner +const Index = () => ( + <> +
+ NextJS on Netlify Banner +
+
+ + NPM version + + {' '} + + MIT license + + {' '} + + NPM downloads + + {' '} + + Tested with Cypress.io + +
-

NextJS on Netlify

+

NextJS on Netlify: Server-Side Rendering Made Easy

This is a demo of a NextJS application with Server-Side Rendering (SSR).
@@ -24,114 +43,143 @@ const Index = ({ shows }) => ( npm package.

-

1. Server-Side Rendering Made Easy

-

- This page is server-side rendered. -
- It fetches a random list of five TV shows - from the TVmaze REST API. -
- Refresh this page to see it change. -

+
+

Examples

+

getInitialProps

-

2. Full Support for Dynamic Pages

-

- Dynamic pages, introduced in NextJS 9.2, are fully supported. -
- Click on a show to check out a server-side rendered page with dynamic - routing (/shows/:id). -

- +

getServerSideProps

-

3. Catch-All Routes? Included ✔

-

You can even take advantage of - {' '} - - NextJS' catch-all routes feature - . -
- Here are three examples: -

+

getStaticProps

-

4. Static Pages Stay Static

-

- next-on-netlify automatically determines which pages are dynamic and - which ones are static. -
- Only dynamic pages are server-side rendered. -
- Static pages are pre-rendered and served directly by Netlify's CDN. -

+

API Routes

+ +

Preview Mode

+
+

Want to Learn More?

- Check out the + Check out this demo's {' '} source code on GitHub - . + + . +
+ Or check out the + {' '} + + next-on-netlify npm package + + .

-
+ ) -Index.getInitialProps = async function() { - // Set a random page between 1 and 100 - const randomPage = Math.floor(Math.random()*100) + 1 - - // Get the data - const res = await fetch(`https://api.tvmaze.com/shows?page=${randomPage}`); - const data = await res.json(); - - return { shows: data.slice(0, 5) } -} - export default Index diff --git a/pages/previewMode/[id].js b/pages/previewMode/[id].js new file mode 100644 index 0000000..6c3407a --- /dev/null +++ b/pages/previewMode/[id].js @@ -0,0 +1,122 @@ +import Error from 'next/error' +import Link from 'next/link' + +const DynamicShow = ({ errorCode, object, preview }) => { + + // If object was not found, render 404 page + if (errorCode) { + return + } + + return ( + <> +

Preview Mode: page with preview mode

+

+ This page uses getServerSideProps() to fetch either + a TV show or a TV actor depending on whether preview mode is enabled. +
+ The ID is set in the URL: /previewMode/:id +

+

+ You can change the ID to any number between 1-10000. Try it! +

+

+ + View code for this page GitHub + +

+

+ + View code for 'enter' API endpoint on GitHub + +

+

+ + View code for 'exit' API endpoint on GitHub + +

+ +
+ + {preview ? ( + <> +

+ You are currently in preview mode. + {' '} + + Exit preview mode. + +

+

Actor #{object.id}: {object.name}

+ + https://api.tvmaze.com/people/{object.id} + + +

+ Country: {object.country?.name}
+ Gender: {object.gender}
+ Timezone: {object.country?.timezone}
+

+ + ) : ( + <> +

+ You are currently not in preview mode. + {' '} + + Enter preview mode. + +

+

Show #{object.id}: {object.name}

+ + https://api.tvmaze.com/shows/{object.id} + + +

+ Type: {object.type}
+ Language: {object.language}
+ Status: {object.status}
+ Premiered: {object.premiered}
+ Official Site: {object.officialSite}
+ Rating (average): {object.rating?.average} +

+ + )} + +
+ + + Go back home + + + ) +} + +export async function getServerSideProps({ params, preview }) { + // Get the ID to render + const { id } = params + + // Set the resource to fetch, depending on whether preview mode is active + const resource = preview ? 'people' : 'shows' + + // Get the data + const res = await fetch(`https://api.tvmaze.com/${resource}/${id}`); + const object = await res.json(); + + // Set error code if object could not be found + const errorCode = res.status > 200 ? res.status : false + + return { props: { errorCode, object, preview: Boolean(preview) } } +} + +export default DynamicShow diff --git a/pages/shows/[...params].js b/pages/shows/[...params].js deleted file mode 100644 index d489aab..0000000 --- a/pages/shows/[...params].js +++ /dev/null @@ -1,64 +0,0 @@ -import Error from 'next/error' -import Link from 'next/link' - -const CatchAll = ({ errorCode, show, params }) => { - - // If show item was not found, render 404 page - if (errorCode) { - return - } - - // Otherwise, render show - return ( -
-

- This is a server-side rendered catch-all page. It catches all requests - made to /shows/:id/any/path/can/go/here... and makes those parameters - available in getInitialProps(): -
- {params.map((param, index) => ( - - [{index}]: {param}
-
- ))} -
- Refresh the page to see server-side rendering in action. -
- You can also try changing the URL to something random, - such as /shows/{show.id}/whatever/path/you/want -

- -
- -

Show #{show.id}

-

- {show.name} -

- -
- - - Go back home - -
- ) -} - -CatchAll.getInitialProps = async ({ res: req, query }) => { - // Get the params to render - const { params } = query - - // Get the ID to render - const id = params[0] - - // Get the data - const res = await fetch(`https://api.tvmaze.com/shows/${id}`); - const data = await res.json(); - - // Set error code if show item could not be found - const errorCode = res.status > 200 ? res.status : false - - return { errorCode, show: data, params } -} - -export default CatchAll diff --git a/pages/shows/[id].js b/pages/shows/[id].js deleted file mode 100644 index c724dbd..0000000 --- a/pages/shows/[id].js +++ /dev/null @@ -1,53 +0,0 @@ -import Error from 'next/error' -import Link from 'next/link' - -const Show = ({ errorCode, show }) => { - - // If show item was not found, render 404 page - if (errorCode) { - return - } - - // Otherwise, render show - return ( -
-

- This page uses getInitialProps() to fetch the show with the ID - provided in the URL: /shows/:id -
- Refresh the page to see server-side rendering in action. -
- You can also try changing the ID to any other number between 1-10000. -

- -
- -

Show #{show.id}

-

- {show.name} -

- -
- - - Go back home - -
- ) -} - -Show.getInitialProps = async ({ res: req, query }) => { - // Get the ID to render - const { id } = query - - // Get the data - const res = await fetch(`https://api.tvmaze.com/shows/${id}`); - const data = await res.json(); - - // Set error code if show item could not be found - const errorCode = res.status > 200 ? res.status : false - - return { errorCode, show: data } -} - -export default Show diff --git a/pages/static.js b/pages/static.js deleted file mode 100644 index 53a2f3b..0000000 --- a/pages/static.js +++ /dev/null @@ -1,27 +0,0 @@ -import Link from 'next/link' - -const Static = props => ( -
-

- This page does not use getInitialProps. -
- It is a static page. -
- It is never server-side rendered. -
- It is served directly by Netlify's CDN. -
- The next-on-netlify npm - package takes care of deciding which pages to render server-side and which - ones to serve directly via CDN. -

- -
- - - Go back home - -
-) - -export default Static diff --git a/pages/static/[id].js b/pages/static/[id].js deleted file mode 100644 index 9904b40..0000000 --- a/pages/static/[id].js +++ /dev/null @@ -1,33 +0,0 @@ -import Link from 'next/link' - -const StaticWithID = props => ( -
-

- This page does not use getInitialProps. -
- It is a static page. -
- It is never server-side rendered. -
- It is served directly by Netlify's CDN. -
-
- But it has a dynamic URL parameter: /static/:id. -
- Try changing the ID. It will always render this page, no matter what you - put. -
- I am not sure what this is useful for. -
- But it's a feature of NextJS, so... I'm supporting it. -

- -
- - - Go back home - -
-) - -export default StaticWithID diff --git a/pages/you-have-been-redirected.js b/pages/you-have-been-redirected.js new file mode 100644 index 0000000..10676d0 --- /dev/null +++ b/pages/you-have-been-redirected.js @@ -0,0 +1,30 @@ +import Link from 'next/link' + +const RedirectionTarget = props => ( +
+

You have been redirected!

+

+ The API endpoint redirected you to this static page. +

+

+ + View code for API endpoint on GitHub + +

+

+ + Visit API endpoint with redirect header again + +

+ +
+ + + Go back home + +
+) + +export default RedirectionTarget diff --git a/patches/next-on-netlify+2.3.0.patch b/patches/next-on-netlify+2.3.0.patch new file mode 100644 index 0000000..b3bfe59 --- /dev/null +++ b/patches/next-on-netlify+2.3.0.patch @@ -0,0 +1,15 @@ +diff --git a/node_modules/next-on-netlify/lib/netlifyFunctionTemplate.js b/node_modules/next-on-netlify/lib/netlifyFunctionTemplate.js +index f9fd1ab..4e65227 100644 +--- a/node_modules/next-on-netlify/lib/netlifyFunctionTemplate.js ++++ b/node_modules/next-on-netlify/lib/netlifyFunctionTemplate.js +@@ -24,6 +24,10 @@ const callbackHandler = callback => ( + ) + + exports.handler = (event, context, callback) => { ++ // Enable base64 support ++ console.log(process.env); ++ process.env.BINARY_SUPPORT = "yes"; ++ + // In netlify dev, we currently do not receive headers as multi value headers. + // So we manually set them from the plain headers. This should become + // redundant as soon as https://github.com/netlify/cli/pull/938 is published. diff --git a/roll.jpg b/roll.jpg new file mode 100644 index 0000000..a437347 Binary files /dev/null and b/roll.jpg differ