# Override a route

{% hint style="warning" %}
Please be sure of your agent type and version and pick the right documentation accordingly.
{% endhint %}

{% tabs %}
{% tab title="Node.js" %}
{% hint style="danger" %}
This is the documentation of the `forest-express-sequelize` and `forest-express-mongoose` Node.js agents that will soon reach end-of-support.

`forest-express-sequelize` v9 and `forest-express-mongoose` v9 are replaced by [`@forestadmin/agent`](https://docs.forestadmin.com/developer-guide-agents-nodejs/) v1.

Please check your agent type and version and read on or switch to the right documentation.
{% endhint %}
{% endtab %}

{% tab title="Ruby on Rails" %}
{% hint style="success" %}
This is still the latest Ruby on Rails documentation of the `forest_liana` agent, you’re at the right place, please read on.
{% endhint %}
{% endtab %}

{% tab title="Python" %}
{% hint style="danger" %}
This is the documentation of the `django-forestadmin` Django agent that will soon reach end-of-support.

If you’re using a Django agent, notice that `django-forestadmin` v1 is replaced by [`forestadmin-agent-django`](https://docs.forestadmin.com/developer-guide-agents-python) v1.

If you’re using a Flask agent, go to the [`forestadmin-agent-flask`](https://docs.forestadmin.com/developer-guide-agents-python) v1 documentation.

Please check your agent type and version and read on or switch to the right documentation.
{% endhint %}
{% endtab %}

{% tab title="PHP" %}
{% hint style="danger" %}
This is the documentation of the `forestadmin/laravel-forestadmin` Laravel agent that will soon reach end-of-support.

If you’re using a Laravel agent, notice that `forestadmin/laravel-forestadmin` v1 is replaced by [`forestadmin/laravel-forestadmin`](https://docs.forestadmin.com/developer-guide-agents-php) v3.

If you’re using a Symfony agent, go to the [`forestadmin/symfony-forestadmin`](https://docs.forestadmin.com/developer-guide-agents-php) v1 documentation.

Please check your agent type and version and read on or switch to the right documentation.
{% endhint %}
{% endtab %}
{% endtabs %}

## Override a route

Overriding a route allows you to change or completely replace a Forest Admin's route behavior.

#### Changing Forest Admin's behavior

To achieve this, use existing snippets of [default routes](https://docs.forestadmin.com/documentation/reference-guide/routes/default-routes) and modify them according to your needs.

Here are a few examples:

**Use extended search by default**

{% tabs %}
{% tab title="SQL" %}
{% code title="/routes/companies.js" %}

```javascript
const express = require('express');
const {
  PermissionMiddlewareCreator,
  RecordsGetter,
  RecordsCounter,
} = require('forest-express-sequelize');
const { companies } = require('../models');

const router = express.Router();
const permissionMiddlewareCreator = new PermissionMiddlewareCreator(
  'companies'
);

//...

// Get a list of Companies - Check out our documentation for more details: https://docs.forestadmin.com/documentation/reference-guide/routes/default-routes#get-a-list-of-records
router.get(
  '/companies',
  permissionMiddlewareCreator.list(),
  (request, response, next) => {
    const { query, user } = request;
    query.searchExtended = '1';

    const recordsGetter = new RecordsGetter(companies, user, query);
    recordsGetter
      .getAll()
      .then((records) => recordsGetter.serialize(records))
      .then((recordsSerialized) => response.send(recordsSerialized))
      .catch(next);
  }
);

// Get a number of Companies - Check out our documentation for more details: https://docs.forestadmin.com/documentation/reference-guide/routes/default-routes#get-a-list-of-records
router.get(
  '/companies/count',
  permissionMiddlewareCreator.list(),
  (request, response, next) => {
    const { query, user } = request;
    query.searchExtended = '1';

    const recordsCounter = new RecordsCounter(companies, user, query);
    recordsCounter
      .count()
      .then((count) => response.send({ count }))
      .catch(next);
  }
);

//...
```

{% endcode %}
{% endtab %}

{% tab title="MongoDB" %}
{% code title="/routes/companies.js" %}

```javascript
const express = require('express');
const {
  PermissionMiddlewareCreator,
  RecordsGetter,
  RecordsCounter,
} = require('forest-express-mongoose');
const { companies } = require('../models');

const router = express.Router();
const permissionMiddlewareCreator = new PermissionMiddlewareCreator(
  'companies'
);

//...

// Get a list of Companies - Check out our documentation for more details: https://docs.forestadmin.com/documentation/reference-guide/routes/default-routes#get-a-list-of-records
router.get(
  '/companies',
  permissionMiddlewareCreator.list(),
  (request, response, next) => {
    const { query, user } = request;
    query.searchExtended = '1';

    const recordsGetter = new RecordsGetter(companies, user, query);
    recordsGetter
      .getAll()
      .then((records) => recordsGetter.serialize(records))
      .then((recordsSerialized) => response.send(recordsSerialized))
      .catch(next);
  }
);

// Get a number of Companies - Check out our documentation for more details: https://docs.forestadmin.com/documentation/reference-guide/routes/default-routes#get-a-list-of-records
router.get(
  '/companies/count',
  permissionMiddlewareCreator.list(),
  (request, response, next) => {
    const { query, user } = request;
    query.searchExtended = '1';

    const recordsCounter = new RecordsCounter(companies, user, query);
    recordsCounter
      .count()
      .then((count) => response.send({ count }))
      .catch(next);
  }
);

//...
```

{% endcode %}
{% endtab %}

{% tab title="Rails" %}
{% code title="/lib/forest\_liana/controllers/companies\_controller.rb" %}

```ruby
if ForestLiana::UserSpace.const_defined?('CompanyController')
  ForestLiana::UserSpace::CompanyController.class_eval do
    alias_method :default_index, :index
    alias_method :default_count, :count

    # Get a list of Companies
    def index
      params['searchExtended'] = '1'
      default_index
    end

    # Get a number of Companies
    def count
      params['searchExtended'] = '1'
      default_count
    end
  end
end
```

{% endcode %}
{% endtab %}

{% tab title="Django" %}
{% code title="app/forest/views.py" %}

```python
from django.http import JsonResponse

from django_forest.resources.utils.resource import ResourceView
from django_forest.resources.views import ListView


class CompaniesListView(ListView):
    def get(self, request):
        # default
        queryset = self.Model.objects.all()

        params = request.GET.dict()
        # override for always setting search extended
        params.update({'searchExtended': '1'})

        try:
            # enhance queryset
            queryset = self.enhance_queryset(queryset, self.Model, params, request)

            # handle smart fields
            self.handle_smart_fields(queryset, self.Model._meta.db_table, many=True)

            # json api serializer
            data = self.serialize(queryset, self.Model, params)

            # search decorator
            data = self.decorators(data, self.Model, params)
        except Exception as e:
            return self.error_response(e)
        else:
            return JsonResponse(data, safe=False)

class CompaniesCountView(ResourceView):
    def get(self, request):
        queryset = self.Model.objects.all()
        params = request.GET.dict()
        params.update({'searchExtended': '1'})
        return self.get_count(queryset, params, request)
```

{% endcode %}

{% code title="app/forest/urls.py" %}

```python
from django.urls import path
from django.views.decorators.csrf import csrf_exempt

from . import views

app_name = 'app'
urlpatterns = [
    path('/companies/count', views.CompaniesCountView.as_view(), name='companies-count'),
    path('/companies', csrf_exempt(views.CompaniesListView.as_view()), name='companies-list'),
]
```

{% endcode %}
{% endtab %}

{% tab title="Laravel" %}
{% code title="app/Http/Controllers/CompaniesController.php" %}

```php
<?php

namespace App\Http\Controllers;

use ForestAdmin\LaravelForestAdmin\Http\Controllers\ResourcesController;
use Illuminate\Http\JsonResponse;

class CompaniesController extends ResourcesController
{
    public function callAction($method, $parameters)
    {
        $parameters['collection'] = 'Company';
        return parent::callAction($method, $parameters);
    }

    public function index()
    {
        request()->query->add(['searchExtended' => '1']);
        return parent::index();
    }

    public function count(): JsonResponse
    {
        request()->query->add(['searchExtended' => '1']);
        return parent::count();
    }
}
```

{% endcode %}

{% code title="routes/web.php" %}

```php
<?php

use App\Http\Controllers\CompaniesController;
use Illuminate\Support\Facades\Route;

Route::get('forest/company', [CompaniesController::class, 'index']);
Route::get('forest/company/count', [CompaniesController::class, 'count']);
```

{% endcode %}
{% endtab %}
{% endtabs %}

With this snippet, only the `companies` collection would use extended search by default.

{% hint style="warning" %}
Using extended search is less performant than default search. Use this wisely.
{% endhint %}

**Protect a specific record**

{% tabs %}
{% tab title="SQL" %}
{% code title="/routes/companies.js" %}

```javascript
router.delete(
  '/companies/:recordId',
  permissionMiddlewareCreator.delete(),
  (request, response, next) => {
    const { params, query, user } = request;

    if (Number(params.recordId) === 82) {
      response
        .status(403)
        .send('This record is protected, you cannot remove it.');
      return;
    }

    const recordRemover = new RecordRemover(companies, user, query);
    recordRemover
      .remove(params.recordId)
      .then(() => response.status(204).send())
      .catch(next);
  }
);
```

{% endcode %}
{% endtab %}

{% tab title="MongoDB" %}
{% code title="/routes/companies.js" %}

```javascript
router.delete(
  '/companies/:recordId',
  permissionMiddlewareCreator.delete(),
  (request, response, next) => {
    const { params, query, user } = request;

    if (Number(params.recordId) === 82) {
      response
        .status(403)
        .send('This record is protected, you cannot remove it.');
      return;
    }

    const recordRemover = new RecordRemover(companies, user, query);
    recordRemover
      .remove(params.recordId)
      .then(() => response.status(204).send())
      .catch(next);
  }
);
```

{% endcode %}
{% endtab %}

{% tab title="Rails" %}
{% code title="/lib/forest\_liana/controllers/company\_controller.rb" %}

```ruby
if ForestLiana::UserSpace.const_defined?('CompanyController')
  ForestLiana::UserSpace::CompanyController.class_eval do
    alias_method :default_destroy, :destroy
    def destroy
      if params["id"] == "50"
        render status: 403, plain: 'This record is protected, you cannot remove it.'
      else
        default_destroy
      end
    end
  end
end
```

{% endcode %}
{% endtab %}

{% tab title="Django" %}

```python
from django.http import HttpResponse

from django_forest.resources.views import DetailView


class CompaniesDetailView(DetailView):

    def delete(self, request, pk):
        if (pk == 82):
            return HttpResponse(
            'This record is protected, you cannot remove it.',
             status=403);

        return super(CompaniesDetailView, self).delete(request, pk)
```

{% endtab %}

{% tab title="Laravel" %}
{% code title="app/Http/Controllers/CompaniesController.php" %}

```php
<?php

namespace App\Http\Controllers;

use ForestAdmin\LaravelForestAdmin\Http\Controllers\ResourcesController;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Response;

class CompaniesController extends ResourcesController
{
    public function callAction($method, $parameters)
    {
        $parameters['collection'] = 'Company';
        return parent::callAction($method, $parameters);
    }

    public function destroy(): JsonResponse
    {
        if (request()->route()->parameter('id') === "50") {
            return response()->json(['error' => 'This record is protected, you cannot remove it.'], Response::HTTP_FORBIDDEN);
        } else {
            return parent::destroy();
        }
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

#### Replacing Forest Admin's behavior

To achieve this, simply remove the `next()` statement of any route:

{% tabs %}
{% tab title="SQL" %}
{% code title="/routes/companies.js" %}

```javascript
...

// Create a Company - Check out our documentation for more details: https://docs.forestadmin.com/documentation/reference-guide/routes/default-routes#create-a-record
router.post('/companies', permissionMiddlewareCreator.create(), (req, res, next) => {
  // >> Add your own logic here <<
});

...
```

{% endcode %}
{% endtab %}

{% tab title="MongoDB" %}
{% code title="/routes/companies.js" %}

```javascript
...

// Create a Company - Check out our documentation for more details: https://docs.forestadmin.com/documentation/reference-guide/routes/default-routes#create-a-record
router.post('/companies', permissionMiddlewareCreator.create(), (req, res, next) => {
  // >> Add your own logic here <<
});

...
```

{% endcode %}
{% endtab %}

{% tab title="Rails" %}
{% code title="/lib/forest\_liana/controllers/companies\_controller.rb" %}

```ruby
if ForestLiana::UserSpace.const_defined?('CompanyController')
  ForestLiana::UserSpace::CompanyController.class_eval do
    # Create a Company
    def create
      # >> Add your own logic here <<
    end
  end
end
```

{% endcode %}
{% endtab %}

{% tab title="Django" %}
{% code title="app/forest/views.py" %}

```python
from django.http import JsonResponse

from django_forest.resources.views import ListView


class CompaniesListView(ListView):
    # Create a Company
    def post(self, request):
        body = self.get_body(request.body)
        model = self.Model
        # >> Add your own logic here <<
```

{% endcode %}
{% endtab %}

{% tab title="Laravel" %}
{% code title="app/Http/Controllers/UsersController.php" %}

```php
<?php

namespace App\Http\Controllers;

use ForestAdmin\LaravelForestAdmin\Http\Controllers\ResourcesController;
use Illuminate\Http\JsonResponse;

class UsersController extends ResourcesController
{
    public function store(): JsonResponse
    {
        // >> Add your own logic here <<
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

For instance, if you have a `Users` collection, you might want to create your users via your own api:

{% tabs %}
{% tab title="SQL" %}
{% code title="/routes/users.js" %}

```javascript
...

const axios = require('axios');
const { RecordSerializer } = require('forest-express-sequelize');
const { users } = require('../models');

...

router.post('/users', permissionMiddlewareCreator.create(), (request, response, next) => {
  const recordSerializer = new RecordSerializer(users);
  const axiosRequest = {
    url: 'https://<your-api>/users',
    method: 'post',
    data: request.body.data.attributes,
  };

  axios(axiosRequest)
    .then(result => recordSerializer.serialize(result.data))
    .then(resultSerialized => response.send(resultSerialized))
    .catch(error => {
      console.log('error:', error);
      next(error);
    });
});
```

{% endcode %}
{% endtab %}

{% tab title="MongoDB" %}
{% code title="/routes/users.js" %}

```javascript
...

const axios = require('axios');
const { RecordSerializer } = require('forest-express-mongoose');
const { users } = require('../models');

...

router.post('/users', permissionMiddlewareCreator.create(), (request, response, next) => {
  const recordSerializer = new RecordSerializer(users);
  const axiosRequest = {
    url: 'https://<your-api>/users',
    method: 'post',
    data: request.body.data.attributes,
  };

  axios(axiosRequest)
    .then(result => recordSerializer.serialize(result.data))
    .then(resultSerialized => response.send(resultSerialized))
    .catch(error => {
      console.log('error:', error);
      next(error);
    });
});
```

{% endcode %}
{% endtab %}

{% tab title="Rails" %}
{% code title="/lib/forest\_liana/controllers/users\_controller.rb" %}

```ruby
require 'net/http'
require 'uri'

if ForestLiana::UserSpace.const_defined?('UserController')
  ForestLiana::UserSpace::UserController.class_eval do

    # Create a User
    def create
      forest_authorize!('add', forest_user, @resource)
      begin
        response = Net::HTTP.post URI('https://<your-api>/users'), params.to_json, "Content-Type" => "application/json"
        render serializer: nil, json: render_record_jsonapi(response.body)
      rescue => errors
        render serializer: nil, json: JSONAPI::Serializer.serialize_errors(errors), status: 400
      end
    end
  end
end
```

{% endcode %}
{% endtab %}

{% tab title="Django" %}
{% code title="app/forest/views.py" %}

```python
import requests
import json
from django.http import JsonResponse
from django_forest.utils.schema.json_api_schema import JsonApiSchema

from django_forest.resources.views import ListView


class CompaniesListView(ListView):
    # Create a Company
    def post(self, request):
        body = self.get_body(request.body)

        r = requests.post('https://<your-api>/users', json.dumps(body), headers={'Content-Type': 'application/json'})
        result = r.json()

        instance = self.Model.objects.create(**result['data'])

        # json api serializer
        Schema = JsonApiSchema.get(self.Model._meta.db_table)
        data = Schema().dump(instance)
        return JsonResponse(data, safe=False)
```

{% endcode %}
{% endtab %}

{% tab title="Laravel" %}
{% code title="app/Http/Controllers/UsersController.php" %}

```php
<?php

namespace App\Http\Controllers;

use App\Models\User;
use ForestAdmin\LaravelForestAdmin\Facades\JsonApi;
use ForestAdmin\LaravelForestAdmin\Http\Controllers\ResourcesController;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Http;

class UsersController extends ResourcesController
{
    public function callAction($method, $parameters)
    {
        $parameters['collection'] = 'User';
        return parent::callAction($method, $parameters);
    }

    public function store(): JsonResponse
    {
        $this->authorize('create', $this->model);
        $response = Http::post('https://<your-api>/users', request()->all())->json();
        $user = User::findOrFail($response['id']);

        return response()->json(JsonApi::render($user, $this->name), Response::HTTP_CREATED);
    }
}
```

{% endcode %}

{% code title="app/Http/Middleware/VerifyCsrfToken.php" %}

```php
<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array<int, string>
     */
    protected $except = [
        'forest/user',
    ];
}
```

{% endcode %}

{% code title="routes/web.php" %}

```php
<?php

use App\Http\Controllers\UsersController;
use Illuminate\Support\Facades\Route;

Route::post('forest/user', [UsersController::class, 'store']);
```

{% endcode %}

{% code title="app/Http/Controllers/UsersController.php" %}

```php
<?php

namespace App\Http\Controllers;

use App\Models\User;
use ForestAdmin\LaravelForestAdmin\Facades\JsonApi;
use ForestAdmin\LaravelForestAdmin\Http\Controllers\ResourcesController;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Http;

class UsersController extends ResourcesController
{
    public function callAction($method, $parameters)
    {
        $parameters['collection'] = 'User';
        return parent::callAction($method, $parameters);
    }

    public function store(): JsonResponse
    {
        $this->authorize('create', $this->model);
        $response = Http::post('https://<your-api>/users', request()->all())->json();
        $user = User::findOrFail($response['id']);

        return response()->json(JsonApi::render($user, $this->name), Response::HTTP_CREATED);
    }
}
```

{% endcode %}

{% code title="app/Http/Middleware/VerifyCsrfToken.php" %}

```php
<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array<int, string>
     */
    protected $except = [
        'forest/user',
    ];
}
```

{% endcode %}

{% code title="routes/web.php" %}

```php
<?php

use App\Http\Controllers\UsersController;
use Illuminate\Support\Facades\Route;

Route::post('forest/user', [UsersController::class, 'store']);
```

{% endcode %}
{% endtab %}
{% endtabs %}
