Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Last Time we challenged you to pull together everything you'd learned to create some Pagination Buttons powered by Graph. Answers here!
For this challenge, we'd asked you to set up your own simple Pagination controls. This combined several of the skills you've learned so far. As usual, don't worry if you need to check these answers before completing the challenge on your own.
On a new Page you create, the User should be able to see the first three Items in the Gallery WebApp.
Then, when they press the Page 2 button, the Page should refresh and they should see the second Page of Results.
Firstly, here's the query without variables.
Now let's add the variables:
Notes:
The most difficult part here might have been working out the type. Here the query expects Page to be an integer. The documentation panel confirms this:
From the documentation panel, you can also see "= 1" after the type. This is a default, which means if no variable is passed through, the variable will be given a default value of 1. Not all variables will have defaults.
The type here does not have an exclamation mark ! so the query won't deliberately fail if no value is passed in.
We set per_page
to 3
We gave you these in the tips. You'll have needed to add them on a Page of your choice.
Pressing one of the buttons will redirect us to the same Page, but will be adding a page
query parameter to the URL. This is the easiest way to pass variables to Liquid- because Liquid runs before the Page loads, and the URL is one of the only available sources of dynamic information at this point.
It's also possible to pass information to Liquid on another Page via an XHR request. This can lead to smoother UX, but is more challenging and will be covered in a future tutorial.
Let's say we pressed the second button, our relative URL will now be:/my-page-slug?page=2
As we showed you in the tips, Liquid can read this parameter at the following dot-notation path. This will output the value on the Page: {{context.params.page}}
Or you can store this in a variable, which we'll need to do here: `
`
By the way, the keys in context.params
are dynamically generated for any query parameter in the URL, so you can pass through any variable you like with this method.
a) First, let's set a default value, just in case a User arrives at the Page without setting the Parameter: <div data-gb-custom-block data-tag="assign" data-0='1' data-1='1' data-2='1' data-3='1' data-4='1' data-5='1' data-6='1' data-7='1' data-8='1' data-9='1' data-10='1' data-11='1' data-12='1' data-13='1' data-14='1' data-15='1' data-16='1' data-17='1' data-18='1' data-19='1' data-20='1' data-21='1' data-22='1' data-23='1'></div>
b) The query is expecting an integer, so let's apply an Integer filter that will change the type to an Int
without changing the value: <div data-gb-custom-block data-tag="assign" data-0='1' data-1='1' data-2='1' data-3='1' data-4='1' data-5='1' data-6='1' data-7='1' data-8='1' data-9='1' data-10='1' data-11='1' data-12='1' data-13='1' data-14='1' data-15='1' data-16='1' data-17='1' data-18='1' data-19='1' data-20='1' data-21='1' data-22='1' data-23='1' data-24='0' data-25='0' data-26='0' data-27='0' data-28='0'></div>
c) Let's add the graphql
tag with the variable parameter.
We can output the results using the variable name we defined in the graphql
tag.
If pressing the HTML anchors changes the results by fetching different "pages" of JSON results, you've been successful. Congratulations.
If you're having difficulty with any of these steps still, please don't hesitate to ask for help on the Forum.
Liquid
GraphQL
The next steps give you ideas for how to take this further. They are not part of the initial challenge!
These JSON results don't look great. Remember, you can use Liquid to pass the results into a Layout- if you can find that Layout's relative path in Code Editor.
However, you won't have access to the user-friendly names for fields in that Layout, because GraphQL will output the raw database IDs for fields.
You'll want to target fields in this Layout with syntax like:{{this.properties.webapp_field_1_2}}
You can view the database IDs for fields in the Admin using the toggle in the top-right of the screenshot, or you can look at the keys in the JSON results.
By returning total_pages
from the query, we'll know exactly how many Pagination buttons to display:
You can then use this to manipulate the HTML pagination controls:
Next time, we'll learn how to sort the results of your Query.
Let's go!
Refer back to to refresh these steps.
In our Tutorial 4 challenge, we asked you to write a query which returned items matching multiple filter rules. Here's a possible solution.
Last time, we asked you to write a single query which utilised a combination of filters to find records
which meet these criteria:
They are Module Items
They are enabled
They have already been released
They have not yet expired
They have a `weighting`
between 1 and 3
They have a meta_title
They fall into the posters Category
This challenge required you to modify and combine the queries we'd already looked at. If you were able to match at least some of the criteria, good work.
To find the category_id for the posters
Category. One way to do it would have been to go to the Siteglide Admin:
After that, it was a case of combining what you'd learned so far to add multiple filters to a query:
Notes:
For the range
of weighting
we've added a gte
and lte
setting, in order to demonstrate the possibility. This is not really necessary as there won't be values less than 0, but it's not a bad idea to rule these out, should data be entered incorrectly.
The category_array
ID may be different from one site to another. Check your site's category_id
in the Admin.
Explorer: Unfortunately, as mentioned last time, Explorer does not yet support arrays, so it's not possible to show an Explorer demo for this challenge.
It's time for the real thing! We'll look at how you can save your GraphQL query in a File and use Liquid to run it on a website Page.
Let's go!
This is a brief overview of the GraphQL tutorials we currently provide and the topics we plan to cover in future.
This Article aims to give an overview of the topics currently covered in our GraphQL tutorials, and the topics we aim to provide in the future.
For a longer introduction to GraphQL and how it can be used with Siteglide, check out the following article:
These GraphQL tutorials are organised by topic, but we'd recommend you work through them one by one.
This is because each tutorial will build upon the skills covered in previous ones.
We also include challenges- because when learning something like GraphQL it always helps to get hands-on experience exploring and applying the skills you've picked up. Don't worry if you can't quite manage the challenges straight away. Give them your best shot and then check the answer pages.
The following Tutorials are currently available:
Some topics will be covered in our challenges. We'll challenge you to build upon what you've learned, before revealing a solution. If the topics interest you, try the tutorial with the same name first.
In the future, we intend to cover the following topics:
Search for Results
Use other types of Query e.g. users
Please let us know if you'd like to see anything else included.
Turning the Page! In tutorial 2, we'll control how many items Graph returns on each Page of results and retrieve specific Pages.
When GraphQL returns results, it will organise them into pages.
This allows it to be more efficient, as it only returns the data that is needed straight away. At the same time, the rest of the pages are organised ready for the next request.
You'll always need to think about Pagination when using GraphQL, even you only want to retrieve the first Page of results.
per page
is an argument used to define the number of results that will be returned on each page.
It's now a mandatory argument on some types of query so we've already got the argument in our query from the last tutorial:
Experiment by changing the integer in the argument from 20
to another value, e.g. 1
. Observe the difference. It's recommended to keep the per_page number as low as possible- for efficiency and performance.
Let's consider a few situations:
You want to display 3 items - Set per_page
to 3- only the three items you need will be retrieved.
You want to display 20 items at a time, but allow the user to view the next 20 when they've finished reading - Set per_page
to 20
If you want the user to be able to change per_page
dynamically, we'll cover this when we look at variables in a future tutorial.
You can return pagination metadata alongside your query results:
current_page
- Returns the number of the current page.
per_page
- Returns the number of items on every page.
has_previous_page
- A boolean which is true if there is a page before the current page.
has_next_page
- A boolean which is true if there is a page after the current page.
total_pages
- Returns an integer representing the total number of pages in these results.
These optional properties can be requested alongside total_entries
and your results
object, like in the example below. Try them out. Code:
Notes:
Properties like has_next_page
and has_previous_page
are most useful for adding pagination buttons of your own.
You can see that by default, we get the first page of results- so page
returns 1
.
Explorer:
You can view other pages by setting the page
argument to the page of results you'd like to see.
Code:
Explorer:
In the next Article, we'll look at how to filter results.
For example, we'll learn how to return items from only a specific WebApp.
Let's go!
You shall not pass! This time, we'll look at how you can use filters to only return results based on specified rules.
records
in the database have a table
which tells us which WebApp or Module they belong to.
This time, we'll look at how you can use filters to only return results with a particular table
, or with a table
which matches a certain pattern.
The Starter Site comes packaged with a ready-built WebApp with the id of 1
and the name webapp_1
.
We'll return to our query from the previous tutorial, but this time, we'll rename it from get_all_records
to get_webapp_1
to reflect the different purpose we intend for it. We'll also be wanting to look at page 1
again.
Code:
Explorer:
Next, we'll add a filter argument:
Code:
Notes:
As an argument for the records
query, this goes inside the round brackets after records
.
Like the other arguments, filter
is followed by a colon :
We have more settings to choose next (filter
is an object) so we add curly braces { }
Explorer:
In this tutorial we'll choose the table
to apply the filter to, because we're looking for items with the table
of webapp_1
.
Code:
Notes:
We've chosen table
as the only filter. In the next tutorial we'll explore using other fields, and filtering by more than one field at once.
This field also contains further options, so we use a colon :
followed by curly braces { }
to contain the next set of options.
Explorer:
We now have a choice about:
How closely our value should match with the contents of a field before a match is returned. We'll use value
(the exact value).
The value we are matching against. We'll use webapp_1
.
Notes:
value
is a key and is followed by a colon :
Our value "webapp_1"
must be a String, so we wrap it in double quotes.
Documentation panel:
Selecting RecordsFilterInput gives you options for different filtering rules you can apply:
After the colons :
you can see the type of value expected for each of these keys. They are mostly strings String
or arrays of strings [String]
. This topic will be covered in more detail in later tutorials. Keep an eye out for the different data types expected by GraphQL in the meantime.
Explorer: When implementing this using the Explorer, the wizard will help you get the type of value correct. In this case, it provides you with quotes so that you can enter the value as a String:
You can adjust the filter to return items from a specific Module item only. In this example, we'll specify module_3
which is the Blog Module.
Code:
Explorer:
This should return Blog Posts from the Blog Module.
You can adjust the filter to return Form Submissions from a specific Form only. In this example, we'll specify form_1 which is the Newsletter Sign Up Form.
Code:
Explorer:
In order to learn GraphQL, you'll need to start experimenting with what you've picked up from this tutorials.
To help you do this, we'll now start to set you some challenges. These will ask you to tweak the examples we've given you so far and see if you can achieve the desired results.
We'll always give you the answers to the challenge in the following Article, so don't worry if you get stuck.
To carry out this challenge, you will need to create a second WebApp and add a couple of items in the Admin. By experimenting with the options in the documentation panel, see if you can filter the results so that:
your query returns all items with the table
of webapp_1
your query returns all items with the table
of webapp_2
your query does not return items which start with module_
your query does not return Form submissions which start with form_
We'll go over the answer to this challenge in the next Article.
We'll look at a possible solution to our challenge. After that, we'll continue to look at filtering queries in more detail, including:
filtering by different fields, or properties
filtering with different kinds of rules
using more than one filter at once
We look at a possible answer to Tutorial 3's challenge. This shows how to write a query which fetches all WebApp items, not Module items.
The trick here was to examine the tables
and spot the common patterns in their values.
The two types of records
we wanted had these tables
beginning with "webapp_":
webapp_1
webapp_2
The types of records
we don't want have table
values without "webapp_", for example:
module_3
- (Blog Module)
module_14
- (eCommerce Module)
form_1
- Newsletter Sign Up Form Submissions
So, in order to filter for the records
we do want and not the records
we don't want, we need records
which start with the string webapp_
.
Code:
Notes:
In this method, there is no need to write one filter to include webapp
items and another to remove module
and form
items from the Results. This is because the given rule efficiently achieves both at once.
Explorer:
This is just one possible answer, you may have found a different method.
Try and make sure you choose the best method for your use case. You should always be looking out for a more efficient way of doing things.
We'll continue to look at filtering queries in more detail, including:
filtering by different fields, or properties
filtering with different kinds of rules
using more than one filter at once
Let's go!
Although it can feel like a big leap at first, using custom GraphQL gives you ultimate control over dynamic data. We'll guide you through.
Firstly, let's go through a few frequently asked questions about GraphQL. In the next article, we'll then get started on our first Query on a new Starter Site.
It's worth noting that you don't need to learn GraphQL to use Siteglide's core features. Most of the time, Siteglide does the querying for you. However, learning GraphQL will allow your Agency to take on more challenging projects- see the next question.
Although the language is open source, actual implementations of it can be quite different across platforms. This is because a GraphQL implementation has two parts:
The Query Language - This is the syntax for writing queries- it is exactly the same across platforms, but can feel very different when the Schema is different.
If you've used GraphQL before with a different Schema, you will start to see lots of similarities.
Liquid is a templating language; GraphQL is a query language. To put it another way, think of GraphQL as a re-usable question.
Liquid will both ask the question- and listen to the answer, using it to build a dynamic website. The question itself is in a different language: GraphQL.
GraphQL files are stored inside the marketplace_builder folder. They can be called by Liquid and re-used as many times as you like.
GraphQL can be tricky to get started with, but most of our Developers report that at a certain stage, it just 'clicks' for them. We want to help any Agency who wants to learn to get there.
We'll provide a series of Tutorials, starting off simple and becoming progressively more challenging. As part of this, we'll aim to give you the skills you need to carry out further learning yourself- often this will mean learning to read the platformOS schema- finding the type of query you need, and experimenting with how to make it work.
We want to give you all the tools you need to learn GraphQL and we'll give our general tips, tricks and links to help you solve problems. Unfortunately, Siteglide support can't build a custom Page for you or assist with custom GraphQL questions - we want to stick to providing first class support on the core product.
If you're stuck, here are some things you can try:
Help us improve our general documentation on GraphQL. We'll keep adding and improving on our articles. See the "did this page help you" buttons at the bottom of each developer docs page.
Writing GraphQL queries gives you ultimate control over your dynamic data. Now, for a small Site, it's probably faster to use Siteglide's pre-built features to cover this for you.
Here are some examples of where some custom GraphQL could open some doors for your Agency. We'll update this list with tutorials and suggestions in the future:
Get any data from your Siteglide instance
Reporting- Your Client wants several bespoke, complex queries of data, all organised into tables and charts? GraphQL can help you be this flexible and if implemented well, can maintain page speed.
GraphQL works inside workflow and autoresponder emails. With mutations, it can also send transactional notifications and API calls from anywhere in your Site.
Performance optimisation- We do our best to make sure all Siteglide features are delivered to you with the maximum flexibility and performance, but for very bespoke Sites we couldn't possibly make the Platform guess your priorities. Understanding GraphQL could help you optimise your most complex features for faster Page load time, while using ready-built features to save development time in other areas.
Update data without loading the Page- When changing Page in WebApp results, you currently have to wait for the Page to reload. Using Siteglide-CLI and GraphQL you can build an XHR endpoint to get updated data after the Page has loaded. (Note: you can do this with CLI, without GraphQL- GraphQL just gives you more flexibility.)
Jump ahead of the Roadmap- We're always updating our Roadmap with new functionality and Community requests. But what if you have that one Client that cannot wait? It'll take more time to Develop and may not be as re-usable as a fully tested official feature, but with GraphQL and Liquid understanding, you've got the power to build your own solutions.
Our first tutorial will get you set up on the GraphQL playground/ sandbox which we make available through Siteglide-CLI.
Last time we looked at how you can use mutations to create a record.
This time, we will look at a mutation to update a single record.
The syntax is very similar to creating a record, but the main differences are:
1) using a different mutation type
2) We need to pass in an ID- to identify the record which needs to be changed.
All queries started with the query
keyword; mutations start with the mutation
keyword.
We've also named our mutation editItem
.
In this example, we'll add the ID as a variable:
Note, we don't need to add an object for the ID as we might when filtering a query.
As with record_create
mutations, the record
object defines the properties the record will have after the mutation has run.
Any properties you set will be updated. Any properties you omit will stay in their current state.
You do not need to specify a table. By default, the record will remain in its current table. Attempting to change the table may not be supported on Siteglide as the new table would not have compatible fields.
As in our last tutorial, it is possible to pass in properties as a single large JSON variable, if you see a convenience in doing so.
As in the last tutorial, it is required to add something to the object which returns with the results of the mutation. This can be useful to confirm results and show you what has changed. However, if you don't need many details, the best performing option is just to ask for id
.
A successful mutation result will look like this:
Imagine you have two different forms, each with an automation attached to them and you want to count how many times the automation runs.
You set up a WebApp with a custom integer field. Each time the automation runs, you:
query the WebApp, get the current count
add 1
to the value with the add
Liquid filter and
update the count with a record_update
mutation.
9 times out of 10, this will behave as expected, but what if two people submit the form at the same time?
If both automations check the current count at the same time, they will both read the same value e.g. 5 and after adding 5, will save 6 as the updated count in the database. Even though the automation ran twice, the count would only increase by 1.
It is to combat this scenario that record_update
contains the following special operations:
array_append will push the provided value to the end of an existing array stored in a property.
array_remove will remove the provided value from an existing array stored in a property, (wherever in the array it currently is)
decrement will reduce an existing integer value by the provided integer value (could be 1 or some other integer)
increment will increase an existing integer value by the provided integer value (could be 1 or some other integer)
In our example, we could do the following:
This safely increases the counter by one per mutation run, even if the two mutations are triggered by Liquid simultaneously. There is no need to query the existing value.
Nice work following through this tutorial.
If you want to try updating users, you can experiement with the user_update
mutation.
Next time, we'll look at how to delete records.
You can change the type and order of sorting. You can also sort by multiple properties at once.
In this Article, we'll look at how you can instruct the query to return items in an order of your choice.
Using sort along with pagination means you can make sure the first pagen of results shows you the items you're interested in. Using sort along with variables allows you to let your end-User customise the sort order themselves.
In this example, we'll start with a query which fetches the Categories.
Code:
Code:
Notes:
As a parameter, sort is contained within the round brackets after the query type
Explorer:
You can either use:
Code:
Explorer:
Code:
Notes:
Remember, any field which is not a Platform default field will be a property. This means any field not listed in the other options, including several Siteglide fields such as "weighting". Default fields tend to be like created_at automatically filled in when an item is created.
Explorer:
You can choose between the keywords DESC (descending) and ASC (ascending).
Code:
Explorer:
Sometimes you may want to initially sort by one property, e.g. weighting, but then some items may have a blank weighting property. Those items where the sort field is blank will go to the end of the results list, but you can add additional sort conditions to sort these further.
Let's say we wish to firstly sort by "weighting", then sort those items without a weighting by the "release date".
Code:
Notes:
Here we use an array with square brackets []
to add multiple sorting objects with curly braces {}
Unfortunately, arrays are not yet supported by the Explorer, so you'll need to add this code manually.
You can use different sort orders for each condition
If you want to challenge yourself, you could choose to try making a query which uses variables to change the order and field by which results are sorted.
Next time, we'll look at how you use what you've learned so far to start to build your own Application Programming Interface (API). This will use GraphQL to query the database, Liquid to display the results on an endpoint Page and JavaScript to GET those results on the client side.
So far, these tutorials have used GraphQL queries; in this article we're going to start looking at mutations.
The difference between queries and mutations is that:
Queries "read" data from the database
Mutations "create", "update" or "delete" data.
Keeping these types of operations separate is helpful, as it means you can be confident that running a query will not endanger the database on a live site.
When learning and testing GraphQL mutations, we strongly recommend you do so on a staging site.
If you need to test with real data, we recommend using the Siteglide Portal's Site Copy feature to create a staging site clone of a production site with a copy of the database.
Siteglide may not be able to recover the exact state of data as it was before running a mutation which modifies that data.
All queries started with the query
keyword; mutations start with the mutation
keyword.
If using explorer, click the dropdown to select mutation instead of query, to unlock different options:
This will create a new record (or user_create to create a new CRM user.)
The required record
object allows you to define both the properties of the new record and which table it should belong to.
Setting the table allows you to define which module or webapp it will belong to:
As with queries, the explorer UI cannot handle multiple properties in a mutation. You can use the explorer to figure out which individual property types are possible and then add the code manually. properties
itself can be written as an array with each object inside the array defining a property to set.
Each property must have a name which matches the ID of the field or custom field in Siteglide. When setting a value, you need to select a property value, value_array, value_boolean etc. depending on the platformOS type of data the field expects.
To find out which Siteglide types your table uses for each field, go to the corresponding WebApp or Module in the Siteglide Admin, or you can use this query to see each field's ID and Siteglide types together (you can also change the filter and properties to see which other tables are available):
When using boolean, float or integer, you don't need to use quotes.
While results are generally less useful in a mutation than in a query, having at least one result is generally required and your mutation won't work properly without it. The idea is simply to give you information about the record in the database after the mutation to confirm that the mutation was successful - even if this is simply an ID.
Mutations which affect multiple records at once often have a count
result which only returns the number of records created.
You can add the results you need in the curly braces after the round brackets which follow record_create
If successful, your results should return like this:
If you have an error, the error information will display instead of the results. In this case, value_int was used instead of value for the Blog Item's title:
In the following example, variables are added to pass in data programatically to create the new Blog post:
```liquid {% assign description = "Lorem Ipsum" %} {% graphql createBlogItem = "create_blog_item", title: "The newest Blog Post", description: description %}
```liquid {% parse_json properties %} [ { "name": "webapp_field_3_1", "value": "Blog Title" }, { "name": "webapp_field_3_3", "value": "Lorem Ipsum" }, { "name": "category_array", "value_array": [{{this.id}}] } ] {% endparse_json %} {% graphql createBlogItem = "create_blog_item", properties: properties %}
You've now used the GraphQL playground to write queries which fetch data. Now you're probably itching to see how you can add one to a Site!
You've completed the "Learning GraphQL" tutorials 1 - 4.
You'll need to be familiar with Siteglide-CLI. In this tutorial we'll be using Siteglide-CLI to add a GraphQL folder and sync our new queries up to the Site.
Last time, we looked at how to use filter
to refine your queries so that they only return results matching particular criteria.
The best way to run your GraphQL query on a Site is to save the query inside a GraphQL file. This keeps it tidy and allows you to easily re-use the same query elsewhere on the Site should you need to.
If you're following this tutorial with the same Site each time, you'll already have a project folder. After all, we have been using the .siteglide-config
file in your Project Folder to interact with the GraphiQL interactive playground.
In this example, my project folder
We need to add a GraphQL query inside a specific folder in order to refer to it with Liquid. Before we do that- we need to see your Site's File Structure so we know where to add the folder.
In terminal, you'll need to change directory to the Project Folder.
If you've not already, run a pull command in terminal to pull down the current file structure: siteglide-cli pull my_environment_name
If you want to refresh your memory on using the Siteglide-CLI, you can learn more** here**.
I'll be using Microsoft Visual Studio Code in this example. Other Editors are available.
All the folders and files that can be synced with your Site are in the marketplace_builder
folder.
graph_queries
Open up the marketplace_builder
folder. If you've not already got a folder inside this called graph_queries
, create one now.
Create a new file to store your query inside. It's best to give the file the exact same name as your query. This will make it easier to find later.
The file should have the extension .graphql
.
So that file now exists on your device, but not yet on your Site. You'll need to either sync
or deploy
the file to your Site.
The choice of command is up to you. sync
will watch for changes in the marketplace_builder
folder, so you'll need to make a change in the file after running the command before your file is uploaded. The sync
command will continue to watch and sync files until you cancel it. deploy
will simply deploy
all your local files to the Site as a one off command.
Here, I'll use sync:
This query file will not be visible in Code Editor, but the files will be on the server and Liquid will be able to access them. The reason for not displaying them here is to hide the most complex code from areas where Clients and non-developers can access it. This might change in the future.
If you wanted to check if the file has synced correctly, you can turn off sync
, delete the file and run a pull
command. In our experience, there's no need to run this check, as sync will tell you if a file is synced and "done" accurately.
We'll use the graphql
Liquid tag provided by platformOS. You may see other developers familiar with platformOS using tags like query_graph
or execute_query
, but graphql
is the most up-to-date: `
`
Notes:
The tag itself is graphql
The first parameter you give should be a new variable name. This will create a Liquid variable containing your results- you can choose your own name.
After the equals = sign, you should write the file name of your GraphQL file, without its file extension or any folders. For example, my filename is "get_items_with_musical_names.graphql"
so I remove the extension: "get_items_with_musical_names"
.
You can output all of your results on the Page, using Liquid output syntax {{ }}
to output the variable we defined earlier.
Notes:
The Liquid output must be on any line below the graphql
tag.
You can output this in any Liquid File; this includes Pages, Page Templates, Headers, Footers, Layouts, Code Snippets and Workflow/ Auto-Responders.
The results will output in Hash format. You can use dot notation to access specific results within the Object.
This example uses dot notation.
It is possible to use your Query to rename the keys in your Results- doing this would require different dot notation. We'll look at this later.
For now, we'll be using the query below, which does not rename any keys. I've set per_page
to 2, in order to make the example data Object shorter and easier to read.
Code:
My Page returns the following results- don't worry- there's no need to read them in this form:
Running these results through a 3rd-party JSON parser gives me the data in a format which is much easier to read. We don't recommend any particular JSON parser, but if you're using a text editor there will normally be an extension available which does this.
The structure of the results here matches the results we see in the GraphQL playground. We're looking for the key which returns an array of results- this is indicated by the square bracket [ ]
:
The dot notation to reach the results is:
Alternatively, you can always run your query in the GraphiQL Playground and work out the dot notation needed from the results shown in the middle-right panel. You'll just need to ignore the very top key in the results data
: and use the variable from your graphql
tag instead e.g. my_results
:
We loop over every item in the Results array. We create a variable called this
with a scope which allows it to be accessed only inside each loop iteration. this
contains all the data for that result.
In this example, we'll output:
the table
An example property name
An example property webapp_field_1_2
. This is the second custom Field defined in the WebApp's structure on Starter Site.
We can now also bring in other Front End languages. I'll add some common HTML tags.
This gets me the following Results on the Page:
You can use a Liquid include
tag to output a Layout to display your results. The trick is to make sure that the data fits the same format as the layout is expecting.
You've reached the end of the first collection of our Learning GraphQL tutorials.
By now, you should be able to:
Set up a development environment to test GraphQL queries
Run GraphQL queries on a Siteglide Site
Understand and use GraphQL Pagination
Use GraphQL filters
New Tutorials will be added soon!
There's no official challenge for this tutorial, because you probably want to experiment with adding queries to your Site.
In the next Tutorial, we'll look at using variables in your Queries to make them more dynamic. We'll look at how you can use variables:
In the GraphiQL playground
Using Liquid in a Page
Let's go!
Hello WebApp! In Tutorial 1, we'll use Siteglide CLI to open the GraphQL sandbox. There, you'll write your first query and retrieve details about some webapp items.
Create a new Site from the Flowbite Portal Site Template. We'll use this in the examples
In this first of the GraphQL Tutorials, you'll need to either create a new Starter Site, or open up a Starter Site you've created previously.
We'll use Siteglide CLI to open the GraphQL sandbox. There, you'll write your first query- which will ask for all the data on the entire Starter Site!
The GraphQl sandbox is a place for testing Graph Queries. It has the additional benefits of giving you suggestions and documentation alongside your code.
When you open the sandbox- you'll be opening it with your chosen environment. This means you're picking a Siteglide Site to give the sandbox access to- it will be able to find database items from this Site.
For these tutorials, we strongly suggest you use a Starter Site- because then you'll have exactly the same data and it will be ready to go.
Change directory into your project folder (create a folder first if you don't have one).
Then add your Site as an environment - the quickest way to do this is to copy the CLI command from your site in the Portal- or you can replace the --url and --email flags with your own. Add your Siteglide Admin password when prompted:
The command for running the sandbox is (replace the staging
placeholder with the name you chose in the previous step):
siteglide-cli gui staging
Success should look like this:
Click, Ctrl+click (if your machine allows), or copy the "GraphQL Browser" link into a browser. This will open the sandbox.
Short Method: If you want to save time and skip the step of opening the link, add the flag -o to automatically open the link after you run the command:
siteglide-cli gui my_env -o
Your First Look at the GraphiQL Sandbox
To start with a few important features are hidden. Click and drag up the QUERY VARIABLES
panel from the bottom left and click the < Docs
button in the top right. We'll come back to these later.
Click the "Toggle Explorer" button to open the Explorer wizard. This is a new tool which will make writing (non-deprecated) queries even easier.
You've now set up your GraphiQL sandbox!
The Central Panel
This is where you write your Query
The Results Panel This is where the data will be displayed in JSON format after your query fetches it.
Query Variables Panel This is where you'll be able to simulate dynamic variable inputs when testing variables in your Query.
We'll cover this in a later Article.
Explorer Panel *NEW* This displays the GraphQL schema in the form of a "wizard". Tick the options you want to include and they will automatically be added to your query. This is the most user-friendly way to write queries for beginners (and possibly for the experienced GraphQL developers as well!). Note the explorer user-interface can't handle arrays of query language very well e.g. when filtering by multiple properties, even though it's valid GraphQL; when you hit a limitation like this, it's time to leave the explorer behind and write the code manually.
The other limitation is that it doesn't currently support older deprecated query and mutation types, if you have a need to use those.
Documentation Explorer Panel
This displays the available GraphQL schema as documentation. It works with nested levels, so each time you choose an option you can "click through" to the next level of options you can include.
This is less user-friendly than the new "Explorer" panel above, but can still be useful:
especially if you want to use a deprecated query like "customisations" (The only reason for using deprecated queries is that some support features like keyword search that are not supported on the more modern queries yet)
If you want to search the schema for features and understand which types are allowed
platformOS sometimes changes the names of elements of their schema for the sake of better communication or as part of a functionality update. For example, models
have been now renamed to records
.
We are in the process of upgrading these docs to refer to records
instead of models
but in the meantime there may be some screenshots which continue to show the older terminology.
For your first query, we'll fetch every record
on the Site. A record
is a catch-all term for WebApp and Module items that we'll use in these tutorials. The term records
does not refer to users
, as these contain more core fields like email
which set them apart and therefore they use a different query type: users
.
In these tutorials, we'll break the process of building a query into steps. For each step we'll show you:
The code you need
Notes explaining the syntax of the code example in detail
A screenshot from the Explorer Panel, showing the checkbox needed to quickly insert this code *NEW*
We'll sometimes also include a screenshot of the Documentation Explorer Panel to illustrate it's use, but this depends on whether it seems useful or not!
Code:
Notes:
The word query tells Graph we want to GET data, as opposed to modifying data.
getEverything
is simply a name we have given the query. It is optional and has no functionality.
Curly braces { }
have been added - these will contain an object containing the next level of instructions we will give the query. The next level from here is also known as RootQuery
as it is the most fundamental level at which we choose which type of query we'll use.
Explorer: The "Explorer" wizard will add this for you if you type out a name here:
The documentation panel on the right shows you the full range of query types available. Click RootQuery
to see the options on this level.
You can also see a list of non-deprecated query types in the Explorer panel:
We'll be choosing records
for our first query. This will get us a list of records
in the database.
Code:
Notes:
The new code goes within the first level of curly braces we opened in the previous step. Notice how our instructions are "nested" inside each other- like a "Russian doll", just as both the explorer panels show options nested inside each other.
We open another pair of curly braces inside records
. We'll add further options here.
Explorer: Quickly implement this level in the Explorer Panel by selecting the records
query type. This will also open the next level of options in the explorer:
A GraphQL query doesn't give you results unless you ask for them. This helps it to stay as efficient as possible. We'll ask the query to pass back some key information about the records it finds, such as their table (this will help us identify which WebApp or Module the item belongs to) and then all of its properties (fields).
Next, we'll ask the query to return results
and total_entries
.
Code:
Notes:
total entries
returns an integer, so we don't need to give it any more information.
results
requires us to specify which fields we want to return with those results. We open a new pair of curly braces and choose fields from the docs. Siteglide doesn't use all of these fields, so some will return null
. You can use properties
to return all fields which have a value. table
will return the name of the table
the record
belongs to (this was previously called model_schema_name
).
Explorer:
You can press the "play" button to test your query:
Ours is not quite ready, and the error message in the middle-right Results Panel will tell us why:
Arguments are passed into a query to modify its behaviour. They always use round brackets ( )
syntax, just like JavaScript. Different types of queries will require some arguments, while allowing others as optional arguments.
This query requires that we specify how many records we would like to retrieve on each page of the results. This is to make sure the query isn't slowed down by retrieving too many records at once. We'll learn how to navigate multiple pages later, for now we'll ask for the first 20 records.
Code:
Notes:
per_page
expects an integer, not a string, so we don't need quotes around 20
Explorer: You can set a value for per_page
in Explorer.
Notice that the asterisk by the option in Explorer lets you know the property is mandatory.
In Explorer, there is a subtle colour scheme to help you differentiate between setting arguments and returning results:
Arguments are in purple
Results, and other returned properties are in dark blue
If successful, you should see an object named "data" containing a tree of your results. Well done, you've written your first query! You've returned all records
, or, in other words: WebApp and Module items.
You'll see there are actually more total_entries
than the 20 results shown. That's because we're only viewing the first Page of the Results. We'll look at Pagination in the next Article.
If you want to get information on Siteglide CRM Users instead of WebApp or Module records, you can use the users
query instead of the records
query. The main difference is that users
can return first_name
, last_name
, email
etc. inside the results
curly braces. See the explorer panel or the documentation panel to learn more.
In the next Article we'll look at how GraphQL organises results into pages. We'll look at how to:
change page
change the number of items per page
output useful pagination metadata
Let's go!
Adding variables to your query allows you to filter results based on User interaction - and re-use queries dynamically.
So far, we've set up queries which return the same set of results each time.
Variables allow you to make queries which:
Can be re-used in different situations
Can search, sort, filter and change page based on User Interaction
Can show information which a particular User has permission to access
There are three main steps to setting up variables in a query:
Define the variables
Replace hard-coded values in the query with variables
Pass in values for the variables
There is no set order in which to follow these steps, as you'll need to follow all three before the query works as expected.
In this Article, we'll use the following query as an example.
As you might have guessed, this query aims to find WebApp or Module items with a particular category assigned to them.
As it stands, the query is not very useful or repeatable. We want to be able to pass in the category we're interested in as a variable. This would allow different Pages to use the same query to view different categories - or allow the User to choose which category they wish to view.
Normally in programming you would define the variables before you use them.
The benefit of adding the variable where you'll use it first is that the error message will tell you information about which type of data the variable will need to be.
In our example query, we'll remove the hardcoded category ID and replace it with the name of our new variable $category
.
Code:
Notes:
All variables inside the query itself must be preceded with a dollar $ sign.
This query will currently fail, because we still need to define the variable.
Explorer: Sorry, explorer does not currently support arrays or variables, so no demo is available this time.
Next, we'll define the variables we will use. These definitions will be entered as arguments on the top level of the query.
Each variable you define has two parts:
A name e.g. $category
A data type e.g. String
Here's what just the top level looks like with the newly added argument:
Here's the whole query:
Notes:
The variable name is again preceded with a dollar sign $.
A colon : and an Title Case keyword are used to set the type. See below for more information on types.
We've deliberately made a mistake with the type of the variable- we'll discuss this below.
Explorer: Sorry, explorer does not currently support arrays or variables, so no demo is available this time.
A variable's type is really important and an error will be thrown if you use the wrong one. For example, running our query now gives the following error:
We can use the error message to find the type we need- in this case it's [String!]
not String
. This indicates that an array of Strings is expected, which makes sense because "value_in" is for filtering array fields. You can also check in the documentation panel:
We'll fix this in our query:
What's important is the type of data that a particular GraphQL property expects as its value, not the type of data you were intending to pass in. In fact, you may have to modify the type of your variable with Liquid beforehand, so that it is acceptable to GraphQL.
An exclamation mark after a type e.g. String!
means that this variable will be mandatory. The query will not run without it. This is useful when that variable is used to control sensitive information- terminating the query if you can't be sure which data you need to return. However, it's important to make sure ordinary user behaviour can't cause this to happen, because the error message can be bad for UX.
Square brackets around a value, indicate that it should be an array (which may have any length). E.g.
[String]
If you require any other conversions, please request them on Intercom and we'll try and include them in the List.
*String to Int *Apply an integer filter:
String to Float
String to [String]
String to Boolean
Boolean to String
Int to String
Float to String
Literal JSON object to HASH object (Needed for advanced variables only- like passing an array of properties objects into a filter). You can also use the parse_json tag to create any of the above types; if you can write the variable in a type that's supported by JSON, the tag will convert that to a variable in the hash format that can be passed into Graph as a variable value.
You can test by entering values in JSON format in the panel in the bottom left. The panel may be hidden, in which case you'll need to click and drag it up.
Firstly, open a new JSON object:
Next, define the key of your property:
And finally, define the value of your category. Make sure the data is in the type you defined earlier. Here are some examples:
String - `"Hello"`
Int - 3
Float - 3.2
Boolean - true
[String] (Array of Strings) - ["Hello", "Hi"]
In our example, we will need an array of Strings:
Notes:
This time, you won't need to use a dollar sign $. That's because this panel represents the input and is not part of the GraphQL query- it doesn't use GraphQL syntax, instead it uses JSON syntax.
Our query is now finished:
Explorer: Sorry, explorer does not currently support arrays or variables, so no demo is available this time.
Okay, so now you can use the GraphiQL Sandbox to test your queries with variables, but the really useful bit is to be able to use Liquid to pass variables in. This will unlock the ability to fetch data dynamically with GraphQL.
Step 1) Add parameters to the graphql tag for each variable you've defined in the Query. Here, we'll continue with our example and add category
Notes:
We will add the value of the category variable in the next step.
You don't need to use a dollar sign $ before the name of your variable.
You can add new lines to the inside of the tag to keep it tidy, or write the tag on one line, it's up to you.
Step 2) Next, add the value of the parameter.
You can either hardcode the value:
Or, you can use a Liquid variable which you defined earlier:
Or, you can use data from Siteglide that's already stored in a Liquid variable. In this case, let's say we're in an item.liquid file for a Product. We already have access to the Categories that this Product is assigned to - luckily, the data is already a Liquid Array. Let's use that data to find other records in the same Category.
For this week's challenge, we'd like you to set up your own simple Pagination controls. This will combine everything you've learned so far. As usual, don't worry if you need to check the answers before completing it.
On a new Page you create, the User should be able to see the first two Items in the Gallery WebApp. Then, when they press the Page 2 button, the Page should refresh and they should see the second Page of Results. See the gif above for a demonstration.
In the Tips section, we'll give you the HTML and Liquid you need to get started, your challenge is to build the GraphQL query which will power the functionality.
Here are some snippets of code you can use to help you:
*User Interaction *You can use anchors to allow the User to refresh the Page and change the Page Parameter in the URL.
*Reading URL parameters *You can use Liquid to read the Page parameter in the URL. You'll then need to work out how to feed this variable into your GraphQL query: `
`
By the way- we're using the filters | default
and | plus: 0
to make sure the page defaults to 1 if no parameter exists, and that the data is converted to an integer.
We'll look over the answers to our toughest challenge yet.
Let's go!
This Article shows the Answers to a Challenge. If you've not had a go at the challenge yet, we'd recommend you
- optional- Read more about GraphQL and when it might be best used.
You have completed the .
- optional- Read more about GraphQL and when it might be best used.
If you think you need to display more than 2000 items at a time (perhaps to provide data to a JavaScript plugin), consider using GET requests to a custom Liquid endpoint page to fetch each page of results asynchronously. You can learn more in but we recommend working through the other tutorials first.
You have completed the Learning GraphQL tutorials 1 - 2. You can find the previous tutorial
- optional- Read more about GraphQL and when it might be best used.
This Article shows the Answers to a Challenge. If you've not had a go at the challenge yet, we'd recommend you .
- optional- Read more about GraphQL and when it might be best used.
GraphQL is an open-source querying language which was originally developed by Facebook. The general idea was to create a language which allowed Developers to quickly develop flexible requests for data, while being efficient and only asking for exactly the data they needed. The structure of the query, including relationships between different tables, matches the structure of results- making it easy to iterate over the data after the query is complete. You can read about the open-source project on their website here:
The Schema - this defines different types of Query you can use, which parameters you can add, and what sort of output you can request. It reflects, in part, the database structure that the Platform uses, so it varies from Platform to Platform. Siteglide runs on platformOS and we use of GraphQL.
Every time we add a new GraphQL Tutorial, you'll be able to access increased support on that topic via the Forum. You can see an overview of the topics we've covered so far
In the next Tutorial, we'll show you how to use the GraphQL sandbox to test out Queries.
Ask the Community on . We have an ever-growing Community of developers from around the globe, join the discussion.
Siteglide's custom support partner, Sitegurus, can provide developers who are experts in using GraphQL with Siteglide.
Maybe this particular solution isn't suited to custom GraphQL, maybe it's better as an official feature? You can request features on the .
**Front End Mutations- **Use at your own risk! With mutations, you can change data in the database and trigger this with Liquid. This can make a lot of complex projects possible- see the platformOS GraphQL schema for a full list:
See for more tips on what to do if you accidentally delete data you didn't mean to. (This won't help if you accidentally change the properties!)
To figure out the correct one, you can reference this page which will show which platformOS types are used by each Siteglide type.
We covered variables in more detail in but they are just as useful in mutations!
In order to use GraphQL results on your Page, you'll need to be familiar with Dot Notation Liquid syntax. You can get started with learning to use . You may find that you want to refer back and forth between articles on Dot Notation and GraphQL as you continue with your learning.
- optional- Read more about GraphQL and when it might be best used.
So far we've been working in GraphiQL, the interative playground. This time, we'll run a GraphQL query on your Starter Site. We'll also take a quick look at how to handle the results- but you can learn more about this by following our Documentation on and .
You can learn more about this topic , as it works in the same way as displaying Collection Results in a Layout.
- You'll need to understand how to open terminal on your machine and navigate to your Project's root folder. You'll need to have installed Siteglide-CLI and its dependency Node.js. You can't use GraphQL without Siteglide-CLI.
- optional- Read more about GraphQL and when it might be best used.
*Remembering Pagination in GraphQL *You may need to refer to , to refresh your understanding of Pagination.
Following on from the previous tutorial, we'll look at more advanced filtering options and show how you can filter with multiple rules.
You have completed the Learning GraphQL tutorials 1 - 3
About GraphQL- optional- Read more about GraphQL and when it might be best used.
In the last tutorial, we looked at how to filter your results so that only items from webapp_1
were returned. We also challenged you to see if you could adjust the query so that it returned all WebApp items.
This time, we'll look at:
filtering by different fields, or properties
filtering with different kinds of rules
using more than one filter at once
Some fields in records
are defined in the GraphQL schema, like table
, this often means they have their own filter option in the documentation. Siteglide fields, and your own custom fields, are very likely to be custom "properties" which are not directly defined by the schema.
If you're not sure, check the schema for the field you're looking for. If you can't find it, it's a property.
For example, release_date
is not a custom field in the Siteglide Admin, but it's not in the list of available fields to filter by in the schema- so we'll need to use properties.
A brief note on name
. You'll see the term name
available in the schema- but this is a deprecated way of referring to the table
e.g. webapp_1
. To fetch the name of the item in the Siteglide Admin, you'll need properties.name
.
In our next example query, we'll demonstrate this. Let's search for items with a properties.name
that contains the string "music".
Code:
Notes:
See that properties uses a colon : and then curly braces { }
as we have usually used so far. Properties can instead be used to store an array of filter objects, but we'll look at this later.
Note that we have to specify inside the curly braces both the name
of the property we want to filter by (which happens here to also be called name
, but it could also have been something else like release_date
) and the method we'll be matching the value by, in this case contains: music
.
Explorer: Adding a single filter to properties can be done with the Explorer wizard. However, if you want to be able to filter by an array of different properties, Explorer has no support for this yet, but it is possible by writing in the query manually.
In the examples so far, we've only filtered by strings- or in other words, groups of letters or characters. Next we'll look at some other data types:
Booleans
Integers / Epoch Date stamps
Arrays
A good example of a Boolean property in Siteglide is enabled
. This is a property which is stored as either true
or false
. Let's find all the items which are currently enabled:
Code:
Notes:
Again, we need to specify the name of the property we'll be filtering by, this time: enabled
.
We'll use value_boolean
as the most appropriate way to match an exact Boolean value. It's not the only method we could choose from the documentation, but it's more specialised to filtering Booleans, so is potentially faster.
Explorer:
When filtering by integers (which also includes Siteglide's release_date
and expiry_date
fields, as these are stored as Epoch Timestamps) you've got a choice whether to filter by values or a range of values.
Code:
Notes:
value_int
works the same as value_boolean
but is designed to handle the different type of data more efficiently.
Be aware that running this query yourself on a Starter Site may produce no results. This is the correct result, because Starter Site does not at the time of writing ship with any weightings set. If you add a weighting of 1
in the Admin, you'll see it appear in the results.
Explorer:
Most often, you'll want to use more complex comparisons for integers. We'll look at how to do this next- at the same time, we'll take a look at how Siteglide normally formats dates.
The dates are stored in Epoch Timestamp format, which is an integer storing the number of seconds which have elapsed since the Epoch. You can convert a date of your choice to this format here: https://www.epochconverter.com/
In this example, I'll use the time at the current time of writing: 1582108820
. Let's query for all items which have already been released. In other words, the value stored in release_date
should be less than, or equal to, the current timestamp. When you're thinking about dates, you can think of "less than" as meaning "before" and "greater than" as meaning "after". So here we're asking for "items with a release_date before now".
Code:
Notes:
The range filter requires you create a new object { }
containing some logical comparison operators. You can choose between the operators in the documentation panel- see below.
Documentation Panel:
Available operators can be seen under RangeFilter
. They are short for Greater Than
, Greater Than or Equal
, Less Than
and Less Than or Equal
.
Explorer:
An array is a list of data. One good example in Siteglide is category_array
, which stores a list of unique IDs that refer to categories. We can now write a query to find items in a particular category.
Code:
Notes:
value_in
is special to fields which have an array data type.
It takes an array of strings ["string_1", "string_2"]
as its value. Here we are just using one category ID as an example. You could experiment with combinations of category_ids.
Explorer: Only partial support is currently available in Explorer for this. It's not so good at handling arrays. So you can select the property in Explorer, but you'll need to add the value manually into your query.
So firstly, implement with the wizard:
Secondly, change the value manually in the code from:
value_in: "158198"
...to: value_in: ["158198"]
For all the filters you've learned in this Article and the previous Article, you can apply more than one at once.
Notes:
In this example, table
and properties are both chosen as filter options.
properties
takes an array (denoted by square brackets [ ]
) of objects (denoted by curly braces { }
Each pair of curly braces { }
inside properties
should contain the name
of the property and an independent filtering method e.g. range
or value
which will be used.
Using a combination of filters in this way generally follows AND logic. Items must pass all of the filtering tests you choose before they are returned.
Explorer: Unfortunately, arrays are not currently supported in Explorer, so you'll have to enter this section of the query manually for now.
You can also find items where a property does, or doesn't exist.
In this example, we're looking for WebApps where a meta_title
was not added.
Code:
Notes:
exists accepts a Boolean, so you can use false
or true
. This shouldn't be wrapped in quotes, because Booleans don't require them.
Explorer:
What if you are looking to filter records
so you can find records
which fit either one rule or another- but don't need to match both rules?
Here's an example from the pOS team of how you can use the "or" option when filtering. In this example we get records
where either a webapp_field_1_1
exists OR a field parent
exists.
Notes:
Inside filter
we add an or property and an array with square brackets []
This array takes one or more objects with curly braces {}
each Object is compared with OR logic. Within the object, you can use the same properties you might normally use inside filter
.
You can add multiple filter properties inside each object, but these will be compared with AND logic. So, to filter records by those which have both ( webapp_field_1_1
AND webapp_field_1_2
) OR "parent", you would do the following:
See if you can write one query which uses multiple filters. Try and return records
which meet these criteria:
They are an item from a record
with a table
starting with module_
.
They are enabled
They have already been released
They have not yet expired
They have a weighting between 1 and 3
They have a meta_title
They fall into the posters
category
Hint: This search is so specific, that by default on Starter Site it will return no results. Try creating an eCommerce Product which meets these criteria before you start- that way, you'll know when you've succeeded.
It's unusual to run this many filters at once. Most of the time in a real use case, you'd have less to write than this! However, putting together everything you've done so far will be good practice.
We'll look at the answer to this Challenge in the next Article.
We'll look at a possible solution to our latest challenge question.
After that, we'll look at how to run a query on a real Siteglide Site for the first time- and look at what you can do with the results.
This article shows a different use-case for the skills you've already learned- using a GET request to run a query.
So far, we've written GraphQL queries which run on Page Load. This is powerful, but wouldn't it be even more useful if you could fetch data after Page load when a User interacts with a Site- e.g. changing page on a list without refreshing the Page? Using XHR (sometimes called Ajax) requests, you can run GraphQL at any time.
This approach can take a little bit of time to set up, but can allow you to create much faster interactive experiences for your Users and opens up a huge range of possible solutions you can build.
We cannot document every way in which you can build this kind of Page, but we will show you the basics and let your imagination do the rest! If you need additional support on this topic beyond the scope of what is documented here, we recommend you speak to one of our Experts or browse the resources we link to at the end of the Article.
An API - (Application Program Interface) is a form of communication between two services on the internet. Communication takes place between 2 or more endpoints.
An endpoint in its simplest form is a URL which allows an API access to a server or application. On Siteglide, we provide you with API endpoints we've built with our public API, but now you've learned GraphQL, you also have the ability to build your own endpoints when you need them.
A method defines the role of the API + Endpoint e.g. a GET method is for "getting" or "fetching" data.
JSON (JavaScript Object Notation) is a common file format for exchanging data which is efficient and human-readable. We'll use it in some of our examples. (It's also the default format in which GraphQL results are outputted on the Page.)
The first step is to create a Page on the Siteglide Admin which will become your API endpoint.
The slug you choose will be the URL via which you'll eventually access the data. It's worth naming the Page and writing a slug which reflect the fact that this Page is not a public-facing Page and should not be editable by Clients. e.g. /api/webapp_1
In this step, we'll be changing the settings of the Page in CLI, as there are available settings here that are not yet editable from Admin. You can learn more about pages in platformOS here: https://documentation.platformos.com/developer-guide/pages/pages#content and more about the Siteglide-CLI here.
Using the Siteglide-CLI, pull down your Site's files to your machine and open them up in a Code Editor of your choice. Set up Siteglide-CLI sync so that changes you make will be pushed to the Site.
Open up the Page you've created in a Code Editor of your choice.
We'll be editing the yaml configuration at the top of the page. In my example, you can see the yaml settings between the triple dashes --- It's important when configuring yaml to use exactly 2 spaces instead of tabs for indenting. Your siteglide-cli sync command will alert you to any validation errors.
So far we've only learned to write GraphQL queries, so your endpoint will be handling GET requests. Set the method property of your page to get:
method: get
Optionally, you can choose to change the format of your Page. If you like, instead of HTML, you can make your Page a different format, like JSON.
format: json
Note that if you set your Page to a different format now, you'll need to append the URL with the extension later. e.g. a Page with the slug my-slug
and format JSON can be accessed at the URL /my-slug.json
.
As your endpoint Page will not be accessed by either humans or search-engine bots, it's much better to remove the Page Template, allowing you to only return the data you need.
layout: ""
Unless you're using the HTML format from 2) b), you will need to remove any
tags automatically added to the Page.
Unlike the other steps, this is not a yaml property on the Page. However, it's included here as a quick sensible step you can take.
You may wish to specify in your robots.txt
file that pages with this URL should be ignored by search engines.
My example looks like this:
Add a query of your choice using the Liquid graphql
tag from tutorial 5 and variables from tutorial 6.
In this example, I'll use the following query to fetch data from webapp_1 and change page with the page
variable:
I'll use the following Liquid to run this query when the endpoint Page is accessed: `
`
Note- I'll be using - before and after my closing Liquid tags to remove unnecessary whitespace from the results- this is optional.
In the example, we'll pass inputs into the endpoint Page using query parameters on the end of the URL, for example, I already have the URL for accessing the endpoint Page: /api/webapp-1.json
Remember
The ".json" extension should be replaced with the "format" you chose in step 2.
I'll be storing the page I want to request from the endpoint in query parameters like so: /api/webapp-1.json?page=2&per_page=1
You can now use context.params
`to read the URL on the endpoint Page and dynamically access each query parameter. I'll store each in a variable before I feed these into the query, in case there is any type coercion to carry out first.
Accessing these values via the above method tends to set them as String values in the variable. For this example we'll need to change the type to integer- as that's what the query expects. You can refresh your knowledge on changing the type of variable in Liquid in tutorial 6.
We can then add them to the query.
If the query expects variables to be Strings you can actually add them straight to the query without assigning as variables first:
Results are accessible via the variable name you defined in the graphql
tag, but at this point we can decide on the format in which we'll display them.
If you decided in step 2 that you didn't want to change the Page format, you should now build the required HTML structure you'd like to send back (this would probably be inserted as it is into a Page via JavaScript).
If you decided in step 2 to change the format of the Page, you'll need to use Liquid to output the content in this format.
As GraphQL already outputs in JSON format, this is easy:
For something like CSV, you'll need to use logic to output the data in the correct format -this is just an example and you may need to alter it for your use-case.
We use {%
rather than {%-
in this example, because we want to preserve new lines to make sure each row of the CSV displays correctly.
In your browser, visit the endpoint Page URL and see if the data displays as expected. Test changing the query parameters to see it change the returned data.
A successful JSON endpoint will return valid JSON in the body, as in the example here (other formats should also be checked for valid formats).
Changing the URL parameters should allow you to return different responses:
Common issues:
Your URL should include the relevant file extension e.g. .json
for json format, .csv
for CSV format. If you are using HTML
format, no extension is needed. Below is an example of a 404 response from using the incorrect extension:
Your URL should contain any query parameters you need for your query. E.g. in my example, page
and per_page
are mandatory. Below is an invalid response body resulting from missing URL parameters:
Congratulations! You've built a working GET endpoint using GraphQL and Liquid!
The last two optional steps will expand upon your options for securing the Endpoint and using the data available.
Is your data sensitive and you want only logged in Users to access it? Is your data public and it doesn't need an additional authorisation step?
This step shows you some different ways to make sure your data is secure, but you can skip it if your data is not sensitive or private.
Adding a Secure Zone to your endpoint Page is a simple way to protect it from Users and bots who should not be accessing it.
A failed access will still generate a successful 2xx response code, because it will succeed in returning HTML body (the 404 message that displays to Users). This is only a problem if you're not using HTML format for your endpoint as it will cause any JavaScript which relies on parsing JSON to fail like so:
In step 9, you'll need to find a way to adapt your JavaScript logic to handle this kind of error which does not rely on the response code, but instead checks the response for the HTML tag <h1>401 - Unauthorised</h1>
. See step 9) a) i) and 9) a) ii) for examples of this logic.
You can use Siteglide-CLI and platformOS to build an authorization policy that checks the request either comes from a trusted Site or from a trusted User. The benefit of this is that it allows you to return HTTP response codes.
As this is custom platformOS code, it won't be covered by our support. Learn more about authorization policies on the platformOS docs: https://documentation.platformos.com/developer-guide/authorization-policy/authorization-policy
These tips are intended as inspiration and do not constitute complete examples. They do not require sending a query parameter over in your URL, making them easier to keep secure.
If the User has been logged in (to any Secure Zone), you can check this on the Endpoint Page Authorization policy.
To check that the request comes from an authorized Page/ Site, you can check this with context:
On any of your other Pages, you can now send requests to your new endpoint and fetch data. For this you could use JavaScript.
The JavaScript examples provided here are intended for inspiration only. Unfortunately, we cannot advise you on how to adapt these to suit individual projects. See the links at the bottom of this Article for more resources you can use to help plan and develop projects of this kind.
This basic example will request data from the example earlier and console log the response.
The if statement logic checks if a 2xx response code is received (meaning any authorization policies have passed) and that there is no HTML tag containing a 401 code from a Secure Zone check failure. See Step 8) for more details.
In this expanded example, we'll fetch the data and then append it to the HTML DOM.
Add HTML and JavaScript
An event listener targets the Form and watches for a click event
When the Form is submitted, the event triggers the function "getWebappOne".
The if statement logic checks if a 2xx response code is received (meaning any authorization policies have passed) and that there is no HTML tag containing a 401 code from a Secure Zone check failure. See Step 8) for more details.
The function requests the data from our new endpoint.
It then loops over the Items and appends each WebApp Name to the HTML DOM.
```liquid
Page Per Page Get WebApp 1
In the previous two examples, we've used an endpoint with a JSON format endpoint.
You may find it easier to build HTML on the endpoint and when it arrives in the destination Page, output it as it is.
Build HTML on the Endpoint Page
Fetch HTML on the Front End Page
Your Liquid endpoint Page will be acting as an extra Layer between your request and platformOS' own GraphQL endpoint. This extra layer is important because it allows you to run your own logic and security checks, before pOS deliver the data.
SiteGurus have created the Live Updates API as part of the SiteBuilder module- designed as an incredibly flexible API endpoint for refreshing almost any Siteglide Layout with different filters- this may save you time implementing your own API endpoint: https://www.sitegurus.io/documentation/sitebuilder/live_updates/introduction
The Siteglide Support Policy explains how you can get support with planning projects and writing custom code.
MDN have comprehensive documentation on the XML HTTP Request and how to use it in your Front End JavaScript Code: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest You can also use the modern https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API as an alternative.
Those developers who prefer to use jQuery when writing JavaScript can read more about Ajax Requests here: https://api.jquery.com/jquery.ajax/
platformOS's documentation on Pages includes lots of information about setting up Pages using the yaml configuration: https://documentation.platformos.com/developer-guide/pages/pages
In this article we'll look at how to delete a record with a GraphQL mutation, but first of all, let's look at what deleting a record means (and how to undo it!).
When we delete records with GraphQL, they are soft-deleted by default.
If you accidentally delete data, it may have been "soft deleted", and you may be able to recover it if you act quickly.
Think of soft-deletion as being a bit like a recycle bin:
Records are given a deleted_at property with a date in 30 days time in platformOS's datetime format (ISO 8601)
When the date is reached, the record will be permanently deleted.
They are removed from the results of all queries by default (this includes all Siteglide tags)
You can get ordinary records
queries to display soft-deleted records (only), see below:
The results will be the same as in a normal query; it's helpful to add deleted_at to the results so you can see when they will be permanently deleted:
If a record has been soft-deleted by mistake, and has not yet been permanently deleted, you can recover it by setting the deleted_at property to null. Make sure to use the query above to find the ID of the record you want to recover and pass it in as a variable to the mutation below.
After changing deleted_at
to null, the record becomes an ordinary record. It appears in queries and is no longer scheduled to be deleted. The mutation results should confirm that deleted_at
is no longer set:
If you want to extend the amount of time the record stays in soft-deleted state, you can change the deleted_at property to set the deletion date to be further in the future (but this is an advanced method we won't cover here).
Use another query to find the ID.
A successful deletion may look like this:
You've now learned how to perform all four CRUD operations using GraphQL:
Create
Read (query)
Update
Delete
If you wish to programatically create, update or delete multiple records at once, it may be more efficient to use the following mutation types instead of looping:
records_create_all
records_update_all
records_delete_all
Be very careful with those and make sure you test with data which is backed up. We recommend using any_table: false
as a precaution.
In these mutation types, you use a filter
to determine which records should be affected instead of only passing id
. You need to add count
to the results.
Next time we'll look at how you can use a query to access data from multiple relational database tables, or in other words, view data from Siteglide datasources.
In this tutorial we're going to show you how you can use a single query to get not only results in a single table, but at the same time return related results from another table.
For example:
Products may be related to categories- and each category may be related to some help articles.
Blog Articles may each be related to an Author, and that Author may have a relationship with some Secure Zones.
Using related_records is powerful because:
It allows you to build more complex applications - giving you complete control over getting the data you need.
It's flexible - if you've stored enough data, you can define relationships in your query without changing any settings.
Combining multiple queries into one will give you much better performance than any other way of getting this data, e.g. nesting Siteglide include tags inside each other.
Here are a few of the terms you may come across in this topic:
The great thing about related records being as flexible as they are is that there are a lot of examples we could choose from. Our first example though will try to keep things simple by fetching Blog posts with their associated authors.
We've already covered how to write a records query and filter it so it contains only blog posts. Here's an example:
Since these two modules - the Blog and Authors Module - are both created by Siteglide, they already store the information needed to join them inside their properties. We only need to look at the existing properties in detail to get the information we need to build our query.
├───forms │ form_1.liquid │ form_2.liquid │ form_3.liquid │ ├───modules │ │ module_17.liquid │ │ module_3.liquid │ │ module_6.liquid │ │ │ └───module_14
For this exercise, we'd be looking at module_3
for blog and module_6
for authors.
In Blog, scroll down or use ctrl-f to search for author
The form configuration will contain both the human-friendly name and the Siteglide ID for each field:
module_field_3_4: name: Author type: datasource live: true hidden: false order: 0 editable: true datasource_id: module_6 required: false validation: {}
Great! It's a datasource field, which is pefect, because it will already be set up to contain IDs of Author records related to each Blog record. We don't need to look at the Author record now to know that the module_field_3_4
property of the Blog and the id
of the Author should match. Sometimes, you'll need to look at both.
Since this is a GraphQL tutorial- here's an alternative way to check the form-configurations- using a query (see how we use filter to look for only the Blog and Author configurations)!
In the Siteglide Admin, we can check that some of the Blogs already have authors set in the datasource
field, in this case this Blog item has an Author ID of 100
in the field:
To start with, look inside results in explorer: related_record
and related_records
appear as possible results. By including these special objects inside the results object, we can define a branching tree of related results.
The two options are similar but have one key difference:
related_record - Use this to define a 1:1 or a many:1 relationship. It will return a single record as an object. This may give you better performance, if you know for example there's only one author.
related_records - Use this to define a 1:many or a many:many relationship. It will return multiple records as an array.
The main reason to pay attention to which you choose is that it will change the way you structure your Liquid to access the data- remember if there is an array in the data you may need to loop over it in order to access it.
For now, let's use related_record, as we have a datasource
field to join with, and only one Author per Blog record
:
Remember, we use a string with no spaces and a colon to "rename" things in GraphQL. It's a good idea to rename this related_record to describe what it will return. In this case, it describes a single author.
This will make it easier to understand the results and allow you to add other related_records
in future (it won't let you if names clash).
As mentioned in the glossary, the join_on_property
is used to define the property in the current result record which will be used to match with a related record. In step 3 we worked out it was module_field_3_4
.
We don't need to write the value of the module_field_3_4 field, just the Siteglide field ID.
you also don't need to add properties
before the field ID, as you may when filtering a query; here, platformOS works this out automatically
this goes inside the curly brackets as an argument
The foreign property is the counterpart to the join_on_property, but it refers to the property in the record we are looking for, in this case the Author record is the record, and the property is id
.
Note the syntax is the same, even though module_field_3_4
is a custom property and id
is a core property in platformOS.
table
here is a filter, despite not being inside a filter object. It must be set to describe the table we should look in for these related records. In this case, the ID of the Authors module: module_6
We've already defined which results we want for Blog items, and that we want to return a related author per blog item, but we also need to define which authors properties should be returned.
We could return the whole properties object, but where possible it's worth being extra efficient when working with relationships. There is more work for the query to do, and it may run more slowly. Maybe we just need the author's name and an image (I use the information we looked at in step 2 to find the module_6 image field ID)?
The results go in the new object under related_record
after the arguments; no results
key is needed:
That's the query itself done!
The results in JSON may look like the below (we've minimised Blog properties which aren't useful to the example):
{ "data": { "records": { "results": [ { "id": "97", "properties": {...}, # note - this record 97 did NOT have a match with an author. Maybe the Author has been deleted or maybe the ID has not been stored in the field we're joining on.
} }
What if you need this to fetch information the other way around, e.g. you are on a page which displays information about an author and you wish to display a list of the Author's Blog Posts? An additional aspect to this is that this is a 1:many relationship- it's no problem- we'll use related_records
plural instead of related_record
to return an array instead of an object.
Starting with the query above, lets make some changes:
Next, you could experiment with some of these options:
using filters to further filter related records e.g. Authors with Blog posts which are enabled
using related_users
an alternative to related_records
which fetches CRM users with a relationship to your record. You don't need to specify a table, but join and foreign properties work the same.
Additional nesting. Inside your related_records results, you can define related_records again. This allows you to build a complex results tree containing as many layers of related results as you like, for example adding to our example above- you could return the categories of each Blog the Author had published- and display a list of category names.
Term | Definition |
---|
There are several ways to do this. One of the easiest to do on an everyday basis is to use to pull
your site, then to look in marketplace_builder/form_configurations:
As always, when outputting in Liquid, you can use dot notation (see or ) to access the results, until you get to an array. Since we only asked for a single author, we can use dot notation inside the blog record to access the author. We still need to loop over the blog results as always:
relational database | A relational database describes a database comprised of tables, where there may be predefined relationships between records in different tables. platformOS's database is a kind of relational database. |
join | In many query languages e.g. SQL, a join defines how two tables are linked together. In platformOS, a join on property is used to define which field should be used to find other related_records. |
tables | In platformOS, as in many other databases, a table is a set of data which contains records of a certain type, with a certain set of properties. |
records | In platformOS, as in many other databases, a record is a single entry of data in a database. |
related_records | In platformOS, records which share a relationship. It can be single:single, many:many or single:many. This is effectively the same concept as a datasource. |
datasource | In Siteglide, a datasource is where there is a relationship between records in the same table or in a different table, and data can be "sourced" from that other table. It can be single:single, many:many or single:many. This is effectively the same concept as platformOS's related_records. |
datasource and datasource_multi field types |
foreign | In platformOS, a foreign property refers to a property outside of the current record. The foreign property is matched with the join on property in order to fetch related records which share a relationship. |
data graph | In computing a data graph describes the relationships between different nodes of data. Think of a node like a city and the relationships like roads. One of the reasons GraphQL is called GraphQL is that it is trying to give queries an intuitive graph-like structure- we define the relationships in the query itself, and the results return with the same relationship structure. |
In Siteglide, some fields can be given the datasource and datasource_multi field types: These are designed to store IDs of other records to make joining easy. However, you can also in GraphQL join other types of fields.