<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Camdyn Rasque</title>
    <description>The latest articles on Forem by Camdyn Rasque (@camdyn_rasque).</description>
    <link>https://forem.com/camdyn_rasque</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2065655%2F4f6a2746-df17-40e5-99fc-f4ca08dce5af.jpg</url>
      <title>Forem: Camdyn Rasque</title>
      <link>https://forem.com/camdyn_rasque</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/camdyn_rasque"/>
    <language>en</language>
    <item>
      <title>Understanding REST APIs - A Guide for the Impatient</title>
      <dc:creator>Camdyn Rasque</dc:creator>
      <pubDate>Sat, 09 Nov 2024 00:41:56 +0000</pubDate>
      <link>https://forem.com/camdyn_rasque/understanding-rest-apis-a-guide-for-the-impatient-1i0g</link>
      <guid>https://forem.com/camdyn_rasque/understanding-rest-apis-a-guide-for-the-impatient-1i0g</guid>
      <description>&lt;p&gt;Crosspost of &lt;a href="https://blog.zingsoft.com/understanding-rest-apis-a-guide-for-the-impatient/" rel="noopener noreferrer"&gt;my article here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;REST (&lt;strong&gt;Re&lt;/strong&gt;presentational &lt;strong&gt;S&lt;/strong&gt;tate &lt;strong&gt;T&lt;/strong&gt;ransfer) APIs are the backbone of modern web development. This article will break down how to create and use modern REST APIs, what design decisions to take into consideration when making one, and the theory being the foundation of REST.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Guide
&lt;/h2&gt;

&lt;p&gt;This section dives into using REST APIs with HTTP covering endpoints, methods, requests, and responses. You'll find everything you need to start making API calls and applying REST in your projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Structure Your URIs
&lt;/h3&gt;

&lt;p&gt;In general there are two main ways you can treat a URI:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;As an &lt;strong&gt;action&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;As a &lt;strong&gt;resource&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Consider the two following URIs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;https://example.com/getUserData?id=1&lt;/code&gt; (action)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://example.com/users/1&lt;/code&gt; (resource)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both examples show us retrieving user data of a user with an &lt;code&gt;id&lt;/code&gt; of &lt;code&gt;1&lt;/code&gt;. The difference being that in the first example the route &lt;code&gt;/getUserData&lt;/code&gt; is performing an action while in the second example the route &lt;code&gt;/users/1&lt;/code&gt; is the location of an asset, it does not indicate what action is being performed. We could say that this type of URI is acting as a noun (as it is a thing instead of an action i.e. a verb).&lt;/p&gt;

&lt;p&gt;The REST pattern dictates that we strictly use URIs like the second example. We want our URIs to be nouns so that we can use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods?ref=blog.zingsoft.com" rel="noopener noreferrer"&gt;HTTP Methods&lt;/a&gt; as our verbs to perform actions upon those nouns. For example, we can use the HTTP method &lt;code&gt;GET&lt;/code&gt; to retrieve information about &lt;code&gt;/users/1&lt;/code&gt;, but we could use &lt;code&gt;PUT&lt;/code&gt; to update that corresponding user's information, or &lt;code&gt;DELETE&lt;/code&gt; to delete the user entirely.&lt;/p&gt;

&lt;p&gt;The last thing to note about URIs is that, as with the example above, when referencing an individual resource (e.g. a single user in this case) the URI should end with the unique identifier for that resource. When referencing all resources in a given category, the unique identifier should be omitted.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;https://example.com/users/1&lt;/code&gt; - References a particular user with an &lt;code&gt;id&lt;/code&gt; of &lt;code&gt;1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://example.com/users&lt;/code&gt; - References all users regardless of &lt;code&gt;id&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What Actions to Support
&lt;/h3&gt;

&lt;p&gt;There are 4 main actions to support in REST, we use the acronym &lt;strong&gt;CRUD&lt;/strong&gt; to remember them: &lt;strong&gt;C&lt;/strong&gt;reate, &lt;strong&gt;R&lt;/strong&gt;ead, &lt;strong&gt;U&lt;/strong&gt;pdate, &lt;strong&gt;D&lt;/strong&gt;elete. Each of these actions maps to an HTTP Method that we can use to perform that action. The mapping is as follows:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;HTTP Method&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Create&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Update&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PUT / PATCH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Delete&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  All Action + URI Combinations to Support
&lt;/h3&gt;

