Create rest API quickly with Axe API framework: Part 3

Pavel Salauyou
6 min readOct 10, 2023

--

In the previous article, we learned how to work with the database, create tables and models, and execute requests to the API. Now we can get data from the API and create users. In this article, we will continue to populate the database with our API and take a closer look at models and relationships.

Let’s create a menu and menu items (dishes) that will then be displayed in the Order.

For regular menu:

curl \
-d '{"name": "Regular", "description": "Main dishes"}' \
-H "Content-Type: application/json" \
-X POST http://localhost:3000/api/v1/menus

For launch menu:

curl \
-d '{"name": "Launch", "description": "Launch dishes"}' \
-H "Content-Type: application/json" \
-X POST http://localhost:3000/api/v1/menus

After executing the queries, two kinds of menu were created ordinary menu with id 1 one and lunch menu with id 2. We have a very small cafe so we will add four dishes to the regular menu and two dishes to the lunch menu.

Regular menu dishes (menu_id=1):

curl \
-d '{"name": "Classic Eggs Benedict", "price": 20, "menu_id": 1}' \
-H "Content-Type: application/json" \
-X POST http://localhost:3000/api/v1/menu-items
curl \
-d '{"name": "Fluffy Pancakes with Maple Syrup and Berries", "price": 15, "menu_id": 1}' \
-H "Content-Type: application/json" \
-X POST http://localhost:3000/api/v1/menu-items
curl \
-d '{"name": "Avocado Toast with Poached Eggs", "price": 22, "menu_id": 1}' \
-H "Content-Type: application/json" \
-X POST http://localhost:3000/api/v1/menu-items
curl \
-d '{"name": "Greek Yogurt Parfait", "price": 18, "menu_id": 1}' \
-H "Content-Type: application/json" \
-X POST http://localhost:3000/api/v1/menu-items

Launch menu dishes (menu_id=2):

curl \
-d '{"name": "Spinach and Feta Stuffed Chicken Breast", "price": 20, "menu_id": 2}' \
-H "Content-Type: application/json" \
-X POST http://localhost:3000/api/v1/menu-items
curl \
-d '{"name": "Mediterranean Grilled Vegetable Panini", "price": 24, "menu_id": 2}' \
-H "Content-Type: application/json" \
-X POST http://localhost:3000/api/v1/menu-items

Let’s add to the order one dish from the regular menu and one dish from the lunch menu:

curl \
-d '{"quantity": 1, "menu_item_id": 1, "user_id": 1}' \
-H "Content-Type: application/json" \
-X POST http://localhost:3000/api/v1/orders
curl \
-d '{"quantity": 1, "menu_item_id": 5, "user_id": 1}' \
-H "Content-Type: application/json" \
-X POST http://localhost:3000/api/v1/orders

So now we have users, two types of menus, dishes and ordering. Let’s move on to the next part and add relationships.

Creating relations

The Axe API is really good at understanding how different models are connected to each other. In this part, we will explain how the Order, MenuItem models are related to other models and how we can use these connections to retrieve information.

Now, let’s define the relationships of the MenuItem model:

import { Model } from 'axe-api';

class MenuItem extends Model {
get fillable() {
return ['name', 'price', 'menu_id'];
}

get validations() {
return {
name: 'required|min:2|max:50',
price: 'required|numeric',
menu_id: 'required|numeric',
};
}

menu() {
return this.hasOne('Menu', 'id', 'menu_id');
}
}

export default MenuItem;

In this explanation, we tell the Axe API that the MenuItem model is directly related to the Menu model. The Axe API is incredibly powerful because it can use this knowledge when executing queries.

By default Axe API receives data without model relations, and to get data with their relations you need to make an http request and specify the name of the relation in the with parameter. you can specify multiple links separated by commas:

curl \
-H "Content-Type: application/json" \
-X GET "http://localhost:3000/api/v1/menu-items?with=menu"

The result can be seen from the link http://localhost:3000/api/v1/menu-items?with=menu. The result is quite large, so I will give here only the first entry:

