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 and modify them according to your needs.
Here are a few examples:

Use extended search by default

SQL
MongoDB
Rails
Django
/routes/companies.js
1
const express = require('express');
2
const { PermissionMiddlewareCreator, RecordsGetter, RecordsCounter } = require('forest-express-sequelize');
3
const { companies } = require('../models');
4
5
const router = express.Router();
6
const permissionMiddlewareCreator = new PermissionMiddlewareCreator('companies');
7
8
//...
9
10
// 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
11
router.get('/companies', permissionMiddlewareCreator.list(), (request, response, next) => {
12
const { query, user } = request;
13
query.searchExtended = '1';
14
15
const recordsGetter = new RecordsGetter(companies, user, query);
16
recordsGetter.getAll()
17
.then(records => recordsGetter.serialize(records))
18
.then(recordsSerialized => response.send(recordsSerialized))
19
.catch(next);
20
});
21
22
// 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
23
router.get('/companies/count', permissionMiddlewareCreator.list(), (request, response, next) => {
24
const { query, user } = request;
25
query.searchExtended = '1';
26
27
const recordsCounter = new RecordsCounter(companies, user, query);
28
recordsCounter.count()
29
.then(count => response.send({ count }))
30
.catch(next);
31
});
32
33
//...
Copied!
/routes/companies.js
1
const express = require('express');
2
const { PermissionMiddlewareCreator, RecordsGetter, RecordsCounter } = require('forest-express-mongoose');
3
const { companies } = require('../models');
4
5
const router = express.Router();
6
const permissionMiddlewareCreator = new PermissionMiddlewareCreator('companies');
7
8
//...
9
10
// 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
11
router.get('/companies', permissionMiddlewareCreator.list(), (request, response, next) => {
12
const { query, user } = request;
13
query.searchExtended = '1';
14
15
const recordsGetter = new RecordsGetter(companies, user, query);
16
recordsGetter.getAll()
17
.then(records => recordsGetter.serialize(records))
18
.then(recordsSerialized => response.send(recordsSerialized))
19
.catch(next);
20
});
21
22
// 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
23
router.get('/companies/count', permissionMiddlewareCreator.list(), (request, response, next) => {
24
const { query, user } = request;
25
query.searchExtended = '1';
26
27
const recordsCounter = new RecordsCounter(companies, user, query);
28
recordsCounter.count()
29
.then(count => response.send({ count }))
30
.catch(next);
31
});
32
33
//...
34
Copied!
/lib/forest_liana/controllers/companies_controller.rb
1
if ForestLiana::UserSpace.const_defined?('CompanyController')
2
ForestLiana::UserSpace::CompanyController.class_eval do
3
alias_method :default_index, :index
4
alias_method :default_count, :count
5
6
# Get a list of Companies
7
def index
8
params['searchExtended'] = '1'
9
default_index
10
end
11
12
# Get a number of Companies
13
def count
14
params['searchExtended'] = '1'
15
default_count
16
end
17
end
18
end
Copied!
app/forest/views.py
1
from django.http import JsonResponse
2
3
from django_forest.resources.utils.resource import ResourceView
4
from django_forest.resources.utils.resource.views import ListView
5
6
7
class CompaniesListView(ListView):
8
def get(self, request):
9
# default
10
queryset = self.Model.objects.all()
11
12
params = request.GET.dict()
13
# override for always setting search extended
14
params.update({'searchExtended': '1'})
15
16
try:
17
# enhance queryset
18
queryset = self.enhance_queryset(queryset, self.Model, params, request)
19
20
# handle smart fields
21
self.handle_smart_fields(queryset, self.Model._meta.db_table, many=True)
22
23
# json api serializer
24
data = self.serialize(queryset, self.Model, params)
25
26
# search decorator
27
data = self.decorators(data, self.Model, params)
28
except Exception as e:
29
return self.error_response(e)
30
else:
31
return JsonResponse(data, safe=False)
32
33
class CompaniesCountView(ResourceView):
34
def get(self, request):
35
queryset = self.Model.objects.all()
36
params = request.GET.dict()
37
params.update({'searchExtended': '1'})
38
return self.get_count(queryset, params, request)
Copied!
app/forest/urls.py
1
from django.urls import path
2
from django.views.decorators.csrf import csrf_exempt
3
4
from . import views
5
6
app_name = 'app'
7
urlpatterns = [
8
path('/companies/count', views.CompaniesCountView.as_view(), name='companies-count'),
9
path('/companies', csrf_exempt(views.CompaniesListView.as_view()), name='companies-list'),
10
]
Copied!
With this snippet, only the companies collection would use extended search by default.
Using extended search is less performant than default search. Use this wisely.

Protect a specific record

