Skip to main content

Introducing the TERN stack and how to migrate from MERN to TERN

ยท 15 min read
Phil Leggetter

The MERN Stack - a web technology stack consisting of MongoDB, Express.js, React, and Node.js - was introduced around 2018 as a popular set of technologies to enable end-to-end development in JavaScript. Although the popularity of fullstack frameworks such as Next.js means that MERN isn't as popular as it once was, it is still a viable and productive choice with its decoupled architecture providing more flexibility than you'll get with a fullstack framework.

Today, I'd like to introduce the TERN (Tigris, Express.js, React, and Node.js) stack which replaces MongoDB with Tigris.

Migrating from MERN to TERN

In this post, you'll learn about MERN and TERN and why you should migrate from MERN to TERN. We'll cover all the steps involved in migrating a MERN application to TERN (Tigris, Express.js, React, and Node.js); first using Tigris MongoDB compatibility to get your existing application working with Tigris instead of MongoDB without any code changes (โš ๏ธ spoiler: it's as simple as updating the MongoDB connection string). Then, we'll make a few changes to get the application using the Tigris SDK instead of the MongoDB Node.js driver, completing the migration.

What is MERN?โ€‹

MERN (MongoDB, Express.js, React, and Node.js) is an alternative to the MEAN (MongoDB, Express.js, Angular, and Node.js). MEAN was introduced in 2013 and MERN followed as React became increasingly popular in 2018.

The MERN stack consists of:

  • Browser: React with either JavaScript or TypeScript (transpiled to JavaScript)
  • Server: Express.js with either JavaScript or TypeScript (transpiled to JavaScript)
  • Database: MongoDB most likely using the MongoDB Node.js driver, although an ODM such as Mongoose maybe used.
MERN stack

What is TERN?โ€‹

TERN (Tigris, Express.js, React, and Node.js) takes the ideas behind MERN and replaces MongoDB with Tigris.

TERN stack

Why migrate from MERN to TERN?โ€‹

So, why would you migrate from MERN (MongoDB) to TERN (Tigris)? Tigris is a serverless NoSQL database and search platform and an alternative to MongoDB.

Tigris has several benefits over MongoDB. Tigris:

Convinced? If yes, fantastic! If not, let us know why.

How to migrate from MERN to TERNโ€‹

To follow along, you'll need the following:

For this tutorial, we'll use a forked and slightly updated version of the MongoDB MERN example used in the official MongoDB MERN stack guide.

The updates that have been applied are to use the newest version of the MongoDB Node.js driver (for MongoDB 6.0+ wire protocol support), remove the unused Mongoose ODM dependency, update the code to use Promises, define the database name in an environment variable, use encodeURIComponent when passing parameters in URLs from the client, and add alt tags to images (not essential for this tutorial, but it felt like the right thing to do).

To follow along, clone the updated TERN stack example repo and switch to the mern branch:

git clone
cd tern-stack-example
git checkout mern

If you have a MongoDB instance running, you can try the MERN example out by following the instructions in the README.

We can now begin the migration.

You can migrate from MERN to TERN (and from any MongoDB application to Tigris) in either a one or two-step process.

The two-step process, and the process covered here, is:

  1. Start by using Tigris MongoDB Compatibility
  2. Migrating your code to using the Tigris TypeScript SDK

Using Tigris MongoDB Compatibilityโ€‹

We recently released Tigris MongoDB compatibility in beta which allows you to connect to Tigris using the MongoDB 6.0+ wire protocol. From a MERN perspective, this probably means an application that is using the MongoDB Node.js driver.

Updating the application we've just cloned to use Tigris MongoDB Compatibility is as simple as updating the connection string. So, head to theTigris Console and create a new project. You'll then land in your project's **Getting Started** section. From there, click the **MongoDB Compatibility** tab and the MongoDB connection string that you see on that tab.

Either update or create a mern/server/config.env file, setting the value of ATLAS_URI to the Tigris MongoDB connection string. Also, set DATABASE_NAME to the name of the Tigris project you just created. Your file will look similar to the following:


Then, follow the existing README instructions to start the server and the client.

In one terminal, start the server with:

cd mern/server
npm install
npm start
Server command line output
$ npm start

> server@1.0.0 start
> node server.js

Server is running on port: 5000
Successfully connected to MongoDB.

In another terminal, start the client with:

cd mern/client
npm install
npm start
Client command line output

Note: Some other dependencies could do with being updated.

Compiled with warnings.

(3769:3) autoprefixer: Replace color-adjust to print-color-adjust. The color-adjust shorthand is currently deprecated.

Search for the keywords to learn more about each warning.
To ignore, add // eslint-disable-next-line to the line before.

WARNING in ./node_modules/bootstrap/dist/css/bootstrap.css (./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[5].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[5].use[2]!./node_modules/source-map-loader/dist/cjs.js!./node_modules/bootstrap/dist/css/bootstrap.css)
Module Warning (from ./node_modules/postcss-loader/dist/cjs.js):

(3769:3) autoprefixer: Replace color-adjust to print-color-adjust. The color-adjust shorthand is currently deprecated.

webpack compiled with 1 warning

When you run the client, the browser automatically opens the browser. By default, the app will be running on http://localhost:3000. Try out the application to see it in action.

Please remember that all we've done to make this application work with Tigris Database is updated the connection string to point to Tigris Cloud.

Migrating from the MongoDB Node.js Driver to the Tigris SDKโ€‹

Tigris MongoDB compatibility is a great first step in migrating a MERN application. But, to take full advantage of TERN and Tigris, it's recommended that the application is updated to use the Tigris TypeScript SDK. There's more work required to do this, but the changes are pretty small, as this section will show.

Since the client and server are decoupled via the API that the Express.js application exposes, we only need to update the code for the Express.js API endpoints.

Update dependenciesโ€‹

Let's begin by updating the MERN application dependencies to use those required by TERN; remove the mongodb driver and add @tigrisdata/core:

npm uninstall monodb
npm i @tigrisdata/core

Add Tigris Configurationโ€‹

Update the mern/server/config.env file to contain additional Tigris config:

- ATLAS_URI=mongodb+srv://<username>:<password>
- DATABASE_NAME=mern-to-tern
+ TIGRIS_PROJECT=mern-to-tern

Replacing {TIGRIS_CLIENT_ID} and {TIGRIS_CLIENT_SECRET} with real values from your Tigris project application keys.

Define your database schema in TypeScriptโ€‹

Install TypeScript as a development dependency:

npm i -D typescript

Add the following to a mern/server/tsconfig.json:

"compilerOptions": {
"target": "es2016",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true

Define a schema in TypeScript to match the auto-generated schema that was created when using the application via Tigris MongoDB compatibility:

import {
} from "@tigrisdata/core";

export class Record {
@Field({ elements: TigrisDataTypes.STRING })
$k?: string[];

@PrimaryKey(TigrisDataTypes.BYTE_STRING, { order: 1, autoGenerate: true })
_id?: string;

name!: string;

position!: string;

level!: string;

The @TigrisCollection("records") decorator and supplied records value informs Tigris that there is a records collection of documents of type Record in the structure defined by that class.

The fields _id, name, position, and level, are all used within the application. The $k field isn't used but is required to allow the application to continue to work with the auto-generated MongoDB compatibility schema. As you'll see, all the application's fields are of type string.


You can also grab this schema from the Tigris Console under Your Project -> Database -> click on the records schema -> select TypeScript from the drop down:

The record schema defined in TypeScript within the Tigris Console

With the schema in place, we need to validate it with Tigris and use it with the Tigris TypeScript SDK. To do that, create a mern/server/script/setup.ts script with the following contents:

import { Tigris } from "@tigrisdata/core";
import { Record } from "../db/record";

import dotenv from "dotenv";
dotenv.config({ path: "./config.env" });

async function main() {
// setup client
const tigrisClient = new Tigris();
// ensure branch exists, create it if it needs to be created dynamically
await tigrisClient.getDatabase().initializeBranch();
// register schemas
await tigrisClient.registerSchemas([Record]);

.then(async () => {
console.log("Setup complete ...");
.catch(async (e) => {

In the above code, the config is loaded using dotenv, and a Tigris client is instantiated and assigned to the tigrisClient variable, which uses the loaded environment variables. Then, the database and database branch are initialized by tigrisClient.getDatabase().initializeBranch(). Finally, register the Record schema via tigrisClient.registerSchemas([Record]).

Update the package.json to make use of the setup script:

"name": "server",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
+ "setup": "npx ts-node scripts/setup.ts",
+ "prestart": "npm run setup",
"test": "echo \"Error: no test specified\" && exit 1"
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
+ "@tigrisdata/core": "^1.0.0-beta.44",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
- "mongodb": "5.2",
- "mongoose": "^5.12.4"
+ "reflect-metadata": "^0.1.13"
+ },
+ "devDependencies": {
+ "typescript": "^5.0.4"

This adds a setup NPM script utilized by prestart, which is automatically before the script defined in the start NPM script.

Note: the above diff also shows the other changed dependencies.

With the schema defined, we can move on to updating the application code.

Update the connection codeโ€‹

Next, update the connection code defined in mern/server/db/conn.js by removing the MongoDB Node.js driver and updating the code to use the Tigris TypeScript SDK:

- const { MongoClient } = require("mongodb");
- const Db = process.env.ATLAS_URI;
+ const { Tigris } = require("@tigrisdata/core");

- const client = new MongoClient(Db, {
- useNewUrlParser: true,
- useUnifiedTopology: true,
- });
+ const client = new Tigris();

var _db;

module.exports = {
connectToServer: async function (callback) {
try {
- const db = await client.connect();
- _db = db.db(process.ENV.DATABASE_NAME);
- console.log("Successfully connected to MongoDB.");
+ _db = await client.getDatabase();
+ console.log("Successfully connected to Tigris.");
return callback();
} catch (err) {
return callback(err);

The Tigris client is instantiated and automatically uses the environment variables we've defined in the mern/server/config.env file.

The other difference worth pointing out is that we don't name the database that is being used in client.getDatabase() since each Tigris Project has only one database.

Common MERN to TERN code changesโ€‹

A consistent difference across all of the Express.js route changes are:

  1. The MongoDB ObjectId is no longer used
  2. The query contains a filter to identify the document. So, from { _id: new ObjectId( } to { filter: { _id: } } is a consistent code change in all routes
  3. Collections are accessed via getCollection(collectionName) instead of collection(collectionName)

Migrate MERN database read to TERN database readโ€‹

Update the GET /record route:

- const ObjectId = require("mongodb").ObjectId;

// This section will help you get a list of all the records.
recordRoutes.route("/record").get(async function (req, res) {
- let db_connect = dbo.getDb("employees");
- const result = await db_connect.collection("records").find({}).toArray();
+ let db_connect = dbo.getDb();
+ const result = await db_connect.getCollection("records").findMany().toArray();

return res.json(result);

To retrieve multiple documents using the Tigris SDK use findMany, passing no parameters. This returns a cursor that exposes a toArray function to get an Array of results.

If you restart the server application, you'll see the main application page displaying the employees but via a call to GET /record which now uses the Tigris SDK.

Migrate MERN database create to TERN database createโ€‹

Update the POST /record/add route:

Note: To be more faithful to REST, this should be POST /record

recordRoutes.route("/record/add").post(async function (req, res) {
let db_connect = dbo.getDb();
let myobj = {
position: req.body.position,
level: req.body.level,

- const result = db_connect.collection("records").insertOne(myobj);
+ const result = await db_connect.getCollection("records").insertOne(myobj);

In this case, the method signature for insertOne stays the same.

Migrate MERN database update to TERN database updateโ€‹

Change the POST /update/:id route:

Note: To be more RESTful, this should be PATCH /record/:id or PATCH /record/:id if it replaces the whole resource.

recordRoutes.route("/update/:id").post(async function (req, res) {
let db_connect = dbo.getDb();
- let myquery = { _id: new ObjectId( };
- let newvalues = {
+ const myquery = {
+ filter: { _id: },
+ fields: {
$set: {
position: req.body.position,
level: req.body.level,
+ },
- const result = await db_connect
- .collection("records")
- .updateOne(myquery, newvalues);
+ const result = await db_connect.getCollection("records").updateOne(myquery);

console.log("1 document updated");

The updateOne Tigris SDK function differs from the MongoDB Node.js driver in that, it takes a single parameter with the following properties to achieve the same result:

  1. filter the filter used to find the single document to be updated
  2. fields where this property can use the $set syntax supported by the MongoDB driver.

Migrate MERN database delete to TERN database deleteโ€‹

Finally, update the DELETE /:id route:

Note: REST police: this should be DELETE /record/:id

recordRoutes.route("/:id").delete(async (req, res) => {
let db_connect = dbo.getDb();
- let myquery = { _id: new ObjectId( };
- const result = await db_connect.collection("records").deleteOne(myquery);
+ let myquery = { filter: { _id: } };
+ const result = await db_connect.getCollection("records").deleteOne(myquery);


As discussed, the only change here is the filter required by the Tigris SDK.

Let's add the Tigris logo ๐Ÿ˜„ Grab the Tigris green logo, save it to tern/client/public and update mern/client/src/components/navbar.js as follows:

<NavLink className="navbar-brand" to="/">
- alt="MongoDB logo"
+ alt="Tigris logo"
style={{ width: 25 + "%" }}
- src=""
+ src="/tigris-logo-green.png"

Run the TERN applicationโ€‹

And with those updates applied, the MERN application is converted to the TERN stack ๐ŸŽ‰

Since there are no functional changes to the front-end (only the logo update), you only need to stop and start the server only to see the changes in action:

npm start

And you'll see the following output:

npm start

> server@1.0.0 prestart
> npm run setup

> server@1.0.0 setup
> npx ts-node scripts/setup.ts

info - Using reflection to infer type of Record#$k
info - Using reflection to infer type of Record#name
info - Using reflection to infer type of Record#position
info - Using reflection to infer type of Record#level
info - Using Tigris at:
info - Using database branch: 'main'
event - Creating collection: 'records' in project: 'mern-to-tern'
Setup complete ...

> server@1.0.0 start
> node server.js

info - Using Tigris at:
Server is running on port: 5000
Successfully connected to Tigris.

Navigate to the client URL, which by default is http://localhost:3000, and try out the TERN app (we did refresh the client so the Tigris logo is present in this video):

If you have a look at the Tigris Console and explore the data, as shown in the video, you'll see the _id values are structured differently, and the $k value is not populated for documents created via the Tigris SDK.

Besides the logo, the application looks and performs exactly as the MERN application did. However, as the intro outlines, you can now also take advantage of the benefits of using Tigris.

You can find all the code changes applied to migrate the MongoDB MERN application to TERN in this diff.

Next Stepsโ€‹

How about raising a pull request to add full-text search to the TERN application using Tigris Search?

What should we call the Tigris version of MEAN? No, I've got it; it has to be NEAT!

Join the Tigris Discord and let us know what you think of TERN and the process of migrating a MERN (or any other MongoDB) application over to TERN.

Tigris is an open-source serverless NoSQL database and search platform. If you have any questions, or you'd like to contribute to the Tigris open source project.