{
"id": 1,
"name": "Classic Eggs Benedict",
"price": "20",
"menu_id": 1,
"created_at": "2023-07-21T13:23:45.000Z",
"updated_at": "2023-07-21T13:23:45.000Z",
"menu": {
"id": 1,
"name": "Regular",
"description": "Main dishes",
"created_at": "2023-07-21T13:09:26.000Z",
"updated_at": "2023-07-21T13:09:26.000Z"
}

As we can see, the menu automatically loaded, isn’t it very convenient? There are also relations in the order, they indicate the user and the ordered dish, let’s add them to the model:

import { Model } from 'axe-api';

class Order extends Model {
get fillable() {
return ['quantity', 'menu_item_id', 'user_id'];
}

get validations() {
return {
quantity: 'required|numeric',
menu_item_id: 'required|numeric',
user_id: 'required|numeric',
};
}

menuItem() {
return this.hasOne('MenuItem', 'id', 'menu_item_id');
}

user() {
return this.hasOne('User', 'id', 'user_id');
}
}

export default Order;

Also the result can be seen from the link http://localhost:3000/api/v1/orders?with=menuItem,user. The first entry:

{
"id": 1,
"menu_item_id": 1,
"user_id": 1,
"quantity": 1,
"created_at": "2023-07-22T10:43:22.000Z",
"updated_at": "2023-07-22T10:43:22.000Z",
"menuItem": {
"id": 1,
"name": "Classic Eggs Benedict",
"price": "20",
"menu_id": 1,
"created_at": "2023-07-21T13:23:45.000Z",
"updated_at": "2023-07-21T13:23:45.000Z"
},
"user": {
"id": 1,
"email": "william.marcus@example.com",
"password": "my-secret-password",
"firstName": "William",
"lastName": "Marcus",
"created_at": "2023-07-20T12:26:28.000Z",
"updated_at": "2023-07-20T12:26:28.000Z"
}
}

It seems to me that this is a very convenient mechanism — to get only the data that is needed in the query itself.

Hiding sensitive data

In the previous section, you may have seen that the Axe API revealed the user’s password by mistake, which is not good.

To fix this and keep sensitive data safe, the Axe API offers several solutions. One of the easiest ways is to use the hiddens getter.

import { Model } from 'axe-api';

class User extends Model {
get fillable() {
return ['email', 'firstName', 'lastName', 'password'];
}

get validations() {
return {
email: 'required|min:3|max:100|email',
firstName: 'required|min:2|max:50',
lastName: 'required|min:2|max:50',
password: 'required|min:6|max:100',
};
}

get hiddens() {
return ['password'];
}
}

export default User;

See the result by link http://localhost:3000/api/v1/users/1. the password field is missing and it will be missing in all other queries where models will be associated with the user.

Querying data

After you’ve created your models, the Axe API offers robust query options for retrieve, sorting and filtering data.

For instance, you can choose which fields to include in the listings. See the results http://localhost:3000/api/v1/menu-items?fields=id,name:

{
"data": [
{
"id": 3,
"name": "Avocado Toast with Poached Eggs"
},
{
"id": 1,
"name": "Classic Eggs Benedict"
},
{
"id": 2,
"name": "Fluffy Pancakes with Maple Syrup and Berries"
},
{
"id": 4,
"name": "Greek Yogurt Parfait"
},
{
"id": 6,
"name": "Mediterranean Grilled Vegetable Panini"
},
{
"id": 5,
"name": "Spinach and Feta Stuffed Chicken Breast"
}
],
"pagination": {
"total": 6,
"lastPage": 1,
"prevPage": null,
"nextPage": null,
"perPage": 10,
"currentPage": 1,
"from": 0,
"to": 6
}
}

As a result above we can see that only two fields ID and name are displayed.

Now we have all dishes from all menus displayed in menu items, but what if we want to display menu items (dishes) from only one menu? axe api has a convenient data filtering mechanism to solve this problem. Let’s take the example of selecting dishes from the main menu only.

Open the link http://localhost:3000/api/v1/menu-items?fields=id,name&q={"menu_id":1} and you will see only four dishes from the regular menu.

{
"data": [
{
"id": 1,
"name": "Classic Eggs Benedict"
},
{
"id": 2,
"name": "Fluffy Pancakes with Maple Syrup and Berries"
},
{
"id": 3,
"name": "Avocado Toast with Poached Eggs"
},
{
"id": 4,
"name": "Greek Yogurt Parfait"
}
],
"pagination": {
"total": 4,
"lastPage": 1,
"prevPage": null,
"nextPage": null,
"perPage": 10,
"currentPage": 1,
"from": 0,
"to": 4
}
}

The whole magic is in this http request parameter q={“menu_id”:1}, you just need to pass the field name with the value and axe API will filter the data automatically.

What about sorting? The axe api solves this problem quite simply by specifying the sort parameter and the field name in the http request. Specify just the field name if you want to sort in ascending order (ASC), if you want to sort in descending order, add a minus sign before the field name.

Sort name in ascending (ASC)- http://localhost:3000/api/v1/menu-items?sort=name

Sort name in descending (DESC) - http://localhost:3000/api/v1/menu-items?sort=-name

Conclusion

In this article we looked at how to organise relations in models and how to query them, we also looked at how to sort and filter data in the api. In the next article we will look at what other options are available for data sampling, as well as one more important functionality like hooks.

--

--