Woodshop
Search…
Import data from a CSV file
This example shows you how to create a Smart Action "Import data" to import data from a CSV file.
Forest Admin natively supports data creation but it’s sometimes more efficient to simply import it.

Requirements

    An admin backend running on forest-express-sequelize/forest-express-mongoose
    bluebird npm package
    parse-data-uri npm package
    csv npm package

How it works

Directory: /models

This directory contains the products.js file where the model is declared.
Sequelize
Mongoose
/models/products.js
1
2
module.exports = (sequelize, DataTypes) => {
3
const { Sequelize } = sequelize;
4
const Products = sequelize.define('products', {
5
price: {
6
type: DataTypes.INTEGER,
7
},
8
label: {
9
type: DataTypes.STRING,
10
},
11
picture: {
12
type: DataTypes.STRING,
13
},
14
...
15
}, {
16
tableName: 'products',
17
underscored: true,
18
schema: process.env.DATABASE_SCHEMA,
19
});
20
21
Produtcs.associate = (models) => {
22
};
23
24
return Products;
25
};
Copied!
/models/products.js
1
const mongoose = require('mongoose');
2
3
const schema = mongoose.Schema({
4
'price': Integer,
5
'label': String,
6
'picture': String,
7
...
8
}, {
9
timestamps: false,
10
});
11
12
module.exports = mongoose.model('products', schema, 'products');
Copied!

Directory: /forest

This directory contains the products.js file where the Smart Action Import datais declared.
Sequelize
Mongoose
/forest/products.js
1
const { collection } = require('forest-express-sequelize');
2
const models = require('../models');
3
4
collection('products', {
5
actions: [{
6
name: 'Import data',
7
endpoint: '/forest/products/actions/import-data',
8
type: 'global',
9
fields: [{
10
field: 'CSV file',
11
description: 'A semicolon separated values file stores tabular data (numbers and text) in plain text',
12
type: 'File',
13
isRequired: true
14
}, {
15
field: 'Type',
16
description: 'Specify the product type to import',
17
type: 'Enum',
18
enums: ['phone', 'dress', 'toy'],
19
isRequired: true
20
}]
21
}],
22
23
// ...
24
});
Copied!
/forest/products.js
1
const { collection } = require('forest-express-mongoose');
2
const models = require('../models');
3
4
Liana.collection('products', {
5
actions: [{
6
name: 'Import data',
7
endpoint: '/forest/products/actions/import-data',
8
type: 'global',
9
fields: [{
10
field: 'CSV file',
11
description: 'A semicolon separated values file stores tabular data (numbers and text) in plain text',
12
type: 'File',
13
isRequired: true
14
}, {
15
field: 'Type',
16
description: 'Specify the product type to import',
17
type: 'Enum',
18
enums: ['phone', 'dress', 'toy'],
19
isRequired: true
20
}]
21
}],
22
23
// ...
24
});
Copied!

Directory: /routes

This directory contains the products.js file where the implementation of the route is handled. The POST /forest/actions/import-data API call is triggered when you click on the Smart Action in the Forest UI.
The CSV file passed into the body of the API call is serialized using a base64 encoding Data URI scheme.
To deserialize the base64 encoded CSV file, we use the NPM package parse-data-uri. We also use the csv parser NPM package to iterate over each line of the CSV file.
You can find a sample CSV file we use here to feed our products table on the Live demo Github repository.
You may find below the coding examples you need to make this Smart action work:
Sequelize
Mongoose
/routes/products.js
1
const P = require('bluebird');
2
const express = require('express');
3
const router = express.Router();
4
const faker = require('faker');
5
const parseDataUri = require('parse-data-uri');
6
const csv = require('csv');
7
const models = require('../models');
8
9
//...
10
11
router.post('/products/actions/import-data',
12
(req, res) => {
13
let parsed = parseDataUri(req.body.data.attributes.values['CSV file']);
14
let productType = req.body.data.attributes.values['Type'];
15
16
csv.parse(parsed.data, { delimiter: ';' }, function (err, rows) {
17
if (err) {
18
res.status(400).send({
19
error: `Cannot import data: ${err.message}` });
20
} else {
21
return P
22
.each(rows, (row) => {
23
// Random price for the example purpose. In a real situation, the price
24
// should certainly be available in the CSV file.
25
let price = faker.commerce.price(5, 1000) * 100;
26
27
return models.products.create({
28
label: row[0],
29
price: price,
30
picture: row[1]
31
});
32
})
33
.then(() => {
34
res.send({ success: 'Data successfuly imported!' });
35
});
36
}
37
});
38
});
39
40
//...
41
42
module.exports = router;
Copied!
/routes/products.js
1
const P = require('bluebird');
2
const express = require('express');
3
const router = express.Router();
4
const faker = require('faker');
5
const parseDataUri = require('parse-data-uri');
6
const csv = require('csv');
7
const models = require('../models');
8
9
...
10
11
router.post('/products/actions/import-data',
12
(req, res) => {
13
let parsed = parseDataUri(req.body.data.attributes.values['CSV file']);
14
let productType = req.body.data.attributes.values['Type'];
15
16
csv.parse(parsed.data, { delimiter: ';' }, function (err, rows) {
17
if (err) {
18
res.status(400).send({
19
error: `Cannot import data: ${err.message}` });
20
} else {
21
return P
22
.each(rows, (row) => {
23
// Random price for the example purpose. In a real situation, the price
24
// should certainly be available in the CSV file.
25
let price = faker.commerce.price(5, 1000) * 100;
26
27
return models.products.create({
28
label: row[0],
29
price: price,
30
picture: row[1]
31
});
32
})
33
.then(() => {
34
res.send({ success: 'Data successfuly imported!' });
35
});
36
}
37
});
38
});
39
40
...
41
42
module.exports = router;
Copied!

Uploading large files

For large file uploads, you should add an option in your Express Server in your app.js file:
1
app.use(bodyParser.urlencoded({ extended: true ,limit: '50mb' }));
2
app.use(bodyParser.json({ limit: '50mb' }));
Copied!
Last modified 8mo ago