SQL
MongoDB
Rails
Django
/routes/companies.js
1
router.delete('/companies/:recordId', permissionMiddlewareCreator.delete(), (request, response, next) => {
2
const { params, query, user } = request;
3
4
if (Number(params.recordId) === 82) {
5
response.status(403).send('This record is protected, you cannot remove it.');
6
return;
7
}
8
9
const recordRemover = new RecordRemover(companies, user, query);
10
recordRemover.remove(params.recordId)
11
.then(() => response.status(204).send())
12
.catch(next);
13
});
Copied!
/routes/companies.js
1
router.delete('/companies/:recordId', permissionMiddlewareCreator.delete(), (request, response, next) => {
2
const { params, query, user } = request;
3
4
if (Number(params.recordId) === 82) {
5
response.status(403).send('This record is protected, you cannot remove it.');
6
return;
7
}
8
9
const recordRemover = new RecordRemover(companies, user, query);
10
recordRemover.remove(params.recordId)
11
.then(() => response.status(204).send())
12
.catch(next);
13
});
Copied!
/lib/forest_liana/controllers/company_controller.rb
1
if ForestLiana::UserSpace.const_defined?('CompanyController')
2
ForestLiana::UserSpace::CompanyController.class_eval do
3
alias_method :default_destroy, :destroy
4
def destroy
5
if params["id"] == "50"
6
render status: 403, plain: 'This record is protected, you cannot remove it.'
7
else
8
default_destroy
9
end
10
end
11
end
12
end
Copied!
1
from django.http import HttpResponse
2
3
from django_forest.resources.utils.resource.views import DetailView
4
5
6
class CompaniesDetailView(DetailView):
7
8
def delete(self, request, pk):
9
if (pk == 82):
10
return HttpResponse(
11
'This record is protected, you cannot remove it.',
12
status=403);
13
14
return super(CompaniesDetailView, self).delete(request, pk)
Copied!

Replacing Forest Admin's behavior

To achieve this, simply remove the next() statement of any route:
SQL
MongoDB
Rails
Django
/routes/companies.js
1
...
2
3
// Create a Company - Check out our documentation for more details: https://docs.forestadmin.com/documentation/reference-guide/routes/default-routes#create-a-record
4
router.post('/companies', permissionMiddlewareCreator.create(), (req, res, next) => {
5
// >> Add your own logic here <<
6
});
7
8
...
Copied!
/routes/companies.js
1
...
2
3
// Create a Company - Check out our documentation for more details: https://docs.forestadmin.com/documentation/reference-guide/routes/default-routes#create-a-record
4
router.post('/companies', permissionMiddlewareCreator.create(), (req, res, next) => {
5
// >> Add your own logic here <<
6
});
7
8
...
Copied!
/lib/forest_liana/controllers/companies_controller.rb
1
if ForestLiana::UserSpace.const_defined?('CompanyController')
2
ForestLiana::UserSpace::CompanyController.class_eval do
3
# Create a Company
4
def create
5
# >> Add your own logic here <<
6
end
7
end
8
end
Copied!
app/forest/views.py
1
from django.http import JsonResponse
2
3
from django_forest.resources.utils.resource.views import ListView
4
5
6
class CompaniesListView(ListView):
7
# Create a Company
8
def post(self, request):
9
body = self.get_body(request.body)
10
model = self.Model
11
# >> Add your own logic here <<
Copied!
For instance, if you have a Users collection, you might want to create your users via your own api:
SQL
MongoDB
Rails
Django
/routes/users.js
1
...
2
3
const axios = require('axios');
4
const { RecordSerializer } = require('forest-express-sequelize');
5
const { users } = require('../models');
6
7
...
8
9
router.post('/users', permissionMiddlewareCreator.create(), (request, response, next) => {
10
const recordSerializer = new RecordSerializer(users);
11
const axiosRequest = {
12
url: 'https://<your-api>/users',
13
method: 'post',
14
data: request.body.data.attributes,
15
};
16
17
axios(axiosRequest)
18
.then(result => recordSerializer.serialize(result.data))
19
.then(resultSerialized => response.send(resultSerialized))
20
.catch(error => {
21
console.log('error:', error);
22
next(error);
23
});
24
});
Copied!
/routes/users.js
1
...
2
3
const axios = require('axios');
4
const { RecordSerializer } = require('forest-express-mongoose');
5
const { users } = require('../models');
6
7
...
8
9
router.post('/users', permissionMiddlewareCreator.create(), (request, response, next) => {
10
const recordSerializer = new RecordSerializer(users);
11
const axiosRequest = {
12
url: 'https://<your-api>/users',
13
method: 'post',
14
data: request.body.data.attributes,
15
};
16
17
axios(axiosRequest)
18
.then(result => recordSerializer.serialize(result.data))
19
.then(resultSerialized => response.send(resultSerialized))
20
.catch(error => {
21
console.log('error:', error);
22
next(error);
23
});
24
});
Copied!
/lib/forest_liana/controllers/users_controller.rb
1
require 'net/http'
2
require 'uri'
3
4
if ForestLiana::UserSpace.const_defined?('UserController')
5
ForestLiana::UserSpace::UserController.class_eval do
6
7
# Create a User
8
def create
9
checker = ForestLiana::PermissionsChecker.new(@resource, 'addEnabled', @rendering_id, user_id: forest_user['id'])
10
return head :forbidden unless checker.is_authorized?
11
12
begin
13
response = Net::HTTP.post URI('https://<your-api>/users'), params.to_json, "Content-Type" => "application/json"
14
render serializer: nil, json: render_record_jsonapi(response.body)
15
rescue => errors
16
render serializer: nil, json: JSONAPI::Serializer.serialize_errors(errors), status: 400
17
end
18
end
19
end
20
end
Copied!
app/forest/views.py
1
import requests
2
import json
3
from django.http import JsonResponse
4
from django_forest.utils.schema.json_api_schema import JsonApiSchema
5
6
from django_forest.resources.utils.resource.views import ListView
7
8
9
class CompaniesListView(ListView):
10
# Create a Company
11
def post(self, request):
12
body = self.get_body(request.body)
13
14
r = requests.post('https://<your-api>/users', json.dumps(body), headers={'Content-Type': 'application/json'})
15
result = r.json()
16
17
instance = self.Model.objects.create(**result['data'])
18
19
# json api serializer
20
Schema = JsonApiSchema.get(self.Model._meta.db_table)
21
data = Schema().dump(instance)
22
return JsonResponse(data, safe=False)
Copied!
Last modified 2mo ago