Skip to main content

Installation

  • npm
  • pnpm
  • yarn
Terminal
npm install @runonatlas/express

Initialization

This only covers the backend initialization with Express. You will also need to initialize your frontend. For example, React or Next.js.
Initializing the Atlas SDK is very easy. You just need to follow these steps:
1

Set the Atlas API key in the environment variables

You can get it from the Atlas Dashboard.
.env
ATLAS_API_KEY="INSERT_YOUR_ATLAS_API_KEY"
2

Create the Atlas Express Client

To initialize the Atlas SDK, you need to instantiate the Atlas Client with the following parameters:
  1. Your Atlas API key.
  2. A function that returns the user id given a request object.
  • Using Clerk
  • Using any authentication provider
When using Clerk, you just need to pass the getAuth function to get the userId.
import { AtlasExpressClient } from "@runonatlas/express";
import { clerkMiddleware, getAuth } from "@clerk/express";
import express from "express";

const app = express();
const atlas = new AtlasExpressClient(
  (req) => getAuth(req).userId
);

app.use(clerkMiddleware());

app.use("/", atlas.router);
If you are having CORS issues only with Atlas, it probably means that your CORS middleware is running after the Atlas server. Make sure to place the CORS middleware before the Atlas server. For example:
import { AtlasExpressClient } from "@runonatlas/express";
import express from "express";
import { clerkMiddleware, getAuth } from "@clerk/express";
import cors from "cors";
import corsOptions from "./corsOptions";

const app = express();
const atlas = new AtlasExpressClient((req) => getAuth(req).userId);
app.use(cors(corsOptions));
app.use(clerkMiddleware());

app.use("/", atlas.router);

Restrict user access based on their subscriptions

You can prevent users from accessing restricted parts of your application based on their subscription. To do so, you can use our backend protection features.

Backend Protection

In order to prevent users from using certain endpoints unless they have a plan with the required features, we only need to add a middleware.
app.post(
  "/api/generate-caption",
  atlas.createFeaturesMiddleware(["data-explorer"]), // Add this line
  (req, res) => {
    // Your endpoint logic here
  }
);
This will automatically prevent users who don’t have the data-explorer feature in their active plan from accessing the endpoint.
Remember that you need to have followed the installation guide before you can use any of these features.

Limit-based features

Sometimes, just having a feature as enabled or disabled is not enough, and our pricing models require limits to be set. For example, 5 users per account, or 20GB of storage. Setting this up with Atlas is very easy. And, if at some point the limits change, you won’t need to change the code again!

Configuring the backend

The backend needs to understand what the limits are and how to check if the limit has been reached. To do so, when initializing the Atlas Client, you can provide a limits object with callbacks per each limit that you might want to use. For example, given a feature whose id is data-explorer, you can provide a callback to check if the limit has been reached:
src/index.ts
import { AtlasExpressClient } from "@runonatlas/express";
import express from "express";
import { getAuth } from "@clerk/express";
import { prisma } from "./db";

const app = express();
const atlas = new AtlasExpressClient((req) => getAuth(req).userId, {
  limits: {
    "data-explorer": (userId: string) =>
      prisma.explorations.count({
        where: {
          userId,
        },
      }),
  },
});

// ... Remaining code
Now, every time you the application needs to check if the data-explorer feature is available, Atlas will use the callback to compute it.
By default, if you haven’t configured a limit callback, Atlas will deny access to the feature if it has a limit. You can easily override this behavior. For example:
src/index.ts
import { AtlasExpressClient } from "@runonatlas/express";
import express from "express";
import { getAuth } from "@clerk/express";
import { prisma } from "./db";

const app = express();
const atlas = new AtlasExpressClient((req) => getAuth(req).userId, {
  baseClientOptions: {
    allowUnknownLimits: true,
  },
});

Usage-based and Credit-based features

Usage-based and credit-based features allow you to bill customers based on their consumption. These features track usage events and apply billing according to your pricing model configuration.

Understanding billing types

