{"id":19710007,"url":"https://github.com/shibukawa/shadow-fetch","last_synced_at":"2025-04-29T17:31:07.325Z","repository":{"id":38956502,"uuid":"125091472","full_name":"shibukawa/shadow-fetch","owner":"shibukawa","description":"Accelerator for SSR","archived":false,"fork":false,"pushed_at":"2024-11-11T03:54:02.000Z","size":354,"stargazers_count":17,"open_issues_count":46,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-22T13:48:25.657Z","etag":null,"topics":["expressjs","fetch","javascript","javascript-library","nextjs","nodejs","server-side-rendering"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shibukawa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-13T17:44:11.000Z","updated_at":"2023-06-04T05:08:26.000Z","dependencies_parsed_at":"2023-02-08T18:32:07.464Z","dependency_job_id":"b8a720be-62b4-4f0e-be26-c9d469f004bd","html_url":"https://github.com/shibukawa/shadow-fetch","commit_stats":{"total_commits":31,"total_committers":1,"mean_commits":31.0,"dds":0.0,"last_synced_commit":"8fe71327d9530d3dc19bf4d2be4adf8a087826f9"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shibukawa%2Fshadow-fetch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shibukawa%2Fshadow-fetch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shibukawa%2Fshadow-fetch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shibukawa%2Fshadow-fetch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shibukawa","download_url":"https://codeload.github.com/shibukawa/shadow-fetch/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251549174,"owners_count":21607365,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["expressjs","fetch","javascript","javascript-library","nextjs","nodejs","server-side-rendering"],"created_at":"2024-11-11T22:05:52.517Z","updated_at":"2025-04-29T17:31:06.646Z","avatar_url":"https://github.com/shibukawa.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# shadow-fetch\n\n[![Build Status](https://travis-ci.org/shibukawa/shadow-fetch.svg?branch=master)](https://travis-ci.org/shibukawa/shadow-fetch)\n[![npm version](https://badge.fury.io/js/shadow-fetch.svg)](https://badge.fury.io/js/shadow-fetch)\n[![codecov](https://codecov.io/gh/shibukawa/shadow-fetch/branch/master/graph/badge.svg)](https://codecov.io/gh/shibukawa/shadow-fetch)\n[![Known Vulnerabilities](https://snyk.io/test/npm/shadow-fetch/badge.svg)](https://snyk.io/test/npm/shadow-fetch)\n[![NPM](https://nodei.co/npm/shadow-fetch.png)](https://nodei.co/npm/shadow-fetch/)\n\nAccelorator of Server Side Rendering (and unit tests).\n\nNext.js and Nuxt.js and some framework improves productivity of development.\nYou just write code once, the code run on the server and the client.\nAlmost all part or code is compatible including server access code.\n\nNext.js's documents uses [isomorphic-unfetch](https://github.com/developit/unfetch/tree/master/packages/isomorphic-unfetch) and Nuxt.js's document uses [Axios](https://github.com/axios/axios).\nThey are both excellent isomorphic libraries, but they make actual packet even if the client and the server work on the same process for server side rendering.\n\nThis library provides ``fetch()`` compatible function and Node.js' ``http.createServer()`` compatible function.\nThey are connected directly and bypass system calls.\n\n* You can make shorten SSR response a little\n* The request object has special attribute to identify actual access or shortcut access. You can skip authentication of BFF's API during SSR safely.\n* shadow-fetch provides special method to handle JSON. That method skip converting JSON into string.\n\n![ScreenShot](https://raw.github.com/shibukawa/shadow-fetch/master/doc/shadow-fetch.png)\n\n## Simple Benchmark\n\n|    | time |\n|:-----------:|:-----------|\n| ``node-fetch`` and ``http.Server`` | 14.3 mS |\n| ``shadow-fetch`` with standard API | 1.8 mS |\n| ``shadow-fetch`` with direct JSON API | 0.13mS |\n\n* 8th Gen Core i5 with Node 9.4.0.\n* You can test via ``node run benchmark``.\n\n## Installation\n\n```sh\n$ npm install shadow-fetch\n```\n\n## Usage\n\n### Node.js's http server\n\nshadow-fetch provides the function that is compatible with ``http.createServer()``. shadow-fetch's server is available inside the same process. So useally you should launch two servers.\n\n```js\nconst { createServer } = require(\"shadow-fetch\");\nconst { http } = require(\"http\");\n\nhandler = (req, res) =\u003e {\n    res.writeHead(200, { \"Content-Type\": \"application/json\" });\n    res.end(JSON.stringify({ message: \"hello\" }));\n};\n\nconst server = createServer(handler);\nserver.listen();\n\nconst server = http.createServer(handler);\nserver.listen(80);\n```\n\nYou can use ``fetch`` function to access this server:\n\n```js\nimport { fetch } from \"shadow-fetch\";\n\nconst res = await fetch(\"/test\");\n\nif (res.ok) {\n    const json = await res.json();\n    console.log(json.message);\n}\n```\n\n### Express.js\n\nExpress.js modifies request object (replace prototype). shadow-fetch's middleware for Express.js enable shadow-fetch's feature even if you uses Express.js\n\nYou should pass ``express()`` result to ``createServer()`` instead of  ``app.listen()``. That uses Node.js's ``createServer()`` internally,\n\n```js\nconst { createServer } = require(\"shadow-fetch\");\nconst { shadowFetchMiddleware } = require(\"shadow-fetch-express\");\n\nconst app = express();\napp.use(shadowFetchMiddleware);\napp.use(bodyParser.json());\napp.post(\"/test\", (req, res) =\u003e {\n    t.is(req.shadow, true);\n    t.is(req.body.message, \"hello\");\n    res.send({ message: \"world\" });\n});\nconst { fetch, createServer } = initFetch();\n\ncreateServer(app);\n```\n\n### Next.js\n\nIt is alomot as same as Express.js. This package provides factory function that makes ``fetch`` and ``createServer()`` pairs. But they are not working on Next.js environment. You should pre careted ``createServer()`` and ``fetch()`` functions they are available via just ``require`` (``import``).\n\n```js\nconst next = require(\"next\");\nconst http = require(\"http\");\nconst express = require(\"express\");\nconst bodyParser = require(\"body-parser\");\nconst createServer = require(\"shadow-fetch\");\nconst { shadowFetchMiddleware } = require(\"shadow-fetch-express\");\n\nconst dev = process.env.NODE_ENV !== \"production\";\nconst app = next({ dev });\nconst handle = app.getRequestHandler();\n\napp.prepare().then(() =\u003e {\n    const server = express();\n    server.use(shadowFetchMiddleware);\n    server.use(bodyParser.json());\n    server.get(\"/api/message\", (req, res) =\u003e {\n        res.json({message: \"hello via shadow-fetch\"});\n    });\n    server.get(\"*\", (req, res) =\u003e {\n        return handle(req, res);\n    });\n    // enable shadow fetch entrypoint\n    createServer(server).listen();\n    // enable standard HTTP entrypoint\n    http.createServer(server).listen(3000, err =\u003e {\n        if (err) throw err;\n        console.log(\"\u003e Ready on http://localhost:3000\");\n    });\n});\n```\n\n## Server Side Rendering and Authentication\n\nSometimes, you want to add authentication feature. The standard ``fetch`` were called from browseres and shadow-fetch was called during server side rendering.\n\nYou can detect the client environment inside event handler. If the API checks authentication, you should check ``shadow`` property.\n\n```js\nconst isAuthenticatedAPI = (req, res, next) =\u003e {\n    if (req.shadow || req.isAuthenticated()) {\n        return next();\n    } else {\n        res.sendStatus(403);\n    }\n};\n\nserver.get(\"/api/item/:id\", isAuthenticatedAPI, (req, res) =\u003e {\n    // API implementation\n});\n```\n\n[This is why I started to make this package](https://github.com/zeit/next.js/issues/3797).\n\n## Client API\n\n```js\nconst { fetch, Headers } = require(\"shadow-fetch\");\n```\n\nIt provides three functions that are almost compatible with standard [``fetch()``](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API):\n\n* ``shadowFetch()``: Provides direct access between ``createServer``.\n* ``fetch()``: Alias of ``shadowFetch()`` or standard ``fetch()``.\n\nUsually ``fetch()`` is the only function you use.\n\n|  Name  | on Node.js | on Browser |\n|:-----------:|:-----------|:------------|\n| ``fetch`` | ``shadowFetch`` | standard ``fetch`` |\n| ``shadowFetch`` | ``shadowFetch`` | ``shadowFetch`` |\n\nIf you want to select actual HTTP access or not explicitly, use regular ``fetch()``.\n\nThis library provides ``Headers`` compatible class too. But there is no ``Request`` class now.\n\n## Server API\n\n* ``createServer()``: It is a compatible function of Node.js's ``http.createServer()``. This package provides ``createShadowServer()`` for your convenience.\n* ``IncomingMessage``: Request object of server code. It is also compatible with Node.js's ``IncomingMessage`` except the following members:\n\n    * ``shadow`` property: It always ``true``. You can identify the request is made by ``shadowFetch`` or regular HTTP access.\n\n* ``ServerResponse``: Response object of server code. It is also compatible with Node.js's ``ServerResponse`` except the following members:\n\n    * ``writeJSON()``: It stores JSON without calling ``JSON.stringify()`` function. You can get JSON directly via ``Response#json()`` method of ``shadowFetch()``.\n\n```js\nconst { fetch, createServer } = require(\"shadow-fetch\");\n```\n\n## Utility Function\n\n* ``initFetch()``: It generates ``fetch()`` (shadow version) and ``createServer()`` they are connected internally. It is good for writing unit tests.\n\n## Trouble Shooting\n\n### ``\"shadow-fetch is not initialized properly. See https://github.com/shibukawa/shadow-fetch#trouble-shooting.\"``\n\nThis error is thrown when ``fetch`` is used without initialization. You should call shadow-fetch's ``createServer`` like [this](https://github.com/shibukawa/shadow-fetch#nodejss-http-server).\n\n### Error occures inside web server handlers\n\nExpress-middleware is not installed. Read [this section](https://github.com/shibukawa/shadow-fetch#expressjs).\n\nExpress.js overwrite prototype of ``ServerResponse``/``IncomingMessage`` with http's ones inside its framework.\nIn that case, required properties are not initilized because original constructor is not called.\nSo some method calls are failed like the following error:\n\n```\n TypeError: Cannot read property 'push' of undefined\n    at ServerResponse._writeRaw (_http_outgoing.js:281:24)\n    at ServerResponse._send (_http_outgoing.js:240:15)\n    at ServerResponse.end (_http_outgoing.js:770:16)\n    at ServerResponse.end (/Users/shibukawa/develop/frx/dam-front/node_modules/compression/index.js:107:21)\n    at ServerResponse.send (/Users/shibukawa/develop/frx/dam-front/node_modules/express/lib/response.js:221:10)\n```\n\n### ``Error: bundles/pages/index.js from UglifyJs Name expected`` during ``next build``\n\nYour next.js version is low. Try 5.1.0.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshibukawa%2Fshadow-fetch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshibukawa%2Fshadow-fetch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshibukawa%2Fshadow-fetch/lists"}