This getting started guide steps you through:
- Installing and configuring Protect.js in a standalone project
- Encrypting, searching, and decrypting data in a PostgreSQL database
Important
Prerequisites: Before you start you need to have this software installed:
- Node.js
- TypeScript
- PostgreSQL — see PostgreSQL's documentation for installing
The following is the basic file structure of the standalone project for this getting started guide.
In the src/protect/
directory, we have the table definition in schema.ts
and the Protect.js client in index.ts
.
📦 <project root>
├ 📂 src
│ ├ 📂 protect
│ │ ├ 📜 index.ts
│ │ └ 📜 schema.ts
│ └ 📜 index.ts
├ 📜 .env
├ 📜 cipherstash.toml
├ 📜 cipherstash.secret.toml
├ 📜 package.json
└ 📜 tsconfig.json
If you're following this getting started guide with an existing app, skip to the next step.
If you're following this getting started guide with a clean slate, create a basic structure by running:
mkdir -p protect-example/src/protect
cd protect-example
git init
npm init -y
Install the @cipherstash/protect
package with your package manager of choice:
npm install @cipherstash/protect
# or
yarn add @cipherstash/protect
# or
pnpm add @cipherstash/protect
Tip
Bun is not currently supported due to a lack of Node-API compatibility. Under the hood, Protect.js uses CipherStash Client which is written in Rust and embedded using Neon.
Lastly, install the CipherStash CLI:
-
On macOS:
brew install cipherstash/tap/stash
-
On Linux, download the binary for your platform, and put it on your
PATH
:
Note
You need to opt out of bundling when using Protect.js.
Protect.js uses Node.js specific features and requires the use of the native Node.js require
.
You need to opt out of bundling for tools like Webpack, esbuild, or Next.js.
Read more about building and bundling with Protect.js.
Important
Make sure you have installed the CipherStash CLI before following these steps.
To set up all the configuration and credentials required for Protect.js:
stash setup
If you haven't already signed up for a CipherStash account, this will prompt you to do so along the way.
At the end of stash setup
, you will have two files in your project:
cipherstash.toml
, which contains the configuration for Protect.jscipherstash.secret.toml
, which contains the credentials for Protect.js
Warning
Don't commit cipherstash.secret.toml
to git; it contains sensitive credentials.
The stash setup
command will append to your .gitignore
file with the cipherstash.secret.toml
file.
Read more about configuration via TOML file or environment variables.
Protect.js uses a schema to define the tables and columns that you want to encrypt and decrypt.
To define your tables and columns, add the following to src/protect/schema.ts
:
import { csTable, csColumn } from "@cipherstash/protect";
export const users = csTable("users", {
email: csColumn("email"),
});
export const orders = csTable("orders", {
address: csColumn("address"),
});
Searchable encryption:
If you want to search encrypted data in your PostgreSQL database, you must declare the indexes in schema in src/protect/schema.ts
:
import { csTable, csColumn } from "@cipherstash/protect";
export const users = csTable("users", {
email: csColumn("email").freeTextSearch().equality().orderAndRange(),
});
export const orders = csTable("orders", {
address: csColumn("address"),
});
Read more about defining your schema.
To import the protect
function and initialize a client with your defined schema, add the following to src/protect/index.ts
:
import { protect } from "@cipherstash/protect";
import { users, orders } from "./schema";
// Pass all your tables to the protect function to initialize the client
export const protectClient = await protect(users, orders);
The protect
function requires at least one csTable
to be provided.
Protect.js provides the encrypt
function on protectClient
to encrypt data.
encrypt
takes a plaintext string, and an object with the table and column as parameters.
Start encrypting data by adding this to src/index.ts
:
import { users } from "./protect/schema";
import { protectClient } from "./protect";
const encryptResult = await protectClient.encrypt("secret@squirrel.example", {
column: users.email,
table: users,
});
if (encryptResult.failure) {
// Handle the failure
console.log(
"error when encrypting:",
encryptResult.failure.type,
encryptResult.failure.message
);
}
const ciphertext = encryptResult.data;
console.log("ciphertext:", ciphertext);
Run this with:
npx tsx src/index.ts
The encrypt
function will return a Result
object with either a data
key, or a failure
key.
The encryptResult
will return one of the following:
// Success
{
data: {
c: '\\\\\\\\\\\\\\\\x61202020202020472aaf602219d48c4a...'
}
}
// Failure
{
failure: {
type: 'EncryptionError',
message: 'A message about the error'
}
}
Tip
Working with large payloads? Check out the model operations with bulk cryptography functions docs.
Use the decrypt
function to decrypt data.
decrypt
takes an encrypted data object as a parameter.
import { protectClient } from "./protect";
const decryptResult = await protectClient.decrypt(ciphertext);
if (decryptResult.failure) {
// Handle the failure
}
const plaintext = decryptResult.data;
The decrypt
function returns a Result
object with either a data
key, or a failure
key.
The decryptResult
will return one of the following:
// Success
{
data: 'secret@squirrel.example'
}
// Failure
{
failure: {
type: 'DecryptionError',
message: 'A message about the error'
}
}
Tip
Working with large payloads? Check out the model operations with bulk cryptography functions docs.
Encrypted data can be stored in any database that supports JSONB.
To store the encrypted data, specify the column type as jsonb
:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email jsonb NOT NULL,
);