diff --git a/CHANGELOG.md b/CHANGELOG.md
index e1544830702feedc316da6018f23eaf253cdc61b..b995ca18bd41c6b08d11db107d522f81071abef0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [3.3.0] - 2020-05
+### Added
+- #90 => Dynamic documentation to explain how to export results via server urls
+
+### Fixed
+
+### Changed
+
+### Security
+
+
 ## [3.2.0] - 2020-04
 ### Added
 - #69 => User can change the number of object displayable (10, 20, 50, 100) in result datatable
diff --git a/VERSION b/VERSION
index 06a445799fe7411076dfdc156c700d3b1e199deb..f30101c08059da605966d4e84d357519b8c77283 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.1
\ No newline at end of file
+3.3
\ No newline at end of file
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 043950cdec8d1dd4a059d6a10ba0267fe9b8ad0b..c59f2ab0474fc9c6dbfb5c9bcd5d01763e0d64e7 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -15,6 +15,7 @@ import { StaticModule } from './static/static.module';
 import { LoginModule } from './login/login.module';
 import { SearchModule } from './search/search.module';
 import { DetailModule } from './detail/detail.module';
+import { DocumentationModule } from './documentation/documentation.module';
 import { AppRoutingModule } from './app.routing';
 import { AppComponent } from './core/containers/app.component';
 import { environment } from '../environments/environment';
@@ -29,6 +30,7 @@ import { environment } from '../environments/environment';
         LoginModule,
         SearchModule,
         DetailModule,
+        DocumentationModule,
         StoreModule.forRoot(reducers, {
             metaReducers,
             runtimeChecks: {
@@ -47,7 +49,6 @@ import { environment } from '../environments/environment';
         !environment.production ? StoreDevtoolsModule.instrument() : [],
         EffectsModule.forRoot([])
     ],
-    // providers: [{ provide: RouterStateSerializer, useClass: CustomRouterStateSerializer }],
     bootstrap: [AppComponent]
 })
 export class AppModule { }
diff --git a/src/app/core/components/nav.component.html b/src/app/core/components/nav.component.html
index f2f73abcdb21881506d645d480be5d6c0942f30e..1412b47e3c49964b7136e0ea542e6ee719f95f72 100644
--- a/src/app/core/components/nav.component.html
+++ b/src/app/core/components/nav.component.html
@@ -17,6 +17,11 @@
                     <span class="fas fa-search"></span> Search
                 </a>
             </li>
+            <li class="nav-item">
+                <a class="nav-link" routerLink="/documentation" routerLinkActive="active">
+                    <span class="fas fa-question"></span> Documentation
+                </a>
+            </li>
         </ul>
         <button *ngIf="!isAuthenticated" class="btn btn-outline-success my-2 my-sm-0" 
             id="button-sign-in"
@@ -74,6 +79,11 @@
                     <span class="fas fa-search fa-fw"></span> Search
                 </a>
             </li>
+            <li role="menuitem">
+                <a class="dropdown-item" routerLink="/documentation">
+                    <span class="fas fa-question fa-fw"></span> Documentation
+                </a>
+            </li>
             <li *ngIf="isAuthenticated" class="divider dropdown-divider"></li>
             <li *ngIf="isAuthenticated" role="menuitem">
                 <a class="dropdown-item" routerLink="/change-password">
