In SailsJS you can define a model and provide attributes for it. This framework also provides you with a Web App code generator, which includes a model: User.js.
Many of the generated attributes for this user have an option called example, like:
email: {
type: 'string',
required: true,
unique: true,
isEmail: true,
maxLength: 200,
example: 'mary.sue#example.com'
},
But Sails documentation about attributes does not mention this 'example' option. Does it have another use besides of just hinting the developer about the attribute's use?
No other use, just a hint of what this field is intended for.
Related
Let's say I am building a web app (using React, in case that matters) where someone can create a new Widget. Some Widget properties have default values defined in data models in the API. These default values need to be displayed in the UI form for creating the new Widget.
I'd like to avoid duplicating these defaults in the app versus the API data models. Here is a sample data model as defined in the API:
Widget
_id
name
messageTextColor (default #999999)
bodyBackgroundColor (default #FFFFFF)
someBooleanValue (default true)
someOtherBooleanValue (default false)
... and so on
There are over a dozen settings, and many have default values.
I could duplicate these defaults in my UI app:
class EditWidgetPage extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
messageTextColor: '#999999',
bodyBackgroundColor: '#FFFFFF',
someBooleanValue: true,
someOtherBooleanValue: false,
... etc
};
}
}
I could also define a resource in my API, GET /widgets/new, which returns a "template" widget:
{
name: '',
messageTextColor: '#999999',
bodyBackgroundColor: '#FFFFFF',
someBooleanValue: true,
someOtherBooleanValue: false,
... etc
}
I'm wondering if anyone can point me to information online (e.g., blog articles) related to a common pattern whereby a "template" resource is accessible in a RESTful API -- or even an alternative approach not listed here.
I worry this is going to be considered 'opinion based', or at least a somewhat unclear answer. Anyway, you might find answers in HATEOAS. Many formats have a 'form' or 'action' concept, and the idea of 'discovering a create form' is a pretty common pattern.
Anyway, I also don't see how you couldn't just:
Fetch a 'template' resource at http://api.example/foo/template with GET
Edit properties, and
Submit the result again at a new endpoint to create the resource with PUT and POST.
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!
I'm new to sails, using 1.0.
I created an app with the --minimal command line switch and now I wish to add in some functionality.
I've already successfully added the ORM functionality (by adding sails-hook-orm, config/datasources and config/models.
Now I wish to enable the automatic blueprint routes. I've already added config/blueprint like so:
module.exports.blueprints = {
prefix: '/api/v1',
actions: true,
rest: true,
// shortcuts: true,
};
Now, if I manually add in the routes and actually write the standalone actions, for example, like below:
'GET /api/v1/users/:id': { action: 'users/find-one' },
It works. But I was expecting that blueprint would abstract those away from me when I set rest: true on the config...
What else should I check?
By the documentation is just put rest as true (is true by default), and this should work, seem like you have missing something else. Check if you have your controller created and your model with the same name.
controllers
UserController.js
models
User.js
If you don't have the controller created then the blueprint is not going to work
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!
What purpose does req.options serve in SailsJs?
From the source, it seems I can somehow use policies to alter existing params on Sails Blueprints, etc. How does this work? I can't find any documentation at all.
req.options was introduced in Sails v0.10 as a way to add options to custom routes. For example, you could always do:
'GET /userList': {
controller: 'UserController',
action: 'find'
}
to point at the default "find" action for a User model, but there was no way to control the query--it would always just return all records. Now you can do:
'GET /userList': {
controller: 'UserController',
action: 'find',
sort: 'name desc',
where: {name: {startsWith: 'j'}}
}
and all of the options (including controller and action) will be added to req.options, which the default blueprint actions use to modify the query. Note the difference to Sails v0.9.x, which used req.target to keep track of the controller and action.
As you pointed out, req.options can also be modified within policies, as opposed to req.params which are reset before every policy and controller action. This can be used to, for example, further restrict the criteria for a query based on a user's role.
Obviously the docs for v0.10.x aren't completely finished (and will always be subject to improvement), but this could definitely find a place in the Route Target Syntax section...