1.0.0 • Published 4 years ago

next-universal-storage v1.0.0

Weekly downloads
-
License
MIT
Repository
github
Last release
4 years ago
import withUniversalStorage, { UniversalStorage } from "next-universal-storage";

const universalDataKeys = ["REQUEST_ID", "ACCEPT_LANGUAGE"] as const;
const serverDataKeys = ["REQUEST_HEADERS"] as const;
const universalStorage = new UniversalStorage(universalDataKeys, serverDataKeys);

// /pages/_app.tsx
export default withUniversalStorage(universalStorage)(Application);

// /pages/about.tsx
const Page = (props: { requestId: string }) => <p>Your request ID is {props.requestId}</p>;
Page.getInitialProps = () => ({
  requestId: universalStorage.getFromUniversal("REQUEST_ID"),
});

Table of Contents

Features

FEATURESWHAT YOU CAN DO
⚛️ Designed for Next.jsAll you need is using HOC for _app.jsx
🔄 Sharing data between a server and clientYou don't need write if anywhere to handle data
🌴 Available anywhereYou can get and set data outside React components
🎩 Type SafeYou can use with TypeScript

Storage Types

next-universal-storage provides two storages. They are can store values by keys like KVS.

Universal Storage

https://github.com/jagaapple/next-universal-storage/blob/master/.docs/figure.png

Universal Storage is available on a server-side and client-side and it shares data between them. This is an interface and using Server Storage or Cookie Store inside.

On a client-side

When your code is running on a client-side, Universal Storage uses Cookie Store in web browsers. If a code sets a data, this storage writes Cookie Store using document.cookie = API. If a code gets a data, this storage gets data from Cookie Store using document.cookie API and converts to an object, then returns a value.

On a server-side

When your code is running on a server-side, firstly Universal Storage saves Cookie field of a request object via ctx.req.header.cookie in getInitialProps to Server Storage. If a code sets a data, this storage writes the data to Server Storage directly. If a code gets a data, this storage attempts to get data from Server Storage. On failure, this gets Cookie field value from Server Storage and converts to an object, then returns it.

Before the server returns a response, finally all data in Universal Storage are serialized and added to Set-Cookie fields of response headers. Web browsers which get the header fields will save them to Cookie Store automatically.

Encoding and decoding strategies

"https://www.example.com" --[SET]--> "https%3A%2F%2Fwww.example.com" --[GET]--> "https://www.example.com"

Because Universal Storage uses Cookie Store, all data in the storage are always encoded using encodeURIComponent . If your code gets data from the storage, this decodes using decodeURIComponent , so you can set a value which includes symbols and non-ASCII characters.

Serializing

Cookie is not simple KVS and it can configured some access limitations for security. When next-universal-storage converts pure objects to strings, it serializes them to follow RFC 6265.

next-universal-storage uses cookie package to serialize securely. Default options are the following.

OPTIONVALUE
path"/"
securetrue
expiresTen years from a timing to set values ( Date object)
sameSite"lax"

Also you can specify more detailed options by giving it to the third argument of a constructor. If you omit some of the above options, next-universal-storage merges the default options and your options and sets them.

const universalStorage = new UniversalStorage(
  universalDataKeys,
  serverDataKeys,
  {
    universalStorageCookieOptions: { secure: environment !== "development", sameSite: "strict" },
  },
);

Server Storage

Server Storage is available on only a server-side and it doesn't share data between a server-side and client-side.

Server Storage saves data to the global object global in Node.js, so you can save all value types. If your code writes data for the first time, Server Storage adds _NEXT_UNIVERSAL_STORAGE property to the global object. All data through Server Storage API are stored in the property as pure objects.

In order to prevent remaining data between requests, next-universal-storage clears all data in Server Storage at an early stages in getInitialProps .

Quick Start

Requirements

  • npm or Yarn
  • Node.js 10.0.0 or higher
  • Next.js 9.0.0 or higher

Installation

$ npm install next-universal-storage

If you are using Yarn, use the following command.

$ yarn add next-universal-storage

Setup

Firstly import UniversalStorage class and create a new instance object. You have to pass keys of data for Universal Storage and Server Storage to a constructor.

import { UniversalStorage } from "next-universal-storage";

const universalDataKeys = ["REQUEST_ID", "ACCEPT_LANGUAGE"] as const;
const serverDataKeys = ["REQUEST_HEADERS"] as const;
export const universalStorage = new UniversalStorage(universalDataKeys, serverDataKeys);