diff --git a/src/app/documentation/containers/documentation.component.css b/src/app/documentation/containers/documentation.component.css
new file mode 100644
index 0000000000000000000000000000000000000000..288c15cc9fa95895666f5b0e358aab583b8466fd
--- /dev/null
+++ b/src/app/documentation/containers/documentation.component.css
@@ -0,0 +1,14 @@
+blockquote {
+    background: #f9f9f9;
+    border-left: 10px solid #ccc;
+    margin: 1.5em 10px;
+    padding: 1em 10px 1em 10px;
+}
+
+table, th, td {
+    background-color: #f9f9f9;
+    padding: 6px 13px;
+    border: 1px solid #ccc;
+    border-collapse: collapse;
+    margin-bottom: 1em;
+}
diff --git a/src/app/documentation/containers/documentation.component.html b/src/app/documentation/containers/documentation.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..2feed592bf8d7d18e2e6f6386c5a48b1d7c9cde3
--- /dev/null
+++ b/src/app/documentation/containers/documentation.component.html
@@ -0,0 +1,203 @@
+<div class="container">
+    <div class="jumbotron">
+        <div class="row align-items-center">
+            <div class="col-md-12 order-md-1 text-justify text-md-left pr-md-5">
+                <h2 class="mb-3">Export server documentation</h2>
+    
+                <h4>URL construction</h4>
+                <p>
+                    To request the server, you need to construct a correct URL. Just below you can find the URL schema and a
+                    description of mandatory parameters:
+                </p>
+                <code>{{ apiPath }}/search/dataset?a=id_attribute&c=id_attribute::operator::value</code>
+    
+                <ul>
+                    <li>
+                        <code>dataset</code>: dataset in which to search. See datasets section for available datasets.
+                    </li>
+                    <li>
+                        <code>a</code>: output parameters as attributes id list semicolon separated. See outputs section for available attributes.
+                    </li>
+                    <blockquote>a=1;2;3</blockquote>
+                    <li>
+                        <code>c</code>: criteria list separeted with semicolon. A criterion is defined by an id_attribute,
+                        an operator and a value. See operators section for available operators.
+                    </li>
+                    <blockquote>c=3::eq::ping;2::eq::pong</blockquote>
+                </ul>
+    
+                <h4>Available parameters</h4>
+    
+                <h5>Datasets</h5>
+                <div *ngIf="datasetListIsLoading | async">
+                    <span class="fas fa-circle-notch fa-spin fa-3x"></span>
+                    <span class="sr-only">Loading...</span>
+                </div>
+                <table *ngIf="datasetListIsLoaded | async" id="table">
+                    <tr>
+                        <th>Dataset</th>
+                        <th>Description</th>
+                    </tr>
+                    <tr *ngFor="let dataset of datasetList | async">
+                        <td>{{ dataset.name }}</td>
+                        <td>{{ dataset.description }}</td>
+                    </tr>
+                </table>
+    
+                <h5>Outputs</h5>
+                <div *ngIf="attributeListIsLoading | async">
+                    <span class="fas fa-circle-notch fa-spin fa-3x"></span>
+                    <span class="sr-only">Loading...</span>
+                </div>
+                <div *ngIf="attributeListIsLoaded | async" class="row">
+                    <div *ngFor="let dataset of datasetList | async" class="col-auto">
+                        <h6>{{ dataset.label }} output list</h6>
+                        <table id="table">
+                            <tr>
+                                <th>id</th>
+                                <th>attribute</th>
+                            </tr>
+                            <tr *ngFor="let attribute of getDatasetAttributes(dataset, attributeList | async)">
+                                <td>{{ attribute.id }}</td>
+                                <td>{{ attribute.name }}</td>
+                            </tr>
+                        </table>
+                    </div>
+                </div>
+                
+                <h5>Operators</h5>
+                <table id="table">
+                    <tr>
+                        <th>operator</th>
+                        <th>description</th>
+                        <th>usage</th>
+                        <th>example</th>
+                    </tr>
+                    <tr>
+                        <td>eq</td>
+                        <td>equal to</td>
+                        <td><code>c=id_attribute::eq::value</code></td>
+                        <td><code>c=1::eq::89</code></td>
+                    </tr>
+                    <tr>
+                        <td>neq</td>
+                        <td>not equal to</td>
+                        <td><code>c=id_attribute::neq::value</code></td>
+                        <td><code>c=1::neq::89</code></td>
+                    </tr>
+                    <tr>
+                        <td>gt</td>
+                        <td>greater than</td>
+                        <td><code>c=id_attribute::gt::value</code></td>
+                        <td><code>c=1::gt::1.5</code></td>
+                    </tr>
+                    <tr>
+                        <td>gte</td>
+                        <td>greater than or equal to</td>
+                        <td><code>c=id_attribute::gte::value</code></td>
+                        <td><code>c=1::gte::2</code></td>
+                    </tr>
+                    <tr>
+                        <td>lt</td>
+                        <td>lower than</td>
+                        <td><code>c=id_attribute::lt::value</code></td>
+                        <td><code>c=1::lt::1.5</code></td>
+                    </tr>
+                    <tr>
+                        <td>lte</td>
+                        <td>lower than or equal to</td>
+                        <td><code>c=id_attribute::lte::value</code></td>
+                        <td><code>c=1::lte::2</code></td>
+                    </tr>
+                    <tr>
+                        <td>bw</td>
+                        <td>between</td>
+                        <td><code>c=id_attribute::bw::value_min|value_max</code></td>
+                        <td><code>c=1::bw::10|90</code></td>
+                    </tr>
+                    <tr>
+                        <td>lk</td>
+                        <td>like</td>
+                        <td><code>c=id_attribute::lk::value</code></td>
+                        <td><code>c=1::lk::ping</code></td>
+                    </tr>
+                    <tr>
+                        <td>nlk</td>
+                        <td>not like</td>
+                        <td><code>c=id_attribute::nlk::value</code></td>
+                        <td><code>c=1::nlk::pong</code></td>
+                    </tr>
+                    <tr>
+                        <td>in</td>
+                        <td>in</td>
+                        <td><code>c=id_attribute::in::value_x|value_y|value_z</code></td>
+                        <td><code>c=1::in::ping|pong|paff</code></td>
+                    </tr>
+                    <tr>
+                        <td>nin</td>
+                        <td>not in</td>
+                        <td><code>c=id_attribute::nin::value_x|value_y|value_z</code></td>
+                        <td><code>c=1::nin::ping|pong|paf</code></td>
+                    </tr>
+                    <tr>
+                        <td>nl</td>
+                        <td>is null</td>
+                        <td><code>c=id_attribute::nl</code></td>
+                        <td><code>c=1::nl</code></td>
+                    </tr>
+                    <tr>
+                        <td>nnl</td>
+                        <td>is not null</td>
+                        <td><code>c=id_attribute::nnl</code></td>
+                        <td><code>c=1::nnl</code></td>
+                    </tr>
+                    <tr>
+                        <td>js</td>
+                        <td>json</td>
+                        <td><code>c=id_attribute::js::extension,keyword|operator|value</code></td>
+                        <td><code>c=1::js::PrimaryHDU,ID|eq|45</code></td>
+                    </tr>
+                </table>
+    
+                <h4>Examples</h4>
+
+                We supposed to have the dataset ping with following attributes:
+                <table id="table">
+                    <tr>
+                        <th>id</th>
+                        <th>attribute</th>
+                    </tr>
+                    <tr>
+                        <td>1</td>
+                        <td>obs_id</td>
+                    </tr>
+                    <tr>
+                        <td>2</td>
+                        <td>ra</td>
+                    </tr>
+                    <tr>
+                        <td>3</td>
+                        <td>dec</td>
+                    </tr>
+                    <tr>
+                        <td>4</td>
+                        <td>instrument</td>
+                    </tr>
+                </table>
+
+                <blockquote>{{ apiPath }}/search/ping?a=1;2;3&c=1::eq::1</blockquote>
+                <p>This will return the <code>obs_id</code> with its value equals to 1 and display <code>obs_id</code>, <code>RA</code> and <code>DEC</code> as
+                    outputs.</p>
+                <blockquote>
+                    {{ apiPath }}/search/ping?a=1;2;3;4&c=4::in::TEL_1|TEL_2
+                </blockquote>
+                <p>This will return a list of <code>TEL_1</code> or <code>TEL_2</code> observations with all available
+                    outputs.</p>
+                <blockquote>
+                    {{ apiPath }}/search/ping?a=1&c=2::gt::1;3::gt::2
+                </blockquote>
+                <p>This will return a list of <code>obs_id</code> where <code>RA</code> is greater than 1 and <code>DEC</code> is greater than 2</p>
+            </div>
+        </div>
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/app/documentation/containers/documentation.component.spec.ts b/src/app/documentation/containers/documentation.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3a50dd865065651dfea5acc8086f453cb7725fdc
--- /dev/null
+++ b/src/app/documentation/containers/documentation.component.spec.ts
@@ -0,0 +1,45 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { provideMockStore, MockStore } from '@ngrx/store/testing';
+
+import { DocumentationComponent } from './documentation.component';
+import * as fromDocumentation from '../store/documentation.reducer';
+import * as documentationActions from '../store/documentation.action';
+import { DATASET, ATTRIBUTE_LIST } from 'src/settings/test-data';
+import { Dataset, Attribute } from 'src/app/metamodel/model';
+
+describe('[Documentation] Component: DocumentationComponent', () => {
+    let component: DocumentationComponent;
+    let fixture: ComponentFixture<DocumentationComponent>;
+    let store: MockStore;
+    const initialState = { documentation: { ...fromDocumentation.initialState }};;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [ DocumentationComponent ],
+            providers: [ provideMockStore({ initialState }) ]
+        });
+        fixture = TestBed.createComponent(DocumentationComponent);
+        component = fixture.componentInstance;
+        store = TestBed.inject(MockStore);
+    });
+
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+
+    it('#getDatasetAttributes() should return attributes for selected dataset', () => {
+        const dataset: Dataset = DATASET;
+        const attributeList: Attribute[] = ATTRIBUTE_LIST;
+        const expectedAttributeList = attributeList.filter(a => a.table_name === dataset.table_ref);
+        expect(component.getDatasetAttributes(dataset, attributeList)).toEqual(expectedAttributeList);
+    });
+
+    it('should execute ngOnInit lifecycle', () => {
+        const action = new documentationActions.RetrieveDatasetList();
+        const spy = spyOn(store, 'dispatch');
+        fixture.detectChanges();
+    
+        expect(spy).toHaveBeenCalledWith(action);
+      });
+});
+
diff --git a/src/app/documentation/containers/documentation.component.ts b/src/app/documentation/containers/documentation.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..14fd652facdc5dd3f137963335498e4a64c17349
--- /dev/null
+++ b/src/app/documentation/containers/documentation.component.ts
@@ -0,0 +1,44 @@
+import { Component, OnInit } from '@angular/core';
+
+import { Observable } from 'rxjs';
+import { Store } from '@ngrx/store';
+
+import * as documentationActions from '../store/documentation.action';
+import * as fromDocumentation from '../store/documentation.reducer';
+import * as documentationSelector from '../store/documentation.selector';
+import { Dataset, Attribute } from 'src/app/metamodel/model';
+import { environment } from '../../../environments/environment';
+
+@Component({
+    selector: 'app-documentation',
+    templateUrl: 'documentation.component.html',
+    styleUrls: ['documentation.component.css']
+})
+export class DocumentationComponent implements OnInit {
+    public apiPath: string = environment.apiUrl;
+    public datasetListIsLoading: Observable<boolean>;
+    public datasetListIsLoaded: Observable<boolean>;
+    public datasetList: Observable<Dataset[]>;
+    public attributeListIsLoading: Observable<boolean>;
+    public attributeListIsLoaded: Observable<boolean>;
+    public attributeList: Observable<Attribute[]>;
+
+    constructor(private store: Store<{ documentation: fromDocumentation.State }>) {
+        this.datasetListIsLoading = store.select(documentationSelector.getDatasetListIsLoading);
+        this.datasetListIsLoaded = store.select(documentationSelector.getDatasetListIsLoaded);
+        this.datasetList = store.select(documentationSelector.getDatasetList);
+        this.attributeListIsLoading = store.select(documentationSelector.getAttributeListIsLoading);
+        this.attributeListIsLoaded = store.select(documentationSelector.getAttributeListIsLoaded);
+        this.attributeList = store.select(documentationSelector.getAttributeList);
+    }
+
+    ngOnInit() {
+        this.store.dispatch(new documentationActions.RetrieveDatasetList());
+    }
+
+    getDatasetAttributes(dataset: Dataset, attributeList: Attribute[]): Attribute[] {
+        return attributeList
+            .filter(attribute => attribute.table_name === dataset.table_ref)
+            .sort((a, b) => a.id - b.id);;
+    }
+}
diff --git a/src/app/documentation/documentation.module.ts b/src/app/documentation/documentation.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..113382034ea9b6009368ca03b42f9a3ce4d4f56e
--- /dev/null
+++ b/src/app/documentation/documentation.module.ts
@@ -0,0 +1,26 @@
+import { NgModule } from '@angular/core';
+
+import { StoreModule } from '@ngrx/store';
+import { EffectsModule } from '@ngrx/effects';
+
+import { SharedModule } from '../shared/shared.module';
+import { DocumentationRoutingModule, routedComponents } from './documentation.routing';
+import { reducer } from './store/documentation.reducer';
+import { DocumentationEffects } from './store/documentation.effects';
+import { DocumentationService } from './store/documentation.service';
+
+@NgModule({
+    imports: [
+        SharedModule,
+        DocumentationRoutingModule,
+        StoreModule.forFeature('documentation', reducer),
+        EffectsModule.forFeature([ DocumentationEffects ])
+    ],
+    declarations: [
+        routedComponents
+    ],
+    providers: [
+        DocumentationService
+    ]
+})
+export class DocumentationModule { }
diff --git a/src/app/documentation/documentation.routing.ts b/src/app/documentation/documentation.routing.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1c157868b1e31836c836b1cd58e50ac23e8ecbfc
--- /dev/null
+++ b/src/app/documentation/documentation.routing.ts
@@ -0,0 +1,18 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { DocumentationComponent } from './containers/documentation.component';
+
+const routes: Routes = [
+    { path: 'documentation', component: DocumentationComponent }
+];
+
+@NgModule({
+    imports: [ RouterModule.forChild(routes) ],
+    exports: [ RouterModule ]
+})
+export class DocumentationRoutingModule { }
+
+export const routedComponents = [
+    DocumentationComponent
+];
diff --git a/src/app/documentation/store/documentation.action.spec.ts b/src/app/documentation/store/documentation.action.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..11cb06849d205431d67cf1f8b0bbde71277f12d8
--- /dev/null
+++ b/src/app/documentation/store/documentation.action.spec.ts
@@ -0,0 +1,45 @@
+import * as documentationActions from './documentation.action';
+import { Dataset, Attribute } from 'src/app/metamodel/model';
+import { DATASET_LIST, ATTRIBUTE_LIST } from 'src/settings/test-data';
+
+describe('[Documentation] Action', () => {
+    it('should create RetrieveDatasetList action', () => {
+        const action = new documentationActions.RetrieveDatasetList();
+        expect(action.type).toEqual(documentationActions.RETRIEVE_DATASET_LIST);
+    });
+
+    it('should create RetrieveDatasetListWip action', () => {
+        const action = new documentationActions.RetrieveDatasetListWip();
+        expect(action.type).toEqual(documentationActions.RETRIEVE_DATASET_LIST_WIP);
+    });
+
+    it('should create RetrieveDatasetListSuccess action', () => {
+        const datasetList: Dataset[] = DATASET_LIST;
+        const action = new documentationActions.RetrieveDatasetListSuccess(datasetList);
+        expect(action.type).toEqual(documentationActions.RETRIEVE_DATASET_LIST_SUCCESS);
+        expect(action.payload).toEqual(datasetList);
+    });
+
+    it('should create RetrieveDatasetListFail action', () => {
+        const action = new documentationActions.RetrieveDatasetListFail();
+        expect(action.type).toEqual(documentationActions.RETRIEVE_DATASET_LIST_FAIL);
+    });
+
+    it('should create RetrieveAttributeList action', () => {
+        const action = new documentationActions.RetrieveAttributeList(['toto']);
+        expect(action.type).toEqual(documentationActions.RETRIEVE_ATTRIBUTE_LIST);
+        expect(action.payload).toEqual(['toto']);
+    });
+
+    it('should create RetrieveAttributeListSuccess action', () => {
+        const attributeList: Attribute[] = ATTRIBUTE_LIST;
+        const action = new documentationActions.RetrieveAttributeListSuccess(attributeList);
+        expect(action.type).toEqual(documentationActions.RETRIEVE_ATTRIBUTE_LIST_SUCCESS);
+        expect(action.payload).toEqual(attributeList);
+    });
+
+    it('should create RetrieveAttributeListFail action', () => {
+        const action = new documentationActions.RetrieveAttributeListFail();
+        expect(action.type).toEqual(documentationActions.RETRIEVE_ATTRIBUTE_LIST_FAIL);
+    });
+});
diff --git a/src/app/documentation/store/documentation.action.ts b/src/app/documentation/store/documentation.action.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7cd0fc627d83606f1d9f189551e424d0221e774d
--- /dev/null
+++ b/src/app/documentation/store/documentation.action.ts
@@ -0,0 +1,62 @@
+import { Action } from '@ngrx/store';
+import { Dataset, Attribute } from 'src/app/metamodel/model';
+
+export const RETRIEVE_DATASET_LIST = '[Documentation] Retrieve Dataset List';
+export const RETRIEVE_DATASET_LIST_WIP = '[Documentation] Retrieve Dataset List WIP';
+export const RETRIEVE_DATASET_LIST_SUCCESS = '[Documentation] Retrieve Dataset List Success';
+export const RETRIEVE_DATASET_LIST_FAIL = '[Documentation] Retrieve Dataset List Fail';
+export const RETRIEVE_ATTRIBUTE_LIST = '[Documentation] Retrieve Attribute List';
+export const RETRIEVE_ATTRIBUTE_LIST_SUCCESS = '[Documentation] Retrieve Attribute List Success';
+export const RETRIEVE_ATTRIBUTE_LIST_FAIL = '[Documentation] Retrieve Attribute List Fail';
+
+
+export class RetrieveDatasetList implements Action {
+    readonly type = RETRIEVE_DATASET_LIST;
+
+    constructor(public payload: {} = null) { }
+}
+
+export class RetrieveDatasetListWip implements Action {
+    readonly type = RETRIEVE_DATASET_LIST_WIP;
+
+    constructor(public payload: {} = null) { }
+}
+
+export class RetrieveDatasetListSuccess implements Action {
+    readonly type = RETRIEVE_DATASET_LIST_SUCCESS;
+
+    constructor(public payload: Dataset[]) { }
+}
+
+export class RetrieveDatasetListFail implements Action {
+    readonly type = RETRIEVE_DATASET_LIST_FAIL;
+
+    constructor(public payload: {} = null) { }
+}
+
+export class RetrieveAttributeList implements Action {
+    readonly type = RETRIEVE_ATTRIBUTE_LIST;
+
+    constructor(public payload: string[]) { }
+}
+
+export class RetrieveAttributeListSuccess implements Action {
+    readonly type = RETRIEVE_ATTRIBUTE_LIST_SUCCESS;
+
+    constructor(public payload: Attribute[]) { }
+}
+
+export class RetrieveAttributeListFail implements Action {
+    readonly type = RETRIEVE_ATTRIBUTE_LIST_FAIL;
+
+    constructor(public payload: {} = null) { }
+}
+
+export type Actions
+    = RetrieveDatasetList
+    | RetrieveDatasetListWip
+    | RetrieveDatasetListSuccess
+    | RetrieveDatasetListFail
+    | RetrieveAttributeList
+    | RetrieveAttributeListSuccess
+    | RetrieveAttributeListFail;
diff --git a/src/app/documentation/store/documentation.effects.ts b/src/app/documentation/store/documentation.effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..01329f35553794c10d0f475854168ab1bd3ac990
--- /dev/null
+++ b/src/app/documentation/store/documentation.effects.ts
@@ -0,0 +1,106 @@
+import { Injectable } from '@angular/core';
+
+import { ToastrService } from 'ngx-toastr';
+import { Effect, Actions, ofType } from '@ngrx/effects';
+import { Store } from '@ngrx/store';
+import { of, Observable, forkJoin } from 'rxjs';
+import { map, tap, switchMap, withLatestFrom, catchError } from 'rxjs/operators';
+
+import * as fromRouter from '@ngrx/router-store';
+import * as fromDocumentation from './documentation.reducer';
+import * as documentationActions from './documentation.action';
+import * as utils from '../../shared/utils';
+import { DocumentationService } from './documentation.service';
+import { Dataset, Attribute } from 'src/app/metamodel/model';
+
+@Injectable()
+export class DocumentationEffects {
+    constructor(
+        private actions$: Actions,
+        private documentationService: DocumentationService,
+        private toastr: ToastrService,
+        private store$: Store<{
+            router: fromRouter.RouterReducerState<utils.RouterStateUrl>,
+            documentation: fromDocumentation.State
+        }>
+    ) { }
+
+    @Effect()
+    retrieveDatasetListAction$ = this.actions$.pipe(
+        ofType(documentationActions.RETRIEVE_DATASET_LIST),
+        withLatestFrom(this.store$),
+        switchMap(([action, state]) => {
+            if (state.documentation.datasetListIsLoaded) {
+                return of({ type: '[No Action] [Documentation] Dataset list is already loaded' });
+            } else {
+                return of(new documentationActions.RetrieveDatasetListWip());
+            }
+        })
+    );
+
+    @Effect()
+    retrieveDatasetListWipAction$ = this.actions$.pipe(
+        ofType(documentationActions.RETRIEVE_DATASET_LIST_WIP),
+        switchMap(_ =>
+            this.documentationService.retrieveDatasetList().pipe(
+                map((datasetList: Dataset[]) =>
+                    new documentationActions.RetrieveDatasetListSuccess(datasetList)),
+                catchError(() => of(new documentationActions.RetrieveDatasetListFail()))
+            )
+        )
+    );
+
+    @Effect({ dispatch: false })
+    retrieveDatasetListFailedAction$ = this.actions$.pipe(
+        ofType(documentationActions.RETRIEVE_DATASET_LIST_FAIL),
+        tap(_ => this.toastr.error('Loading Failed!', 'Dataset list loading failed'))
+    );
+
+    @Effect()
+    retrieveDatasetListSuccessAction$ = this.actions$.pipe(
+        ofType(documentationActions.RETRIEVE_DATASET_LIST_SUCCESS),
+        map(action => {
+            const retrieveDatasetListSuccessAction = action as documentationActions.RetrieveDatasetListSuccess;
+            const datasetList = retrieveDatasetListSuccessAction.payload;
+            const dnames: string[] = [];
+            datasetList.forEach(dataset => {
+                dnames.push(dataset.name);
+            });
+            return new documentationActions.RetrieveAttributeList(dnames);
+        })
+    );
+
+    @Effect()
+    retrieveAttributeListAction$ = this.actions$.pipe(
+        ofType(documentationActions.RETRIEVE_ATTRIBUTE_LIST),
+        switchMap((action) => {
+            const retrieveAttributetListAction = action as documentationActions.RetrieveAttributeList;
+            const dnames = retrieveAttributetListAction.payload;
+            let requests: Observable<any>[] = [];
+            dnames.forEach(dname => {
+                requests.push(this.documentationService.retrieveAttributetList(dname));
+            });
+            return forkJoin(requests);
+        }),
+        switchMap((data) => {
+            let attributes: Attribute[] = [];
+            data.forEach(d => {
+                attributes = [...attributes, ...d]
+            });
+            return of(new documentationActions.RetrieveAttributeListSuccess(attributes));
+        }),
+        catchError(_ => {
+          return of(new documentationActions.RetrieveAttributeListFail());
+        })
+    );
+    
+    @Effect({ dispatch: false })
+    retrieveAttributeListFailedAction$ = this.actions$.pipe(
+        ofType(documentationActions.RETRIEVE_DATASET_LIST_FAIL),
+        tap((action) => {
+            const retrieveAttributeListFailedAction = action as documentationActions.RetrieveAttributeListFail;
+            const dname = retrieveAttributeListFailedAction.payload;
+            this.toastr.error('Loading Failed!', 'Attribute list for dataset ' + dname + ' loading failed');
+        })
+    );
+}
diff --git a/src/app/documentation/store/documentation.reducer.spec.ts b/src/app/documentation/store/documentation.reducer.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d883352cf7bcd3d1c17fcefec471b7d8936ae88c
--- /dev/null
+++ b/src/app/documentation/store/documentation.reducer.spec.ts
@@ -0,0 +1,143 @@
+import * as fromDocumentation from './documentation.reducer';
+import * as documentationActions from './documentation.action';
+import { Dataset, Attribute } from 'src/app/metamodel/model';
+import { DATASET_LIST, DATASET, ATTRIBUTE_LIST } from 'src/settings/test-data';
+
+describe('[Documentation] Reducer', () => {
+    it('should return init state', () => {
+        const { initialState } = fromDocumentation;
+        const action = {} as documentationActions.Actions;
+        const state = fromDocumentation.reducer(undefined, action);
+
+        expect(state).toBe(initialState);
+    });
+
+    it('should set datasetListIsLoading to true', () => {
+        const { initialState } = fromDocumentation;
+        const action = new documentationActions.RetrieveDatasetListWip();
+        const state = fromDocumentation.reducer(initialState, action);
+
+        expect(state.datasetListIsLoading).toBeTruthy();
+        expect(state.datasetListIsLoaded).toBeFalsy();
+        expect(state.datasetList).toBeNull();
+        expect(state.attributeListIsLoading).toBeFalsy();
+        expect(state.attributeListIsLoaded).toBeFalsy();
+        expect(state.attributeList).toBeNull();
+        expect(state).not.toEqual(initialState);
+    });
+
+    it('should set datasetList, datasetListIsLoaded to true and datasetListIsLoading to false', () => {
+        const datasetList: Dataset[] = DATASET_LIST;
+        const initialState = { ...fromDocumentation.initialState, datasetListIsLoading: true };
+        const action = new documentationActions.RetrieveDatasetListSuccess(datasetList);
+        const state = fromDocumentation.reducer(initialState, action);
+
+        expect(state.datasetListIsLoading).toBeFalsy();
+        expect(state.datasetListIsLoaded).toBeTruthy();
+        expect(state.datasetList).toEqual(datasetList);
+        expect(state.attributeListIsLoading).toBeFalsy();
+        expect(state.attributeListIsLoaded).toBeFalsy();
+        expect(state.attributeList).toBeNull();
+        expect(state).not.toEqual(initialState);
+    });
+
+    it('should set datasetListIsLoading to false', () => {
+        const initialState = { ...fromDocumentation.initialState, datasetListIsLoading: true };
+        const action = new documentationActions.RetrieveDatasetListFail();
+        const state = fromDocumentation.reducer(initialState, action);
+
+        expect(state.datasetListIsLoading).toBeFalsy();
+        expect(state.datasetListIsLoaded).toBeFalsy();
+        expect(state.datasetList).toBeNull();
+        expect(state.attributeListIsLoading).toBeFalsy();
+        expect(state.attributeListIsLoaded).toBeFalsy();
+        expect(state.attributeList).toBeNull();
+        expect(state).not.toEqual(initialState);
+    });
+
+    it('should set attributeListIsLoading to true', () => {
+        const dataset: Dataset = DATASET;
+        const { initialState } = fromDocumentation;
+        const action = new documentationActions.RetrieveAttributeList([dataset.name]);
+        const state = fromDocumentation.reducer(initialState, action);
+
+        expect(state.datasetListIsLoading).toBeFalsy();
+        expect(state.datasetListIsLoaded).toBeFalsy();
+        expect(state.datasetList).toBeNull();
+        expect(state.attributeListIsLoading).toBeTruthy();
+        expect(state.attributeListIsLoaded).toBeFalsy();
+        expect(state.attributeList).toBeNull();
+        expect(state).not.toEqual(initialState);
+    });
+
+    it('should set datasetList, attributeListIsLoaded to true and attributeListIsLoading to false', () => {
+        const attributeList: Attribute[] = ATTRIBUTE_LIST;
+        const initialState = { ...fromDocumentation.initialState, attributeListIsLoading: true };
+        const action = new documentationActions.RetrieveAttributeListSuccess(attributeList);
+        const state = fromDocumentation.reducer(initialState, action);
+
+        expect(state.datasetListIsLoading).toBeFalsy();
+        expect(state.datasetListIsLoaded).toBeFalsy();
+        expect(state.datasetList).toBeNull();
+        expect(state.attributeListIsLoading).toBeFalsy();
+        expect(state.attributeListIsLoaded).toBeTruthy();
+        expect(state.attributeList).toEqual(attributeList);
+        expect(state).not.toEqual(initialState);
+    });
+
+    it('should set attributeListIsLoading to false', () => {
+        const initialState = { ...fromDocumentation.initialState, attributeListIsLoading: true };
+        const action = new documentationActions.RetrieveAttributeListFail();
+        const state = fromDocumentation.reducer(initialState, action);
+
+        expect(state.datasetListIsLoading).toBeFalsy();
+        expect(state.datasetListIsLoaded).toBeFalsy();
+        expect(state.datasetList).toBeNull();
+        expect(state.attributeListIsLoading).toBeFalsy();
+        expect(state.attributeListIsLoaded).toBeFalsy();
+        expect(state.attributeList).toBeNull();
+        expect(state).not.toEqual(initialState);
+    });
+
+    it('should get datasetListIsLoading', () => {
+        const action = {} as documentationActions.Actions;
+        const state = fromDocumentation.reducer(undefined, action);
+
+        expect(fromDocumentation.getDatasetListIsLoading(state)).toBeFalsy();
+    });
+
+    it('should get datasetListIsLoaded', () => {
+        const action = {} as documentationActions.Actions;
+        const state = fromDocumentation.reducer(undefined, action);
+
+        expect(fromDocumentation.getDatasetListIsLoaded(state)).toBeFalsy();
+    });
+
+    it('should get datasetList', () => {
+        const action = {} as documentationActions.Actions;
+        const state = fromDocumentation.reducer(undefined, action);
+
+        expect(fromDocumentation.getDatasetList(state)).toBeNull();
+    });
+
+    it('should get attributeListIsLoading', () => {
+        const action = {} as documentationActions.Actions;
+        const state = fromDocumentation.reducer(undefined, action);
+
+        expect(fromDocumentation.getAttributeListIsLoading(state)).toBeFalsy();
+    });
+
+    it('should get attributeListIsLoaded', () => {
+        const action = {} as documentationActions.Actions;
+        const state = fromDocumentation.reducer(undefined, action);
+
+        expect(fromDocumentation.getAttributeListIsLoaded(state)).toBeFalsy();
+    });
+
+    it('should get attributeList', () => {
+        const action = {} as documentationActions.Actions;
+        const state = fromDocumentation.reducer(undefined, action);
+
+        expect(fromDocumentation.getAttributeList(state)).toBeNull();
+    });
+});
diff --git a/src/app/documentation/store/documentation.reducer.ts b/src/app/documentation/store/documentation.reducer.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ff5b20a05f1103c2c9e656bfb054f9a1a2f93eeb
--- /dev/null
+++ b/src/app/documentation/store/documentation.reducer.ts
@@ -0,0 +1,77 @@
+import * as actions from './documentation.action';
+import { Dataset, Attribute } from 'src/app/metamodel/model';
+
+export interface State {
+    datasetListIsLoading: boolean;
+    datasetListIsLoaded: boolean;
+    datasetList: Dataset[];
+    attributeListIsLoading: boolean;
+    attributeListIsLoaded: boolean;
+    attributeList: Attribute[];
+}
+
+export const initialState: State = {
+    datasetListIsLoading: false,
+    datasetListIsLoaded: false,
+    datasetList: null,
+    attributeListIsLoading: false,
+    attributeListIsLoaded: false,
+    attributeList: null
+};
+
+export function reducer(state: State = initialState, action: actions.Actions): State {
+    switch (action.type) {
+        case actions.RETRIEVE_DATASET_LIST_WIP:
+            return {
+                ...state,
+                datasetListIsLoading: true
+            };
+        
+        case actions.RETRIEVE_DATASET_LIST_SUCCESS:
+            const datasetList = action.payload as Dataset[];
+            return {
+                ...state,
+                datasetList,
+                datasetListIsLoading: false,
+                datasetListIsLoaded: true
+            };
+        
+        case actions.RETRIEVE_DATASET_LIST_FAIL:
+            return {
+                ...state,
+                datasetListIsLoading: false
+            };
+        
+        case actions.RETRIEVE_ATTRIBUTE_LIST:
+            return {
+                ...state,
+                attributeListIsLoading: true
+            };
+        
+        case actions.RETRIEVE_ATTRIBUTE_LIST_SUCCESS:
+            const attributeList = action.payload as Attribute[];
+
+            return {
+                ...state,
+                attributeList,
+                attributeListIsLoading: false,
+                attributeListIsLoaded: true
+            };
+        
+        case actions.RETRIEVE_ATTRIBUTE_LIST_FAIL:
+            return {
+                ...state,
+                attributeListIsLoading: false
+            };
+
+        default:
+            return state;
+    }
+}
+
+export const getDatasetListIsLoading = (state: State) => state.datasetListIsLoading;
+export const getDatasetListIsLoaded = (state: State) => state.datasetListIsLoaded;
+export const getDatasetList = (state: State) => state.datasetList;
+export const getAttributeListIsLoading = (state: State) => state.attributeListIsLoading;
+export const getAttributeListIsLoaded = (state: State) => state.attributeListIsLoaded;
+export const getAttributeList = (state: State) => state.attributeList;
diff --git a/src/app/documentation/store/documentation.selector.spec.ts b/src/app/documentation/store/documentation.selector.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b1fdb8f0bb96c68f765e5b9f8ddccf49dffff7af
--- /dev/null
+++ b/src/app/documentation/store/documentation.selector.spec.ts
@@ -0,0 +1,39 @@
+import * as documentationSelector from './documentation.selector';
+import * as fromDocumentation from './documentation.reducer';
+
+describe('[Documentation] Selector', () => {
+    it('should get datasetIsLoading', () => {
+        const state = { documentation: { ...fromDocumentation.initialState }};
+        expect(documentationSelector.getDatasetListIsLoading(state)).toBeFalsy();
+    });
+    
+    it('should get datasetListIsLoaded', () => {
+        const state = { documentation: { ...fromDocumentation.initialState }};
+        expect(documentationSelector.getDatasetListIsLoaded(state)).toBeFalsy();
+    });
+    
+    it('should get datasetListIsLoaded', () => {
+        const state = { documentation: { ...fromDocumentation.initialState }};
+        expect(documentationSelector.getDatasetListIsLoaded(state)).toBeFalsy();
+    });
+    
+    it('should get datasetList', () => {
+        const state = { documentation: { ...fromDocumentation.initialState }};
+        expect(documentationSelector.getDatasetList(state)).toBeNull();
+    });
+    
+    it('should get attributeListIsLoading', () => {
+        const state = { documentation: { ...fromDocumentation.initialState }};
+        expect(documentationSelector.getAttributeListIsLoading(state)).toBeFalsy();
+    });
+    
+    it('should get attributeListIsLoaded', () => {
+        const state = { documentation: { ...fromDocumentation.initialState }};
+        expect(documentationSelector.getAttributeListIsLoaded(state)).toBeFalsy();
+    });
+    
+    it('should get attributeList', () => {
+        const state = { documentation: { ...fromDocumentation.initialState }};
+        expect(documentationSelector.getAttributeList(state)).toBeNull();
+    });
+});
diff --git a/src/app/documentation/store/documentation.selector.ts b/src/app/documentation/store/documentation.selector.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b904e9cedd7f0ec9ca0d72c65548745d93108e7d
--- /dev/null
+++ b/src/app/documentation/store/documentation.selector.ts
@@ -0,0 +1,35 @@
+import { createSelector, createFeatureSelector } from '@ngrx/store';
+
+import * as documentation from './documentation.reducer';
+
+export const getDocumentationState = createFeatureSelector<documentation.State>('documentation');
+
+export const getDatasetListIsLoading = createSelector(
+    getDocumentationState,
+    documentation.getDatasetListIsLoading
+);
+
+export const getDatasetListIsLoaded = createSelector(
+    getDocumentationState,
+    documentation.getDatasetListIsLoaded
+);
+
+export const getDatasetList = createSelector(
+    getDocumentationState,
+    documentation.getDatasetList
+);
+
+export const getAttributeListIsLoading = createSelector(
+    getDocumentationState,
+    documentation.getAttributeListIsLoading
+);
+
+export const getAttributeListIsLoaded = createSelector(
+    getDocumentationState,
+    documentation.getAttributeListIsLoaded
+);
+
+export const getAttributeList = createSelector(
+    getDocumentationState,
+    documentation.getAttributeList
+);
diff --git a/src/app/documentation/store/documentation.service.ts b/src/app/documentation/store/documentation.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eb19869543058596870ebb4cfe35b8b7f631f359
--- /dev/null
+++ b/src/app/documentation/store/documentation.service.ts
@@ -0,0 +1,22 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+
+import { environment } from '../../../environments/environment';
+import { Dataset, Attribute } from 'src/app/metamodel/model';
+import { Observable } from 'rxjs';
+
+@Injectable()
+export class DocumentationService {
+    API_PATH: string = environment.apiUrl;
+    instanceName: string = environment.instanceName;
+
+    constructor(private http: HttpClient) { }
+
+    retrieveDatasetList(): Observable<Dataset[]> {
+        return this.http.get<Dataset[]>(this.API_PATH + '/instance/' + this.instanceName + '/dataset');
+    }
+    
+    retrieveAttributetList(dname: string): Observable<Attribute[]> {
+        return this.http.get<Attribute[]>(this.API_PATH + '/dataset/' + dname + '/attribute');
+    }
+}
diff --git a/src/app/metamodel/model/attribute.model.ts b/src/app/metamodel/model/attribute.model.ts
index 4ee8b3431d71b05d1845284cfcf735ff057e1b07..af702198f6db2669cd608c7ee57ecd36f969d150 100644
--- a/src/app/metamodel/model/attribute.model.ts
+++ b/src/app/metamodel/model/attribute.model.ts
@@ -35,4 +35,5 @@ export interface Attribute {
     vo_size: number;
     id_criteria_family: number;
     id_output_category: number;
+    
 }
