> Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Step 5 | Implement a Self-hosted Catalog Service Plugin ## Article: Step 5 | Implement a Self-hosted Catalog Service Plugin ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-5-implement-a-self-hosted-catalog-service-plugin.md ## Article Content: # Step 5 | Implement a Self-hosted Catalog Service Plugin > [< Previous](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-4-create-a-dashboard-page-to-manage-the-catalog.md) | [Next >](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-6-test-your-business-solution-on-a-site.md) In [Step 2](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-2-create-a-catalog-database-in-blocks.md), we created a catalog database to store the poems a Wix user sells on their site. In order to connect that catalog to the eCommerce platform, we need to create a server to implement the [Catalog service plugin](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/catalogs/catalog-service-plugin/introduction.md). The service plugin includes a single method called [Get Catalog Items](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/catalogs/catalog-service-plugin/get-catalog-items.md). Wix calls this method when a cart or checkout on the Wix user’s site is updated. In the call, it includes the IDs of any items in the cart, and in return it expects information from the catalog about each item. Wix doesn’t know where the catalog database is located–it only knows the location of the service plugin endpoint. Therefore, the service plugin must retrieve the items from the catalog database and return them to Wix in the correct format. In our case, this means our service plugin needs to communicate with the Wix site to access the catalog database and return the required item details. In this example app, we’ve placed the catalog database on a Wix site. But remember that in your own business solution, you can choose to locate the catalog database anywhere, including on your server or in an external database. You just need to change your server logic to fetch from that location. There are several things our server code must do to fulfill a Get Catalog Items request: 1. Verify and decode the incoming request sent as a JSON web token. 2. Extract the app instance ID and requested item IDs from the decoded payload. 3. Create a client to communicate with the Wix site that sent the request. 4. Use the client to request information from the site catalog database about each item. 5. Return all item information in a form that matches the Get Catalog Items response object. We’ll write our server code in [Express](https://expressjs.com/), so the first thing we need to do is set up Express and other necessary packages. 1. Install the following `npm` packages from Wix: ```bash npm install @wix/sdk npm install @wix/data ``` 1. [Install Express](https://expressjs.com/en/) and set up an app. Use version 4.17.0. You can choose to use `express-generator`, but it isn’t necessary for this example. 1. Once your Express app is created, open the `app.js` file and paste the following code: ```js const express = require('express'); const jose = require('jose'); const { createClient, AppStrategy } = require('@wix/sdk'); const { collections, items } = require("@wix/data"); const app = express(); const port = 3000; app.use(express.text()); async function verify(jwt) {} app.post('/get-catalog-items', async (request, response) => {}) app.listen(port, () => { console.log(`Example app listening on port ${port}`) }) module.exports = app; ``` Most of the above code is standard Express. However, we’ve added some extra lines that are specific to our business solution: **Lines 2-4**. Import 3 libraries that we’ll need in our server code: - Jose for JWT verification. - Wix SDK to create a Wix client. - Wix Data to work with collection data on a site. **Line 8**. Set up the [express.text()](https://expressjs.com/en/4x/api.html#express.text) middleware function. This function parses the incoming request, including the encoded body, into a string that our verification function can handle. **Line 10**. Create our `verify()` function, which will accept a JWT token as a string and decode it. We’ll write the code for this in a moment. **Line 12**. A POST method that receives Get Catalog Items requests from Wix and returns the correct information from the catalog. We’ll write the code for this shortly. Let’s start by writing the code for the `verify()` function. ## Verify the JWT token As with all [service plugins at Wix](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/supported-extensions/backend-extensions/add-self-hosted-service-plugin-extensions-with-rest.md), when a Wix site calls Get Catalog Items, it encodes the request body as a JWT. Therefore, in the service plugin we must decode the token in order to extract information like the app instance and catalog IDs. In this tutorial, we use the [jose](https://github.com/panva/jose) library for JWT verification. 1. In the `verify()` function we created before, add a `try`-`catch` block. Above the `try` block, add 2 `const` variables `alg` and `spki`. The first variable defines the RSA algorithm we’re using. In the case of requests from Wix, this is RS256, so we’ll assign the variable this value as a string: ```javascript const alg = 'RS256'; ``` The second variable, `spki`, is our public key. Each app in the Custom Apps list has a unique public key. To find this key, return to the **App ID & keys** modal in the [app dashboard](https://dev.wix.com/app-selector?title=Select+an+App&primaryButtonText=Select+Site&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fhome). Copy the public key from the modal. ![Copy the public key from the App ID & keys modal in your app dashboard](https://wixmp-833713b177cebf373f611808.wixmp.com/images/a3835faeae944997fab2a4e09b30b720.png) Paste the public key as the value for `spki`. 1. Let’s start adding code to the `try` block. We first add some error checking code that ensures the `jwt` parameter is a string: ```js try { if (typeof jwt !== 'string') { throw new Error('JWT must be a string'); } } catch (error) {} ``` Next, we add the functions that actually do the decoding. The first function we add is [importSPKI()](https://github.com/panva/jose/blob/main/docs/key/import/functions/importSPKI.md): ```javascript const publicKey = await jose.importSPKI(spki, alg); ``` We pass the `spki` and `alg` variables to `importSPKI()` and store the result in a new variable called `publicKey`. We then pass this result to the [`jwtVerify()`](https://github.com/panva/jose/blob/main/docs/jwt/verify/functions/jwtVerify.md) function, along with the `jwt` parameter and an [JWTVerifyOptions](https://github.com/panva/jose/blob/main/docs/jwt/verify/interfaces/JWTVerifyOptions.md) object: ```javascript const { payload, protectedHeader } = await jose.jwtVerify(jwt, publicKey, {       issuer: 'wix.com',       audience: '',       maxTokenAge: 60,       clockTolerance: 60     }); ``` Set the attributes in the `options` parameter as shown. These options allow us to [validate](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/self-hosting/supported-extensions/backend-extensions/add-self-hosted-service-plugin-extensions-with-rest.md#validating-request-signatures) requests received from Wix. To set the `audience` value, copy your app ID from the app dashboard. Finally, we return the payload. ```js return payload; ``` 1. Now we’ll add code to the `catch` block. You can add more complex error handling later, but for now we’ll simply throw an error with a message that the verification failed. ```javascript catch (error) {     throw new Error('JWT verification failed');   } ``` The full code for the function should now look like this: ```js async function verify(jwt) { const alg = 'RS256' const spki = ``; try { if (typeof jwt !== 'string') { throw new Error('JWT must be a string'); } const publicKey = await jose.importSPKI(spki, alg) const { payload, protectedHeader } = await jose.jwtVerify(jwt, publicKey, { issuer: 'wix.com', audience: '', maxTokenAge: 60, clockTolerance: 60 }) return payload; } catch (error) { throw new Error('JWT verification failed'); } } ``` ## Implement Get Catalog Items Now that we’ve set up our `verify()` function, we can implement the [Get Catalog Items](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/catalogs/catalog-service-plugin/get-catalog-items.md) endpoint, so that Wix can make calls to our server to get information about catalog products. 1. Go to the POST method we created below the `verify()` function: ```javascript app.post('/get-catalog-items', async (reqest, response) => {}) ``` Note a couple of important things here: - The path we route catalog requests to must be in snakecase. Make sure your  path is identical to the one shown above. - We will need to make asynchronous calls inside our handler function, so mark it as `async`. 1. Inside the handler function add a `try`-`catch` block. In the `try` block add the following lines of code: ```js const token = request.body; const body = await verify(token); const instanceId = body.data.metadata.instanceId; const requestedItems = body.data.request.catalogReferences; ``` Let’s break down what this code is doing: **Line 1.** Isolate the request body, which is in JWT format, and store it in a variable `token`. **Line 2**. Pass the token to `verify()`. If the token is valid, `verify()` returns the request body as a decoded object, which we store in a JS object called `body`. **Lines 3-4**. Parse `body` to extract 2 important pieces of information: - The instance ID. This tells us which instance of our app made the request. We’ll use this shortly to query the site catalog. - The list of items that the Wix site is requesting information about. This tells us which items to retrieve information about from the catalog. 1. Because our server is self-hosted, we need to [create a client](https://dev.wix.com/docs/sdk/articles/set-up-a-client/about-the-wix-client.md) in order to make calls to a Wix site. We use the Wix SDK [createClient()](https://dev.wix.com/docs/sdk/core-modules/sdk/wix-client.md) function to do this. `createClient()` expects a configuration object with at least 2 attributes: - `modules`: The Wix SDK modules your self-hosted app uses. This typically matches the modules that you imported. - Your app’s [authentication strategy](https://dev.wix.com/docs/build-apps/develop-your-app/access/authentication/about-authentication.md). In our example the app will make API calls, so we use the [`AppStrategy`](https://dev.wix.com/docs/sdk/core-modules/sdk/app-strategy.md) authentication strategy. Add the following code to the try block: ```js const wixClient = createClient({ modules: { items }, auth: AppStrategy({ appId: "", appSecret: "", publicKey: `` instanceId: instanceId, }), }); ``` **Line 2**. Define the modules we use in this client. In this case, we only use the [items](https://dev.wix.com/docs/sdk/business-solutions/data/items/introduction.md) module. **Lines 3-8**. Define the `AppStrategy` object. This object requires your app ID, app secret, and public key from the [app dashboard](https://dev.wix.com/app-selector?title=Select+an+App&primaryButtonText=Select+Site&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fhome). The `AppStrategy` object also requires the ID of the app instance. This tells the app which site it’s communicating with. We take the ID we parsed from the decoded request body and pass it to the `AppStrategy` constructor. This sets up our client and prepares us to make calls to the site collection. 1. Before adding the next piece of code, we need the ID of the **Poems** collection we created in Step 2. Return to the Blocks app and go to your collection in the CMS. Click **Edit Settings** to open the collection settings. ![Go to CMS settings in Blocks to get your collection ID](https://wixmp-833713b177cebf373f611808.wixmp.com/images/26a056eb002090892cd4a3f75c87d478.png) Beneath the collection name, copy the collection ID. 1. Return to the Express code. Beneath the createClient() method we added, add this code: ```js const catalogItems = await Promise.all(requestedItems.map(async (reference) => { const results = await wixClient.items .query("") .eq("mainProductId", reference.catalogReference.catalogItemId) .find(); const item = results.items[0]; const options = (reference.catalogReference.options !== null) ? reference.catalogReference.options : {}; return { "catalogReference": { "appId": "", "catalogItemId": item.mainProductId, "options": options }, "data": { "productName": { "original": item.title }, "itemType": { "preset": "SERVICE" }, "price": item.price, "priceDescription": { "original": "Number of lines" } } }; })); response.send({catalogItems}); ``` Before we review this code line by line, it’s important to understand its overall purpose. The code takes in the `requestedItems` array we created containing catalog references for the requested items, and returns a new array of catalog items in the required format. To do so, it performs the following steps: 1. Extracts the catalog ID of each item in the `requestedItems` array. 2. Queries the site catalog for that ID. 3. Organizes the returned information from the site into an object that matches the Get Catalog Items response. Let’s break down the code line by line: **Line 1**. Wrap the Javascript `map()` function in a `Promise.all()` statement that returns the array of objects only after the query has been resolved for all items. **Lines 3-6**. Using the Wix client we created, [query](https://dev.wix.com/docs/sdk/business-solutions/data/items/query.md) the catalog database on the site. Paste your collection ID as the parameter. **Line 7**. Store the query result. **Line 8**. Extract the variant information from the requested item and place it in an object `options` that can be passed to the returned item. **Lines 10-28**. Set up the response object in the correct format. **Line 31**. Returns the complete array of catalog references `catalogItems` to the caller. 1. To complete the Get Catalog Items endpoint, we’ll fill out the `catch` block. As with `verify()`, you can add more complex error handling later, but for now we’ll just return a 400 status with an error message: ```javascript catch (error) {     response.status(400).send({ error: error.message });   } ``` 1. Our server code is complete, but we need to complete one extra step to make sure it can communicate with the product catalog on the site. In our code, we call the [`query()`](https://dev.wix.com/docs/sdk/business-solutions/data/items/query.md) method of the Wix Data `items` module. Like all API methods at Wix, in order for our app to call this method, it must have the correct [permissions](https://dev.wix.com/docs/build-apps/develop-your-app/access/authorization/about-permissions.md). We can locate these permissions in the reference above the method declaration: ![Location of API method permissions](https://wixmp-833713b177cebf373f611808.wixmp.com/images/c57e470ce368f5899f2acd26e0b56e45.png) The specific permission we need to call `query()` is **READ DATA ITEMS**. To add this permission to our app, return to the app dashboard and go to [permissions](https://dev.wix.com/app-selector?title=Select+an+App\&primaryButtonText=Select+Site\&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fdev-center-permissions). Click **+ Add Permissions**. The easiest way to locate the permission you need to add is to search by name or ID. Search for “read data items”. Select the correct permission and click **Save** to add it to your app. ![Add read data items permissions to your app](https://wixmp-833713b177cebf373f611808.wixmp.com/images/8bce6c9441adf55786718d32d50ff754.png) The permission should now appear in the permissions list: ![Permissions list app dashboard](https://wixmp-833713b177cebf373f611808.wixmp.com/images/196ee15e0d1481346b419878aea7714c.png) Now our app can communicate with the product catalog on a site. Our service plugin code is now complete. Here’s the full code for `app.js`: ```js const express = require('express'); const jose = require('jose'); const { createClient, AppStrategy } = require('@wix/sdk'); const { collections, items } = require("@wix/data"); const app = express(); const port = 3000; app.use(express.json()); app.use(express.text()); async function verify(jwt) { const alg = 'RS256' const spki = ``; try { if (typeof jwt !== 'string') { throw new Error('JWT must be a string'); } const publicKey = await jose.importSPKI(spki, alg) const { payload, protectedHeader } = await jose.jwtVerify(jwt, publicKey, { issuer: 'wix.com', audience: '', maxTokenAge: 60, clockTolerance: 60 }) return payload; } catch (error) { throw new Error('JWT verification failed'); } } app.post('/get-catalog-items', async (request, response) => { try { const token = request.body; const body = await verify(token); const instanceId = body.data.metadata.instanceId; const requestedItems = body.data.request.catalogReferences; const wixClient = createClient({ modules: { collections, items }, auth: AppStrategy({ appId: "", appSecret: "", publicKey: ``, instanceId: instanceId, }), }); const catalogItems = await Promise.all(requestedItems.map(async (reference) => { const results = await wixClient.items .query("") .eq("mainProductId", reference.catalogReference.catalogItemId) .find(); const item = results.items[0]; const options = (reference.catalogReference.options !== null) ? reference.catalogReference.options : {}; return { "catalogReference": { "appId": "", "catalogItemId": item.mainProductId, "options": options }, "data": { "productName": { "original": item.title }, "itemType": { "preset": "SERVICE" }, "price": item.price, "priceDescription": { "original": "Number of lines" } } }; })); response.send({catalogItems}); } catch (error) { response.status(400).send({ error: error.message }); } }) app.listen(port, () => { console.log(`Example app listening on port ${port}`) }) module.exports = app; ``` Deploy your code on your chosen server. ## Create an Ecom Catalog in the app dashboard Once we deploy our server code, we need to tell Wix where to send its requests. To do this, we’ll return to our app dashboard and create an Ecom Catalog extension: 1. In the app dashboard, go to the [extensions](https://dev.wix.com/app-selector?title=Select+an+App\&primaryButtonText=Select+Site\&actionUrl=https%3A%2F%2Fdev.wix.com%2Fapps%2F%7BappId%7D%2Fextensions) page. 1. Click **+ Create Extension** and search for **Ecom Catalog**. Click **+ Create**. 1. In the JSON editor, add a key `deploymentUri`. As its value, paste the deployment URI of your server. For example: ```javascript {   "deploymentUri": "https://the-poems-manager.com" } ``` 1. Click **Save**. The **Ecom Catalog** extension now appears on our app’s extension list: ![Extensions list app dashboard](https://wixmp-833713b177cebf373f611808.wixmp.com/images/fb61042bfd4d2c6585761417f220d8bd.png) Our app is now complete and able to function on a site. In the last article, we'll build the app, install it on a site, and test the functionality we created throughout this tutorial. **Next up:** [Step 6 | Test your Business Solution on a Site](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-6-test-your-business-solution-on-a-site.md)