NestJS, MongoDB, Mongoose, GraphQL - Repeating myself describing a User or any model - mongodb

Edit: I implemented below, published it to GitHub for a user auth.
Edit based on comment: Can a DTO file be replaced by the classes that #nestjs/graphql generates based on GraphQL types? Can I create a NestJS / MongoDB / Mongoose / GraphQL app by generating these classes, then extending them for my MongoDB Schema. Then, after this question, any best practices opinions are welcomed, but the answer will be accepted that answers the above. Below is the original post:
What is the best way to describe a user model? Is it to define the graphQL types and use that to generate classes to replace dto files and import into Mongoose for the MongoDB schema? Below I'll explain what I'm doing, and what may work better. The number of files I have repeating myself doesn't seem to be scalable.
Here are the many ways I describe the same user:
users.types.graphql - GraphQL types, which contains create user inputs, update user inputs, etc. It contains things such as:
type Mutation {
createUser(createUserInput: CreateUserInput): User
}
input CreateUserInput {
username: String
email: String
password: String
}
type User {
_id: String!
username: String
email: String
password: String
}
user.interfaces.ts - Describes the user type, used by MongoDB Schema and my user.service.ts which contains:
export interface IUser {
email: string;
password: string;
username: string;
}
user.schema.ts - MongoDB Schema. Describes a user to Mongoose. It also extends the user interface in user.interfaces.ts and Document to expose instance methods for strict type checking (I can add .checkPassword to an IUserDocument):
export interface IUserDocument extends IUser, Document {
checkPassword(
password: string,
callback: (error?: Error, same?: boolean) => any,
): void;
}
export const UserSchema: Schema = new Schema(....
UserSchema.pre<IUserDocument>('save', function(next) {
UserSchema.methods.checkPassword = function(....
create-user.dto.ts and all kinds of dtos for each operation. These seem redundant with my GraphQl types file with describing inputs above. Here is a dto:
export class CreateUserDto {
readonly email: string;
readonly password: string;
readonly username: string;
}
I'm wondering what the best practice is to have one truth data to my user models. I'm thinking:
Use
GraphQLModule.forRoot({
definitions: {
path: join(process.cwd(), 'src/graphql.classes.ts'),
outputAs: 'class',
},
And use that for my interfaces and my dto files since it outputs:
export class CreateUserInput {
username?: string;
email?: string;
password?: string;
}
export class User {
_id: number;
username?: string;
email?: string;
password?: string;
}
Would dto files even be needed then? Does it matter they aren't readonly? Can I split up these classes into my respective folders (Users into user folder, products into products folder) automatically?
I'll post a public GitHub link when I'm done with a cookie cutter NestJS, MongoDB, Passport-JWT, GraphQL backend with user authentication so people have a reference (there is one out there that uses DTOs).

I stumbeld over your question having similar issues setting up some nest.js/graphQL/mongoose kind of API.
Coming from a laravel/REST/SQL background I was very annoyed with the redundancy and have no idea of how to build up some kind of generic CRUD standard, where adding new resources would be easy and fast e.g. using a node script to create the boilerplate code etc.
So one could focus on implementing the "new" functionalities instead of writing tons of code for always the same stuff.
I took a look into your GitHub project and it seems to me you already have optimized this in a way (e.g. user.schema is both for mongoose and graphql)? I started using the code first approach concerning graphQL but I think you are following the schema first approach there?
Would be very interested in exchanging some thoughts about that topic as there is not much to be found here or somewhere else concerning nest.js!

Related

Storing complex items in postgresql

I'm trying to implement a password manager and am having trouble structuring the data. I basically want to store a "Passwords" struct in the DB for each user. I was thinking about implementing composite types for "PasswordComponent" and doing it that way but it got messy. These are the basic structs:
struct PasswordComponent {
name: String,
email: String,
password: String, // before someone says something, passwords aren't stored in plaintext
websites: Vec<String>
}
struct Passwords {
id: i32,
passwords: Vec<PasswordComponent>
}
I feel like this is the wrong way to go about this and shouldn't be trying to use this like "object oriented" style in postgresql though

OpenAPI spec for reactive REST service using Quarkus

The jouney so far
I'm trying to get a reactive REST service up and running, following the official guide, using RESTEasy and JSON-B.
I also added support for OpenAPI for testing the service following this guide.
Both parts work on their own, the service properly returns the hard coded demo data. The Swagger UI shows the available routes and allows to invoke them.
However, it's not as smooth as I liked it to be...
From the simple, non-reactive routes, schemas have been correctly extracted:
Fruit:
type: object
properties:
description:
type: string
name:
type: string
But from the reactive routes, empty schemas have been extracted. For example, introducing
#GET
#Path("/{name}")
public Uni<Fruit> getOne(#PathParam(value = "name") String name) {
}
resulted in the schema:
UniFruit:
type: object
Is there a way to re-use the existing Fruit schema?
I tried annotating the route, but that didn't have any effect:
#GET
#Path("/{name}")
// #Schema(ref = "#/components/schemas/Fruit") // Nope...
// #Schema(ref = "Fruit") // Nope...
public Uni<Fruit> getOne(#PathParam(value = "name") String name) {
}
Ideally, I wouldn't want to annotate every reactive method separately anyways.
The question
Is there a way to configure project-wide to use the schema of T whenever a route returns Uni<T> or Multi<T>?
I have browsed the MicroProfile OpenAPI spec https://github.com/eclipse/microprofile-open-api/blob/master/spec/src/main/asciidoc/microprofile-openapi-spec.adoc but could not find a way to do the project wide schema change you request. You can do it for a given class, but here the class is Uni and you want different schemas depending on the parameterized type.
So the fix in Quarkus seems like the viable approach unless SmallRye OpenAPI (the implementation) has some specific ways to do this.
In the meantime, you can use the implementation attribute of the #Schema annotation:
#GET
#Path("/{name}")
#APIResponse(
content = #Content(mediaType = MediaType.APPLICATION_JSON,
schema = #Schema(implementation = Fruit.class)))
public Uni<Fruit> getOne(#PathParam(value = "name") String name) {
}

AWS Api Gateway Documentation - cant create schema

Today i got into swagger and swagger-ui to create the documentation of our API.
We are using AWS API Gateway with a Lambda function, since AWS is comming with an in-built option for documentation we are going with it.
Sadly, I am pretty limited with this option or I am doing it wrong.
As an example
responses:
'200':
description: 200 response
schema:
$ref: '#/definitions/Empty'
I canĀ“t create an alternative schema, nor im able to edit the existing /Empty schema.
Is there a solution for my problem?
For example
... to not use an schema and just write the whole response in there?
... to show me how to create an alternative schema?
EDIT
For me it seems like an AWS problem, not my swagger file in generall. If someone reads over this i added more informations.
It doesnt matter if i use "create Documetation Part" --> Type = Response (Body) or i go to Ressources --> Method which i want to set the Response (Body) --> Method Response and set the Respone Body to an Modell.
My Modell looks like this
{
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description" : "Example Promotion",
"title" : "Promotion",
"type" : "object",
"properties" : {
"Status" : { "type" : "string"}
}
}
}
It gives me no error, but if i go to "Publish Documentation" it seems to no put the Respone (Body) i set into the swagger.json on the Method Response part, nor on the Defenitions at the end of the file and set the schema path right.
I found it easier to not use $ref when I was starting out. After you have the knack how to write requests or response definitions, you can easily transition to referencing schemas using $ref.
What comes after schema statement? That depends on what you expect to be returned -- text, an array, a JSON object, or an array of JSON objects, etc. Typically it's the later two. So here is an example of each.
schema:
type: object
description: This is a JSON object
properties:
fullName:
type: string
age:
type: number
which defines: { fullName: "Jane Smith", age: 30 }
schema:
type: array
description: This is an array of JSON object
items:
type: object
properties:
carMake:
type: string
carModel:
type: string
which defines: [{ carMake: "Ford", carModel: "Mustang" } ... ]
Clone github's swagger-ui onto your computer and run it as a local server. Or you have free use of the SwaggerHub if you don't mind the API definition to be public (or, after a trial period, pay for your APIs to be private).
The specification has changed over the years, so its important to know whether you are dealing with swagger v2 or openapi v3. www.swagger.io has a good multi-page tutorial. And you can find several public API examples at the SwaggerHub website. I do not work for Smartbear, the originators of both the original swagger spec and swaggerhub tooling, but I've found them to be very helpful in the past. Some of their staff monitor this website and answer questions.
Good luck!

Building user database model in Firebase

so I already finished all of the actual app for this. I just need to setup the backend. I figured Firebase was the best solution since Parse is no longer a thing. What I wanted was:
Users with profiles - These profiles can be viewed by added friends but only edited (written) to by the actual profile owner.
So I read through the Firebase Docs and still cannot really figure out how to do this. They only have 1 Swift application example that does not do anything similar and the one Obj C twitter one, will not even build. All of their docs still have println for Swift which just makes me think it is not updated frequently.
Does anyone have any good examples / tutorials of this? I keep trying to search for things but nothing is as similar enough to what I want. I am more looking on how to setup the db for each user and access it rather actually using Firebase in Swift.
As I wrote in my comment to your question, this answer is based on what we do in a real social app Impether using Swift + Firebase.
Data structure
Let's assume that you want to store the following information for a single user:
email
username
name
followers - number of people who follow a particular user
following - number of people who a particular user follows
avatar_url - url of their avatar
bio - some additional text
Since in Firebase everything is stored a JSON objects, you can store the above structure under node with path like users/$userId, where $userId is Firebase User UID which is created for each registered user if you use simple email/password Firebase authorization.
Firebase email/password authorization is described in their docs:
https://www.firebase.com/docs/ios/guide/user-auth.html
https://www.firebase.com/docs/ios/guide/login/password.html
Notice that there are both Obj-C and Swift snippets. I find Firebase documentation really great as it helped me a lot when I was building our app.
For the purpose of this answer let's assume that we have user with username jack and Firebase User UID equal to jack_uid (in reality this will be a string generated by Firebase).
Then an example data for this user will be store under a path users/jack_uid and can look like this:
{
"email" : "jack#example.com",
"username" : "jack",
"name" : "Jack",
"followers" : 8,
"following" : 11,
"avatar_url" : "http://yourstoragesystem.com/avatars/jack.jpg",
"bio" : "Blogger, YouTuber",
}
Firebase email/password authorization works really well, but let's be honest, if user wants to sign in into the app, it's a lot better for him to use his username than his email he gave while he registering his account.
In order to do that, we decided to store a mapping from usernames to user ids. The idea is that if user inputs his username and password in a login form, we use that mapping to retrieve his user id and then we try to sign him in using his user id and provided password.
The mapping can be stored for example under a path username_to_uid and looks like this:
{
"sample_username_1": "firebase_generated_userid_1",
"sample_username_2": "firebase_generated_userid_2",
...
"jack": "jack_uid",
"sample_username_123": "firebase_generated_userid_123"
}
Then creating a profile may looks like this and it's done as soon as registration of a new account was successful (this snippet is very close to the exact code we use in the production):
func createProfile(uid: String, email: String,
username: String, avatarUrl: String,
successBlock: () -> Void, errorBlock: () -> Void) {
//path to user data node
let userDataPath = "/users/\(uid)"
//path to user's username to uid mapping
let usernameToUidDataPath = "/username_to_uid/\(username)"
//you want to have JSON object representing user data
//and we do use our User Swift structures to do that
//but you can just create a raw JSON object here.
//name, avatarUrl, bio, followers and following are
//initialized with default values
let user = User(uid: uid, username: username, name: "",
avatarUrl: avatarUrl, bio: "",
followers: 0, following: 0)
//this produces a JSON object from User instance
var userData = user.serialize()
//we add email to JSON data, because we don't store
//it directly in our objects
userData["email"] = email
//we use fanoutObject to update both user data
//and username to uid mapping at the same time
//this is very convinient, because either both
//write are successful or in case of any error,
//nothing is written, so you avoid inconsistencies
//in you database. You can read more about that technique
//here: https://www.firebase.com/blog/2015-10-07-how-to-keep-your-data-consistent.html
var fanoutObject = [String:AnyObject]()
fanoutObject[userDataPath] = userData
fanoutObject[usernameToUidDataPath] = uid
let ref = Firebase(url: "https://YOUR-FIREBASE-URL.firebaseio.com/images")
ref.updateChildValues(fanoutObject, withCompletionBlock: {
err, snap in
if err == nil {
//call success call back if there were no errors
successBlock()
} else {
//handle error here
errorBlock()
}
})
}
In addition to this you possibly want to store for each user a list of his followers and a separate list of users he follows. This can be done just by storing user ids at a path like followers/jack_uid, for example it can look like this:
{
"firebase_generated_userid_4": true,
"firebase_generated_userid_14": true
}
This is the way we store sets of values in our app. It very convenient, because it is really user to update it and check if some value is there.
In order to count the number of followers, we put this counter into user's data directly. This makes reading the counter very efficient. However, updating this counter requires using transactional writes and the idea is almost exactly the same as in my answer here: Upvote/Downvote system within Swift via Firebase
Read/write permissions
A part of your question is how to handle permissions to data you store. The good news is that Firebase is exceptionally good here. If you go to your Firebase dashboard there is a tab named Security&Rules and this is the place where you control permissions to your data.
What's great about Firebase rules is that they are declarative, which makes them very easy to use and maintain. However, writing rules in pure JSON is not the best idea since it's quite hard to control them when you want to combine some atomic rules into a bigger rule or your app simple grows and there are more and more different data you store in your Firebase database. Fortunately, Firebase team wrote Bolt, which is a language in which you can write all rules you need very easily.
First of all I recommend to read Firebase docs about Security, especially how does permission to a node influences permission for its children. Then, you can take a look at Bolt here:
https://www.firebase.com/docs/security/bolt/guide.html
https://www.firebase.com/blog/2015-11-09-introducing-the-bolt-compiler.html
https://github.com/firebase/bolt/blob/master/docs/guide.md
For example, we use rules for managing users data similar to this:
//global helpers
isCurrentUser(userId) {
auth != null && auth.uid == userId;
}
isLogged() {
auth != null;
}
//custom types, you can extend them
//if you want to
type UserId extends String;
type Username extends String;
type AvatarUrl extends String;
type Email extends String;
type User {
avatar_url: AvatarUrl,
bio: String,
email: Email,
followers: Number,
following: Number,
name: String,
username: Username,
}
//user data rules
path /users/{$userId} is User {
write() { isCurrentUser($userId) }
read() { isLogged() }
}
//user's followers rules
//rules for users a particular
//user follows are similar
path /followers/{$userId} {
read() { isLogged() }
}
path /followers/{$userId}/{$followerId} is Boolean {
create() { isCurrentUser($followerId) && this == true }
delete() { isCurrentUser($followerId) }
}
//username to uid rules
path /username_to_uid {
read() { true }
}
path /username_to_uid/{$username} is UserId {
create() { isCurrentUser(this) }
}
The bottom line is that you write rules you want using Bolt, then you compile them into JSON using Bolt compiler and then you deploy them into your Firebase, using command line tools or by pasting them into dashboard, but command line is way more efficient. A nice additional feature is that you can test your rules by using tools in Simulator tab in your dashboard.
Summary
For me Firebase is a great tool for implementing a system you want. However, I recommend to start with simple features and learn how to use Firebase in the first place. Implementing social app with functionality like for example Instagram is quite a big challenge, especially if you want to do it right :) It's very tempting to put all functionality there very quickly and Firebase makes it relatively easy to do, but I recommend to be patient here.
In addition, take your time and invest in writing tools. For example, we have two separated Firebase databases, one for production and second for testing, which is really important if you want to write unit and UI tests efficiently.
Also, I recommend building permission rules from the beginning. Adding them later may be tempting, but also quite overwhelming.
Last but not least, follow Firebase blog. They post regularly and you can be up to date with their latest features and updates - this is how I learnt how to use concurrent writes using fanout technique.

How to get the URI of a resource in Grails?

A very basic question. I want to provide URIs for some objects in my application. For example, a resource is available at:
http://localhost:8080/myapp/user/1
However, I'd like to serialize such a User object. This serialization should contain the public URI of the object itself. So for example, the model has a new method serializeToSomething, which would serialize:
id: 1
username: JohnDoe
email: johndoe#example.com
publicURI: http://localhost:8080/myapp/user/1
How can I let the model instance know of its URL?
Some notes:
This has to happen within the scope of the model, controller or service, and not within the view. Also I don't want to hardcode this.
See related question Can I use grails tag outside of GSP?
Basically you can use g.createLink in a controller or service, and it will return a string. So you can do something like:
def uri = g.createLink(controller: 'user', action: 'show', id: user.id, absolute: true)
Personally I ended up serializing 3 things: controller name, action name (typically "show") and id. Then I used these three in g.createLink when displaying deserialized objects in a View.
Sure, this won't work if you need deserialized object for external usage.
You can get the url in a controller using
request.forwardURI
A similar question has been asked before