&lt;p&gt;Every REST API is really just (at a minimum) 5-6 routes. In our example, the base endpoint will be &lt;code&gt;/users&lt;/code&gt; and we'll pretend to host it on &lt;code&gt;https://example.com&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET https://example.com/users&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: Return all user assets (each asset is one user)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request Body&lt;/strong&gt;: Empty&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response Body&lt;/strong&gt;: List of user assets (as a JSON array)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;GET &lt;a href="https://example.com/users/%5Bid%5D" rel="noopener noreferrer"&gt;https://example.com/users/[id]&lt;/a&gt;&lt;/code&gt; (&lt;code&gt;[id]&lt;/code&gt; is a variable)

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: Returns just the singular requested user asset&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request Body&lt;/strong&gt;: Empty&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response Body&lt;/strong&gt;: Just the user asset with the matching &lt;code&gt;id&lt;/code&gt; &lt;em&gt;(as JSON)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;POST &lt;a href="https://example.com/users" rel="noopener noreferrer"&gt;https://example.com/users&lt;/a&gt;&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: Adds one user asset to the collection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request Body&lt;/strong&gt;: All data needed to create the new user asset &lt;em&gt;(no specific format required, JSON recommended)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response Body&lt;/strong&gt;: The newly created asset that was inserted + a unique ID &lt;em&gt;(as JSON)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;PUT &lt;a href="https://example.com/users/%5Bid%5D" rel="noopener noreferrer"&gt;https://example.com/users/[id]&lt;/a&gt;&lt;/code&gt; (&lt;code&gt;[id]&lt;/code&gt; is a variable)

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: Fully replaces just one existing user's data with the given data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request Body&lt;/strong&gt;: All data needed to replace an existing user's data, whether or not it has changed (minus the &lt;code&gt;id&lt;/code&gt; - &lt;em&gt;no specific format required, JSON recommended&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response Body&lt;/strong&gt;: The newly updated asset with the matching &lt;code&gt;id&lt;/code&gt; &lt;em&gt;(as JSON)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;em&gt;(Optional)&lt;/em&gt; &lt;code&gt;PATCH &lt;a href="https://example.com/users/%5Bid%5D" rel="noopener noreferrer"&gt;https://example.com/users/[id]&lt;/a&gt;&lt;/code&gt; (&lt;code&gt;[id]&lt;/code&gt; is a variable)

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: Partially replaces just one existing user's data with the given data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request Body&lt;/strong&gt;: Only the data that needs updating (minus the id - &lt;em&gt;no specific format required, JSON recommended&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response Body&lt;/strong&gt;: The newly updated asset with the matching id &lt;em&gt;(as JSON)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;DELETE &lt;a href="https://example.com/users/%5Bid%5D" rel="noopener noreferrer"&gt;https://example.com/users/[id]&lt;/a&gt;&lt;/code&gt; (&lt;code&gt;[id]&lt;/code&gt; is a variable)

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: Deletes just one record from the users table&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request Body&lt;/strong&gt;: None&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response Body&lt;/strong&gt;: None (just HTTP response code) &lt;u&gt;OR&lt;/u&gt; The data from the asset that was just deleted with matching &lt;code&gt;id&lt;/code&gt; &lt;em&gt;(as JSON)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Design Considerations
&lt;/h2&gt;

&lt;p&gt;Outside of what defines an endpoint as using the REST pattern or not, there are many things to take into consideration before you start building one. Is there a chance that you might want to update your endpoint in the future? Should your output give helpful hints to users? Is REST even the right pattern to use in your situation? Let's answer some of these questions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Versioning Your Endpoint
&lt;/h3&gt;

&lt;p&gt;It might be a good idea to start thinking about versioning of your API right from the start so that making changes is easier in the future. There are a few different methods for determining what API version your users are choosing to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;URI Versioning

&lt;ul&gt;
&lt;li&gt;The version numbers are incorporated into a URL path, usually at the base&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Examples&lt;/strong&gt;:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://example.com/v1/users/1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://example.com/v2/users/1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Query Parameter

&lt;ul&gt;
&lt;li&gt;The version number is appended as a query parameter in the API endpoint&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Examples&lt;/strong&gt;:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://example.com/users/1?apiVersion=1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://example.com/users/1?apiVersion=2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Header based

&lt;ul&gt;
&lt;li&gt;The version number is a specific and unique header field&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Examples (Request Headers)&lt;/strong&gt;:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;x-api-version: 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;x-api-version: 2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Content Negotiation

&lt;ul&gt;
&lt;li&gt;The version is determined based on the representational state or media type.&lt;/li&gt;
&lt;li&gt;In the example below, the server code would know that &lt;code&gt;firstName&lt;/code&gt; is for the first version and that it was changed to &lt;code&gt;givenName&lt;/code&gt; in the next version.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Examples (Request Body)&lt;/strong&gt;:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{ firstName: 'Henry' }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{ givenName: 'Henry' }&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Mocking a Quick REST API
&lt;/h2&gt;

&lt;p&gt;Sometimes playing around with one is the best tool to learn how they work. One of my favorite libraries to demo REST is &lt;a href="https://www.npmjs.com/package/json-server" rel="noopener noreferrer"&gt;json-server&lt;/a&gt;. Setting it up is pretty simple - just a few steps required.&lt;/p&gt;

&lt;p&gt;Install JSON Server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;json-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a simple data store&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gorwell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gorwell@gmail.com"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cdickens"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cdickens@gmail.com"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jausten"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jausten@gmail.com"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vwoolf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vwoolf@gmail.com"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sking@gmail.com"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start up the server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx json-server db.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make an HTTP request against your local server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET http://localhost:3000/users/1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  An Easy CRUD Data Grid
&lt;/h3&gt;

&lt;p&gt;A fully functioning REST endpoint can be hooked up to a data grid easily with &lt;a href="https://www.zinggrid.com/" rel="noopener noreferrer"&gt;ZingGrid&lt;/a&gt;, just point the base REST URI at the &lt;code&gt;&amp;lt;zing-grid&amp;gt;&lt;/code&gt; element's &lt;code&gt;src&lt;/code&gt; attribute like below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;zing-grid&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:3000/users"&lt;/span&gt;
  &lt;span class="na"&gt;editor-controls&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/zing-grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn90ltel0ot42s94jmdkw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn90ltel0ot42s94jmdkw.gif" alt="CRUD actions being performed on a ZingGrid that automatically synchronizes changes with REST endpoint" width="600" height="236"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;REST APIs come in many shapes and sizes across the web, each tailored to meet specific needs. By structuring URIs thoughtfully, choosing the right actions, and keeping versioning in mind, you can create a straightforward, flexible API that developers will find a pleasure to work with. With these foundational steps, even a quick prototype can evolve into a robust, reliable API that stands the test of time.&lt;/p&gt;

</description>
      <category>rest</category>
      <category>api</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Add an Excel-like Table to Your Astro Site (the Easy Way)</title>
      <dc:creator>Camdyn Rasque</dc:creator>
      <pubDate>Tue, 22 Oct 2024 23:02:23 +0000</pubDate>
      <link>https://forem.com/camdyn_rasque/how-to-add-an-excel-like-table-to-your-astro-site-the-easy-way-55c6</link>
      <guid>https://forem.com/camdyn_rasque/how-to-add-an-excel-like-table-to-your-astro-site-the-easy-way-55c6</guid>
      <description>&lt;p&gt;Cross post from &lt;a href="https://blog.zingsoft.com/adding-data-tables-and-grids-to-your-astro-site-the-easy-way-2/" rel="noopener noreferrer"&gt;my article on the Zing blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The philosophy of &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt; is one of performance, flexibility, and customization. A web framework that prides itself on rendering as much as it can on the server first, allowing you to choose where your content lives, and giving you as many handles as possible to customize it how you like. The datagrid library you go with should be the same.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftoq1taudcmi1789zuh2z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftoq1taudcmi1789zuh2z.png" alt="A beautifully rendered ZingGrid data table featuring custom row colors, emoji formatting, and column templates" width="800" height="675"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Setup - Installing Astro
&lt;/h2&gt;

&lt;p&gt;First things first, we have to setup a basic app—we'll do that by following the prompts in the following command&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm create astro@latest&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9xzooh1eds6sojhbxie.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9xzooh1eds6sojhbxie.gif" alt="Gif showing the result of executing npm create astro@latest" width="912" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Setup - Including ZingGrid Library
&lt;/h2&gt;

&lt;p&gt;The library itself should be included as a script in the front-end (preferably deferred using &lt;code&gt;defer&lt;/code&gt;) for a few reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It needs to be included exactly once&lt;/li&gt;
&lt;li&gt;While the library itself is small for the sheet volume of features it packs (~&lt;strong&gt;259kb&lt;/strong&gt; compressed), it is too big to be inlined in the HTML and maintain &lt;a href="https://web.dev/articles/rail" rel="noopener noreferrer"&gt;RAIL&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Quick side note: the &lt;code&gt;is:inline&lt;/code&gt; directive in Astro really means to opt the script out of any bundling and processing, not that the script will actually be fetched and swapped in place during the build process&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;A direct integration is not needed as ZingGrid was already build with similar philosophy to Astro in mind&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;src/layouts/Layout.astro&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- ... ---&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Astro description"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/svg+xml"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/favicon.svg"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"generator"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;{Astro.generator}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{title}&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- ZingGrid Library --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;is:inline&lt;/span&gt; &lt;span class="na"&gt;defer&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/zinggrid.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating the Activity Tracker Grid
&lt;/h2&gt;

&lt;p&gt;We'll start with the simplest grid we can make and build up from there. First things first, we create a &lt;code&gt;ActivityGrid.astro&lt;/code&gt; component in our &lt;code&gt;src/components&lt;/code&gt; directory and add the following code.&lt;/p&gt;

&lt;p&gt;We'll be passing our data to this component, so we create a prop for that, and we hand it to &lt;code&gt;&amp;lt;zing-grid&amp;gt;&lt;/code&gt; using the &lt;code&gt;data&lt;/code&gt; attribute.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;src/components/ActivityGrid.astro&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;---
const { data } = Astro.props;
---

&lt;span class="nt"&gt;&amp;lt;zing-grid&lt;/span&gt; &lt;span class="na"&gt;data=&lt;/span&gt;&lt;span class="s"&gt;{data}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/zing-grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Over in our &lt;code&gt;index.astro&lt;/code&gt; page in &lt;code&gt;src/pages&lt;/code&gt;, we need to add our grid to the page. To do this we'll first need to import the component in our frontmatter (fenced off by &lt;code&gt;---&lt;/code&gt;), and we'll need to pull in our sample JSON as well. Now the only thing we have to be careful of is that we stringify the JSON as it will be passed into &lt;code&gt;&amp;lt;zing-grid&amp;gt;&lt;/code&gt; as an attribute.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;src/pages/index.astro&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;---
import Layout from '../layouts/Layout.astro';
import ActivityGrid from '../components/ActivityGrid.astro';

import activityData from '../data/activities.json';
const activityDataStr = JSON.stringify(currencyData);
---

&lt;span class="nt"&gt;&amp;lt;Layout&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"ZingGrid + Astro"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ActivityGrid&lt;/span&gt; &lt;span class="na"&gt;data=&lt;/span&gt;&lt;span class="s"&gt;{activityDataStr}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Layout&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we load up the page, we'll see a grid that looks like this. Nothing fancy yet, but surprisingly easy to get something going.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhzwpoxfx8d8b45nnuni2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhzwpoxfx8d8b45nnuni2.png" alt="A plain basic chart with no caption, no color, no anything." width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding User Controls and a Caption
&lt;/h2&gt;

&lt;p&gt;To enhance our grid we'll start by configuring our  element. With 100+ attributes to choose from the  element has a ton of configuration available to it, but I'll pick out a few I think will be useful to us here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;layout&lt;/code&gt; : Determines whether or not the grid is shown in row or card mode&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;layout-controls&lt;/code&gt; : Allow / prevent users from changing the layout mode&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sort&lt;/code&gt; : Add controls to sort each column&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pager&lt;/code&gt; : Paginates the grid instead of leaving it as one long list&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;page-sizer&lt;/code&gt; : The number of rows for each page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll also add a &lt;code&gt;&amp;lt;zg-caption&amp;gt;&lt;/code&gt; inside of our &lt;code&gt;&amp;lt;zing-grid&amp;gt;&lt;/code&gt; so we can give our grid a title&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;---
const { data } = Astro.props;
---

&lt;span class="nt"&gt;&amp;lt;zing-grid&lt;/span&gt;
  &lt;span class="na"&gt;data=&lt;/span&gt;&lt;span class="s"&gt;{data}&lt;/span&gt;
  &lt;span class="na"&gt;layout=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;
  &lt;span class="na"&gt;layout-controls=&lt;/span&gt;&lt;span class="s"&gt;"disabled"&lt;/span&gt;
  &lt;span class="na"&gt;sort&lt;/span&gt;
  &lt;span class="na"&gt;pager&lt;/span&gt;
  &lt;span class="na"&gt;page-size=&lt;/span&gt;&lt;span class="s"&gt;"6"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;zg-caption&amp;gt;&lt;/span&gt;💃🏼 Activity Tracker&lt;span class="nt"&gt;&amp;lt;/zg-caption&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/zing-grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4vq07q0htx0tpu2l3w2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4vq07q0htx0tpu2l3w2.png" alt="The plain basic chart now has a caption and some pagination." width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Formatting the Columns
&lt;/h2&gt;

&lt;p&gt;Having a Distance column without any units makes it a bit hard to read. Luckily ZingGrid allows us to create templates for our columns. We can access the values from our row entry using the &lt;code&gt;index&lt;/code&gt; object inside of &lt;code&gt;[[double bracket]]&lt;/code&gt; notation. Below is a sample of what our data looks like for reference, the &lt;code&gt;"distance"&lt;/code&gt; key is what we're pulling in when we write &lt;code&gt;[[index.distance]]&lt;/code&gt; in the template.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;src/data/activities.json&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;[
  {
    "activityType": "Outdoor Bike",
    "city": "Tempe",
    "date": "10/17/24",
    "distance": "48.27",
    "elapsedTime": "03:26:35",
    "movingTime": "01:53:15",
    "state": "Arizona"
  },
  /* ... */
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;src/data/ActivityGrid.astro&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;zing-grid&lt;/span&gt;
  &lt;span class="na"&gt;data=&lt;/span&gt;&lt;span class="s"&gt;{data}&lt;/span&gt;
  &lt;span class="na"&gt;layout=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;
  &lt;span class="na"&gt;layout-controls=&lt;/span&gt;&lt;span class="s"&gt;"disabled"&lt;/span&gt;
  &lt;span class="na"&gt;sort&lt;/span&gt;
  &lt;span class="na"&gt;pager&lt;/span&gt;
  &lt;span class="na"&gt;page-size=&lt;/span&gt;&lt;span class="s"&gt;"6"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;zg-caption&amp;gt;&lt;/span&gt;💃🏼 Activity Tracker&lt;span class="nt"&gt;&amp;lt;/zg-caption&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;zg-colgroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;zg-column&lt;/span&gt; &lt;span class="na"&gt;index=&lt;/span&gt;&lt;span class="s"&gt;'date'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/zg-column&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;zg-column&lt;/span&gt; &lt;span class="na"&gt;index=&lt;/span&gt;&lt;span class="s"&gt;'activityType'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/zg-column&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;zg-column&lt;/span&gt; &lt;span class="na"&gt;index=&lt;/span&gt;&lt;span class="s"&gt;'city'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/zg-column&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;zg-column&lt;/span&gt; &lt;span class="na"&gt;index=&lt;/span&gt;&lt;span class="s"&gt;'state'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/zg-column&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;zg-column&lt;/span&gt; &lt;span class="na"&gt;index=&lt;/span&gt;&lt;span class="s"&gt;'distance'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;[[index.distance]] mi&lt;span class="nt"&gt;&amp;lt;/zg-column&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;zg-column&lt;/span&gt; &lt;span class="na"&gt;index=&lt;/span&gt;&lt;span class="s"&gt;'elapsedTime'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/zg-column&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;zg-column&lt;/span&gt; &lt;span class="na"&gt;index=&lt;/span&gt;&lt;span class="s"&gt;'movingTime'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/zg-column&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/zg-colgroup&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/zing-grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the result is the &lt;code&gt;mi&lt;/code&gt; unit being added to each &lt;code&gt;Distance&lt;/code&gt; value just as in our template&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F06ihkrheanrdwz5015jh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F06ihkrheanrdwz5015jh.png" alt="The same chart from before now has " width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Styling the Grid with CSS Vars and Open Props
&lt;/h2&gt;

&lt;p&gt;ZingGrid is built with modern native web components for performance and resiliency, but as a side effect of that a lot of the components exist inside &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM" rel="noopener noreferrer"&gt;Shadow DOM&lt;/a&gt;s. Still wanting to allow users to style any part they desire, &lt;a href="https://www.zinggrid.com/docs/api/styling/reference/" rel="noopener noreferrer"&gt;hundreds of CSS Variables&lt;/a&gt; were created to pierce these shadows. Below we show how they can be used in conjunction &lt;a href="https://open-props.style/" rel="noopener noreferrer"&gt;Open Props&lt;/a&gt; for added flexibility.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;zing-grid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--zg-body-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--gray-8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--zg-caption-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--zg-caption-border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--gray-3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--zg-caption-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--gray-8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Custom Cell Render Functions
&lt;/h2&gt;

&lt;p&gt;To add some extra life to our grid it would be nice to be able to dynamically add an emoji in front of each activity. To do this, we'll create a function called &lt;code&gt;activityRender&lt;/code&gt; on the &lt;code&gt;window&lt;/code&gt; object (so it's accessible to ZingGrid) and add specify the function name the &lt;a href="https://www.zinggrid.com/docs/api/methods/functions" rel="noopener noreferrer"&gt;&lt;code&gt;renderer&lt;/code&gt; attribute&lt;/a&gt; for the &lt;code&gt;&amp;lt;zg-column&amp;gt;&lt;/code&gt; we want to apply it to. Inside this function we'll have the &lt;code&gt;record&lt;/code&gt; text passed into us that we can then modify before it gets inserted into the cell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;zing-grid&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;zg-colgroup&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;zg-column&lt;/span&gt; &lt;span class="na"&gt;index=&lt;/span&gt;&lt;span class="s"&gt;'activityType'&lt;/span&gt; &lt;span class="na"&gt;renderer=&lt;/span&gt;&lt;span class="s"&gt;'activityRender'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/zg-column&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/zing-grid&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activityRender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sportEmoji&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Outdoor Bike&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;sportEmoji&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🚴‍♂️&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sport&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;sportEmoji&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⛳️&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Run&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;sportEmoji&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🏃‍♂️&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sportEmoji&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;strong&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/strong&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsp74b1kyhqcm3eb0j1j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsp74b1kyhqcm3eb0j1j.png" alt="The same chart from before now has emojis next to each corresponding activity type." width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamically Adding Classes to Rows
&lt;/h2&gt;