Atlas supports two types of billing for consumption-based features: Usage-based billing: Events are rolled up for the entire billing period, the price is applied, and the user is billed in arrears according to what they used. This is traditional usage-based billing where customers pay for what they consume after the fact. Credit-based billing: Users are allocated a specific amount of a custom pricing unit (e.g., credits) each month. When an event is received in Atlas that matches a credit-based price, their balance of custom pricing units is instantly deducted. This provides near real-time consumption tracking with immediate balance updates.
Atlas offers the ability to block access to a usage-based and credit-based features via the Max usage and Block overage options, respectively.Max usage: (Number) The maximum amount of a usage-based feature a customer is allowed to consume within a single billing period. If specified, Atlas will block access after usage reaches the amount specified and then unblock access at the start of the next billing period. If not specified, Atlas will allow unlimited usage in any given billing period.Block overage: (Boolean) Whether or not to prevent a customer from drawing custom pricing unit balance negative. If enabled, Atlas will block access to a feature when a balance of 0 has been detected. Otherwise, Atlas will continue accepting events and further reduce the customers credit balance negative. The next allocation of custom pricing units will be applied as normal, increasing the customers balance from the current negative balance by the amount of the allocation.Because Atlas’s event ingestion system is designed to be eventually consistent, it is possible for a small number of events to be processed before Atlas feature flagging has recognized that the customer has hit a balance of 0. This means that small overages are possible even if blockOverage is enabled. For more real time access control, please reach out to customer support at support@runonatlas.com.

Options to report events

To bill your customers correctly, your application must send an event to Atlas each time a usage-based or credit-based feature is consumed. There are three ways to report these usage events: Enjoy Atlas’ magic! By default, the Atlas Express Client automatically sends a single usage event to Atlas every time an endpoint protected by the createFeaturesMiddleware is called.
src/index.ts
app.post(
  "/api/generate-caption",
  atlas.createFeaturesMiddleware(["usage-based-feature"]), // Same middleware as before. It's smart enough to know it now needs to send events
  (req, res) => {
    // Your endpoint logic here
  }
);

Option B: Report a Custom Quantity

If an endpoint’s usage should count as more than one event, you can specify a fixed quantity directly in the middleware options.
src/index.ts
app.post(
  "/api/generate-caption",
  atlas.createFeaturesMiddleware(["usage-based-feature"], {
    events: { quantity: 2 },
  }),
  (req, res) => {
    // Your endpoint logic here
  }
);

Option C: Manual Event Reporting

For more complex scenarios, such as when the event quantity depends on request-specific logic or needs to be sent conditionally, you can take full manual control. First, disable automatic sending for the middleware, then use the atlas.client.enqueueEvents method within your endpoint logic to report the usage.
src/index.ts
app.post(
  "/api/generate-caption",
  atlas.createFeaturesMiddleware(["usage-based-feature"], {
    events: { sendAutomatically: false },
  }),
  async (req, res) => {
    // Your endpoint logic here
    await atlas.client.enqueueEvents({
      customerId: getAuth(req).userId,
      featureIds: ["usage-based-feature"],
      quantity: Math.floor(Math.random() * 10),
    });
  }
);
To disable automatic event reporting for all endpoints by default, set events: { sendAutomatically: false } in the defaultMiddlewareOptions when initializing the Atlas Express Client. This is useful if you plan to report most events manually.
src/index.ts
import { AtlasExpressClient } from "@runonatlas/express";
import express from "express";
import { getAuth } from "@clerk/express";

const app = express();
const atlas = new AtlasExpressClient((req) => getAuth(req).userId, {
  defaultMiddlewareOptions: {
    events: { sendAutomatically: false },
  },
});

Batching events

To optimize performance and avoid rate limits, the Atlas client automatically batches usage events before sending them. You can customize this batching behavior by configuring the eventsFlushAt (number of events) and eventsFlushInterval (time in milliseconds) options during client initialization.
src/index.ts
import { AtlasExpressClient } from "@runonatlas/express";
import express from "express";
import { getAuth } from "@clerk/express";

const app = express();
const atlas = new AtlasExpressClient((req) => getAuth(req).userId, {
  // Events will be sent every 10 events or every 1 second, whichever comes first.
  eventsFlushAt: 10,
  eventsFlushInterval: 1000,
});
You can also trigger a manual flush of all enqueued events at any time using the flushEvents method. This is particularly useful for ensuring no events are lost during a graceful shutdown of your application.
src/index.ts
async function onProcessShutdown() {
  await atlas.client.flushEvents();
}

Organizations

This only covers the backend configuration. You will also need to configure your frontend. For example, React or Next.js.
In many applications, it’s common to allow multiple users to belong to the same organization and share a single subscription. For example: Alice creates an organization called Standard Corp, purchases a plan, and invites Bob and Carol to join. Since all three are members of the same organization, they should share access to the same plan and limits. To enable this, you should use the organization ID as the user identifier instead of the individual user ID. This tells Atlas that access and usage should be scoped to the organization — not the individual — ensuring that Bob and Carol both have access under Standard Corp’s plan.
import { AtlasExpressClient } from "@runonatlas/express";
import { getAuth } from "@clerk/express";
import express from "express";

const app = express();
const atlas = new AtlasExpressClient(
  (req) => getAuth(req).orgId
);

app.use("/", atlas.router);