Validation().dataMapper()
Sets a data mapper function to map request body values with form fields.
Syntax
Validation().dataMapper(mappingFunction)
Parameters
-
mappingFunction
A function to add that will be called with the following arguments:-
req
A request object. -
form
A form object created with theValidation.profile()
method.
-
Return value
The Validation
object.
Exceptions
-
If anything other than a function is passed in the corresponding error will be thrown.
-
If invoked on a
Validation
which was not created with theValidation.profile()
method, throws the corresponding error.
Description
The main purpose of this method is to provide a way of mapping a request object’s body with form fields specified in the Validation.profile()
method in cases when they do not correspond to each other when a Validation
is used as a middleware function.
Examples
Validating file metadata before uploading to S3 storage
In this example the Validation().dataMapper()
method is used to map the values passed as URL search parameters to the form fields created with the Validation.profile()
method.
The project can be downloaded here.
Directorypublic
Directorylibs
- isomorphic-validation.mjs the library file is copied and imported from here to be available on the client side without using a module bundler
- index.html
- index.js the main frontend script
- validation.js file size and type validation code
- config.js a config for S3 storage
- index.js the main backend script
- repository.js a mock repository API
- …
import express from 'express';import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';import { getSignedUrl } from '@aws-sdk/s3-request-presigner';import { bucketName, endpoint, accessKeyId, secretAccessKey } from "./config.js";import uploadValidation from './public/validation.js';import * as repository from './repository.js';
const s3client = new S3Client({ region: 'REGION', endpoint, credentials: { accessKeyId, secretAccessKey },});
const app = express();
app.use(express.static('public'));
uploadValidation.dataMapper((req, form) => { Object.assign(form.file.files[0], req.query);});
app.get('/get-signed-url', uploadValidation, async (req, res, next) => { if (req.validationResult.isValid) { const { type, size } = req.query;
// create a file record in the database const id = await repository.create({ type, size, uploaded: false });
const putObjCmd = new PutObjectCommand({ Bucket: bucketName, Key: id, ContentType: type, ContentLength: size, });
res.json({ url: await getSignedUrl(s3client, putObjCmd, { expiresIn: 60 }), id, });
} else { res.json({ error: 'The file is inappropriate.' }); }
});
app.get('/image-uploaded/:id', async (req, res) => { // mark the file record as permanent (successfully uploaded to the s3 storage) const { id } = req.params; const ok = await repository.update(id, { uploaded: true }); res.json(ok);});
app.listen(8080, () => { console.log('listening port 8080...')});
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Test image upload to S3 storage</title> </head> <body> <form method="dialog" name="upload"> <input type="file" name="file"> <input type="button" name="uploadBtn" value="Upload" disabled> </form> <script type="module" src="index.js"></script> </body></html>
import uploadValidation from "./validation.js";
// UI side effects
const enableElement = (el) => () => el.disabled = false;const disableElement = (el) => () => el.disabled = true;
const indicateStart = (el) => { el.disabled = true; el.value = `${el.value} ⏳`};
const indicateEnd = (el) => (res) => { el.value = res ? `${el.value.split(' ')[0]} ✔` : `${el.value.split(' ')[0]} ❌` ;};
const indicateError = (el) => (err) => { el.disabled = false; el.value = `${el.value.split(' ')[0]} ❌`; el.insertAdjacentHTML('afterend', `❌ ${err.message}`)};
const uploadFile = (fileInput) => ({ target }) => { const file = fileInput.files[0]; const { size, type } = file;
// get signed url const getSignedUrl = (size, type) => fetch(`/get-signed-url?size=${size}&type=${type}`) .then(res => res.json());
// make a PUT request to S3 const uploadToS3 = (file) => ({ id, url }) => fetch(url, { method: 'PUT', body: file }) .then(res => ({ res, id }));
// notify the server about the image being successfuly uploaded const uploaded = ({ res, id }) => fetch(`/image-uploaded/${id}`) .then(res => res.json());
indicateStart(target);
getSignedUrl(size, type) .then(uploadToS3(file)) .then(uploaded) .then(indicateEnd(target)) .catch(indicateError(target)) .catch(console.error);};
const form = document.upload;
uploadValidation .valid(enableElement(form.uploadBtn)) .invalid(disableElement(form.uploadBtn));
uploadValidation.constraints.forEach(validator => { const constraint = document.createElement('div');
form.appendChild(constraint);
validator .valid(() => { constraint.innerText = ''; }) .invalid(() => { constraint.innerText = `🚫 ${validator.msg}`; });});
form.addEventListener('change', uploadValidation);form.uploadBtn.addEventListener('click', uploadFile(form.file));
import { Validation } from './libs/isomorphic-validation.mjs';
// predicate functionsconst isFileLargerThan = (number) => ({size}) => size > number;const isFileSmallerThan = (number) => ({size}) => size < number;const isImage = ({type}) => type === 'image/jpeg' || type === 'image/png';
const fileValidation = Validation({}, { path: 'files.0', initValue: {} }) .constraint( isFileLargerThan(200), { msg: 'Must not be an empty file.' }, ) .constraint( isFileSmallerThan(1000001), { msg: 'Must be smaller than 1MB.' }, ) .constraint( isImage, { msg: 'Must be a .png or .jpeg image.'}, );
const [, uploadValidation] = Validation.profile( '[name=upload]', ['file'], [fileValidation]);
export default uploadValidation;