Shopify Api Scopes that used for this guide:
write_metaobject_definitionsread_metaobject_definitionswrite_metaobjectsread_metaobjects
We will use ngrok tool to create secure tunnel to our express local server for this get started example.
npm i @drizzle-team/tento
      Create a .env file in the root of your project and add your Shopify Application variables:
SHOPIFY_API_KEY=
SHOPIFY_API_SECRET_KEY=
SHOPIFY_SCOPES=
SHOPIFY_SHOP=Shopify Api Scopes that used for this guide:
write_metaobject_definitionsread_metaobject_definitionswrite_metaobjectsread_metaobjectsCreate a schema.ts file and declare your metaobjects and metafields schema:
As of now Tento only supports one schema file.
import { metaobject, metafield } from '@drizzle-team/tento';
export const description = metafield({
  name: "Description",
  key: "description",
  namespace: "custom",
  ownerType: "PRODUCT",
  fieldDefinition: (f) => f.multiLineTextField(),
});
export const designer = metaobject({
  name: "Designer",
  type: "designer",
  fieldDefinitions: (f) => ({
    name: f.singleLineTextField({
      name: "Title",
      required: true,
      validations: (v) => [v.min(1), v.max(50)],
    }),
    description: f.multiLineTextField({
      name: "Description",
    }),
    website: f.url({
      name: "Website",
    }),
  }),
});Start your ngrok tunnel to localhost using:
ngrok http 5000We use port 5000 for our get started example, but you can use any port if you need.
Create a index.ts file and initialize express app. Put hostName and hostScheme variables from ngrok.
This is actually official @shopify/shopify-api OAuth example using express, you can find it here.
import "dotenv/config";
import "@shopify/shopify-api/adapters/node";
import express from "express";
import {
  CookieNotFound,
  InvalidOAuthError,
  LATEST_API_VERSION,
  Session,
  shopifyApi,
} from "@shopify/shopify-api";
import * as schema from "./db/schema";
import { tento } from "@drizzle-team/tento";
const shopify = shopifyApi({
  apiKey: process.env.SHOPIFY_API_KEY!,
  apiSecretKey: process.env.SHOPIFY_API_SECRET_KEY!,
  scopes: process.env.SHOPIFY_SCOPES!.split(/,\s*/),
  hostName: "***.ngrok-free.app", // ngrok link
  hostScheme: "https", // https for ngrok
  apiVersion: LATEST_API_VERSION,
  isEmbeddedApp: true,
});
async function main() {
    const app = express();
    app.use(express.json());
    // our future Shopify session
    let session: Session | undefined = undefined;
    const SHOPIFY_SHOP = process.env.SHOPIFY_SHOP!;
    app.get("/auth", async (req, res) => {
      await shopify.auth.begin({
        shop: shopify.utils.sanitizeShop(SHOPIFY_SHOP, true)!,
        callbackPath: "/auth/callback",
        isOnline: false,
        rawRequest: req,
        rawResponse: res,
      });
    });
    app.get("/auth/callback", async (req, res) => {
      try {
        const callback = await shopify.auth.callback({
          rawRequest: req,
          rawResponse: res,
        });
        session = callback.session;
        /*
        session: {
          id: string
          shop: string
          state: string
          isOnline: boolean
          accessToken: string
          scope: string
        }
        */
        return res.redirect("/");
      } catch (e: any) {
        if (e instanceof InvalidOAuthError) {
          return res.status(400).json({
            status: "ERROR",
            message: e.message,
            code: 400,
          });
        }
        if (e instanceof CookieNotFound) {
          await shopify.auth.begin({
            shop: shopify.utils.sanitizeShop(SHOPIFY_SHOP, true)!,
            callbackPath: "/auth/callback",
            isOnline: false,
            rawRequest: req,
            rawResponse: res,
          });
        }
      }
    });
    app.get("/", async (req, res) => {
      if (!session) {
        return res.redirect("/auth");
      }
      // initialize Shopify Graphql client using session from /auth/callback
      const gqlClient = new shopify.clients.Graphql({
        session,
      });
      // initialize Tento client using Shopify Graphql client
      const client = tento({ client: gqlClient, schema });
      // apply your previouse declared schema to Shopify Store
      await client.applySchema();
      return res.json({ success: true });
    });
    app.listen(5000, () => {
      console.log("App listening on the port 5000");
    });
}
main();Open Shopify Partners — Login to your account
Click Apps (in the left toolbar) — Click on your App
Click Configuration (in the left toolbar)
Scroll to URLs section and add:
APP URL
https://***.ngrok-free.app/Allowed redirection URL(s)
https://***.ngrok-free.app/auth/callbackClick Save and Release
To run any TypeScript files, you have several options, but let’s stick with one: using tsx
You’ve already installed tsx, so we can run our queries now
Run index.ts script
npx tsx src/index.ts
      Open your Shopify Store — Click Apps (in the left toolbar) — Click on your App
Wait until Shopify Store get response from your express server. Once it get it, you’ll see this screen.
After succeed you can go to Settings — Custom data and find generated Metafields in Products and Designer Metaobject definition