&lt;p&gt;And then finally to color each row based on the activity type, we can use the &lt;a href="https://www.zinggrid.com/docs/api/methods/functions" rel="noopener noreferrer"&gt;&lt;code&gt;row-class&lt;/code&gt; attribute&lt;/a&gt; to pass a function that will dynamically add a class to each row.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;zing-grid&lt;/span&gt;
  &lt;span class="na"&gt;row-class=&lt;/span&gt;&lt;span class="s"&gt;"activityClassRender"&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/zing-grid&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activityClassRender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rowIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rowDOMRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;rowCQlass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;activity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activityType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activity&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Outdoor Bike&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;rowClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;outdoor-bike&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activity&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sport&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;rowClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sport&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activity&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Run&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;rowClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;run&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;rowClass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll then need to create those classes in a &lt;em&gt;global&lt;/em&gt; CSS style block as the &lt;code&gt;&amp;lt;zg-row&amp;gt;&lt;/code&gt;s are created dynamically on the front-end and thus do not get tagged with the usual style scoping attributes that Astro adds at build time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;style &lt;/span&gt;&lt;span class="na"&gt;is:global&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;zing-grid&lt;/span&gt; &lt;span class="nt"&gt;zg-row&lt;/span&gt;&lt;span class="nc"&gt;.outdoor-bike&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--zg-row-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--blue-8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nt"&gt;zing-grid&lt;/span&gt; &lt;span class="nt"&gt;zg-row&lt;/span&gt;&lt;span class="nc"&gt;.sport&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--zg-row-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--teal-8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nt"&gt;zing-grid&lt;/span&gt; &lt;span class="nt"&gt;zg-row&lt;/span&gt;&lt;span class="nc"&gt;.run&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--zg-row-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--red-8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et Voilà! 😄 We have a dynamically styled and formatted ZingGrid inside of Astro&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbnwep725yok6cy0ia8iy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbnwep725yok6cy0ia8iy.png" alt="The same chart from before now has color coordinated rows based on activity type." width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;With just a little configuration and very little code we were able to construct a robust and dynamic datagrid that fit with our Astro site. We've only scratched the surface of what ZingGrid can do in this article, so there will be a part 2 later on to show what the library can truly do, so &lt;a href="https://blog.zingsoft.com/" rel="noopener noreferrer"&gt;watch out for that here&lt;/a&gt; if you're interested.&lt;/p&gt;

