skip to Main Content

Problem Solved

If you’re struggling with the same issue, look at the accepted answer which is one way to achieve it by using serverMiddleware


I’m using an API which required a private key. I’ve stored the key inside a .env file, and called it in the nuxt configuration file, like this :

privateRuntimeConfig: {
        secretKey: process.env.MY_SECRET_KEY
},

My API call is done inside the asyncData() hook on my index page. It works fine when i load this page, or reload it, but everytime i use the navigation to come back to this page, i end up with an error (I use a buffer to convert my API key to base64)

First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.

After some research and debugging, i found out that my private key wasn’t available at the time, and the "secret" value used in my api call was "undefined".

The thing I don’t get is why is this working on initial load / reload but not on page navigation ? And is there a way to fix it without using a backend ? (SSR for SEO and the ability to use private keys without exposing them are the main reasons why i used Nuxt for my project)

Here is my code :

async asyncData({ $content, store, $config }) {
    const secret = Buffer.from($config.secretKey).toString('base64')
    const request = await fetch('https://app.snipcart.com/api/products', {
        headers: {
            'Authorization': `Basic ${secret}`,
            'Accept': 'application/json'
        }
    })
    const result = await request.json()
    store.commit('products/addProducts', result)
    const stocks = store.getters['products/getProducts']



    return { stocks }
},

3

Answers


  1. Chosen as BEST ANSWER

    UPDATE :

    It's not working when the website is fists accessed from any other page than the one one where the API call is, since the store won't have any data from the API call)


    Okay, now I feel dumb. I found a way to make it work. I guess taking the time to explain my problem helped me understand how to solve it.

    For those who encounter a similar issue, i fixed it by wrapping my API call with a If statement.

    if ($config.secretKey) {
        const secret = Buffer.from($config.secretKey).toString('base64')
        const request = await fetch('https://app.snipcart.com/api/products', {
            headers: {
                'Authorization': `Basic ${secret}`,
                'Accept': 'application/json'
            }
        })
        const result = await request.json()
        store.commit('products/addProducts', result)
    }
    const stocks = store.getters['products/getProducts']
    

    This way, i can just skip the API call and access values from my vuex store.


  2. Update

    Looking at the @nuxtjs/snipcart module’s key key and since it’s a buildModules, you can totally put it there since it will be available only during the build (on Node.js only)!

    For more info, Snipcart do have a lot of blog posts, this one based on Nuxt may help clearing things up: https://www.storyblok.com/tp/how-to-build-a-shop-with-nuxt-storyblok-and-snipcart


    You do have your key initially because you’re reaching the server when you enter the page or hard refresh it.

    If you navigate after the hydration, it will be a client side navigation so you will not be able to have access to the private key. At the end, if your key is really private (nowadays, some API provide keys that can be exposed), you’ll need to work around it in some ways.

    Looking at Snipcart: https://docs.snipcart.com/v3/api-reference/authentication, it clearly states that the key should be available in

    Appear in your compiled front-end assets (HTML, JavaScript)

    Meanwhile, if you need to make another call to your backend (trying to access something else than products), you’ll need to make a second call.

    With Nuxt2, you cannot reach for the backend each time as of right now since you will stay in an SPA context (Nuxt is a server then client Vue app basically). But you could write down the token into a cookie or even better, use a backend as a proxy to hide this specific key (or even a serverless function).

    Some more info can be found on my other answer here: https://stackoverflow.com/a/69575243/8816585

    Login or Signup to reply.
  3. Thanks @kissu for your (very) quick answer 🙂

    So, based on what you said and your other answer on the subject, i’ve made a server Middleware in Nuxt in my server folder.

    server/snipcart.js

    const bodyParser = require('body-parser')
    const axios = require('axios')
    const app = require('express')()
    
    app.use(bodyParser.json())
    app.all('/getProducts', (request, response) => {
        
        const url = 'https://app.snipcart.com/api/products'
        const secret = Buffer.from(process.env.SNIPCART_SECRET).toString('base64')
        const config = {
            headers: {
                'Authorization': `Basic ${secret}`,
                'Accept': 'application/json'
            }
        }
    
        axios
            .get(url, config)
            .then(res => {
                const products = {}  
                res.data.items.forEach(
                    item => {
                        const productId = item.userDefinedId.replace(/-/g, '')
                        const stocks = {}
    
                        item.variants.forEach(
                            variant => {
                                const size = variant.variation[0].option
                                const stock = variant.stock
                                stocks[size] = stock
                            }
                        )
                        products[productId] = stocks
                        
                    }
                )
                response.json(products)
            })
            .catch( err => response.json(err) )
    })
    
    module.exports = app  
    

    Correct me if i’m wrong, but I think that’s basically the same as using a server as a proxy right ? Based on Nuxt lifecycle hooks, the serverMiddleware one is only run on the server, so my API key shouldn’t be exposed to the client ? (I still need to do some refactoring to clean the code, but at least it’s working) (https://nuxtjs.org/docs/concepts/nuxt-lifecycle/#server & https://nuxtjs.org/docs/configuration-glossary/configuration-servermiddleware/)

    nuxt.config.js

    serverMiddleware: [
      { path: "/server", handler: "~/server/snipcart.js" }
    ]
    

    index.vue (where my snipcart API call was previously made, i guess now I should move this call directly from the product card component where the data is needed) :

    async asyncData({ $content, store, $axios }) {
     
        await $axios
            .get('/server/getProducts')
            .then(res => store.commit('products/addProducts', res.data))
            .catch(err => console.log(err))
     
        const stocks = store.getters['products/getProducts']
    
        return {stocks, masterplanProducts }
    },
    

    PS : Snipcart does provide a public API key, but the use is very limited. In order to access the remaining stock for each product, i have to use the private key (which allows for some other operations, like removing products / accessing orders and such)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search