diff --git a/src/settings/settings.ts b/src/settings/settings.ts
index 4985cdaf40e6733786320a582ffec9c80010ab11..24bc1992be1040202cabbfbdd5bf65e3ce15fc4e 100644
--- a/src/settings/settings.ts
+++ b/src/settings/settings.ts
@@ -1,5 +1,5 @@
 export const VERSIONS = {
-    anisClient: '3.2',
+    anisClient: '3.3',
     anisServer: '3.2',
     anisAuth: '3.2'
 };
diff --git a/src/settings/test-data/attribute-list.ts b/src/settings/test-data/attribute-list.ts
index d0bdfbf788ac36ba6cb49ce112bc101a903eee64..a3fe9362f93f2a6c680518db8ff9142e20479014 100644
--- a/src/settings/test-data/attribute-list.ts
+++ b/src/settings/test-data/attribute-list.ts
@@ -4,7 +4,7 @@ export const ATTRIBUTE_LIST: Attribute[] = [
     {
         id: 1,
         name: 'name_one',
-        table_name: 'table_one',
+        table_name: 'table_1',
         label: 'label_one',
         form_label: 'form_label_one',
         description: 'description_one',
@@ -43,7 +43,7 @@ export const ATTRIBUTE_LIST: Attribute[] = [
     {
         id: 2,
         name: 'name_two',
-        table_name: 'table_two',
+        table_name: 'table_2',
         label: 'label_two',
         form_label: 'form_label_two',
         description: 'description_two',
diff --git a/src/settings/test-data/dataset.ts b/src/settings/test-data/dataset.ts
index c9d8eda11fb460f8ce697f673a89137aaaf31424..f4302a5060f07ae9c926f9a740aeebaf0a4ea387 100644
--- a/src/settings/test-data/dataset.ts
+++ b/src/settings/test-data/dataset.ts
@@ -2,7 +2,7 @@ import { Dataset } from 'src/app/metamodel/model';
 
 export const DATASET: Dataset = {
     name: 'cat_1',
-    table_ref: '',
+    table_ref: 'table_1',
     label: 'Cat 1',
     description: 'Description of cat 1',
     display: 10,