</description>
      <category>astro</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>html</category>
    </item>
    <item>
      <title>Connecting ZingGrid to Supabase: Add a Backend in Minutes</title>
      <dc:creator>Camdyn Rasque</dc:creator>
      <pubDate>Fri, 13 Sep 2024 00:03:45 +0000</pubDate>
      <link>https://forem.com/camdyn_rasque/connecting-zinggrid-to-supabase-add-a-backend-in-minutes-1ebe</link>
      <guid>https://forem.com/camdyn_rasque/connecting-zinggrid-to-supabase-add-a-backend-in-minutes-1ebe</guid>
      <description>&lt;p&gt;Cross post from &lt;a href="https://blog.zingsoft.com/connecting-zinggrid-to-supabase-add-a-backend-in-minutes/" rel="noopener noreferrer"&gt;my article on the Zing blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://supabase.com/?ref=blog.zingsoft.com" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt; is an open source Firebase alternative. There are a variety of services they offer, but for the purposes of this article, we'll be diving into how it can act as a simple backend for our grids.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Setup
&lt;/h2&gt;

&lt;p&gt;There are a few steps we need to run through on the Supabase side of things before we can start configuring our grids.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Account Creation
&lt;/h3&gt;

&lt;p&gt;We first need to create the Supabase account that we will be connecting to. &lt;a href="https://supabase.com/dashboard/projects?ref=blog.zingsoft.com" rel="noopener noreferrer"&gt;You can sign up for Supabase using this link&lt;/a&gt;. Once your account has been created and email confirmed, continue to the next step.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Create Your First Project
&lt;/h3&gt;

