Storing data about a user
How the metadata updates work#
In the SuperTokens core, we update the metadata using the following steps:
- Load old metadata from DB or use an empty object if we have nothing stored.
- Overwrite all root-level properties with the fields of the update object.
- Remove all root-level properties with a nullvalue.
This way, you can update parts of the metadata object without loading and merging the whole thing yourself.
important
Only root-level properties are merged into the stored object. Nested objects and all lower-level properties will be replaced.
Example#
- The stored object has a theme set in preferences, emails enabled in notifications, and a single todo item, telling them to switch to text messages:
{
  "preferences": { "theme": "dark" },
  "notifications": { "email": true },
  "todos": ["use-text-notifs"]
}
- Now, we want to update this by changing the notification setting and removing the entire todo list:
{
  "notifications": { "sms": true },
  "todos": null
}
- The result will be then:
{
  "preferences": { "theme": "dark" },
  "notifications": { "sms": true }
}
Multi Tenancy
User metadata that is associated with a user is shared across all tenants that that user is a part of. If instead, you want to store usermetadata on a tenent level, you can add a custom key in the JSON like:
{
  "tenant1": {
    "someKey": "specific to teannt1"
  },
  "tenant2": {
    "someKey": "specific to teannt2"
  },
  "someKey": "common for all tenants"
}
and then read the appropriate key based on the tenantId.
How to use#
- NodeJS
- GoLang
- Python
- Other Frameworks
Important
For other backend frameworks, you can follow our guide on how to spin up a separate server configured with the SuperTokens backend SDK  to authenticate requests and issue session tokens.
- Express
- Hapi
- Fastify
- Koa
- Loopback
- AWS Lambda / Netlify
- Next.js (Pages Dir)
- Next.js (App Dir)
- NestJS
import express from "express";
import { verifySession } from "supertokens-node/recipe/session/framework/express";
import UserMetadata from "supertokens-node/recipe/usermetadata";
let app = express();
app.post("/updateinfo", verifySession(), async (req, res) => {
  const session = req.session;
  const userId = session.getUserId();
  await UserMetadata.updateUserMetadata(userId, { newKey: "data" });
  res.json({ message: "successfully updated user metadata" });
});
import Hapi from "@hapi/hapi";
import { verifySession } from "supertokens-node/recipe/session/framework/hapi";
import { SessionRequest } from "supertokens-node/framework/hapi";
import UserMetadata from "supertokens-node/recipe/usermetadata";
let server = Hapi.server({ port: 8000 });
server.route({
  path: "/updateinfo",
  method: "post",
  options: {
    pre: [
      {
        method: verifySession(),
      },
    ],
  },
  handler: async (req: SessionRequest, res) => {
    const session = req.session;
    const userId = session!.getUserId();
    await UserMetadata.updateUserMetadata(userId, { newKey: "data" });
    return res.response({ message: "successfully updated user metadata" }).code(200);
  },
});
import Fastify from "fastify";
import { verifySession } from "supertokens-node/recipe/session/framework/fastify";
import UserMetadata from "supertokens-node/recipe/usermetadata";
let fastify = Fastify();
fastify.post(
  "/updateinfo",
  {
    preHandler: verifySession(),
  },
  async (req, res) => {
    const session = req.session;
    const userId = session.getUserId();
    await UserMetadata.updateUserMetadata(userId, { newKey: "data" });
    res.send({ message: "successfully updated user metadata" });
  },
);
import { verifySession } from "supertokens-node/recipe/session/framework/awsLambda";
import { SessionEvent } from "supertokens-node/framework/awsLambda";
import UserMetadata from "supertokens-node/recipe/usermetadata";
async function updateinfo(awsEvent: SessionEvent) {
  const session = awsEvent.session;
  const userId = session!.getUserId();
  await UserMetadata.updateUserMetadata(userId, { newKey: "data" });
  return {
    body: JSON.stringify({ message: "successfully updated user metadata" }),
    statusCode: 200,
  };
}
exports.handler = verifySession(updateinfo);
import KoaRouter from "koa-router";
import { verifySession } from "supertokens-node/recipe/session/framework/koa";
import { SessionContext } from "supertokens-node/framework/koa";
import UserMetadata from "supertokens-node/recipe/usermetadata";
let router = new KoaRouter();
router.post("/updateinfo", verifySession(), async (ctx: SessionContext, next) => {
  const session = ctx.session;
  const userId = session!.getUserId();
  await UserMetadata.updateUserMetadata(userId, { newKey: "data" });
  ctx.body = { message: "successfully updated user metadata" };
});
import { inject, intercept } from "@loopback/core";
import { RestBindings, post, response } from "@loopback/rest";
import { verifySession } from "supertokens-node/recipe/session/framework/loopback";
import { SessionContext } from "supertokens-node/framework/loopback";
import UserMetadata from "supertokens-node/recipe/usermetadata";
class UpdateInfo {
  constructor(@inject(RestBindings.Http.CONTEXT) private ctx: SessionContext) {}
  @post("/updateinfo")
  @intercept(verifySession())
  @response(200)
  async handler() {
    const session = this.ctx.session;
    const userId = session!.getUserId();
    await UserMetadata.updateUserMetadata(userId, { newKey: "data" });
    return { message: "successfully updated user metadata" };
  }
}
import { superTokensNextWrapper } from "supertokens-node/nextjs";
import { verifySession } from "supertokens-node/recipe/session/framework/express";
import { SessionRequest } from "supertokens-node/framework/express";
import UserMetadata from "supertokens-node/recipe/usermetadata";
export default async function updateInfo(req: any, res: any) {
  await superTokensNextWrapper(
    async (next) => {
      await verifySession()(req, res, next);
    },
    req,
    res,
  );
  const session = (req as SessionRequest).session;
  const userId = session!.getUserId();
  await UserMetadata.updateUserMetadata(userId, { newKey: "data" });
  res.json({ message: "successfully updated user metadata" });
}
import { NextResponse, NextRequest } from "next/server";
import SuperTokens from "supertokens-node";
import { withSession } from "supertokens-node/nextjs";
import UserMetadata from "supertokens-node/recipe/usermetadata";
import { backendConfig } from "@/app/config/backend";
SuperTokens.init(backendConfig());
export function POST(request: NextRequest) {
    return withSession(request, async (err, session) => {
        if (err) {
          return NextResponse.json(err, { status: 500 });
        }
        const userId = session!.getUserId();
        await UserMetadata.updateUserMetadata(userId, { newKey: "data" });
        return NextResponse.json({ success: "successfully updated user metadata" });
    });
}
import { Controller, Post, UseGuards, Session } from "@nestjs/common";
import { SessionContainer } from "supertokens-node/recipe/session";
import UserMetadata from "supertokens-node/recipe/usermetadata";
import { AuthGuard } from "./auth/auth.guard";
@Controller()
export class ExampleController {
  // For more information about "AuthGuard" and the "Session" decorator please read our NestJS guide.
  @Post("example")
  @UseGuards(new AuthGuard())
  async postExample(@Session() session: SessionContainer): Promise<{ message: string }> {
    const userId = session.getUserId();
    await UserMetadata.updateUserMetadata(userId, { newKey: "data" });
    return { message: "successfully updated user metadata" };
  }
}
import "github.com/supertokens/supertokens-golang/recipe/usermetadata"
func main() {
    userId := "..."
    usermetadata.UpdateUserMetadata(userId, map[string]interface{}{
        "newKey": "data",
    })
}
- Asyncio
- Syncio
from supertokens_python.recipe.usermetadata.asyncio import update_user_metadata
async def some_func():
    user_id = "..."
    await update_user_metadata(user_id, {
        "newKey": "data"
    })
from supertokens_python.recipe.usermetadata.syncio import update_user_metadata
user_id = "..."
update_user_metadata(user_id, {
    "newKey": "data"
})