<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by slow code_ on Medium]]></title>
        <description><![CDATA[Stories by slow code_ on Medium]]></description>
        <link>https://medium.com/@slowcode_?source=rss-8402cf00b9d0------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*1QtB_GnIR3tQY8tfUeX62A.png</url>
            <title>Stories by slow code_ on Medium</title>
            <link>https://medium.com/@slowcode_?source=rss-8402cf00b9d0------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 15 May 2026 18:36:46 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@slowcode_/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Create Sortable Lists in OutSystems]]></title>
            <link>https://medium.com/@slowcode_/create-sortable-lists-in-outsystems-2fe5d768b0?source=rss-8402cf00b9d0------2</link>
            <guid isPermaLink="false">https://medium.com/p/2fe5d768b0</guid>
            <category><![CDATA[outsystems]]></category>
            <category><![CDATA[outsystems-development]]></category>
            <category><![CDATA[sortable]]></category>
            <category><![CDATA[low-code]]></category>
            <category><![CDATA[sortablejs]]></category>
            <dc:creator><![CDATA[slow code_]]></dc:creator>
            <pubDate>Mon, 20 Feb 2023 15:24:13 GMT</pubDate>
            <atom:updated>2023-02-20T15:24:13.511Z</atom:updated>
            <content:encoded><![CDATA[<p>In this post, we’ll explain how to use the <a href="https://github.com/SortableJS/Sortable">SortableJS</a> library on OutSystems, with the help of a Forge component, with the same name.</p><blockquote>Sortable is a JavaScript library for reorderable drag-and-drop lists.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*ytCYJSneaTvkjyDu6X5_YQ.gif" /><figcaption>Example of what you can achieve with the usage of the SortableJS component</figcaption></figure><h3>Where to place the component</h3><p>After having it installed on your environment you have to add its dependencies to the module where you wish to make use of the component, like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eiOo7tVoKjRpUDjd5dKmOQ.png" /><figcaption>Add SortableJS dependencies</figcaption></figure><p>The next step is to drag and drop the Webblock SortableJSList into the screen in the position you’ll put your list.<br>This Webblock has a placeholder, inside of which you should place the list to which you intend to add the sorting behavior. This list can be inside of another element, like a container, it doesn’t need to be the direct child inside of the placeholder, however, for readability purposes, you should avoid wrapping the list with too many elements.</p><p>Besides the placeholder, this block has three inputs, namely:</p><ul><li>ListWrapperQuerySelector</li><li>ListName</li><li>OptionsJSON</li></ul><p>Let’s look at each one separately.</p><h3>Inputs</h3><h4>ListWrapperQuerySelector:</h4><p>This is a mandatory input, and it should take in a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector">CSS selector string</a> referencing the direct parent of the items you wish to add the drag and drop behaviour to.</p><pre>&lt;table id=“tableid”&gt;<br>  &lt;thead&gt;<br>    &lt;tr&gt;<br>      &lt;th&gt;Month&lt;/th&gt;<br>      &lt;th&gt;Savings&lt;/th&gt;<br>    &lt;/tr&gt;<br>  &lt;/thead&gt;<br>  &lt;tbody&gt; &lt;!—- this is the parent --&gt;<br>    &lt;tr&gt; &lt;!— this is a children —&gt;<br>      &lt;td&gt;January&lt;/td&gt;<br>      &lt;td&gt;$100&lt;/td&gt;<br>    &lt;/tr&gt;<br>    &lt;tr&gt;<br>      &lt;td&gt;February&lt;/td&gt;<br>      &lt;td&gt;$80&lt;/td&gt;<br>    &lt;/tr&gt;<br>  &lt;/tbody&gt;<br>  &lt;tfoot&gt;<br>    &lt;tr&gt;<br>      &lt;td&gt;Sum&lt;/td&gt;<br>      &lt;td&gt;$180&lt;/td&gt;<br>    &lt;/tr&gt;<br>  &lt;/tfoot&gt;<br>&lt;/table&gt;</pre><p>Looking at the example table above, if you wanted the lines of a table to be draggable, one option would be to use <em>“#tableid tbody”</em> since the rows are direct children of the tbody. Some other examples would be just <em>“tbody”</em> or <em>“table tbody”</em>, however, these have the disadvantage of not using the unique ID of the table, so they could lead to some strange behaviors if you have more than one table inside of the SortableJS placeholder.</p><p>In our app, we decided to use: <em>“#” + Left_table.Id + “ tbody”</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/346/1*tBEvZ6PQXTrigXWAbGhj9g.png" /><figcaption>Example of the usage of the component with a table</figcaption></figure><p>Similarly, if using a List, you could do something like <em>“.list.list-group”</em> or, using id’s again, <em>“#” + List.Id</em>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/333/1*30L68yyYouw5FTLrBT2zRw.png" /><figcaption>Example of the usage of the component with a list</figcaption></figure><h4>ListName:</h4><p>This input is also mandatory and is used to identify the list or lists that were edited in the <em>onDragEnd</em> event. This event is discussed later in this post.</p><p><strong>Please note that to have a basic usage of this component, these two inputs are all you need.</strong></p><h4>OptionsJSON:</h4><p>This input is not mandatory, since it has a default value. This one will allow you to customize the behavior of your draggable list. The format of the input is a JSON string and you can find all the possible parameters <a href="https://github.com/SortableJS/Sortable#options">here</a>, however, we’ll explore a few of them below.</p><pre>{<br>    group: {<br>        name: &#39;list_group&#39;<br>    },<br>    sort: true,<br>    handle: &#39;.list-handler&#39;<br>}</pre><p>From the example above , the first parameter is the <em>“group -&gt; name”</em>, which is used to group lists, allowing its items to be dragged from one to the other. In order for the items in one list to be dropped into another, they must belong to the same group. You can further customize the conditions in which this is allowed, by following <a href="https://github.com/SortableJS/Sortable#group-option">this documentation</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*ytCYJSneaTvkjyDu6X5_YQ.gif" /><figcaption>Example of what you can achieve with the usage of the SortableJS component</figcaption></figure><p>Then, we see the <em>“sort”</em> parameter. This one is simply to enable the sorting on this list, so, most of the time, you want to have it set to true.</p><p>Finally, the last one is the “handle”, which allows you to set a handle for your draggable element. This means that, when a handle is set up, to be able to drag an element, the user has to do so by dragging on that predefined handle. The value that this parameter accepts is a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector">CSS selector string</a> as well.</p><h3>OutSystems Variables</h3><p>So far we have been able to make the elements on a list or table draggable. However, all that does is affect the look of the webpage on the client’s browser, without any impact on the variables in OutSystems.</p><p>To be able to reflect the changes made by the user in your OutSystems variables, you have to code a bit and create some logic. There are a lot of different alternatives to this process, which would be too extensive to explore in one post. For example, one idea would be to read the order of the elements on the screen using JavaScript, only when the user stops moving things around and saves. This way you could store the sorted order of ids or add a parameter on the Database representing the “Order”, which would take the index of each item on the sorted list.<br>Another method is to store the changes as they happen, making use of the event <em>OnDragEnd</em> triggered by the SortableJS block.</p><h4>OnDragEnd event</h4><p>This event has one structure as its output, called OnDragEndElement, which is composed of four parameters that give you information about the element that was dragged by the user:</p><ul><li>OldIndex — (Zero Indexed) The index of the element in the original list from which it was dragged from;</li><li>NewIndex — (Zero Indexed) The index of the element in the list to which it has been dragged to.</li><li>From — The original list group name where the element was dragged from;</li><li>To — The list group name where the element was dragged to;</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/456/1*bK7NezmXHFqn-jBUQiuVIw.png" /><figcaption>Structure of the OnDragEnd event input</figcaption></figure><p>Using this event, we can create a Handler action that will manipulate the OutSystems List to reflect what has been performed by the client on the <a href="https://www.w3schools.com/js/js_htmldom.asp">DOM</a>.</p><p>One example of such an action could be as follows:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KTrTHClXoatz6IMWLdKGRA.png" /><figcaption>Example of an OutSystems Client Action handler for the OnDragEnd event</figcaption></figure><p>Here we start by locally storing a copy of the dragged element in the variable, SelectedElement. Then, we remove this element from its list, by referencing it by the <em>OldIndex</em>. Finally, we insert the copied element back into the list in its new index.</p><p>Please note that this example does not take into account the possibility of dragging and dropping elements between different lists. To achieve that, you’d have to add some logic to look at the parameters <em>“From”</em> and <em>“To”</em> of this event. That use case will not be covered in this post, but please reach out to us if you need help with that.</p><h3>Conclusion</h3><p>As you can see this is a component with almost infinite use cases, input combinations, and also a lot of different ways to interact with it depending on your necessities and Data Model. However, despite all of this customizability, the basic usage of the component is quite simple, since you only have to set the <em>ListWrapperQuerySelector</em> and the <em>ListName</em>.</p><p>Please, share with us your usage of this component and any ideas for further development.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2fe5d768b0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[GraphQL Integration with OutSystems]]></title>
            <link>https://medium.com/@slowcode_/graphql-integration-with-outsystems-6826dcc6542e?source=rss-8402cf00b9d0------2</link>
            <guid isPermaLink="false">https://medium.com/p/6826dcc6542e</guid>
            <category><![CDATA[graphql]]></category>
            <category><![CDATA[outsystems]]></category>
            <category><![CDATA[integration]]></category>
            <category><![CDATA[rest]]></category>
            <category><![CDATA[low-code]]></category>
            <dc:creator><![CDATA[slow code_]]></dc:creator>
            <pubDate>Tue, 07 Jun 2022 16:40:57 GMT</pubDate>
            <atom:updated>2022-11-14T18:28:46.464Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rw_zqogKSSyo791Jkzq4fQ.png" /></figure><p>TLDR: You can also check this content on YouTube: <a href="https://t.co/5JgbHUyBJd">https://youtu.be/2Mdkz_FZLsQ</a></p><p>Lots of teams have their data layer exposed through GraphQL.</p><p>Unfortunately, OutSystems doesn’t natively support this query language, but that’s no reason to worry! Since a GraphQL request is, at its core, just a more complex POST call, we can definitely integrate with it in OutSystems by:</p><ul><li>Building a .Net integration with Integration Studio</li><li><strong>Consuming a REST call in Service Studio</strong></li></ul><p>In this article, we will focus only on the latter. If you’re curious, you can check our <a href="https://luis-kramp.outsystemscloud.com/GraphQL_REST/GetCountryDetail">live demo here</a>.</p><h3>Basics of GraphQL</h3><p>GraphQL provides a standardized and flexible way to structure queries and exchange data among various database and server platforms.</p><p>When you perform a GraphQL call, you have three main areas:</p><ul><li>The Query or Mutation, where you define what you want to get or update;</li><li>The Variables, like search filters or data to be inserted into the database;</li><li>The Response, like the result of your query or error messages and codes.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BgnDh_--uR_v02BbHo2TJA.png" /></figure><p>Below, we’ll look at each of these three areas and present some syntax fundamentals like doing a query with arguments. To do so, we’ll consume a GraphQL endpoint to retrieve country information, exposed at <a href="https://countries.trevorblades.com">https://countries.trevorblades.com</a>.</p><h4>Query:</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9bd775fda6dd6f9abf5537625e6c1982/href">https://medium.com/media/9bd775fda6dd6f9abf5537625e6c1982/href</a></iframe><p>In line 1, we have the keyword “query”, indicating that we intend to retrieve some data. If our goal were to change or insert data in the database, we would use the keyword “mutation” instead.</p><p>Moving forward, you have to pick a name for your query. We selected “GetCountryByName<em>”</em>. Then, since this query is for a specific country, we have the input variable <em>$countryCode: </em><strong><em>ID</em></strong><em>!</em>. Variables are prepended with a dollar sign, and you must give them a name and specify their type. In this case, the variable is called “countryCode” and is of type ID, which is defined in the schema of the GraphQL endpoint. Pay attention to the exclamation mark in ID!. This means that this is a non-nullable input, so you cannot leave it blank or ignore it when you perform the query. In other words, this field is mandatory.</p><p>Looking now at the second line of the query, we see that we are calling a query already defined in the documentation of the GraphQL endpoint.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OSb8sOvM6uegJ5GgcqqSzw.png" /><figcaption>Country query documentation</figcaption></figure><p>This query is called “country” and receives an input called “code” of type “ID!”. In our use case, we’ll pass in our variable “$countryCode”.</p><p>You might be wondering, if the “country” query already exists, why are we creating a query of our own to call it? There are at least a couple of reasons. First, we want the input “code” to be dynamic, so we need to define a variable to send into the query. If we only wanted the values for one country, e.g. “BE”, we could call the “country” query like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/77c103546af3dbddbafaa1100d8cb7f7/href">https://medium.com/media/77c103546af3dbddbafaa1100d8cb7f7/href</a></iframe><p>Then, the second reason to create a query of our own is related to the Response we want to get. One of the great things about GraphQL is that you can tailor the Response to have the information you need in the exact structure that you need it in. Because of this, whenever you create a query, you must specify what you are querying in the same structure in which you want the Response to be. This leads to the possibility of having two queries for the same data but with different response structures, meaning that you can have the same fields in a different order or other fields altogether. You can find this structure in the remaining lines of the example query.</p><h4>Variables:</h4><p>Sometimes, when we perform a GraphQL query, we need to pass in some inputs, like search filters, or, when performing a mutation, we need to pass the data that needs to be stored in the database. This is done through the variables section.</p><p>In the case of our example, this section would look like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/147aa58ffa4566ea591c720f0b0984eb/href">https://medium.com/media/147aa58ffa4566ea591c720f0b0984eb/href</a></iframe><p>Here you can see that we have a JSON structure with the variable “countryCode”, which we’ve defined for our query, assigned with the value “PT”, to retrieve the data of Portugal.</p><h4>Response:</h4><p>Finally, after performing the query, we’ll get our Response:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4ae984ef6e926ce39363af0fe9471c8a/href">https://medium.com/media/4ae984ef6e926ce39363af0fe9471c8a/href</a></iframe><p>You can see that it came in a JSON structure with the same format we defined in the request.</p><p>Putting everything together, you have something like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LOPrIxypKaJB04RnpK8Izw.png" /><figcaption>Test the queries in the playground <a href="https://countries.trevorblades.com/">here</a></figcaption></figure><h4>GraphQL as a POST:</h4><p>Now that we have a notion of how GraphQL works and the sections that compose a GraphQL call, let’s take a look behind the scenes. As we said in the beginning, a GraphQL call performs a simple POST, enabling us to integrate with it in any platform that supports REST integrations, like OutSystems.</p><p>Let’s take a look then at how the example query GetCountryByName looks like in its REST format to have a general understanding of the request structure of a GraphQL call.</p><h4>Getting the call structure:</h4><p>After opening up the Web Inspector, we need to follow the steps below:</p><ol><li>Go to the Network tab;</li><li>Find the request that you’ve made in the playground app;</li><li>Open the payload tab;</li><li>Copy the request payload;</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8vt4Sdn8PzCbkIkZcSmTjg.png" /><figcaption>Viewing the GraphQL call in its POST format</figcaption></figure><p>In the end, the body of your request will look something like the following example:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3c5591cf5739c34177c772de4d25f805/href">https://medium.com/media/3c5591cf5739c34177c772de4d25f805/href</a></iframe><p>As you can see, it is composed of three simple parts:</p><ul><li>“operationName” - string with the name you gave to your query;</li><li>“variables” - structure that will have precisely the same value that you’ve placed on the variables section of your GraphQL call;</li><li>“query” - string which will have the same value as your query in the GraphQL call;</li></ul><p>When doing a query in GraphQL, these three parameters’ structure will always be followed, and only their values will change depending on the query itself.</p><h3>Consuming GraphQL in Service Studio</h3><p>Moving now to the OutSystems world, let’s continue with our GetCountryByName example. At this point, you might already have an idea of how to consume a GraphQL endpoint in OutSystems, since you’ve just learned what they look like in their REST form.</p><p>In theory, you simply have to create a REST integration in Service Studio; however, there are a few things to pay attention to, to ensure that everything works and that you reduce complexity to the minimum.</p><h4>Removing New Lines:</h4><p>Looking closely at the Request body presented above, we find that the “query” string is full of new lines, represented with “\n”. This character is not allowed when doing the call via OutSystems and will lead to runtime errors. So, when building the integration, we must make sure that we remove the unnecessary new lines and replace the structural ones with commas. In other words, the new lines that we used to define the structure of the Response in the GraphQL environment need to be replaced with commas, and the ones that don’t have any syntax value, need to be removed.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f73ae8edaca8cbdaf0eed9c1370fe184/href">https://medium.com/media/f73ae8edaca8cbdaf0eed9c1370fe184/href</a></iframe><p>Below you can find the new version of the request:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/655b26f8e5ce9713fa5dc02eec792777/href">https://medium.com/media/655b26f8e5ce9713fa5dc02eec792777/href</a></iframe><h4>Building the integration in Service Studio:</h4><p>With the step above concluded, you can open Service Studio.</p><p>In the module where you wish to consume the endpoint, you can go to the Logic tab and then, right-clicking on REST, select “Consume REST API”. Finally, select “Add single method”.</p><p>In the new window, you must specify that this call is a POST, and then add the API URL, the Request without “\n” and an example of the Response, as displayed below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yIdSjVsZmCCb4MumExNEMA.png" /><figcaption>Outsystems REST Integration Detail</figcaption></figure><p>At this stage, you can also add any Authentication settings you may need as you would for any other REST API.</p><p>After hitting the Finish button, you’ll end up with something like this:</p><p>Query variables:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/274/1*UImTZoZJ01pZCHz7eLDKCw.png" /><figcaption>Query variables</figcaption></figure><p>Response:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/346/1*7hPaCETjRHBOj_BY5tk--A.png" /><figcaption>Response</figcaption></figure><p>As you can see, OutSystems built everything as expected. In our Request, we have the three parameters “Query”, “Variables” and “OperationName”. Even though OutSystems sees them as parameters, we know that both the Query and the OperationName, are constant for this call, since we always want to retrieve the same result structure. This means that you can assign default values to them, simplifying the way you consume the method afterward.</p><p>Please, remember to use the query string without “\n” and to set “Send Default Value” to Yes on both parameters.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*p1Qj5_g-bwQCTCPpXztDMA.png" /><figcaption>Query default value</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/302/1*GxdU2qk7F3OX2bUXdDq9Fg.png" /></figure><p>Lastly, the only thing left to do is to encapsulate the method we just created in a Server or Service action to be consumed by another module. Even though you could consume the method directly, we highly recommend doing this last step, since it reduces the number of inputs and the complexity of the structures towards the consumer module.</p><p>In the encapsulating action, you need to add the inputs and outputs of your method, however, some simplification is due.</p><p>Regarding inputs, you don’t need the Query nor the OperationName, so you can simply ignore them. As for the Variables, they have a structure called “Variable” that encapsulates the value that you need to pass in. In this case, it looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/964/1*465Iaze5_zpcl2b8_BDmLA.png" /><figcaption>Variable Structure</figcaption></figure><p>Since we just need the string of the CountryCode, we can add a simple Text input to our encapsulating action without the need to make it of type “Variable”. If, in your case, you have an input of a more complex structure, you can still make the input of the encapsulating action of the type of the inner layer of the “Variable” structure, thus, removing one unnecessary layer that has no value for the consumer side.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/984/1*XOfb45b54SPQkdeSxKPpsw.png" /><figcaption>The input of the encapsulating action</figcaption></figure><p>Looking now at the output structure, we have the same case as we did for the “Variable”. Since the JSON response of the service always starts with “data”:{} the output of our GraphQL method will always be of type “Data”. This means that, as we did for the “Variable”, we can skip this layer in the output structure.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/976/1*ErLjWrjrgRWEYb7P_c5OBw.png" /><figcaption>Response Structure</figcaption></figure><p>We can, therefore, set the output of our encapsulating action to the type of the inner layer of the “Data” structure. In our example, that would be “Country”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/980/1*4KuY8NJClXyZ6FiMXAG2IQ.png" /><figcaption>The output of the encapsulating action</figcaption></figure><p>Bringing this all together, we can then take a look at our encapsulating action:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/497/1*T9LgbFJDL_fasu0e8Rn3iQ.png" /><figcaption>Encapsulating action</figcaption></figure><p>Please, notice that you should leave the “Query” and “OperationName” empty since they’ll be populated with their default value. To minimize the number of assigns and variables, we also “expanded” the method’s input and passed the “CountryCode” directly. You’ll probably be able to do the same with your use case.</p><p>As for the Response, you can do a single assignment, as displayed below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/956/1*CNUbA3X-jV_r_hBWiMxCqQ.png" /><figcaption>Assignment of the Response in the encapsulating action</figcaption></figure><h3>Consuming the GraphQL Method on a screen</h3><p>After the completion of the encapsulating action, there’s nothing new. You can simply consume it on a screen as you would do for any other action. Below you can find an example of the GraphQL method being used in an OutSystems application to retrieve the data of Belgium. You can also <a href="https://luis-kramp.outsystemscloud.com/GraphQL_REST/GetCountryDetail">test it yourself here</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ro95I99spSZvni7ABxCMNw.png" /><figcaption>GraphQL method being used in an OutSystems application</figcaption></figure><p>That’s it! As you can see, after understanding that a GraphQL call is just a POST method with a specific structure, it becomes relatively simple to consume this type of method in OutSystems.</p><p>Please reach out if you have any comments or questions. We’d love to discuss this topic with you!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6826dcc6542e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Consume Multiple REST methods]]></title>
            <link>https://medium.com/@slowcode_/consume-multiple-rest-methods-9d5444d4d9fe?source=rss-8402cf00b9d0------2</link>
            <guid isPermaLink="false">https://medium.com/p/9d5444d4d9fe</guid>
            <category><![CDATA[outsystems-development]]></category>
            <category><![CDATA[rest]]></category>
            <category><![CDATA[open-api]]></category>
            <category><![CDATA[outsystems]]></category>
            <category><![CDATA[low-code]]></category>
            <dc:creator><![CDATA[slow code_]]></dc:creator>
            <pubDate>Mon, 18 Apr 2022 19:32:03 GMT</pubDate>
            <atom:updated>2022-04-20T11:08:57.812Z</atom:updated>
            <content:encoded><![CDATA[<h3>Consume Multiple REST methods in OutSystems</h3><p>OutSystems is a potent tool, with a few tricks we can explore!</p><p>One such trick is the option to <strong>Add Multiple Methods from a REST API at once</strong>, with all inputs, outputs, and even descriptions! All you need is a JSON file with your App’s <strong>OpenAPI specifications (OAS)</strong>. If your curiosity is ticking, we have an example of such a file at the end of the document, but that’s not important if all you want is to start coding!</p><h3>In this post, you’ll learn:</h3><ul><li>How to get the JSON file from:<br>– OutSystems;<br>– An External app, converting a YAML file into a JSON file;</li><li>How to upload the JSON file in OutSystems;</li><li>How to get the <a href="https://swagger.io/specification/">OAS</a> for loads of public APIs for your projects;</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*Fxby0P9C3cgU268BvZ103Q.gif" /><figcaption>Consuming Multiple methods of an API in OutSystems.</figcaption></figure><h3>How to get the JSON file?</h3><p>You have two scenarios.</p><h4>REST API Exposed in OutSystems:</h4><p>If you‘re consuming a REST API exposed by another OutSystems application, it’s effortless since OutSystems does that documentation automatically. <strong>You can get the file by following the steps below in the Exposing application</strong>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vlIBKG9cRyg6SBy6_53wkA.png" /><figcaption>Go to the “Logic” tab -&gt; “Integrations” -&gt; “REST” -&gt; Right click in the API -&gt; “Open Documentation”.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cFN13OHJHXLShtqYzbmTbg.png" /><figcaption>Click on the link “swagger.json”.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kVSGOcYjiSQqmpxr2Hs9xg.png" /><figcaption>In the new tab it “ctrl + S” or “cmd + S” to Save the file.</figcaption></figure><h4>REST API Exposed in an External System:</h4><p>However, in many situations you’ll be consuming endpoints from different sources and/or teams. Thankfully, our backend colleagues follow some specifications for documentation, one of them being the <a href="https://swagger.io/specification/">OpenAPI Specification (OAS)</a>, which, in their own words:</p><blockquote>defines a standard, language-agnostic interface to RESTful APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection.</blockquote><p><strong>All you have to do is to ask them to provide you with the JSON file directly</strong>, since:</p><blockquote>An OpenAPI document that conforms to the OpenAPI Specification is itself a JSON object, which may be represented either in JSON or YAML format.</blockquote><p><strong>Alternatively, if they only provided you with the file in YAML format, you can convert it to JSON by going to </strong><a href="https://editor.swagger.io"><strong>https://editor.swagger.io</strong></a><strong>, then importing the YAML file, and finally exporting it in JSON.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*S13clOdur9-_YB3Qu6Uerw.png" /><figcaption>Go to editor.swagger.io -&gt; “File” -&gt; “Import file”.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Xb2xgk684pzTCGpBhdU9nw.png" /><figcaption>Select the YAML file and click “Upload”.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4PeD90-qno3M-eKragpzQA.png" /><figcaption>The API documentation will be automatically displayed.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/948/1*UrRusD7Daek8xBxvO4-pkQ.png" /><figcaption>Finally, click “File” -&gt; “Convert and save as JSON”.</figcaption></figure><p>Then the JSON file will be saved in your disk, ready to be uploaded into the OutSystems Environment!</p><h3>Upload the JSON file in OutSystems</h3><p>If you have the JSON file, all you have to do in Outsystems is use the “Add multiple methods” option when consuming a REST API. Just follow the steps on the images below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/702/1*sYnjofGyeI9iN3ZN8tMDMw.png" /><figcaption>Go to the “Logic” tab-&gt; “Integrations” -&gt; Right click on “REST” -&gt; “Consume REST API…”.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Y-PupQHDH6f9hx3GcAtfUw.png" /><figcaption>On the Pop-up, select “Add multiple methods”.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*l7vyTdjBvav25u6o3lPG9A.png" /><figcaption>Select your file and click “Add methods”.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*km4gKEZFcvdnnHOSLuGuZA.png" /><figcaption>Finally, select the methods you want to add (probably all) and hit “Finish”.</figcaption></figure><p>After a few seconds, you’ll see that OutSystems consumes all the methods in the specification! And just like that, you’ve saved a lot of time.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T_XpS-BdEIT5Mht9gKfyXA.png" /><figcaption>Example of an API consumed in OutSystems using the “Add multiple methods” button.</figcaption></figure><h3>Bonus tip</h3><p>If you want to consume a public API, like Google Calendar, or Microsoft Azure, you can find loads upon loads of YAML files created by the API-Guru team here <a href="https://apis.guru">https://apis.guru</a> or in their GitHub repository <a href="https://github.com/APIs-guru">https://github.com/APIs-guru</a>.</p><p>We’ve used them in the past with great success!</p><h3>Example of an OpenAPI specification in JSON</h3><p>Just for curiosity’s sake, you can find below an example of an OpenAPI specification in JSON format:</p><pre>{<br>   &quot;swagger&quot;:&quot;2.0&quot;,<br>   &quot;info&quot;:{<br>      &quot;title&quot;:&quot;TaskAPI&quot;,<br>      &quot;description&quot;:&quot;DEMO API from ToDoItem\n\n## Authentication\n\nBasic Authentication is required for all requests.&quot;,<br>      &quot;version&quot;:&quot;1&quot;<br>   },<br>   &quot;basePath&quot;:&quot;/REST_API/rest/TaskAPI&quot;,<br>   &quot;schemes&quot;:[<br>      &quot;https&quot;<br>   ],<br>   &quot;paths&quot;:{<br>      &quot;/CompleteTask&quot;:{<br>         &quot;put&quot;:{<br>            &quot;tags&quot;:[<br>               &quot;TaskAPI&quot;<br>            ],<br>            &quot;description&quot;:&quot;Marks a task as complete&quot;,<br>            &quot;operationId&quot;:&quot;CompleteTask&quot;,<br>            &quot;consumes&quot;:[<br>               &quot;text/plain&quot;<br>            ],<br>            &quot;produces&quot;:[<br>               &quot;application/json&quot;<br>            ],<br>            &quot;parameters&quot;:[<br>               {<br>                  &quot;name&quot;:&quot;TaskId&quot;,<br>                  &quot;in&quot;:&quot;body&quot;,<br>                  &quot;required&quot;:true,<br>                  &quot;schema&quot;:{<br>                     &quot;type&quot;:&quot;integer&quot;,<br>                     &quot;example&quot;:1234567891234567,<br>                     &quot;format&quot;:&quot;int64&quot;,<br>                     &quot;description&quot;:&quot;Id of the task to complete&quot;<br>                  },<br>                  &quot;description&quot;:&quot;Id of the task to complete&quot;<br>               }<br>            ],<br>            &quot;responses&quot;:{<br>               &quot;200&quot;:{<br>                  &quot;description&quot;:&quot;True if an error ocurred&quot;,<br>                  &quot;schema&quot;:{<br>                     &quot;$ref&quot;:&quot;#/definitions/Error&quot;<br>                  }<br>               }<br>            }<br>         }<br>      },<br>      &quot;/CreateTask&quot;:{<br>         &quot;post&quot;:{<br>            &quot;tags&quot;:[<br>               &quot;TaskAPI&quot;<br>            ],<br>            &quot;description&quot;:&quot;Creates a new task&quot;,<br>            &quot;operationId&quot;:&quot;CreateTask&quot;,<br>            &quot;consumes&quot;:[<br>               &quot;application/json&quot;<br>            ],<br>            &quot;produces&quot;:[<br>               &quot;text/plain&quot;<br>            ],<br>            &quot;parameters&quot;:[<br>               {<br>                  &quot;name&quot;:&quot;Task&quot;,<br>                  &quot;in&quot;:&quot;body&quot;,<br>                  &quot;description&quot;:&quot;Task to create&quot;,<br>                  &quot;required&quot;:true,<br>                  &quot;schema&quot;:{<br>                     &quot;$ref&quot;:&quot;#/definitions/Task&quot;<br>                  }<br>               }<br>            ],<br>            &quot;responses&quot;:{<br>               &quot;200&quot;:{<br>                  &quot;description&quot;:&quot;Id of the newly created task&quot;,<br>                  &quot;schema&quot;:{<br>                     &quot;type&quot;:&quot;integer&quot;,<br>                     &quot;example&quot;:1234567891234567,<br>                     &quot;format&quot;:&quot;int64&quot;,<br>                     &quot;description&quot;:&quot;Id of the newly created task&quot;<br>                  }<br>               }<br>            }<br>         }<br>      },<br>      &quot;/DeleteTask&quot;:{<br>         &quot;delete&quot;:{<br>            &quot;tags&quot;:[<br>               &quot;TaskAPI&quot;<br>            ],<br>            &quot;description&quot;:&quot;Deletes the given task&quot;,<br>            &quot;operationId&quot;:&quot;DeleteTask&quot;,<br>            &quot;parameters&quot;:[<br>               {<br>                  &quot;name&quot;:&quot;TaskId&quot;,<br>                  &quot;in&quot;:&quot;query&quot;,<br>                  &quot;required&quot;:true,<br>                  &quot;type&quot;:&quot;integer&quot;,<br>                  &quot;x-example&quot;:1234567891234567,<br>                  &quot;format&quot;:&quot;int64&quot;,<br>                  &quot;description&quot;:&quot;Id of the task to delete&quot;<br>               }<br>            ],<br>            &quot;responses&quot;:{<br>               &quot;200&quot;:{<br>                  &quot;description&quot;:&quot;&quot;<br>               }<br>            }<br>         }<br>      },<br>      &quot;/GetTask&quot;:{<br>         &quot;get&quot;:{<br>            &quot;tags&quot;:[<br>               &quot;TaskAPI&quot;<br>            ],<br>            &quot;description&quot;:&quot;Returns a task by Id&quot;,<br>            &quot;operationId&quot;:&quot;GetTask&quot;,<br>            &quot;produces&quot;:[<br>               &quot;application/json&quot;<br>            ],<br>            &quot;parameters&quot;:[<br>               {<br>                  &quot;name&quot;:&quot;TaskId&quot;,<br>                  &quot;in&quot;:&quot;query&quot;,<br>                  &quot;required&quot;:true,<br>                  &quot;type&quot;:&quot;integer&quot;,<br>                  &quot;x-example&quot;:1234567891234567,<br>                  &quot;format&quot;:&quot;int64&quot;,<br>                  &quot;description&quot;:&quot;Id of the item to retrieve&quot;<br>               }<br>            ],<br>            &quot;responses&quot;:{<br>               &quot;200&quot;:{<br>                  &quot;description&quot;:&quot;Retrieved item&quot;,<br>                  &quot;schema&quot;:{<br>                     &quot;$ref&quot;:&quot;#/definitions/Task&quot;<br>                  }<br>               }<br>            }<br>         }<br>      },<br>      &quot;/SearchTasks&quot;:{<br>         &quot;post&quot;:{<br>            &quot;tags&quot;:[<br>               &quot;TaskAPI&quot;<br>            ],<br>            &quot;description&quot;:&quot;Gets a set of tasks based on the given search filters&quot;,<br>            &quot;operationId&quot;:&quot;SearchTasks&quot;,<br>            &quot;consumes&quot;:[<br>               &quot;application/json&quot;<br>            ],<br>            &quot;produces&quot;:[<br>               &quot;application/json&quot;<br>            ],<br>            &quot;parameters&quot;:[<br>               {<br>                  &quot;name&quot;:&quot;Request&quot;,<br>                  &quot;in&quot;:&quot;body&quot;,<br>                  &quot;description&quot;:&quot;Search Filters&quot;,<br>                  &quot;required&quot;:true,<br>                  &quot;schema&quot;:{<br>                     &quot;$ref&quot;:&quot;#/definitions/GetTasksRequest&quot;<br>                  }<br>               }<br>            ],<br>            &quot;responses&quot;:{<br>               &quot;200&quot;:{<br>                  &quot;description&quot;:&quot;List of founded tasks&quot;,<br>                  &quot;schema&quot;:{<br>                     &quot;$ref&quot;:&quot;#/definitions/SearchTasksResponse&quot;<br>                  }<br>               }<br>            }<br>         }<br>      }<br>   },<br>   &quot;definitions&quot;:{<br>      &quot;Error&quot;:{<br>         &quot;description&quot;:&quot;Error structure&quot;,<br>         &quot;type&quot;:&quot;object&quot;,<br>         &quot;properties&quot;:{<br>            &quot;HasErrors&quot;:{<br>               &quot;type&quot;:&quot;boolean&quot;,<br>               &quot;example&quot;:false,<br>               &quot;description&quot;:&quot;True if an error ocurred&quot;<br>            },<br>            &quot;ErrorMessage&quot;:{<br>               &quot;type&quot;:&quot;string&quot;,<br>               &quot;default&quot;:&quot;&quot;,<br>               &quot;description&quot;:&quot;Message describing the error&quot;<br>            }<br>         }<br>      },<br>      &quot;GetTasksRequest&quot;:{<br>         &quot;description&quot;:&quot;Request structure of get tasks&quot;,<br>         &quot;type&quot;:&quot;object&quot;,<br>         &quot;properties&quot;:{<br>            &quot;UserId&quot;:{<br>               &quot;type&quot;:&quot;integer&quot;,<br>               &quot;example&quot;:0,<br>               &quot;format&quot;:&quot;int32&quot;,<br>               &quot;description&quot;:&quot;Id of the user that created the tasks&quot;<br>            },<br>            &quot;Title&quot;:{<br>               &quot;type&quot;:&quot;string&quot;,<br>               &quot;default&quot;:&quot;&quot;,<br>               &quot;description&quot;:&quot;Title of the task, can be partial&quot;<br>            },<br>            &quot;Description&quot;:{<br>               &quot;type&quot;:&quot;string&quot;,<br>               &quot;default&quot;:&quot;&quot;,<br>               &quot;description&quot;:&quot;Description of the task, can be partial&quot;<br>            },<br>            &quot;DateTimeFrom&quot;:{<br>               &quot;type&quot;:&quot;string&quot;,<br>               &quot;format&quot;:&quot;date-time&quot;,<br>               &quot;example&quot;:&quot;2014–12–31T23:59:59.938Z&quot;,<br>               &quot;description&quot;:&quot;Initial date time for which to get tasks&quot;<br>            },<br>            &quot;DateTimeTo&quot;:{<br>               &quot;type&quot;:&quot;string&quot;,<br>               &quot;format&quot;:&quot;date-time&quot;,<br>               &quot;example&quot;:&quot;2014–12–31T23:59:59.938Z&quot;,<br>               &quot;description&quot;:&quot;Final date time from which to get tasks&quot;<br>            },<br>            &quot;Status&quot;:{<br>               &quot;type&quot;:&quot;string&quot;,<br>               &quot;default&quot;:&quot;&quot;,<br>               &quot;description&quot;:&quot;Can be &amp;quot;COMPLETED&amp;quot;; &amp;quot;OPEN&amp;quot;; &amp;quot;ALL&amp;quot;&quot;<br>            },<br>            &quot;Pagination&quot;:{<br>               &quot;$ref&quot;:&quot;#/definitions/Pagination&quot;<br>            }<br>         },<br>         &quot;required&quot;:[<br>            &quot;Status&quot;<br>         ]<br>      },<br>      &quot;Pagination&quot;:{<br>         &quot;description&quot;:&quot;Pagination data&quot;,<br>         &quot;type&quot;:&quot;object&quot;,<br>         &quot;properties&quot;:{<br>            &quot;Page&quot;:{<br>               &quot;type&quot;:&quot;integer&quot;,<br>               &quot;example&quot;:0,<br>               &quot;format&quot;:&quot;int32&quot;,<br>               &quot;description&quot;:&quot;Current page number, 1 indexed&quot;<br>            },<br>            &quot;PageSize&quot;:{<br>               &quot;type&quot;:&quot;integer&quot;,<br>               &quot;example&quot;:0,<br>               &quot;format&quot;:&quot;int32&quot;,<br>               &quot;description&quot;:&quot;Size of the page&quot;<br>            },<br>            &quot;TotalResults&quot;:{<br>               &quot;type&quot;:&quot;integer&quot;,<br>               &quot;example&quot;:1234567891234567,<br>               &quot;format&quot;:&quot;int64&quot;,<br>               &quot;description&quot;:&quot;[OUTPUT] Total number of tasks that correspond to the given search filters&quot;<br>            }<br>         },<br>         &quot;required&quot;:[<br>            &quot;Page&quot;<br>         ]<br>      },<br>      &quot;SearchTasksResponse&quot;:{<br>         &quot;description&quot;:&quot;Response Structure for the SearchTasks Method&quot;,<br>         &quot;type&quot;:&quot;object&quot;,<br>         &quot;properties&quot;:{<br>            &quot;TasksList&quot;:{<br>               &quot;type&quot;:&quot;array&quot;,<br>               &quot;items&quot;:{<br>                  &quot;$ref&quot;:&quot;#/definitions/Task&quot;<br>               },<br>               &quot;description&quot;:&quot;List of founded tasks&quot;<br>            },<br>            &quot;Pagination&quot;:{<br>               &quot;$ref&quot;:&quot;#/definitions/Pagination&quot;<br>            }<br>         }<br>      },<br>      &quot;Task&quot;:{<br>         &quot;description&quot;:&quot;Single task&quot;,<br>         &quot;type&quot;:&quot;object&quot;,<br>         &quot;properties&quot;:{<br>            &quot;Id&quot;:{<br>               &quot;type&quot;:&quot;integer&quot;,<br>               &quot;example&quot;:1234567891234567,<br>               &quot;format&quot;:&quot;int64&quot;<br>            },<br>            &quot;Title&quot;:{<br>               &quot;type&quot;:&quot;string&quot;,<br>               &quot;default&quot;:&quot;&quot;,<br>               &quot;description&quot;:&quot;Title of the task&quot;<br>            },<br>            &quot;Description&quot;:{<br>               &quot;type&quot;:&quot;string&quot;,<br>               &quot;default&quot;:&quot;&quot;,<br>               &quot;description&quot;:&quot;Small descriptor of the task&quot;<br>            },<br>            &quot;IsCompleted&quot;:{<br>               &quot;type&quot;:&quot;boolean&quot;,<br>               &quot;example&quot;:false,<br>               &quot;description&quot;:&quot;True if the to do task is completed&quot;<br>            },<br>            &quot;CreatedBy&quot;:{<br>               &quot;type&quot;:&quot;integer&quot;,<br>               &quot;example&quot;:0,<br>               &quot;format&quot;:&quot;int32&quot;,<br>               &quot;description&quot;:&quot;Identifies the user that created this task&quot;<br>            },<br>            &quot;CreatedAt&quot;:{<br>               &quot;type&quot;:&quot;string&quot;,<br>               &quot;format&quot;:&quot;date-time&quot;,<br>               &quot;example&quot;:&quot;2014–12–31T23:59:59.938Z&quot;,<br>               &quot;description&quot;:&quot;Date time when this task was created&quot;<br>            },<br>            &quot;CompletedAt&quot;:{<br>               &quot;type&quot;:&quot;string&quot;,<br>               &quot;format&quot;:&quot;date-time&quot;,<br>               &quot;example&quot;:&quot;2014–12–31T23:59:59.938Z&quot;,<br>               &quot;description&quot;:&quot;Date time of when the task was completed; nulldate if not completed&quot;<br>            }<br>         },<br>         &quot;required&quot;:[<br>            &quot;Id&quot;,<br>            &quot;Title&quot;,<br>            &quot;IsCompleted&quot;,<br>            &quot;CreatedBy&quot;,<br>            &quot;CreatedAt&quot;<br>         ]<br>      }<br>   },<br>   &quot;securityDefinitions&quot;:{<br>      &quot;basic&quot;:{<br>         &quot;type&quot;:&quot;basic&quot;<br>      }<br>   },<br>   &quot;security&quot;:[<br>      {<br>         &quot;basic&quot;:[<br>            <br>         ]<br>      }<br>   ],<br>   &quot;tags&quot;:[<br>      {<br>         &quot;name&quot;:&quot;TaskAPI&quot;<br>      }<br>   ]<br>}</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9d5444d4d9fe" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>