&lt;p&gt;Go ahead and head over to the &lt;a href="https://supabase.com/dashboard/projects" rel="noopener noreferrer"&gt;dashboard page&lt;/a&gt; and create a new project. Make sure to note down the project name and database password.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Note Down Project Info
&lt;/h3&gt;

&lt;p&gt;It is at this point you should see your &lt;code&gt;Project URL&lt;/code&gt; and your &lt;code&gt;API Key&lt;/code&gt;. We will need to give both of these to ZingGrid in our code later so make sure to store them in a secure local file.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Create Your First Table
&lt;/h3&gt;

&lt;p&gt;From the side bar click the &lt;code&gt;Table Editor&lt;/code&gt; section. From here we can create our first table&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu3op97uh8k9fe038pci3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu3op97uh8k9fe038pci3.png" alt="An empty Supabase dashboard with a button in the center that says Create new table" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First step is to give our table a name, here I'll use &lt;code&gt;demoTable&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftz2zrnfsp5p8wmrvrpeg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftz2zrnfsp5p8wmrvrpeg.png" alt="A new table creation form in Supabase, this one has a name field filled in with demoTable" width="728" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then we can edit the columns, I'll have two columns for this example. One for first names and one for last names.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkwogjs06ammdu7n7ccft.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkwogjs06ammdu7n7ccft.png" alt="A new table creation field a little further down specifying columns. This table specifies first and last name columns as text." width="728" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Add Some Sample Data
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F73wj9ic3cgeplcuab1rp.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F73wj9ic3cgeplcuab1rp.gif" alt="A gif showing a user inserting a row into the table with the name Julia Smith being filled into the fields." width="896" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Update Security Settings
&lt;/h3&gt;

