Skip to main content

Backend Integration

Integrating the frontend with the backend involves configuring adapters, models, and serializers on the frontend, and creating migrations, models, REST controllers, and routes on the backend. This guide will help you through each step to ensure a seamless integration.

The first few topics will cover configuring your engine for the backend integration.

Engine Adapters

Your engine will need to setup adapters which determine how request for resources and the resource data is persisted. In EmberData, an Adapter determines how data is persisted to a backend data store. Things such as the backend host, URL format and headers used to talk to a REST API can all be configured in an adapter.

EmberData's default Adapter has some built-in assumptions about how a REST API should look. If your backend conventions differ from those assumptions, EmberData allows either slight adjustments or you can switch to a different adapter if your backend works noticeably differently.

Fleetbase provides a default ApplicationAdapter which all adapters should extend from.

In the following example let's say we're creating a inventory extension and we have a model Warehouse. In order for the engine to run CRUD operations and persist warehouse data we first need to create an adapter.

If all your resources exist on the same namespace in your backend, it is a good idea to create a base adapter for your model's adapters to extend from. Let's create the base adapter first:

ember g adapter inventory

After it's created it might look something like this:

import ApplicationAdapter from '@fleetbase/ember-core/adapters/application';

export default class InventoryAdapter extends ApplicationAdapter {
namespace = 'inventory/int/v1';
}

You'll notice this adapter only has one property which is namespace, this is because the provided ApplicationAdapter should cover everything else your backend will require, espescailly a Fleetbase integrated backend. The namespace determines the endpoint where all your REST routes will exist.

Now let's create the adapter for our Warehouse model.

ember g adapter warehouse

Now you can make your warehouse adapter just export your base InventoryAdapter:

export { default } from './pallet';

This is because of ember's naming convention. Because the adapter name is warehouse this will create a generated URL based on the namespace for all Warehouse model request which look like inventory/int/v1/warehouses. You can read more about customizing adapters on the official Ember docs if you need more customization.

Engine Models

With your adapter created you can now create the model WarehouseModel, the model allows you to define attributes and methods your model will have:

ember g model warehouse

Your Warehouse model might look something like this:

import Model, { attr, belongsTo, hasMany } from '@ember-data/model';

export default class WarehouseModel extends Model {
@attr('string') name;
@attr('string') address;
@attr('string') city;
@attr('string') state;
@attr('string') zip;
@attr('number') capacity;
@attr('boolean') is_primary;
@belongsTo('location') location;
@hasMany('warehouse-section') sections;
@hasMany('warehouse-dock') docks;
}

The Model has an example of attributes and relationships.

Engine Serializers

If your model has relationships this needs to be defined inside your serializer.

ember g serializer warehouse
import ApplicationSerializer from '@fleetbase/ember-core/serializers/application';
import { EmbeddedRecordsMixin } from '@ember-data/serializer/rest';

export default class WarehouseSerializer extends ApplicationSerializer.extend(EmbeddedRecordsMixin) {
/**
* Embedded relationship attributes
*
* @var {Object}
*/
get attrs() {
return {
location: { embedded: 'always' },
sections: { embedded: 'always' },
docks: { embedded: 'always' },
};
}
}

Migrations

On your backend you need to create the migrations which will create your database table schema which your model will use.

php artisan make:migration create_warehouses_table
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateWarehousesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('warehouses', function (Blueprint $table) {
$table->increments('id');
$table->uuid('uuid')->nullable()->unique();
$table->foreignUuid('company_uuid')->nullable()->index()->references('uuid')->on('companies');
$table->foreignUuid('location_uuid')->nullable()->index()->references('uuid')->on('locations');
$table->string('name');
$table->string('address');
$table->string('city');
$table->string('state');
$table->string('zip');
$table->integer('capacity');
$table->boolean('is_primary')->default(false);
$table->timestamps();
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('warehouses');
}
}

Eloquent Model

Your backend will also need a model which corresponds to your new table created via the migration above:

<?php

namespace Fleetbase\Inventory\Models;

use Fleetbase\Models\Model;
use Fleetbase\Traits\HasApiModelBehavior;
use Fleetbase\Traits\HasUuid;
use Fleetbase\Traits\Searchable;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Warehouse extends Model
{
use HasUuid;
use HasApiModelBehavior;
use Searchable;

/**
* The database table used by the model.
*
* @var string
*/
protected $table = 'warehouses';

/**
* The primary key for the model.
*
* @var string
*/
protected $primaryKey = 'uuid';

/**
* The singularName overwrite.
*
* @var string
*/
protected $singularName = 'warehouse';

/**
* These attributes that can be queried.
*
* @var array
*/
protected $searchableColumns = ['name'];

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
'address',
'city',
'state',
'zip',
'capacity',
'is_primary',
];

/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'is_primary' => 'boolean'
];

/**
* @return BelongsTo
*/
public function location()
{
return $this->belongsTo(Location::class);
}

/**
* @return HasMany
*/
public function sections()
{
return $this->belongsTo(WarehouseSection::class);
}

/**
* @return HasMany
*/
public function docks()
{
return $this->belongsTo(WarehouseDock::class);
}
}

Notice the traits which are required to create an API resource HasApiModelBehavior, this is a trait which provides the models with the necessary methods to easily integrate with a REST controller providing CRUD operations.

REST Controller

Your REST Controller will be required for CRUD operations on your model. Create your REST Controller src/Http/Controllers/WarehouseController.php:

<?php

namespace Fleetbase\Inventory\Http\Controllers;

class WarehouseController extends PalletResourceController
{
/**
* The resource to query.
*
* @var string
*/
public $resource = 'warehouse';
}

After this you can easily define the REST routes in src/routes.php:

$router->fleetbaseRoutes('warehouse');

Using the fleetbaseRoutes method will automatically define the REST routes required which look like this:

GET /warehouses
GET /warehouses/:uuid
POST /warehouses
PUT /warehouses/:uuid
DELETE /warehouses/:uuid

Making Requests

Now in your engine.js you can make request to your resource model easily using the Store Service which is provided by Ember.js, this utilizes your adapter, serializer, and model to build the request and also persist the data in the store.

const warehouse = await this.store.findRecord('warehouse', id);
warehouse.set('name', 'Warehouse #1');
warehouse.save();

Or creating a warehouse model:

const warehouse = this.store.createRecord('warehouse', {
name: 'Warehouse #1'
});

warehouse.save().then((warehouse) => {
console.log(`New Warehouse Created: ${warehouse.id}`);
});