NestJS serialization from snake_case to camelCase - serialization

I want to achieve automatic serialization/deserialization of JSON request/response body for NestJS controllers, to be precise, automatically convert snake_case request body JSON keys to camelCase received at my controller handler and vice versa.
What I found is to use class-transformer's #Expose({ name: 'selling_price' }), as on the example below (I'm using MikroORM):
// recipe.entity.ts
#Entity()
export class Recipe extends BaseEntity {
#Property()
name: string;
#Expose({ name: 'selling_price' })
#Property()
sellingPrice: number;
}
// recipe.controller.ts
#Controller('recipes')
export class RecipeController {
constructor(private readonly service: RecipeService) {}
#Post()
async createOne(#Body() data: Recipe): Promise<Recipe> {
console.log(data);
return this.service.createOne(data);
}
}
// example request body
{
"name": "Recipe 1",
"selling_price": 50000
}
// log on the RecipeController.createOne handler method
{ name: 'Recipe 1',
selling_price: 50000 }
// what I wanted on the log
{ name: 'Recipe 1',
sellingPrice: 50000 }
There can be seen that the #Expose annotation works perfectly, but going further I want to be able to convert it as the attribute's name on the entity: sellingPrice, so I can directly pass the parsed request body to my service and to my repository method this.recipeRepository.create(data). Current condition is the sellingPrice field would be null because there exists the selling_price field instead. If I don't use #Expose, the request JSON would need to be written on camelCase and that's not what I prefer.
I can do DTOs and constructors and assigning fields, but I think that's rather repetitive and I'll have a lot of fields to convert due to my naming preference: snake_case on JSON and database columns and camelCase on all of the JS/TS parts.
Is there a way I can do the trick cleanly? Maybe there's a solution already. Perhaps a global interceptor to convert all snake_case to camel_case but I'm not really sure how to implement one either.
Thanks!

You could use mapResult() method from the ORM, that is responsible for mapping raw db results (so snake_case for you) to entity property names (so camelCase for you):
const meta = em.getMetadata().get('Recipe');
const data = {
name: 'Recipe 1',
selling_price: 50000,
};
const res = em.getDriver().mapResult(data, meta);
console.log(res); // dumps `{ name: 'Recipe 1', sellingPrice: 50000 }`
This method operates based on the entity metadata, changing keys from fieldName (which defaults to the value based on selected naming strategy).

Related

Why am I seeing the _entities request in one service when the entity is native to another?

I'm working on implementing services compatible with Apollo GraphQL federation; my providing services are written in Lacinia (GraphQL library for Clojure).
I have one service that defines Users:
type User #key(fields: "id") {
id: String!
name: String!
}
type Query {
user_by_id(id:String!) : User
}
schema { query: Query }
and and a second that defines Products and extends Users:
type User #extends #key(fields: "id") {
id: String! #external
favorite_products: [Product]
}
type Product #key(fields: "upc") {
upc: String!
name: String!
price: Int!
}
type Query {
product_by_upc(upc: String!) : Product
}
schema { query: Query }
When I execute a query that spans services:
{
user_by_id(id: "me") {
id
name
favorite_products {
upc
name
price
}
}
}
I get a failure; the following request is sent to the products service:
INFO products.server - {:query "query($representations:[_Any!]!){_entities(representations:$representations){...on User{favorite_products{upc name price}}}}", :vars {:representations [{:__typename "User", :id "me"}]}, :line 52}
and that fails, because the products service shouldn't, as far as I know, have to provide the equivalent of __resolveReference for type User (which it extends); just type Product.
This is very unclear in the documentation and I'll experiment with providing a kind of stub reference resolver in Product for stubs of User.
Yes, indeed, you must provide the __resolveReference (or equivalent) for each type the service schema extends. In retrospect, it makes sense, as it provides the "kernel" of a raw value to be passed down the resolver tree.

Express custom GET/POST parameter binding nestjs / routing-controllers

