node-convict icon indicating copy to clipboard operation
node-convict copied to clipboard

Loading an object config value fails if format isn't specified explicitly

Open myndzi opened this issue 8 years ago • 5 comments

I'm not sure what you want to do about this one, really. Most of the time, the type of a value is inferred from the default value if it's not specified. If that type is an object, e.g.:

var conf = convict({
  foo: {
    default: { foo: 'bar' }
  }
});

and you try to load some data...

conf.load({
  foo: { baz: 'quux' }
});

... it succeeds. But if that data contains deeper properties...

conf.load({
  foo: {
    bar: { baz: 'quux' }
  }
});

... you get an error (cannot access property of undefined)

If you change the schema to specify format: Object, the error does not occur.

The weird way convict mixes configuration schema and data may mean that there's no clean solution, but if the alternative is "fail with a difficult to comprehend error", it seems likely that simply inferring the type of an object property to be Object will make the error go away as well as make convict more useful.

Full failing test case:

'use strict';

var convict = require('convict');

var conf = convict({
  foo: {
    default: { some: 'data' }
  }
});
conf.load({
  foo: {
    bar: {
      baz: 'quux'
    }
  }
});

myndzi avatar Mar 23 '16 21:03 myndzi

Thanks for reporting this! I'll investigate when I can.

madarche avatar Apr 11 '16 14:04 madarche

Same problem is exposed when trying to define custom type accepting object value

    convict.addFormat({
        name: 'custom',
        validate: (val) => {
            console.error('validate called', val);
        },
        coerce: (val) => {
            console.error('coerce called', val);
            return val;
        }
    });

    const conf = convict({
        foo: {
            format: 'custom',
            default: {},
        }
    });


    conf.load({
        foo: {
            bar: { baz: 'quux' }
        }
    });

Xerkus avatar Sep 23 '16 22:09 Xerkus

Any updates? I have the same problem as @Xerkus

yurtaev avatar Oct 31 '17 12:10 yurtaev

Not reproductible anymore, your code don't throw an error.

code
var convict = require('convict');

var conf = convict({
  foo: {
    default: { some: 'data' }
  }
});
conf.load({
  foo: {
    bar: {
      baz: 'quux'
    }
  }
});

conf.validate();


convict.addFormat({
    name: 'custom',
    validate: (val) => {
        console.error('validate called', val);
    },
    coerce: (val) => {
        console.error('coerce called', val);
        return val;
    }
});

const conf = convict({
    foo: {
        format: 'custom',
        default: {},
    }
});


conf.load({
    foo: {
        bar: { baz: 'quux' }
    }
});
conf.validate();

A-312 avatar Jan 06 '20 22:01 A-312

I still get an error when using a function as the format:

import convict from "convict";

interface Foo {
  a: { b: number };
}

export function foo(val: any): asserts val is Foo {
  if (typeof val?.a?.b !== "number") {
    throw new Error();
  }
}

const config = convict({
  foo: {
    format: foo,
    default: {
      a: { b: 1 },
    },
  },
});

config.load({ foo: { a: { b: 2 } } });

const f = config.get("foo");

console.dir(f);

awlayton avatar May 14 '21 21:05 awlayton