CORS violations on API calls?

Hey, I’m a novice developer trying to work with a database through JavaScript.
I’m trying to find a way to use the API to search/create/update Records without incurring a CORS violation.
It seems like everyone is discussing this as if it isn’t an issue and yet I get intermittent CORS errors on any AJAX calls I make. This code is being run by any users logged into our application and has to be able to run client-side. I’d give examples but I’d rather not accidentally expose confidential information.

Is there some way to interact with the API simply through the builder’s JavaScript integration that I’m just not seeing? It feels like the Application ID and API Key ought to be enough to allow unhindered access.

Hi Zack

Have you looked at view based API calls?

You can find more info here

Using view based request means you do not need (and I’d advise against using) object based requests which expose your API key.

Take a look and let me know if you need any further help.

Craig

1 Like

I have looked into this, however, I’m trying to make more specific uses of the data such as populating PDFs and updating multiple records with a custom form. I don’t think I can accomplish this with the built in view-based implementation. At least I don’t think so?

When you say custom forms what are you using to create the custom forms?

Are you looking to integrate an external app with Knack?

There is also Make/Integromat that is excellent for doing those jobs that knack can’t.

Any other info you can provide.

We had all sorts of issues working with API when we started with Knack so hopefully I can help.

Craig

I’m just injecting an HTML form into the page with JavaScript. The form itself has fields that vary based on information from the database so it couldn’t just be something static. I had hoped to be able to update multiple fields at once rather than using multiple pages to accomplish the same feat.

Hi Zack

I understand you don’t want to share on the forum. It is very hard to understand what is going on without seeing what you are doing. If you’d like me to help you can send me a message and we can arrange a chat. I have had the CORS error message before and have setup multiple API calls throughout my app including inserting fields into the html.

Craig

I’m also under an NDA so I can’t really share what I’m doing outside of the broad strokes. I know that doesn’t really help and I appreciate you trying to assist me.

I was following along with API documentation’s examples such as this for updating records:

$.ajax({
    url: 'https://api.knack.com/v1/objects/object_1/records/' + recordID,
    crossDomain: true,
    method: 'PUT',
    headers: {
        'X-Knack-Application-Id': 'App_id',
        'X-Knack-REST-API-Key': 'App_key'
    },
    contentType: 'application/json',
    data: JSON.stringify({
        'field_1': 'Updated name',
        'field_18': 'Other'
    })
}).done(function(response) {
    console.log(response);
});

Even just accessing the records to get data fails occasionally due to the domain not matching.

$.ajax({
    url : 'https://us-east-1-renderer-read.knack.com/v1/objects/object_1/records,
    type: 'GET',

Mostly, I was wondering if there was some way to send the headers or format the URL that prevents errors or if there was some other method for accessing the database that I’m unaware of. I know JavaScript ES6 has changed the way some of this is written normally but I was only trained in ES5.

Hi Zack

Using the above code in the builder even behind a login would mean anyone with your URL could access any data in your app.

For all our API’s we use view-based API calls as this does not expose your API key. The only downside to this is that you need an existing view in your app. For a GET any view will do, for a POST you will need a form and a PUT a grid with inline edit turned on.

I have used the object-based API when I am connecting an external app to access the data i.e. Excel, Make/Integromat, Zapier.

What you are trying to do:

  • Create a form with inputs that correlate to different objects in Knack
  • Use code to PUT that data into their respective objects

Are you doing all this in Knack’s JavaScript pane or are you wanting to use any external services?

Craig

I’m just using Knack’s JavaScript pane. As far as external services, I unfortunately wouldn’t even begin to know how to go about integrating anything through Knack’s system. I suppose I’m mostly circumventing Knack’s built-in views as they seem limited in scope and use. When updating records, I’d like to be able to update multiple records at a time, however, Knack views are tied to a single record at a time as far as I’m aware. When making GET requests, I’m cycling through a lot of records as well that aren’t necessarily from the same object even.

I’m more so used to accessing a SQL database through PHP so I’m not used to these limitations.

If you have no use for a view as the data being accessed is simply being converted into another format such as PDF or a page for printing, how do you go about making a view-based call? What sort of view do you create and is it doing anything in this case (aside from allowing you to make a call without including the API key)? Sorry if these are dumb questions.

None of what you are asking is dumb, we found it very frustrating when we started. I completely agree about the limitations of Knack. When we first started, we could not believe that you could only update one object at a time with a form.

Having said that, the view-based requests are the way to go. We have quite a few views that are hidden and we have a whole login page where we store views that are only used for API calls, which we untick the box show in menu:

image

If you are updating existing records, then you will need tables that show each field you would like to update. With inline editing turned on. You will also need the Knack record ID of the record that connects all your objects. You can get this dynamically using code or from an API GET or even from the URL.

You can also use filters to zone in on specific records, so you don’t have to do as much cycling through records.

Here is an example of a function that will create some filters:

function createFilters(recordId, valToMatch) {
    return {
         'match': 'and',
         'rules': [
             {'field': 'field_xxx', 'operator': 'is', 'value': recordId}, 
             {'field': 'field_yyy', 'operator': 'is', 'value': 'valToMatch' }
         ]
    };
}

We have an API function that deals with all of our call and it got quite complex but it will do:

  • Parent > Child GET - used if you need data from the child record of the record ID you pass in this works slightly differently to other type of GET as you need to pass in the recordId and the slug of the scene where the view exists that you are trying to get the data the URL looks a bit like this:
https://api.knack.com/v1/pages/" + sceneId + "/views/" + viewId+ "/records/?" + sceneSlug + "_id=" + recordId
  • It can also take filters to get specific records the URL looks like this:
https://api.knack.com/v1/pages/" + sceneId + "/views/" + viewId+ "/records/?filters=" + encodeURIComponent(JSON.stringify(apiFilter))
  • PUT URL:
"https://api.knack.com/v1/pages/" + sceneId + "/views/" + viewId+ "/records/" + recordId
  • POST URL:
"https://api.knack.com/v1/pages/" + sceneId + "/views/" + viewId+ "/records/"

In all URLs
sceneId = scene_1234
viewId = view_5678
recordId = The Knack record id which can be found in the URL or the row Id of a grid
sceneSlug = the Page URL

I hope this makes sense. It took us quite a while to figure everything out.

The link above is a question my partner and I asked which has the Ajax call we pretty much use for all API types.

If you need any help with this or something isn’t clear please let me know.

Craig

This is perfect. Thank you very much for all your help.