I have a Controller (nestjs / routing-controllers) and I'm passing a GET request the following way: /collect?t=My-name
t is actually a full name which I can't change.
Bellow im injecting #QueryParams() data: CollectData, Im looking for a way (like java strong and .net) to tell the controller that fullName is actually t.
Something like
export interface CollectData{
#PropertyName('t')
fullName: string
}
I'm expecting fullName to represent the t variable.
#JsonController()
export class CollectController {
#Get('/collect')
collect(#QueryParams() data: CollectData){
return data.fullName;
}
}
You could use some sort of solution using the class-transformer library and the ValidationPipe given by Nest (it also does transformations!) and have your CollectionData class (use a class so that the data can be serialized after transpiling, interfaces go away in JavaScript) look like this:
// CollectData.ts
export class CollectData{
#Expose({ name: 'fullName' })
t: string
}
//Collect.controller.ts
#Controller() // Changed from JSONController to Controller
export class CollectController {
#Get('/collect')
collect(#Query(new ValidationPipe({ tranform: true }) data: CollectData){ //Changed from QueryParams to Query
return data.fullName;
}
}
OR in your main.ts add the app.useGlobalPipes(new ValidationPipe({ tranform: true }) to set the validation pipe to run against all your incoming requests

Import variables into aurelia-dialog view-model or view

Is there a way to import additional variables/data from the dialog-service to the controller?
For example I have an array of possible options in a form of my app-view. I fetch the data via an API from a server.
I'd like to edit an entry with an aurelia-dialog and don't want to fetch the data again to avoid unnecessary traffic in my app.
How can i pass the array additionally to the model. Pack it all together in an Object and unwrap it in the controller?
As far as I know the activate-method of the controller only takes one argument, doesn't it?
Thank you
Isn't the example in the repository exactly what you are looking for?
The person attribute is passed to the dialog service via the settings object (model: this.person). This may be data you fetched from the server. As you mentioned, you can of course add multiple objects to the model as well which will be available in the activate() method of your dialogs vm.
import {EditPerson} from './edit-person';
import {DialogService} from 'aurelia-dialog';
export class Welcome {
static inject = [DialogService];
constructor(dialogService) {
this.dialogService = dialogService;
}
person = { firstName: 'Wade', middleName: 'Owen', lastName: 'Watts' };
submit(){
this.dialogService.open({ viewModel: EditPerson, model: this.person}).then(response => {
if (!response.wasCancelled) {
console.log('good - ', response.output);
} else {
console.log('bad');
}
console.log(response.output);
});
}
}

How to retrieve stores based on the store id

I am creating a Memory store as
var someData = [
{id:1, name:"One"},
{id:2, name:"Two"}
];
store = new Memory({
data: someData,
id:”userStore”
});
I was wondering if there is a way to query the Memory store to return the store instance by id. Like
var storePresent = Memory.getById(“userStore”)
something similar to
dijit.registry.byId();
that returns the instance of dijit specified by id
To my knowledge, there is not a store registry as you describe. You will need to code this yourself in your application's controller code.
A store is a simple Object.
You could:
Pass the store manually around your code.
Code a registry AMD module (caution, here be dragons).
The only exception to this rule is if you're already using dojox/app as your controller layer. That has some named store abilities. If not, I would not recommend refactoring to use it.
There's no build-in static repository of memory stores in module dojo/store/Memory. If you need something like that, the easiest way is to write custom factory of memory stores that will hold the static references to all stores that are created:
define(["dojo/store/Memory"], function(Memory){
var repository = {}
return {
getStore: function(id) {
return repository[id]
},
createStore: function(id, params) {
var memory = new Memory(params)
repository[id] = memory
return memory
}
}
});
The usage:
require(["modules/MemoryRepository"], function(MemoryRepository) {
MemoryRepository.createStore("userStore", {data: someData})
...
var userStore = MemoryRepository.getStore("userStore")
})
If you are to create a lot of stores on demand, you should think of deregistering them (removing the references from the factory) as well. Memory issues are probably the reason something like that is not provided out-of-the-box.
Like the other answerers already said, there's no specific repository or registry for stores. However, the dijit/registry can be used to store the reference as well by using the dijit/registry::add() function, for example:
// Add to registry
registry.add(new Memory({
id: "userStore",
data: [{
name: "Smith",
firstname: "John"
}, {
name: "Doe",
firstname: "John"
}]
}));
Then you can retrieve it by using the dijit/registry::byId() function, for example:
// Query the store by using the registry
var person = registry.byId("userStore").query({
firstname: "John"
}).forEach(function(person) {
console.log(person.firstname + " " + person.name);
});
A full example can be found on JSFiddle: http://jsfiddle.net/mn94f/

Serialize ember-data models into query-params

I want to bind a controller property that contains an ember-data object to a query string param. I'm using 1.6.0-beta.1+canary with the following in my controller.
export
default Ember.Controller.extend({
queryParams: ['project'],
project: null
});
I can't find a way to serialize/deserialize the ember-data object (project) to/from the url though. Is it possible yet?
I'm not sure why but it seems that the ember team don't want to support serialization of query-params. The best suggestion seems to be to do use a computed property like this:
App.MyController = Ember.ObjectController.extend({
queryParams: ['recordId']
recordId: null,
record: function(key, value) {
if (arguments.length > 1) {
//handle set by setting recordId
this.set('recordId', value.get('id');
}
return this.store.find('record', this.get('recordId'));
}.property('recordId')
});
It's workable but seems cludgy to say the least...