text: How to Implement an API Using Getters and Setters in Joystick
  November 5th, 2021
  What You Will Learn in This Tutorial
  How to define an HTTP API using getters and setters in Joystick and call to those getters and setters from your user interface via the get() and set() methods in @joystick.js/ui..
  Defining and Loading a schema in Joystick
  In a Joystick app, the entirety of your API is referred to as a schema. A schema is a JavaScript object containing two properties:
  getters and
  setters, both of which are set to their own objects.
  As their names imply, the
  getters object contain your API endpoints for getting data or reading data from a database and the
  setters object contains your API endpoints for setting or creating, updating, and deleting data.
  To begin, we're going to wire up a basic schema without any getters or setters defined and load it into our app via the
  node.app() function which starts up the server for our app.
  /api/index.js
  export default { getters: {}, setters: {}, };
  We want to define our schema in the
  index.js file under the
  /api directory at the root of our project. Again, your schema is just an object with a
  getters and
  setters property, each set to an object. Because we intend to import this inside of our
  /index.server.js file next, we use an
  export default statement.
  , }, }); }, }, });
  Here in our
  /index.server.js file, we've imported our
  api file up at the top. Notice that we use the word
  api as this is the name the
  node.app() function expects us to pass our schema as (again,
  api and
  schema are used interchangeably and a good phrase to memorize is "the API is defined by the schema"). Because we did an
  export default back in
  /api/index.js, here, we omit curly braces (used for creating named exports).
  On the options object passed to
  node.app(), we've set
  api as a property, using JavaScript short-hand to automatically assign the value of the
  api value we've imported up top as the value of the
  api property on our
  node.app() options object. So it's clear, this is equivalent to saying:
  import node from "@joystick.js/node"; import api from "./api"; node.app({ api: api, routes: { ... }, });
  That's it for defining our base schema and loading it as our API. Now, when our app starts up (or in this case, restarts as we already started our app above), the schema will be loaded and available for requests.
  Next, we're going to build out our schema by adding a getter endpoint.
  Defining a getter endpoint
  Like we hinted at earlier, in a Joystick app, there are two types of API endpoints: getters and setters. Getters are HTTP endpoints that anticipate an HTTP
  GET request being sent to them. What makes getters special is three-fold:
  - Getters can optionally have input validation to help you validate that the input values passed to a getter when it's called are correct.
  - When called, getters can be passed an
  outputdefinition which allows you to customize the return value of a getter and describes what values you expect in return to the call.
  - If available, getters are automatically given access to any databases you've loaded in your app as well as the logged in user if one exists.
  What's nice is that this functionality is not just accessible via Joystick's built-in methods for calling getters (we'll look at these later)—they're also defined as plain HTTP endpoints like:
   or
   This means that you can use a regular
  fetch() function to access your getters or, access your API endpoints outside of Joystick without any special code.
  /api/posts/getters.js
  export default { posts: { input: {}, get: () => { // We'll respond to the getter request here... }, }, };
  To keep our API organized, we're going to split up our getter definitions into their own file (we could technically write them directly into our schema but this is a bad habit that can create messes as our schema grows). Above, under our existing
  /api folder at the root of our app, we've created another folder
  posts and within that, a
  getters.js file.
  The idea here is that our API is made up of "resources" or "topics." Each resource or topic has multiple endpoints related to itself. For example, here, our resource is
  posts which will have getter endpoints related to posts and, later, setter endpoints related to posts. Using this pattern, we keep our code easy to navigate and more importantly: easy to maintain long-term.
  Just like we saw with our schema earlier, defining an individual getter only requires writing a JavaScript object. Here, we
  export default an object to which all of our posts-related getters will be assigned. Each getter is defined as a property on that object (e.g.,
  posts) assigned to an object with two properties of its own:
  input and
  get().
  input is where we define the optional validation for any inputs passed to our getter when it's called.
  get() is a function where we can perform whatever work is necessary to respond to the getter request (i.e., get the requested data from some data source). The
  get() function is technically open-ended. While typically we'd want to make calls to a database inside of the function, Joystick doesn't care where your data comes from, just that you return it from the function.
  /api/posts/getters.js
  export default { posts: { input: { category: { type: "string", optional: true, }, }, get: (input, context) => { // We'll respond to the getter request here... }, }, };
  Expanding our
  posts getter slightly, now, we're adding in some validation for the inputs we anticipate when our getter is called. Validation is defined using Joystick's built-in validation library. The library takes in an object like the one we see being passed to
  input above and compares it to the
  input value we receive when our getter is called.
  Note: Validation on getters and setters is optional, though highly recommended to help with the elimination of buggy code.
  On that object, we define properties with a name identical to the name of the property on the input we're passing with our getter request. For example, assuming we sent an object like this with our request:
  Example input object
  { category: 1234 }
  We'd look for a matching
  category property on our validation object—known as a
  field—to see if it has a
  validator assigned to it (the name we use for the object assigned to the properties on our validation). If it does, we check to see if the value passed with the request conforms to the expectations of the
  rules on the validator.
  What's cool about this validation is that it can be nested indefinitely to fit the structure of your input object. You can even validate nested objects and arrays of objects making it incredibly flexible. For our needs here, we're keeping things simple and focusing on a single field for now
  category which we want to validate has a value equal to a typeof string if it exists (if because the field is marked as optional).
  In the example above, notice that we're intentionally passing
  category as an integer in our example call, not a string. This is to make the point that when our getter is called, the validation will fail and stop the request because the validation expects the
  category field to contain a string, not an integer.
  /api/posts/getters.js
  export default { posts: { input: { category: { type: "string", optional: true, }, }, get: (input, context) => { const query = {}; if (input.category) { query.category = input.category; } return context.mongodb.collection('posts').find(query).toArray(); }, }, };
  Next, with our validation set, we want to wire up our
  get() function. Remember, this is the function that is called and is expected to return the data we're trying to get assuming the data we've passed for
  input makes it through the validation step.
  Our
  get() function takes two arguments:
  input the validated input object passed with the getter request and
  context.
  context is an object containing a few different things:
  context.reqthe inbound HTTP request given to us by the Express.js route the getter is defined as.
  context.resthe inbound HTTP response given to us by the Express.js route the getter is defined as.
  context.userthe logged in user for the app (if available).
  context.<db>where
  <db>is the name of one of the databases loaded in your app (e.g.,
  context.mongodb).
  Focusing on the body of our
  get() function, remember: we're defining a getter called
  posts so we expect our getter to return some posts.
  To do that, we anticipate a connection to MongoDB being defined as
  context.mongodb (this is the default database that Joystick automatically starts up when you run a freshly created app with
  joystick start).
  Before we make use of it, first, we create a variable
  query that will act as the "base" query we want to pass to MongoDB (an empty object means "all" in MongoDB's query language). If
  input.category is defined (remember, it's optional so it may not be present in
  input), we want to set the passed category on the query object. Assuming we passed
  "tutorials" for
  input.category, we'd expect to get something like this for
  query:
  { category: "tutorials" }
  With our
  query defined, next, we call to the MongoDB driver and run our query. This may seem strange. When it comes to databases, Joystick does nothing more than start the database on your local machine and make a connection using that database's Node.js driver. In other words, everything after
  context.mongodb here is "just how the MongoDB driver works in Node." Joystick doesn't modify this—it makes the connection to the database and sets it on
  context.mongodb. That's it.
  What we expect in return from this line is a JavaScript array of objects with each object representing a post that's defined in the database.
  That's it for defining a getter! Next, we'll take a look at defining a setter (following a near identical pattern to what we learned above) and then learn how to assign that setter and the getter we just defined above back to our schema.
  Defining a setter endpoint
  Just like we did above, we want to split our setter definitions off into their own file. Again, since we're working on the
  posts resource (or "topic" if you prefer), we're going to stick to the
  /api/posts folder, however this time, we're going to create a
  /api/posts/setters.js file:
  /api/posts/setters.js
  export default { createPost: { input: { title: { type: "string", required: true, }, category: { type: "string", required: true, }, body: { type: "string", required: true, }, tags: { type: "array", optional: true, element: { type: "string" } }, }, set: (input, context) => { return context.mongodb.collection('posts').insertOne({ _id: joystick.id(), ...input }); }, }, };
  Same exact conventions at play. The big difference here is that we're using a different name for the property we set on our exported object (for our getter we used the name
  posts, now we're using the name
  createPost for our setter) and the
  get() function on that property's value has been changed to
  set().
  Everything else is the same in terms of behavior and expectations. Technically speaking, if we wanted to, we could "get" instead of "set" some data. The name of the
  set() function here is suggestive but not technically limited in any way. Setters behave identical to getters in that they take in some input, pass it through some validation (if defined), and then hand off that
  input alongside the
  context to a function.
  Again, that function is open-ended, just like it was for the
  get() function on our getter. You can call to any code you like here—the naming is just a convention to help organize your API.
  Looking at our validation, the big difference is that we've added more fields and we've made use of the
  "array" type rule for the
  tags field. Notice that when we've set
  type to "array," we can additionally pass an
  element field set to a nested Joystick validation object. Remember: Joystick validation can be nested indefinitely.
  For our
  set() function, just like we saw before, we're accessing the MongoDB driver assigned to
  context.mongodb. This time, however, we're calling to the
  posts collection's
  insertOne method. To that method, we're passing an object we're creating which is a combination of the
  input value (we use the JavaScript spread operator to "unpack" the contents onto the object we're passing to
  .insertOne()) and an
  _id field.
  That field is being set to a call to
  joystick.id(). Behind the scenes, Joystick exposes a global variable on the server called
  joystick which has an
  .id() method for generating random hex string IDs of
  n length (the default being 16 characters) like this:
  FYIlLyqzTBJdGPzz.
  That does it for our setters. Next, let's add our getters and setters to the schema we set up earlier in the tutorial.
  Assigning our getters and setters back to the schema
  Recall that earlier we defined our base schema and added it to the
  node.app() options object as
  api. That schema, though, didn't have any getters or setters defined on it—just empty objects for each. Real quick, let's pull in the
  /api/posts/getters.js file and
  /api/posts/setters.js file we just created and set them on the schema.
  /api/index.js
  import postGetters from './posts/getters'; import postSetters from './posts/setters'; export default { getters: { ...postGetters, }, setters: { ...postSetters, }, };
  Simple. Here, all we're doing to add our getters and setters to the schema is to import the objects we exported from each file and then, in the appropriate
  getters or
  setters object, use the JavaScript spread operator
  ...to "unpack" those objects onto their parent object. Here, we use a naming convention of the singular form of our resource/topic name followed by either "getters" or "setters" in camel case.
  That's it. To wrap up, let's take a look at how to actually call our getters and setters in the app.
  Calling to getters and setters via @joystick.js/ui
  As a full-stack JavaScript framework, Joystick combines together our front-end and back-end conveniently into a single app. Now, we're going to move away from the server-side of our app and focus on the client (browser). When we ran
  joystick create earlier, Joystick gave us an example page component rendered to the
  index.server.js (you may have spotted this when we were wiring up the API). Let's open up that page component now in
  /ui/pages/index/index.js.
  /index route of our app in
  /ui/pages/index/index.js
  import ui from "@joystick.js/ui"; import Quote from "../../components/quote"; const Index = ui.component({ methods: { handleLogHello: () => { console.log("Hello!"); }, }, events: { "click .say-hello": (event, component) => { component.methods.handleLogHello(); }, }, css: ` div p { font-size: 18px; background: #eee; padding: 20px; } `, render: ({ component, i18n }) => { return ` <div> <p>${i18n("quote")}</p> ${component(Quote, { quote: "Light up the darkness.", attribution: "Bob Marley", })} </div> `; }, }); export default Index;
  Inside of this file we have an example Joystick component created using the
  @joystick.js/ui package (the companion to the
  @joystick.js/node package we saw earlier on the server).
  @joystick.js/ui is a library for creating user interface components using pure HTML, CSS, and JavaScript.
  Want to learn more about writing Joystick components? Check out this tutorial that goes in-depth on defining your own.
  The bulk of the code above isn't terribly important for us right now. What we're going to do now is modify this component to render two things:
  - A form for creating a new post.
  - A way to display posts that we've retrieved via our
  postsgetter endpoint.
  ', { input: { title: event.target.title.value, category: event.target.category.value, body: event.target.body.value, tags: event.target.tags.value.split(',').map((tag) => tag.trim()), }, }).then(() => { event.target.reset(); component.methods.handleFetchPosts(); }); }, }, css: ` ul { list-style: none; padding: 0; margin: 0 0 20px; } li { border: 1px solid #eee; padding: 20px; } li strong span { font-weight: normal; color: #aaa; } `, render: ({ state, each }) => { return ` <div> <div class="posts"> <h4>Posts</h4> <ul> ${each(state.posts, (post) => { return ` <li> <strong>${post.title} <span>${post.category}</span></strong> <p>${post.body}</p> </li> `; })} </ul> </div> <form> <label for="title">Title</label><br /> <input name="title" placeholder="title" /> <br /> <label for="category">Category</label><br /> <select name="category"> <option value="tutorials">Tutorials</option> <option value="opinion">Opinion</option> <option value="meta">Meta</option> </select> <br /> <label for="body">Body</label><br /> <textarea name="body"></textarea> <br /> <label for="tags">Tags</label><br /> <input name="tags" placeholder="tag1,tag2,tag3" /> <br /> <button type="submit">Create Post</button> </form> </div> `; }, }); export default Index;
  Keeping the skeleton from the existing component, here, we're swapping out what's being rendered and the core functionality of the component. This is purposeful. Re-using the
  /ui/pages/index/index.js component was to avoid the need for wiring up a brand new component and route and keeping us focused on our getters and setters.
  Looking at the code here, the most important part is down in the
  render() function. When building a component with
  @joystick.js/ui, we
  return a string of HTML from the
  render() function using backticks. This allows us to take advantage of JavaScript's string interpolation (also known as "template literals") to dynamically "inject" values into the HTML in our string.
  Behind the scenes, Joystick takes the HTML with our injected values and renders it to the browser. In our code here, to demo our getters and setters in action, we want to render two things: a list of existing posts (retrieved from our getter) from the database and a form for adding new posts (who's contents are relayed via our setter).
  Because we don't have any posts in our database yet, next, we want to look at the
  events property set on the object we're passing to
  ui.component(). This is where we define JavaScript DOM events in a Joystick component. Each event we want to listen for is assigned to the object we pass to events. We create a listener by setting the key or property name as a string containing first the type of DOM event we want to listen for (in our example,
  submit) and the element we want to listen for that event on (in our example,
  form).
  To that property, we assign a function that's called whenever that event occurs in the browser/DOM. For our example, we want to call our setter
  createPost on the server whenever this event takes place. To call it, up top we've added a named import (denoted by the curly braces) for the
  set() function that's included in
  @joystick.js/ui. This is a wrapper function around the JavaScript
  fetch() method that's built-in to browsers for performing HTTP requests.
  It gives us a simple API for performing our request. It takes in the name of the setter we want to call as a string for its first argument, followed by an options object. On that options object, here, we're passing the values from our form. We do this by accessing the DOM
  event object passed to our function by Joystick.
  Because Joystick is giving us access to the native DOM event, we can access the value of our inputs directly by saying
  event.target.<field>.value where
  event.target refers to the
  <form></form> element where the submit event was received and
  <field>.value equals the value of the input with a
  name attribute equal to
  <field> in our rendered HTML.
  So that's clear, if we had an input like
  <input name="pizza" /> in our HTML, we'd write something like
  event.target.pizza.value.
  With that, our setter is ready to be called. Remember: all we're doing on the server is handing off the validated input to MongoDB to put into our
  posts collection in the database.
  After our setter is called, we're ready to move onto the next step: fetching our posts from the database.
  Because we expect the
  set() method imported from
  @joystick.js/ui to return a JavaScript Promise, on the end of our call to that function we chain a
  .then() method, passing a callback function we'd like to run after the setter request is complete.
  Inside, we call to the
  .reset() method on our form (reusing the
  event.target passed to our DOM event listener) to clear out the field and then, call to a custom method defined on our component
  handleFetchPosts(). We can access this because all DOM event listeners defined on the
  events object of a component receive the DOM
  event as the first argument and the entire
  component instance as the second argument.
  ', { ... }).then(() => { document.querySelector('form').reset(); component.methods.handleFetchPosts(); }); }, }, css: `...`, render: ({ state, each }) => { return ` <div> <div class="posts"> <h4>Posts</h4> <ul> ${each(state.posts, (post) => { return ` <li> <strong>${post.title} <span>${post.category}</span></strong> <p>${post.body}</p> </li> `; })} </ul> </div> <form> ... <button type="submit">Create Post</button> </form> </div> `; }, }); export default Index;
  The
  methods object assigned to a Joystick component contains miscellaneous functions that we want to call in relation to our component. These methods can be accessed from anywhere in our component via the
  component instance (passed to all functions in a Joystick component).
  Like we just saw in the
  .then() callback of our
  set() call in
  events, we can call to a method directly by writing
  component.methods.<methodName>.
  For our needs, we want to wire up a method that calls our
  posts getter on the server and retrieves our data. Similar to how we called our setter via
  set(), a sibling method for getters is also exported from
  @joystick.js/ui called
  get().
  Predictably,
  get() takes in the name of the getter we want to call as a string for its first argument and then an options object as its second argument. Remember that earlier when wiring up our getter endpoint we anticipated a possible
  category value being passed for our input. In our example here, we're passing
  "opinion" as a category name to say "when you run this getter, only return posts with a
  category field equal to
  opinion."
  If we look down in our
  render() function, we can use one of three categories here:
  tutorials,
  meta, or
  opinion.
  In addition to our
  input, a unique feature of getters and setters in Joystick is the
  output option (known as SelectiveFetch in Joystick). Output allows you to pass specific fields on an object or array of objects returned from a getter to customize its output. This makes it possible to reuse a getter in multiple places, tailoring the output to the needs of your UI. To demonstrate this here, we're passing two of the four fields defined on each of our posts
  title and
  body.
  On the server, before Joystick sends our data back, it will pass it through the
  output array and remove any data that you didn't ask for. Here, because we pass
  title and
  body in our array, we're saying "on each post object, only give me back the title and body of the post, discarding everything else." If we comment the
  output option out and re-run our getter, we'll see that all fields show up as opposed to only what we passed in
  output.
  Just like with the
  set() method we saw previously, we expect
  get() to return a JavaScript Promise. To show off a different way of handling the response from
  get() (you could use the same pattern with
  set()), we use the JavaScript async/await pattern to skip the need for a
  .then() callback.
  This works by assigning the keyword
  async to the parent function where the
  await keyword is going to be used. We put the
  await keyword here in front of our call to
  get() to say "wait until this getter responds and then assign the response value to the variable
  With that variable (assuming it contains an array of posts like we returned from our getter), we take in the
  component instance (for
  methods, this is automatically passed as the last possible argument—in this case, it's first because we're not passing any other arguments to
  handleFetchPosts() when we call it) and utilize its
  .setState() method to set the
  state.posts value on the component to the array we received from our getter.
  We can see that the
  state object at the top of our component is given a default
  posts value set to an empty array. When our getter runs, assuming we return data from our getter, we'll automatically populate this array. Down in our
  render() method, we use the
  each() render function in Joystick to say "for each of the posts in
  state.posts, call this function which renders a string of HTML and receives the current post (or value) we're looping over."
  In turn, we expect an
  <li></li> tag to be rendered on screen with each post's
  title,
  category and
  body injected into it.
  One last note before we give this a test. Notice that we've also added an object
  lifecycle to our component. This object allows us to define functions that are called at different stages of our component's "life cycle," or, "what the component is currently doing." There are three lifecycle methods in Joystick:
  onBeforeMount,
  onMount, and
  onBeforeUnmount.
  Here, we're using
  onMount to say "when this component renders, call to the
  handleFetchPosts() method in our
  methods object." What we expect is that when our component renders on screen for the first time, it will go and fetch any existing posts and put them on state, triggering a re-render in our HTML and showing the list of posts on screen. Each time we add a new post, too, we expect the same behavior (meaning posts show up on screen as soon as they're added to the database).
  That's it! Let's give this a test run and see how it works.
  Wrapping up
  In this tutorial, we learned how to create a simple API using Joystick's getters and setters. We learned how to create a getter and setter and then load them into our schema and attach that schema to our app as our API. We also learned how to call to getters and setters in the browser, using the
  get() and
  set() methods included in the
  @joystick.js/ui library.
  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.