&lt;p&gt;We'll temporarily disable Row Level Security so we can easily read and write from our table for the purpose of this demo. In production we'll want to set up proper roles with authentication.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;NOTE&lt;/strong&gt;: This settings change is just for the purpose of this demo, this is not meant for production&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgv27473tf5pgowv67r6v.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgv27473tf5pgowv67r6v.gif" alt="A gif of a user navigating to the authentication tab in Supabase and disabling the row level security policy" width="850" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Supabase Adapter - REST API
&lt;/h2&gt;

&lt;p&gt;ZingGrid supports both ways of interacting with Supabase - via the REST API and via the client script. We'll first go over using the REST API.&lt;/p&gt;

&lt;p&gt;Using the initial demo code below, make sure to replace the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;***link***&lt;/code&gt; - The Project URL that you noted down earlier&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;***tableName***&lt;/code&gt; - The name of the Supabase table you just created&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;***apiKey***&lt;/code&gt; - The Supabase API Key you noted down earlier
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"ie=edge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/lib/zinggrid.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Supabase&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;zing-grid&lt;/span&gt;
    &lt;span class="na"&gt;page-size=&lt;/span&gt;&lt;span class="s"&gt;"5"&lt;/span&gt;
    &lt;span class="na"&gt;sort&lt;/span&gt;
    &lt;span class="na"&gt;pager&lt;/span&gt;
    &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Supabase"&lt;/span&gt;
    &lt;span class="na"&gt;editor-controls&lt;/span&gt;
    &lt;span class="na"&gt;editor-disabled-fields=&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;
    &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://***link***.supabase.co/rest/v1/***tableName***"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;zg-data&lt;/span&gt; &lt;span class="na"&gt;adapter=&lt;/span&gt;&lt;span class="s"&gt;"supabase"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;zg-param&lt;/span&gt;
        &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"headers"&lt;/span&gt;
        &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;'
          {
            "Authorization": "Bearer ***apiKey***",
            "apikey": "***apiKey***"
          }'&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;gt;&amp;lt;/zg-param&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/zg-data&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/zing-grid&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When viewing that page in the browser, you should start to see the initial data we populated come through!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhdk3b8ita2hyiflk30xg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhdk3b8ita2hyiflk30xg.png" alt="A simple ZingGrid showing one row, the Julia Smith entry that was previously entered in the Supabase dashboard" width="761" height="217"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  SupabaseJS Adapter - Client Script