Then, import a HOC from a default exported in _app.jsx (or _app.tsx ). Give the instance object you created to the first argument and give an application component for Next.js to a returned function.

import App from "next/app";
import withUniversalStorage from "next-universal-storage";

class Application extends App {
  ...
}

export default withUniversalStorage(universalStorage)(Application);

The setup is done.

API

UniversalStorage.constructor

import { UniversalStorage } from "next-universal-storage";

const universalStorage = new UniversalStorage(
  ["foo", "bar"],
  ["baz"],
  {
    universalStorageCookieOptions: { secure: false },
  },
);

This creates a new instance object of UnviersalCookie class.

  • universalDataKeys: string[]
    • Required.
    • A list of keys used in the universal storage.
  • serverDataKeys: string[]
    • Required.
    • A list of keys used in the server storage.
  • options
    • Optional, a default value is {}
    • universalStorageCookieOptions: CookieSerializeOptions

UniversalStorage.prototype.universalDataKeys

const universalStorage = new UniversalStorage(["foo", "bar"], ["baz"]);

universalStorage.universalDataKeys; // ["foo", "bar"]

This returns keys for Universal Storage. The keys are a value you gives to the first argument of a constructor.

UniversalStorage.prototype.serverDataKeys

const universalStorage = new UniversalStorage(["foo", "bar"], ["baz"]);

universalStorage.serverDataKeys; // ["baz"]

This returns keys for Server Storage. The keys are a value you gives to the second argument of a constructor.

UniversalStorage.prototype.setToUniversal

const universalStorage = new UniversalStorage(["foo", "bar"], ["baz"]);

universalStorage.setToUniversal("bar", "123456");

This sets Universal Storage to a value.

  • key: string
    • Required.
    • You have to give a key listed in UniversalStorage.prototype.universalDataKeys , otherwise setting is ignored.
  • value: string
    • Required.

UniversalStorage.prototype.getFromUniversal

const universalStorage = new UniversalStorage(["foo", "bar"], ["baz"]);

universalStorage.getFromUniversal("bar");

This gets a string value from Universal Storage.

  • key: string
    • Required.
    • You have to give a key listed in UniversalStorage.prototype.universalDataKeys , otherwise undefined is always returned.
    • If a data could not be found, this returns undefined .

UniversalStorage.prototype.setToServer

const universalStorage = new UniversalStorage(["foo", "bar"], ["baz"]);

universalStorage.setToServer("baz", "123456"); // string

This sets Server Storage to a value.

  • key: string
    • Required.
    • You have to give a key listed in UniversalStorage.prototype.serverDataKeys , otherwise setting is ignored.
  • value: any
    • Required.

UniversalStorage.prototype.getFromUniversal

const universalStorage = new UniversalStorage(["foo", "bar"], ["baz"]);

universalStorage.getFromUniversal<boolean>("bar"); // boolean | undefined

This gets a value from Universal Storage. In TypeScript files, you can specify a returned type via Generic Types.

  • key: string
    • Required.
    • You have to give a key listed in UniversalStorage.prototype.serverDataKeys , otherwise undefined is always returned.
    • If a data could not be found, this returns undefined .

HOC (default exported value)

import withUniversalStorage from "next-universal-storage";

// If you use CommonJS...
const withUniversalStorage = require("next-universal-storage");

withUniversalStorage(universalStorage)(Application);

This prepares to use storages and returns a new React component. Use in _app.jsx (or _app.tsx ) and give an application component for Next.js to an argument of a returned function.

  • universalStorage: UniversalStorage
    • Required.
    • Returns a funciton.
  • nextApplicationComponent: Next.App (the first argument of a returned function)
    • Required.
    • Returns a new React component for Next.js.

Redux (Flux) vs next-universal-storage

First of all, I recommend to use next-universal-storage hardly. This is for developers who want to set and get data outside React components. If you want to refer data in any components, you should use state management architectures such as Redux and Flux, and others.

next-universal-storage is useful when you want to share data between a client-side and server-side such as a unique request ID and language settings.

Contributing to next-universal-storage

Bug reports and pull requests are welcome on GitHub at https://github.com/jagaapple/next-universal-storage. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

Please read Contributing Guidelines before development and contributing.

License

The library is available as open source under the terms of the MIT License.

Copyright 2020 Jaga Apple. All rights reserved.

1.0.0

4 years ago