shadow-fetch
v0.2.1
Published
fake fetch/httpServer for SSR
Downloads
1
Maintainers
Readme
shadow-fetch
Accelorator of Server Side Rendering (and unit tests).
Next.js and Nuxt.js and some framework improves productivity of development. You just write code once, the code run on the server and the client. Almost all part or code is compatible including server access code.
Next.js's documents uses isomorphic-unfetch and Nuxt.js's document uses Axios. They 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.
This library provides fetch()
compatible function and Node.js' http.createServer()
compatible function.
They are connected directly and bypass system calls.
- You can make shorten SSR response a little
- The request object has special attribute to identify actual access or shortcut access. You can skip authentication of BFF's API during SSR safely.
- shadow-fetch provides special method to handle JSON. That method skip converting JSON into string.
Simple Benchmark
| | time |
|:-----------:|:-----------|
| node-fetch
and http.Server
| 14.3 mS |
| shadow-fetch
with standard API | 1.8 mS |
| shadow-fetch
with direct JSON API | 0.13mS |
- 8th Gen Core i5 with Node 9.4.0.
- You can test via
node run benchmark
.
Installation
$ npm install shadow-fetch
Usage
Node.js's http server
shadow-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.
const { createServer } = require("shadow-fetch");
const { http } = require("http");
handler = (req, res) => {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ message: "hello" }));
};
const server = createServer(handler);
server.listen();
const server = http.createServer(handler);
server.listen(80);
You can use fetch
function to access this server:
import { fetch } from "shadow-fetch";
const res = await fetch("/test");
if (res.ok) {
const json = await res.json();
console.log(json.message);
}
Express.js
Express.js modifies request object (replace prototype). shadow-fetch's middleware for Express.js enable shadow-fetch's feature even if you uses Express.js
You should pass express()
result to createServer()
instead of app.listen()
. That uses Node.js's createServer()
internally,
const { createServer } = require("shadow-fetch");
const { shadowFetchMiddleware } = require("shadow-fetch-express");
const app = express();
app.use(shadowFetchMiddleware);
app.use(bodyParser.json());
app.post("/test", (req, res) => {
t.is(req.shadow, true);
t.is(req.body.message, "hello");
res.send({ message: "world" });
});
const { fetch, createServer } = initFetch();
createServer(app);
Next.js
It 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
).
const next = require("next");
const http = require("http");
const express = require("express");
const bodyParser = require("body-parser");
const createServer = require("shadow-fetch");
const { shadowFetchMiddleware } = require("shadow-fetch-express");
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = express();
server.use(shadowFetchMiddleware);
server.use(bodyParser.json());
server.get("/api/message", (req, res) => {
res.json({message: "hello via shadow-fetch"});
});
server.get("*", (req, res) => {
return handle(req, res);
});
// enable shadow fetch entrypoint
createServer(server).listen();
// enable standard HTTP entrypoint
http.createServer(server).listen(3000, err => {
if (err) throw err;
console.log("> Ready on http://localhost:3000");
});
});
Server Side Rendering and Authentication
Sometimes, you want to add authentication feature. The standard fetch
were called from browseres and shadow-fetch was called during server side rendering.
You can detect the client environment inside event handler. If the API checks authentication, you should check shadow
property.
const isAuthenticatedAPI = (req, res, next) => {
if (req.shadow || req.isAuthenticated()) {
return next();
} else {
res.sendStatus(403);
}
};
server.get("/api/item/:id", isAuthenticatedAPI, (req, res) => {
// API implementation
});
This is why I started to make this package.
Client API
const { fetch, Headers } = require("shadow-fetch");
It provides three functions that are almost compatible with standard fetch()
:
shadowFetch()
: Provides direct access betweencreateServer
.fetch()
: Alias ofshadowFetch()
or standardfetch()
.
Usually fetch()
is the only function you use.
| Name | on Node.js | on Browser |
|:-----------:|:-----------|:------------|
| fetch
| shadowFetch
| standard fetch
|
| shadowFetch
| shadowFetch
| shadowFetch
|
If you want to select actual HTTP access or not explicitly, use regular fetch()
.
This library provides Headers
compatible class too. But there is no Request
class now.
Server API
createServer()
: It is a compatible function of Node.js'shttp.createServer()
. This package providescreateShadowServer()
for your convenience.IncomingMessage
: Request object of server code. It is also compatible with Node.js'sIncomingMessage
except the following members:shadow
property: It alwaystrue
. You can identify the request is made byshadowFetch
or regular HTTP access.
ServerResponse
: Response object of server code. It is also compatible with Node.js'sServerResponse
except the following members:writeJSON()
: It stores JSON without callingJSON.stringify()
function. You can get JSON directly viaResponse#json()
method ofshadowFetch()
.
const { fetch, createServer } = require("shadow-fetch");
Utility Function
initFetch()
: It generatesfetch()
(shadow version) andcreateServer()
they are connected internally. It is good for writing unit tests.
Trouble Shooting
"shadow-fetch is not initialized properly. See https://github.com/shibukawa/shadow-fetch#trouble-shooting."
This error is thrown when fetch
is used without initialization. You should call shadow-fetch's createServer
like this.
Error occures inside web server handlers
Express-middleware is not installed. Read this section.
Express.js overwrite prototype of ServerResponse
/IncomingMessage
with http's ones inside its framework.
In that case, required properties are not initilized because original constructor is not called.
So some method calls are failed like the following error:
TypeError: Cannot read property 'push' of undefined
at ServerResponse._writeRaw (_http_outgoing.js:281:24)
at ServerResponse._send (_http_outgoing.js:240:15)
at ServerResponse.end (_http_outgoing.js:770:16)
at ServerResponse.end (/Users/shibukawa/develop/frx/dam-front/node_modules/compression/index.js:107:21)
at ServerResponse.send (/Users/shibukawa/develop/frx/dam-front/node_modules/express/lib/response.js:221:10)
Error: bundles/pages/index.js from UglifyJs Name expected
during next build
Your next.js version is low. Try 5.1.0.
License
MIT