&lt;/h2&gt;

&lt;p&gt;If your app happens to use the Supabase JavaScript Client Library (&lt;a href="https://supabase.com/docs/reference/javascript/introduction?ref=blog.zingsoft.com" rel="noopener noreferrer"&gt;which you can read more about on their docs site&lt;/a&gt;), you are able to store your Supabase credentials outside of your markup making it much more flexible.&lt;/p&gt;

&lt;p&gt;Amending the previous example, we first create a Superbase client object (&lt;a href="https://supabase.com/docs/reference/javascript/installing?ref=blog.zingsoft.com" rel="noopener noreferrer"&gt;more on that in their docs&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supabaseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://***link***.supabase.co/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supabaseKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;***apiKey***&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supabaseClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;supabaseUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;supabaseKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then &lt;a href="https://www.zinggrid.com/docs/api/methods/zinggrid-object?ref=blog.zingsoft.com#registerclient" rel="noopener noreferrer"&gt;register that client with ZingGrid&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;ZingGrid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;supabaseClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally we set the &lt;code&gt;adapter&lt;/code&gt; attribute on &lt;code&gt;zg-data&lt;/code&gt; to &lt;code&gt;supabaseJS&lt;/code&gt; and we have the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"ie=edge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Supabase&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- ZingGrid --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.zinggrid.com/zinggrid.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Supabase Client Library --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supabaseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://***link***.supabase.co/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supabaseKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;***apiKey***&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supabaseClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;supabaseUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;supabaseKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;ZingGrid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;supabaseClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;zing-grid&lt;/span&gt;
    &lt;span class="na"&gt;page-size=&lt;/span&gt;&lt;span class="s"&gt;"5"&lt;/span&gt;
    &lt;span class="na"&gt;sort&lt;/span&gt;
    &lt;span class="na"&gt;pager&lt;/span&gt;
    &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"SupabaseJS"&lt;/span&gt;
    &lt;span class="na"&gt;editor-controls&lt;/span&gt;
    &lt;span class="na"&gt;editor-disabled-fields=&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;zg-data&lt;/span&gt; &lt;span class="na"&gt;adapter=&lt;/span&gt;&lt;span class="s"&gt;"supabaseJS"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;zg-param&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"dataTable"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"***tableName***"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/zg-param&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/zg-data&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/zing-grid&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Working the same as before&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhdk3b8ita2hyiflk30xg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhdk3b8ita2hyiflk30xg.png" alt="A simple ZingGrid showing one row, the Julia Smith entry that was previously entered in the Supabase dashboard" width="761" height="217"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>zinggrid</category>
      <category>supabase</category>
      <category>javascript</category>
      <category>database</category>
    </item>
  </channel>
</rss>
