text: How to Fetch and Render Data in Joystick Components
  February 4th, 2022
  What You Will Learn in This Tutorial
  Adding the data option to Joystick components to fetch data on the server and render it in components on the server and the client. this, we need to install one additional dependency,
  node-fetch:
  Terminal
  cd app && npm i node-fetch
  Once this is installed, from the same
  app directory you just
  cd'd into, you can start up the app:
  Terminal
  joystick start
  After this, your app should be running and we're ready to get started.
  Wiring up an API endpoint using getters
  The first thing we need to do is get access to some data that we'll render in our component. While we could just render some static (or hard-coded) data, it'd be better to pull some data from a third-party API so we can see the power and flexibility of this technique.
  /api/index.js
  import fetch from 'node-fetch'; import { URL, URLSearchParams } from 'url'; export default { getters: { posts: { get: (input = {}) => { const url = new URL(' if (input?.id) { const searchParams = new URLSearchParams(input); url.search = searchParams.toString(); } return fetch(url).then((response) => response.json()); }, }, }, setters: {}, };
  In a Joystick application, "getters" allow us to define API endpoints for "getting" data. Behind the scenes, getters are turned into plain HTTP REST API endpoints in your app (e.g.,
  
  Above, we're defining a new getter called
  posts which will get a list of posts from the JSON Placeholder API—a free REST API that provides test data for testing and prototyping.
  Getters are one of two types of API endpoints in a Joystick app with the other being setters (these "set" data in our application—the "create, update, and delete" part of CRUD). In a Joystick app, getters and setters are defined together on a single object exported from the
  /api/index.js file we see above (referred to as your API's "schema" in Joystick).
  This object is then imported into
  /index.server.js and passed as part of the options to the
  node.app() function—as
  api—from the
  @joystick.js/node package. This tells Joystick to automatically load all of the getters and setters defined in the file we see above when it starts up the server side of our app.
  For this tutorial, we're defining a single getter
  posts which returns data from the JSON Placeholder API. To make it work, we add a new property
  posts to the object assigned to
  getters which itself is assigned an object.
  That object contains a property
  get which is assigned to a function that's responsible for "getting" our data and returning it to the HTTP request that called the getter. Inside of that function, we begin by create an instance of a URL object via the
  new URL() constructor (notice we've imported this up top from the
  url package—this is built-in to Node.js and we do not need to install it separately).
  To that constructor, we pass the URL that we want to create the object for. In this case, we want to use the
  /posts endpoint from the JSON Placeholder API located at
  
  Next, we make a check to see if our getter was passed any
  input variables when it was called (how this works will make more sense later, but think of this like being passed as a
  POST body to an HTTP request). If we have an
  id value defined on our input (the ID of a post on the JSON Placeholder API like
  1 or
  5), we want to create a new instance of the
  URLSearchParams class, passing in our input object. Here, each property on the object will be turned into a query parameter. For example, an
  input value of...
  { id: 5 }
  will be turned into...
  ?id=5
  To make that value useful, we set the
  .search property of the
  url object we created above to the value of
  searchParams cast as a string value (using the
  .toString() function).
  Finally, with our complete
  url object, we call to the
  fetch() function we imported from the
  node-fetch package up top, passing the
  url object (
  fetch understands how to interpret this object). Because we expect
  fetch() to return us a JavaScript Promise, on the end, we call to
  .then() to say "after we get a response then do this."
  The "this" that we're doing is taking the
  response object and converting it into a JSON format with the
  .json() method. What we expect to return from this chain of methods is an array of objects representing posts from the JSON Placeholder API.
  With this in place, now we're ready to get our data wired up. To do that, we're going to need a route where we can render the component that we're going to create. Real quick, let's jump over to the
  /index.server.js file and set up that route.
  Wiring up a route for our component
  If we open up the
  /index.server.js file at the root of our app, we'll see that the
  joystick create app function we called earlier created a file that automatically imports and runs
  node.app() for us along with some example routes.
  /index.server.js
  import node from "@joystick.js/node"; import api from "./api"; node.app({ api, routes: { "/": (req, res) => { res.render("ui/pages/index/index.js", { layout: "ui/layouts/app/index.js", }); }, "/posts": (req, res) => { res.render("ui/pages/posts/index.js"); }, "*": (req, res) => { res.render("ui/pages/error/index.js", { layout: "ui/layouts/app/index.js", props: { statusCode: 404, }, }); }, }, });
  By default, a root route at
  404 route at
  /posts. To that route, we've assigned a function to handle the inbound request taking in the
  req and
  res objects. Though it may not look like it, behind the scenes, Joystick turns this into a plain Express.js route, similar to us writing
  app.get('/posts', (req, res) => { ... }).
  /and a catch-all or
  *(meaning, everything that doesn't match a route above this one) are pre-defined for us. Here, we've added an additional route
  Inside of that function, we make a call to a special function added by Joystick to the
  res object called
  .render(). This function, like the name implies, is designed to render a Joystick component in response to a request. To it, we pass the path to a component in our app that we want it to render, along with an object of options (if necessary, which it isn't here so we've omitted it).
  When this route is matched in a browser, Joystick will go and get this component and server-side render it into HTML for us and send that HTML back to the browser. Internally,
  res.render() is aware of the
  data option on Joystick components. If it sees this on a component, it "scoops up" the call and fetches the data as part of the server-side rendering process.
  This is how we're going to call to the
  posts getter we defined above. Our goal being to make it so that when our page loads, we get back server-side rendered HTML with out data already loaded in it.
  Next, we need to actually create the component at the path we're passing to
  res.render() above.
  Wiring up a Joystick component with data from the API
  To start, first, we need to add the file we assumed will exist at
  /ui/pages/posts/index.js:
  /ui/pages/posts/index.js
  import ui from '@joystick.js/ui'; const Posts = ui.component({ render: () => { return ` <div> </div> `; }, }); export default Posts;
  Here, we're just adding a skeleton component using the
  ui.component() function imported from the
  @joystick.js/ui package (automatically installed for us by
  joystick create).
  In the HTML string we return from our
  render function, for now we're just rendering an empty
  <div></div>. If we visit the route we added on the server in our browser at
   we should see a blank white page.
  Now we're ready to wire up our data. Let's add everything we need and walk through it (we don't need much code):
  /ui/pages/posts/index.js
  import ui from '@joystick.js/ui'; const Posts = ui.component({ data: async (api = {}, req = {}, input = {}) => { return { posts: await api.get('posts', { input, }), }; }, render: ({ data, each }) => { return ` <div> <ul> ${each(data?.posts, (post) => { return ` <li> <h4>${post.title}</h4> <p>${post?.body?.slice(0, 80)}...</p> </li> `; })} </ul> </div> `; }, }); export default Posts;
  Believe it or not, this is all we need to get our data fetched and server-side rendered in our app and rendered in the browser.
  At the top of our component definition, we've added a new option
  data assigned to a function. This function receives three arguments:
  apiwhich is an object containing an isomorphic (meaning it works in the browser and on the server) version of the
  get()and
  set()functions built-in to both
  @joystick.js/uiand
  @joystick.js/nodefor calling to our getters and setters.
  reqwhich is a browser-safe version of the inbound HTTP request (this gives us access to
  req.paramsand
  req.context.userso we can reference them when fetching data).
  inputany input data passed when refetching data via the
  data.refetch()method (we'll cover this in a bit).
  Inside of that function, we return an object that we want to assign as the value of
  data on our component instance. Here, because we want to get back a list of posts, we define a property
  posts and set it equal to a call to
  api.get('posts') where the
  'posts' part is the name of the getter we defined earlier in the tutorial.
  Because we expect an array of objects representing our posts to be returned from that getter, we assign our call directly to that function, prefixing the
  await keyword (and adding
  async to the function we pass to
  data) to tell JavaScript to wait until this call responds before continuing to interpret the code.
  The end result here is that on the server, our data is fetched automatically and set to the
  data property on our component instance. Down in the
  render function, we can see that we've added a call to destructure or "pluck off" a
  data and
  each property from the argument passed to the render function (this is an object representing the component instance).
  Instead of doing this, we could pass a single argument
  componentto the render function and then write something like
  component.dataand
  component.eachto reference the exact same values as we are here.
  Down in our HTML, we've added a
  <ul></ul> unordered list tag, and inside of it, we're using the JavaScript interpolation
  each() function passing the value of
  data.posts."
  ${}syntax to say "in these brackets, call the
  That function,
  each() will loop over the array of posts we're passing it and for each one, return a string of HTML from the function we pass as the second argument to it. That function takes in the current item or, in this case,
  post being looped over for use in the HTML being returned.
  Here, we output the
  title of each post and a truncated version of the
  body for each post in the array.
  If we load up our browser now, we should see some posts rendering in the browser.
  While we're technically done, before we wrap up, let's quickly learn how to refetch data after the initial page load.
  /ui/pages/posts/index.js
  import ui from '@joystick.js/ui'; const Posts = ui.component({ data: async (api = {}, req = {}, input = {}) => { return { posts: await api.get('posts', { input, }), }; }, events: { 'submit form': (event, component) => { event.preventDefault(); const input = component.DOMNode.querySelector('input'); if (input.value) { component.data.refetch({ id: input.value }); } else { component.data.refetch(); } }, }, render: ({ data, each }) => { return ` <div> <form> <input type="text" placeholder="Type a post ID here..." /> <button type="submit">Get Post</button> </form> <ul> ${each(data?.posts, (post) => { return ` <li> <h4>${post.title}</h4> <p>${post?.body?.slice(0, 80)}...</p> </li> `; })} </ul> </div> `; }, }); export default Posts;
  If we're building a non-trivial UI, it's likely that at some point we'll want to refetch data based on some sort of user interaction, or, on some interval (e.g., polling for new data every 5 seconds).
  On the
  data property assigned to our component instance, Joystick gives us a
  .refetch() method that we can call to perform a refetch on-demand. If we look at the HTML returned from our
  render() function, we can see that we've added a few more lines, adding in a simple
  <form></form> with an input and a button.
  Recall that earlier on the server when we defined our getter, we added the potential for an
  id to be passed so we could fetch a specific post. By default, we're not passing anything, but to demonstrate our use of
  data.refetch() (and the ability to pass input values to it), here, we're adding an event listener for our form's
  submit event to do exactly that.
  Looking at the
  events property we've added to our component definition, when our form is submitted, first, we want to make sure we call to the
  event.preventDefault() function on the
  event argument we're passed (this is the browser DOM event as it's happening) to prevent the standard or built-in form submission handler from being called in the browser (this triggers a page refresh that we want to skip).
  Beneath this, we take the
  component instance that's automatically passed as the second property to our event handlers in Joystick. On that object, a
  DOMNode property is added which gives us access to the current component as it's rendered in the browser (the code we write here—our Joystick component—is just an abstraction for generating these DOM nodes dynamically).
  On that
  component.DOMNode value we call the
  querySelector method, passing in the selector of an element we want to access. Here, we want to get the
  <input /> tag that's rendered in our component. In return, we expect to get back the DOM node for that input element (why we're storing it in a variable called
  input).
  Beneath this, we conditionally call to
  component.data.refetch() based on whether or not our
  input has a value. If it does, we want to pass that value as the
  id property on our input object. Here, the object we pass to
  component.data.refetch() is automatically assigned to the
  input value we pass to the server when we call
  api.get('posts') up in our
  data function.
  If
  input.value is empty, we want to skip passing any input.
  The end result of this is that if we do pass a value (the ID of a post, e.g.,
  1 or
  5), we'll pass that to our getter and expect to get back a single post from the JSON Placeholder API. If we do not pass a value, we'll expect the default response of our full list of posts.
  Back in the browser, if we load this up and type a number into the input and hit "Get Post," we should see our list automatically reduced down to that one post. If we remove the number and hit "Get Posts" again, we should see the full list restored.
  Wrapping up
  In this tutorial, we learned how to wire up an API endpoint using the getters feature in Joystick that we call from a component using the Joystick
  data property to automatically fetch and server-side render our HTML with the data inside. We also learned how to render a component via a route using the
  res.render() method in Joystick and how to refetch data inside of a component in response to a user's behavior.
  Get the latest free JavaScript and Node.js tutorials, course announcements, and updates from CheatCode in your inbox.
  No spam. Just new tutorials, course announcements, and updates from CheatCode.