Build a blog backend with a publishing workflow

Time to build: approx. 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 user that publishes many posts. The relationship between user and post can be described as follows:

One user can have many posts and a post can belong to only one user

Let's get started!

You can fork this Gadget project and try it out yourself. Make sure to test the auto-generated API out the API Playground!

Fork on Gadget

Create a new application

Directly after authenticating, you'll be prompted to create a new application. Click "Create new application" 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 the 'Models' and name your model Post since we're building a model for our blog posts.

Create a model

You'll notice that every model in Gadget comes with a set of non-configurable system fields: ID, State, Created At, Updated At. These fields cannot be added or removed from a model and allow Gadget to track the record's lifecycle for you. Let's teach Gadget that our blog posts have additional attributes. We can do this by adding fields. Click on “Add field” and name the new field you created 'Title'. This field will store all of our blog post's titles. Set the field type to String.

Finally, let's add a couple of validations to this field. Make the field required 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 RichText 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 BelongsTo) 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 the default User model provided by Gadget which powers authentication and permissions. To build this relationship, go to the User model and 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 modelled 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 behaviour icon in the left navigation menu to check out the state chart for the Post model.

Adding a relationship

Gadget represents a model's behaviours in an editable state chart. Your state chart should look like this:

Adding a relationship

To build our publishing workflow, we'll add two new substates inside the created state. These states will be called 'Unpublished' and 'Published' which represent the initial state of a blog post upon creation ('Unpublished') and the action transitions a record into a Published state. For this guide, we won't define an action to unpublish a post, though that would be a useful feature to add later.

First, let's add the Unpublished state to the state chart. Select the created state and press the add state button.

Adding a relationship

In the action panel on the right, change the name from New State to Unpublished. Note the circle with a black dot in it above the Unpublished state. This indicates that this is the initial state of the record, as soon as it's created.

Adding a relationship

Next, let's add a sibling state called Published. To do this, click on the -> button on Unpublished.

Adding a relationship

Once that is done, we can add an action that will transition our record from Unpublished to Published. Click on the arrow between two new states and change the name of the action to Publish.

Adding a relationship

That's it for the state chart. Let's move on to defining the behaviour of our API.

Interact with the generated API

Your schema is now ready to start being used! To do this we'll send queries to the automatically generated GraphQL API that Gadget creates for all apps.

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.

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 }
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 }

You can view your data in the User and Post data viewers by clicking on the database icon (pictured below) to see the new records we've created!

Data in data viewerData 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 }
"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.

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