Get Started with Tento and Shopify OAuth
This guide assumes familiarity with:
@shopify/shopify-api - package for access for the Shopify Admin API - read here
express - package for Node.js web framework - read here
dotenv - package for managing environment variables - read here
tsx - package for running TypeScript files - read here
ngrok - tool for creating secure tunnels to localhost, ideal for testing webhooks - read here
Shopify Application and developer Store - read here
Secret variables by Shopify official manual - read here
Understanding of Shopify Metaobject instance - read here
Understanding of Shopify Metafield instance - read here
We will use ngrok
tool to create secure tunnel to our express
local server for this get started example.
Step 1 - Install dependencies
npm i @drizzle-team/tento
yarn add @drizzle-team/tento
pnpm add @drizzle-team/tento
bun add @drizzle-team/tento
Step 2 - Setup connection variables
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=
tips
Shopify Api Scopes that used for this guide:
write_metaobject_definitions
read_metaobject_definitions
write_metaobjects
read_metaobjects
Step 3 - Declare schema file
Create a schema.ts
file and declare your metaobjects and metafields schema:
info
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" ,
}) ,
}) ,
});
Step 4 - Start ngrok
Start your ngrok tunnel to localhost
using:
ngrok http 5000
info
We use port 5000 for our get started example, but you can use any port if you need.
Step 5 - Setup express web app for OAuth
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 ();
Step 6 - Add ngrok link to your Shopify Application
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/callback
Click Save and Release
Step 7 - Run index.ts file
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
yarn tsx src/index.ts
pnpm tsx src/index.ts
bun tsx src/index.ts
Step 8 - Open Application in Shopify Store
Open your Shopify Store — Click Apps (in the left toolbar) — Click on your App
info
Wait until Shopify Store get response from your express server. Once it get it, you’ll see this screen.
info
After succeed you can go to Settings — Custom data and find generated Metafields in Products and Designer Metaobject definition