json-api icon indicating copy to clipboard operation
json-api copied to clipboard

How to work around `id` and `type` field names?

Open jacobq opened this issue 5 years ago • 4 comments

I have some Mongoose models/Schema that include id and/or type keys. If I pass these models to JSON_API.dbAdapters.Mongoose(...) and then use that to set up JSON_API.ResourceTypeRegistry it runs fine until a request comes in. Then, if I have debug logging enabled, I can see json-api:warn Errors converted to json-api Result Error: typeandid cannot be used as field names. How can I handle this situation? Perhaps there's a way I can tell json-api that these fields should be remapped/renamed somehow (e.g. type -> typeField)?

jacobq avatar May 17 '20 03:05 jacobq

Looking through the source, it seems like I should be able to pass a custom queryTransform somewhere.... There's an example where it's done with Front.customAPIRequest, but could I still use something like app.get('/:type(users|posts|...)', Front.apiRequest) or do I need to split the "odd one" out?

jacobq avatar May 17 '20 03:05 jacobq

Tried a hack like the following, but this approach doesn't seem feasible.

class ModMongooseAdapter extends JSON_API.dbAdapters.Mongoose {
  constructor(models, toTypeName, idGenerator) {
    super(models, toTypeName, idGenerator);
    log('ModMongooseAdapter constructor', ...arguments);
  }

  mapReservedFieldNames(doc) {
    const reservedNames = ['id', 'type'];
    const docProps = {};
    for (const fieldName of Object.keys(doc.schema.paths).filter(key => !key.startsWith('__'))) {
      if (reservedNames.includes(fieldName)) {
        const newFieldName = `${fieldName}Field`;
        docProps[newFieldName] = doc[fieldName];
      } else {
        docProps[fieldName] = doc[fieldName];
      }
    }
    // This part doesn't quite work since we don't actually want to change the underlying data
    // plus the Model/Schema still lists `id` and `type` and we still end up with
    // "Error: Cannot construct a ResourceSet with missing data"`
    const modDoc = doc.overwrite({
      ...docProps
    });
    log(`mapReservedFieldNames returning`, modDoc);
    return modDoc;
  }

  docsToResourceData(docs, isPlural, fields) {
    log('docsToResourceData', ...arguments);
    const modDocs = docs.map(this.mapReservedFieldNames);
    super.docsToResourceData(modDocs, isPlural, fields);
  }

  docToResource(doc, fields) {
    log('docToResource', doc, fields);
    const modDoc = this.mapReservedFieldNames(doc);
    return super.docToResource(modDoc, fields);
  }
}

I'm going to try creating alternate schema/models with something like a virtual to get id and type under alternate names. I feel dumb for being so stuck here, but this is a big problem for me.

jacobq avatar May 17 '20 19:05 jacobq

I honestly haven't looked at the code for this library in a long time, so I'd have to refresh my memory to be able to help. I'll try to take a look soonish

ethanresnick avatar May 18 '20 17:05 ethanresnick

OK. For now I just made a branch with models having different names, e.g. foo.type becomes foo.fooType. This seems like something that shouldn't be all that hard though, and I'd be surprised if it's uncommon as Mongoose doesn't impose requirements about using id or type in Schema.

jacobq avatar May 18 '20 18:05 jacobq