Build a blog backend with a publishing workflow

Topics covered: Building models, Relationships, Actions
Time to build: ~10 minutes

This tutorial will teach you how to build a simple backend for a blog and expose a set of declarative APIs to power your frontend experience. In the blog, every author is a record of a User model that writes and publishes many records of the Post model. In this tutorial, we'll construct these models, adding fields and relationships between them, as well as some business logic in actions that modify them.

Let's get started!

Create a new application

Login to Gadget and create a new application. Click "Create a New App", and choose a name for your blog.

Create new application in Gadget

Create a new model

Our blog's schema is going to need a post model, where each Post record represents a blog post. To create the post model click on the “+” icon to the right of 'Data Models' and name your model Post since we're building a model for our blog posts.

Create a model

Every model in Gadget comes with a set of non-configurable system fields:

  • id for identifying the record with an automatically assigned unique number
  • createdAt and updatedAt for tracking when the record was created and last updated.

Let's teach Gadget that beyond these system fields, our blog posts have additional attributes. We can do this by adding fields. Click on the + button at the top of the list of Fields, and name the new field you created 'Title'. Set the field type to string. Now, each blog post will have a title attribute stored in this field.

Finally, let's add a couple of validations to this field. In the Validations section, you can require every blog post to have a title by adding a Required validation. Let's also add a String Length validation to ensure that none of our writers put up ridiculously long titles. Once finished, your field should look like this:

Create a field

Let's define another field to hold the body of each blog post and call it Content. For the field type, choosing rich text will allow authors to include media files in their blog posts. Let's also make this field required. Your field should look like this once finished:

Create a field

Add a relationship

In Gadget, relationships between models are created using fields (like belongs to) that enable you to model your data with models that are otherwise unrelated to each other in a normalized fashion. Like foreign keys in SQL, one model receives and stores the id in its data to set up the relationship.

Let's create a relationship between our Post model and a new model to represent the author of the post. Add a new User model by clicking the "+" icon to the right of Models again, and name your model User.

In this blog, one user can create many posts, and each post is written by exactly one user. We model these relationships in Gadget using Relationship fields. To add a new relationship field, on the newly added User model, add a new field called Posts. This field will store a post object on your user model. Select a Has Many relationship type from the field type dropdown and point it at the Post model we created earlier. In Gadget, relationships are modeled as fields. so we'll need to create the inverse side of this relationship on the Post model. Let's call this field User. Your field should look like this once completed:

Adding a relationship

Adding a publishing workflow to our blog

Now we have a way to create users and blog posts, let's add some logic that allows us to only show potential readers posts that are published by adding a new Published state. Click the model entry in the left navigation menu to check out the model page for the Post model.

To build our publishing workflow, we'll add a new enum field called State with two options called 'Unpublished' and 'Published'. This field represents if users can see the blog post or not. The initial value of this field should be 'Unpublished' when a blog post is created, and we'll create an Action for post authors to publish the post by setting this field's value to Published.

For this guide, we won't define an action to unpublish a post, though that would be a useful feature to add later.

An enum state field

Now that we have a field to capture whether a model is published or not, we can build a new Action that will transition a blog post from 'Unpublished' to 'Published'. For this guide, we won't define an inverse action to unpublish a post, though that would be a useful feature to add later.

First, let's add a publish action to the model. Click the Add Action button: In the Action Panel on the right, change the name from Action A to Publish.

An action for publishing a Post

In the Run Effects, update the record.state value to Published. Note that the code snippet needs to be before the Update Record effect in the effect list.

Second, let's add a precondition that only allows us to publish Posts if they are Unpublished. Click "+" next to "PRECONDITIONS" and add the following code file.

A precondition for checking the post state value

That's it for the Actions. Let's move on to testing out our API.

Interact with the generated API

Your API is live and ready to be tested! To try it out, we'll send mutations to the automatically generated GraphQL API that Gadget creates for each app. We can test this out directly within Gadget by clicking on the "API Playground" link that can be found towards the bottom of the left-hand navigation menu.

The automatically generated API in Gadget allows nested mutations. We'll be leveraging that to create a user and their blog posts at the same time. Copy the following code snippet into your API Playground and run the mutation to create the desired records.

1mutation {
2 createUser(
3 user: {
5 password: "fake-password"
6 posts: [
7 {
8 create: {
9 title: "My first blog post"
10 content: { markdown: "How cool is _this_?" }
11 }
12 }
13 ]
14 }
15 ) {
16 success
17 errors {
18 __typename
19 message
20 code
21 }
22
23 user {
24 id
25 email
26 posts {
27 edges {
28 node {
29 id
30 title
31 content {
32 truncatedHTML # rich text fields in Gadget support options on how to present the stored data
33 html
34 markdown
35 }
36 }
37 }
38 }
39 }
40 }
41}

You can view your data in the user and post model's "Data" page by clicking on "Data" below the respective model's name in the left-hand navigation menu.

Data in data viewer

And to publish the blog post we just created above, you can run the GraphQL mutation below. Make sure to use the correct post ID!

1mutation PublishPost($id: GadgetID!) {
2 publishPost(id: $id) {
3 success
4 errors {
5 message
6 }
7 post {
8 id
9 title
10 content {
11 markdown
12 }
13 }
14 }
15}
Variables
json
{ "id": 1 }

If you check the "Data" page for the post model again, you will see that the record is now in the Published state! You've just built a simple backend for a blog with a generated GraphQL API. You can now use the API to build out and power a front-end experience for your blog.

If you'd like to learn more about Gadget you can check out the other tutorials available in our Guides.