Auth Persistence Layer (APL)
Auth Persistence Layer (APL) is a technology-agnostic interface for managing auth data of registered Apps. APL provides a common set of operations that can be used by your application and App SDK.
The following doc contains a JavaScript / TypeScript implementation used by official Saleor apps.
Available methods
-
get: (saleorApiUrl: string) => Promise<AuthData | undefined>
- If the entry for given saleorApiUrl exists, returns AuthData object. -
set: (authData: AuthData) => Promise<void>
- Save auth data. -
delete: (saleorApiUrl: string) => Promise<void>
- Remove auth data from the given API URL. -
getAll: () => Promise<AuthData[]>
- Returns all auth data available. -
isReady: () => Promise<AplReadyResult>
- Check if the persistence layer behind APL is ready. For example, when a database connection was established. -
isConfigured: () => Promise<AplConfiguredResult>
- Check if the persistence layer behind APL is configured. For example, when an env variable is required by the database connection.
AuthData
Interface containing data used for communication with the Saleor API:
export interface AuthData {
domain: string;
token: string;
saleorApiUrl: string;
appId: string;
jwks: string;
}
domain
- Domain of the APItoken
- Authorization tokensaleorApiUrl
- Full URL to the Saleor GraphQL APIappID
- ID of the app assigned during the installation processjwks
- JSON Web Key Set available athttps://<your-saleor-domain>/.well-known/jwks.json
, cached in the APL for the faster webhook validation
AplReadyResult & AplConfiguredResult
Responses from isReady()
and isConfigured()
should match following:
type AplReadyResult =
| {
ready: true;
}
| {
ready: false;
error: Error;
};
type AplConfiguredResult =
| {
configured: true;
}
| {
configured: false;
error: Error;
};
Example implementation
Let's create an APL which uses Redis for data storage:
import { createClient } from "redis";
import { APL, AuthData } from "@saleor/app-sdk/apl";
const client = createClient();
await client.connect();
/**
* The APL uses API URL as a key to store and retrieve AuthData.
* If you intend to use the same Redis instance for multiple Apps,
* add prefix to the keys to avoid overwriting the data by different apps.
* Keys will be formatted as below:
* - `APPID1:https://other-example.saleor.cloud/graphql`
* - `APPID1:https://example.saleor.cloud/graphql/`
* - `APPID2:https://example.saleor.cloud/graphql/`
**/
const prepareAuthDataKey = (apiUrl: string) => `${APP_ID}:${apiUrl}`;
const redisAPL: APL = {
get: async (saleorApiUrl: string) => {
const response = await client.get(prepareAuthDataKey(saleorApiUrl));
if (response) {
return JSON.parse(response);
}
return;
},
set: async (authData: AuthData) => {
await client.set(
prepareAuthDataKey(authData.saleorApiUrl),
JSON.stringify(authData)
);
},
delete: async (saleorApiUrl: string) => {
await client.del(prepareAuthDataKey(saleorApiUrl));
},
getAll: async () => {
throw new Exception("Not implemented.");
},
};
You'll be able to use it directly:
import { redisAPL } from "./apl";
const getSavedAuthData = async () => {
await redisAPL.get("https://example.saleor.cloud/graphql/");
};
Or access it from the context of API helpers from the SDK:
Using different APL depending on the environment
Depending on the environment your app is working on, you may want to use a different APL. For example, you may like to use FileAPL
during local development because it does not require any additional infrastructure. Deployed apps, on the other hand, need a more robust solution.
To handle both scenarios, initialize the proper APLs in your code based on its environment. In your application code:
// lib/saleorApp.ts
import { FileAPL, UpstashAPL } from "@saleor/app-sdk/APL";
// Based on the environment variable, the app will use a different APL:
// - For local development store auth data in the `.auth-data.json`.
// - For app deployment on hosted environment like Vercel, use UpstashAPL
export const apl =
process.env.APL === "upstash" ? new UpstashAPL() : new FileAPL();
Now you can use it for in your view:
import { SALEOR_API_URL_HEADER } from "@saleor/app-sdk/const";
import { NextApiHandler } from "next";
// import created APL
import { apl } from "@lib/saleorApp";
const handler: NextApiHandler = async (request, response) => {
const saleorApiUrl = request.headers[SALEOR_API_URL_HEADER];
// Get auth data
const authData = apl.get(saleorApiUrl);
// view logic...
return response.status(200).end();
};
export default handler;
Available APLs
saleor/app-sdk
exports some built-in implementations that are using popular platforms for easier bootstrap.
FileAPL
File-based storage of auth data intended for local development. Data are stored in the .saleor-app-auth.json
file. You'll be able to develop an app without additional dependencies or infrastructure.
Please note: this APL supports single tenant only (new registrations overwrite previous ones) and should not be used on production.
UpstashAPL
Upstash is a Redis SaaS targeting serverless applications. Its free tier is more than enough to start developing multi-tenant Saleor Apps, and credit card info is not required to create an account.
APL implementation uses REST endpoints to store the data, so no extra dependencies are required.
To use UpstashAPL, you'll need an endpoint URL and password. After creating a database, both will be available at dashboard. Credentials can be passed to APL during its creation:
import { UpstashAPL } from "@saleor/app-sdk/APL";
const apl = new UpstashAPL({
restToken: "XXXXXXXXXXXXXX",
restURL: "https://eu2-red-panda-00000.upstash.io",
});
Or using environment variables: UPSTASH_TOKEN
, UPSTASH_URL
.
Note: Recently, Vercel introduced KV storage, which uses Upstash under the hood. Upstash APL will work with values provided by Vercel KV Storage too.
EnvAPL
For static & single-tenant applications, you may want to set everything once, usually in env variables, and leave it.
There is an APL for this use case: EnvAPL
.
Warning Using this APL is highly discouraged in any production environment. It will break if the app token is regenerated. It will not work with any flow related to updates of the app.
Usage
1. Configuring the app to use env apl
import { EnvAPL } from "@saleor/app-sdk/APL";
const apl = new EnvAPL({
env: {
/**
* Map your env variables here. You don't have these values yet
*/
token: envVars.SALEOR_APP_TOKEN,
appId: envVars.SALEOR_APP_ID,
saleorApiUrl: envVars.SALEOR_API_URL,
},
/**
* Set it to "true" - check your app logs during app registration. APL will print the values you need
*/
printAuthDataOnRegister: true,
});
2. Setting env variables
After step 1, you should see your logs in a similar way:
┌──────────────┬─────────────────────────────────────────────┐
│ (index) │ Values │
├──────────────┼─────────────────────────────────────────────┤
│ saleorApiUrl │ 'https://my-saleor-instance.cloud/graphql/' │
│ appId │ 'app-id' │
│ token │ 'some-token' │
│ jwks │ '{}' │
│ domain │ 'my-saleor-instance.cloud' │
└──────────────┴─────────────────────────────────────────────┘
You need to set env variables in your provider, e.g., in Vercel, you need appId
, token
, and saleorApiUrl
.
Ensure the names of the variables match the constructor options from Step 1.
3. Turning off logging and redeploying
After setting the env var, you should disable printing it for security reasons.
Change the constructor to include:
printAuthDataOnRegister: false
Then, redeploy the app. It should be configured and work for this specific Saleor instance.
Keep in mind that:
- If you promote the environment, you need to update
saleorApiUrl
- If you reinstall the app, you need to repeat the process to receive a new token and ID
VercelKvApl
Introduced in app-sdk 0.51.0
VercelKV is a key-value, Redis based distributed database that Vercel provides in their platform. As an APL it can be used both for single and multi-tenant approach.
To configure the APL in the app:
1: Install vercel/kv
SDK
todo
2: Import APL from @saleor/app-sdk
import { VercelKvApl } from "@saleor/app-sdk/APL/vercel-kv";
export const apl = new VercelKvApl()
3: Set environment variables
# Provided automatically by Vercel
KV_REST_API_URL=
KV_REST_API_TOKEN=
KV_REST_API_READ_ONLY_TOKEN=
# A key to the Redis collection. All APL items will be stored inside this collection
KV_STORAGE_NAMESPACE=