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

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

Related

NestJS serialization from snake_case to camelCase

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).

pass data to another route without messing with url

DISCLAIMER: I'm a noob.. sorry
Say I have 2 different components that are siblings:
comp1 and comp2
I wish to route from comp1 to comp2 with a bunch of data. How can I achieve this without getting a fugly url-bar containing everything?
I've tried using a separate class, lets call it DataTransmitter:
data-transmitter.js:
export class DataTransmitter {
constructor() {
this.val= "a";
}
}
comp1.js:
import { DataTransmitter } from './data-transmitter';
#inject(DataTransmitter)
export class comp1{
constructor(DataTransmitter){
this.DataTransmitter = DataTransmitter;
}
someMethod(){
this.DataTransmitter.val = "b";
console.log('comp1: ' + this.DataTransmitter.val);
}
}
comp2.js:
import { DataTransmitter } from './data-transmitter';
#inject(DataTransmitter)
export class comp2{
constructor(DataTransmitter){
this.DataTransmitter = DataTransmitter;
}
someMethod(){
console.log('comp2: ' + this.DataTransmitter.val);
}
}
This gives me the output:
comp1: b
comp2: a
I've also tried messing around with EventAggregator, but no success.
Is there some way of routing with parameters WITHOUT having a url that looks like site/comp2?data=stuff&things=otherstuff&params=values&more=etc?
You absolutely want to use a singleton class and then inject it inside of whatever components you need your data. The link that Gaby posted is definitely what you want to do.
The reason your posted code does not work is because you're attempting to use the inject decorator, but you're not importing it. Please see this working example of what you are trying to do on Gist.run here. I have two components, you can click to route between them and set the value. You'll notice the set value remains when you navigate back and forth.

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);
});
}
}

Authentication views for Laravel 5.1

Laravel 5.1 has just been released, I would like to know how could I tell the AuthController to get the login & register view from a custom directory? the default is: resources/views/auth...
The trait AuthenticateAndRegisterUsers only has this:
trait AuthenticatesAndRegistersUsers
{
use AuthenticatesUsers, RegistersUsers {
AuthenticatesUsers::redirectPath insteadof RegistersUsers;
}
}
The code you're showing there only fills one function: it tells our trait to use the redirectPath from the AuthenticatesUsers trait rather than the one from RegistersUsers.
If you check inside the AuthenticatesUsers trait instead, you will find a getLogin() method. By default, this one is defined as
public function getLogin()
{
return view('auth.login');
}
All you have to do to get another view is then simply overwriting the function in your controller and returning another view. If you for some reason would like to load your views from a directory other than the standard resources/Views, you can do so by calling View::addLocation($path) (you'll find this defined in the Illuminate\View\FileViewFinder implementation of the Illuminate\View\ViewFinderInterface.
Also, please note that changing the auth views directory will do nothing to change the domain or similar. That is dependent on the function name (as per the definition of Route::Controller($uri, $controller, $names=[]). For more details on how routing works, I'd suggest just looking through Illuminate\Routing\Router.
for those who is using laravel 5.2, you only need to override property value of loginView
https://github.com/laravel/framework/blob/5.2/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php
public function showLoginForm()
{
$view = property_exists($this, 'loginView')
? $this->loginView : 'auth.authenticate';
if (view()->exists($view)) {
return view($view);
}
return view('auth.login');
}
so to override the login view path, you only need to do this
class yourUserController {
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
.....
protected $loginView = 'your path';
}

ember data: customize serializer used by toJSON

In my ember-data-using application I have an application serializer (mainly to take care of polymorphic relationships properly). However, its not used when I call toJSON() on a model instance, its not used -- rather the default JSONSerializer is used instead.
How can I customize the serializer used by toJSON()?
First of all i hope i understood properly your question...
Well, If you want to get the same result as in your ApplicationSerializer just call this.serialize() in your model.
If you want absolutely use the toJSON method without overwriting the default DS.Model of your whole app you can still make something like that :
App.CustomModel = DS.Model.extend({
toJSON: function(){
return this.serialize();
}
});
and then make all the concerned model extend this CustomModel
App.Color = App.CustomModel.extend({
name : DS.attr("string")
});
here is a working jsbin
In case you are looking to customize toJSON output for a particular model, you can do the following in your models/user.js file:
DS.Model.extend({
toJSON: function(options){
let json = this._super(options);
// Do something with json
return json;
}
});
Magic of Ember Object structure. In my case, I had to remove a property from toJSON response if it doesn't have value and this worked.

Categories