https://amplication.com/blog/eating-our-own-dog-food---building-amplication%27s-blog-with-amplication-cl4wnpayg963101s6f7svecil Amplication is open source. Star our GitHub repo! []AmplicationAmplication * Docs * Features * Pricing * Community * Team * Careers * Roadmap * Blog Contact us * contact@amplication.com Login * Home / * Blog / * Eating Our Own Dog Food - Building Amplication's Blog with Amplication Eating Our Own Dog Food - Building Amplication's Blog with Amplication APIAuthenticationCustom Code Michael SolatiMichael Solati Michael Solati Jun 27, 2022 Eating Our Own Dog Food - Building Amplication's Blog with AmplicationEating Our Own Dog Food - Building Amplication's Blog with AmplicationEating Our Own Dog Food - Building Amplication's Blog with AmplicationEating Our Own Dog Food - Building Amplication's Blog with Amplication When we decided to revamp Amplication's blog website, it was obvious that we would "eat our own dog food". That is, we would use our own tool to develop the solution. After all, when you develop an amazing platform to automate backend development, you would be crazy not to use it on your own projects (actually, we prefer to say "eat our own gourmet cuisine" but maybe we're biased). We even use Amplication to build new services in Amplication, but that's for another post... Using Amplication enabled us to understand better how consumers experience our product and we were able to perform a form of quality control by testing the product in a real use-case situation. For these reasons, eating your own dog food is a valuable doctrine that you should consider in your own organization whenever possible. During the development of the new website, we encountered several problems, and in this article, we will discuss what we did to solve them. We think this is a good case study of how using your own product can help you understand its strengths and overcome its limitations. Building the Amplication blog platform We built the API server hosting our blog content with Amplication. Amplication made the development process simpler by providing the following: * A database: where we save the content of the blog posts and the data about the authors and tagging. * A GraphQL API: to be used by the blog client to fetch the data to be presented * An admin UI: to be used by us to create new content, add blogs, authors, and tags. In addition, we built the site that you visit, the blog client, with Next.js using GraphQL to fetch data from the API server. The client we built is also open-source, so feel free to use it for your next blog project. What was missing * Public endpoints for readers of the blog to get the articles without authentication. * Filtering by relation, allowing readers to filter blog posts by tag. The immediate solution Amplication generated quality code that allowed us to easily alter and customize the API server we were building. So we were able to implement custom code in a few places. Link to commit 1 - Add public access to posts Link to commit 2 - Filter posts by tags Link to commit 3 - Resolve public fields for posts Once we had the custom code in place, we figured that these features would also be used by other developers and decided it would benefit everyone if we took the custom code we implemented and added it to the platform. The new features Tag Filtering As of version 0.11.2 of Amplication, there wasn't a way to filter entities based on their relation to another entity. You can review the GitHub issue here. A blog site has a one-to-many relationship between posts and tags. Each post in the blog has many tags, and users will want to query all posts based on tags. However, the WhereInput filter was not able to filter posts based on their relation to tags. To get all posts belonging to a tag we would have had to call the /api/tags/{id} endpoint for each tag, instead of querying all posts using the /api/posts endpoint and filtering the results to only those belonging to a certain tag. This problem was quickly resolved by implementing the "-to-many" relation filter from Prisma. You can check out the pull request here. This enhancement allows filtering based on the some, every, and none options when filtering an entity based on a "-to-many." For example, we can now filter posts that have some number of tags, we can also search for every post that has a tag, and we can find all posts that have no (none) tags tied to them. This feature landed in Amplication version 0.11.4. You can learn more about v0.11.4 here. Public Endpoints One of the most requested features of Amplication was the ability to create models without auth guards. Out of the box, Amplication provides a role-based permission model that is baked into your application. Developers can authorize access to data models for all roles and also granularly to specific roles. However, this requires all requests to be authenticated, which doesn't work well for a public blog. To resolve this, a new entity permission type named Public was added alongside All Roles and Granular. As expected, the Public option makes an endpoint open to the public to make requests against, no longer requiring authentication or any specific role to access an endpoint. Perfect for querying blog posts. This feature landed in Amplication version 0.12.7. You can learn more about v0.12.7 here. A big thanks to Amit Barletz, an engineer on the Amplication team, who developed the Tag Filtering and Public Endpoint features that enabled us to build our website. We're excited for you to use Amplication to build your next application, just like we've used it to build the site you're reading this on now! Check out our tutorial to build a full-stack application with Amplication and React. If you're interested in what new features we're working on, we keep our roadmap public for you to review as well. Share this post. * * * Sign up to stay up-to-date with our latest developments. We promise not to spam you. [ ] [ ] [ ] [Subscribe] Related Posts. Amplication Release 0.12.7 - Good Code and Public Endpoints Amplication Release 0.12.7 - Good Code and Public Endpoints Amplication Release 0.12.7 - Good Code and Public Endpoints Amplication Release 0.12.7 - Good Code and Public Endpoints Yuval hazazYuval hazaz Yuval hazaz May 12, 2022 Open SourceNode.jsAPINew Release Amplication Release 0.12.7 - Good Code and Public Endpoints Here at Amplication, we believe in good code. While we work night and day to develop new features that will add value for our users, we don't forget to look under the hood and make sure that Amplication is generating code that meets our exacting standards. When we say good code, we mean that the code is easily understood, can be easily maintained (even by less experienced developers), does what it is intended to do, and does it well. Good, readable code enables Amplication to fulfill one of its main goals, to enable developers to focus on the code that matters, rather than writing repetitive and boilerplate code. Moreover, we believe developers should maintain full control over the generated code and have the freedom to change it based on their requirements without being constrained by the limitations that typify every black-box solution. Amplication release 0.12.7 is a good example of how we keep our code fine-tuned while introducing awesome new features. We have done code refactoring with significant improvements to the generated code while introducing support for public endpoints - a feature that was requested by many of our enterprise users. To see how we did it, check out the examples below. They include major refactoring on the generated code of the controllers, used for the REST API endpoints, and resolvers, used for the GraphQL queries and mutations. New interceptors for access controls We created two new NestJS Interceptors to enforce Access Control policies: AclValidateRequestInterceptor - this interceptor is used to validate that users are not updating or creating data they are not allowed to, based on the permissions that were defined for their role. AclFilterResponseInterceptor - this interceptor is used to filter the response data based on the permissions that were defined for their role. These interceptors are replacing the boilerplate code that was manually used in each of the controllers' endpoints, and resolvers' queries and mutations. Interceptor refactored code example 1 Before: When creating a customer record, the request data was checked for any property that is not allowed to be updated by the current user, and an exception is thrown when needed. The function was not easily readable and included a lot of boilerplate code. @nestAccessControl.UseRoles({ resource: "Customer", action: "create", possession: "any", }) @common.Post() async create( @common.Body() data: CustomerCreateInput, @nestAccessControl.UserRoles() userRoles: string[] ): Promise { const permission = this.rolesBuilder.permission({ role: userRoles, action: "create", possession: "any", resource: "Customer", }); const invalidAttributes = abacUtil.getInvalidAttributes(permission, data); if (invalidAttributes.length) { const properties = invalidAttributes .map((attribute: string) => JSON.stringify(attribute)) .join(", "); const roles = userRoles .map((role: string) => JSON.stringify(role)) .join(","); throw new errors.ForbiddenException( providing the properties: ${properties} on ${"Customer"} creation is forbidden for roles: ${roles} ); } return await this.service.create({ data: data, select: { id: true, createdAt: true, updatedAt: true, name: true, }, }); } After: Now, the boilerplate code has been removed, and the function includes only a single line of code that calls the service.create function. Instead of the boilerplate code, the AclValidateRequestInterceptor interceptor was added as a decorator to the function. @common.UseInterceptors(AclValidateRequestInterceptor) @nestAccessControl.UseRoles({ resource: "Customer", action: "create", possession: "any", }) @common.Post() async create(@common.Body() data: CustomerCreateInput): Promise { return await this.service.create({ data: data, select: { id: true, createdAt: true, updatedAt: true, name: true, }, }); } Interceptor refactored code example 2 Before: Before returning the customer records to the client, the response data was filtered so only allowed properties are returned. The function was not easily readable and included a lot of boilerplate code. @common.UseGuards( defaultAuthGuard.DefaultAuthGuard, nestAccessControl.ACGuard ) @nestAccessControl.UseRoles({ resource: "Customer", action: "read", possession: "any", }) @common.Get() async findMany( @common.Req() request: Request, @nestAccessControl.UserRoles() userRoles: string[] ): Promise { const args = plainToClass(CustomerFindManyArgs, request.query); const permission = this.rolesBuilder.permission({ role: userRoles, action: "read", possession: "any", resource: "Customer", }); const results = await this.service.findMany({ ...args, select: { id: true, createdAt: true, updatedAt: true, name: true, }, }); return results.map((result) => permission.filter (result)); } After: Now, the boilerplate code has been removed, and the function includes only two lines of code. Instead of the boilerplate code, The AclFilterResponseInterceptor interceptor was added as a decorator to the function. @common.UseInterceptors (AclFilterResponseInterceptor) @nestAccessControl.UseRoles({ resource: "Customer", action: "read", possession: "any", }) @common.Get() async findMany(@common.Req() request: Request): Promise { const args = plainToClass(CustomerFindManyArgs, request.query); return this.service.findMany({ ...args, select: { id: true, createdAt: true, updatedAt: true, name: true, }, }); } UseGuard decorator moved to the class level Decorator refactored code example Before: We moved the UseGuard decorator to the class level instead of using it individually on each endpoint. Before, the UseGuard decorator was added to each controller endpoint individually @common.UseGuards( defaultAuthGuard.DefaultAuthGuard, nestAccessControl.ACGuard ) async delete( @common.Param() params: CustomerWhereUniqueInput ): Promise { After: Now, the UseGuard decorator has been removed from the function level and it is defined once at each controller or resolver. @common.UseGuards (defaultAuthGuard.DefaultAuthGuard, nestAccessControl.ACGuard) export class CustomerControllerBase { Morgan interceptor moved to the global level We moved the morgan interceptor to the global level instead of using it individually on each endpoint. Interceptor refactored code example Before: Before, the morgan interceptor was added to each controller endpoint individually @common.UseInterceptors (nestMorgan.MorganInterceptor("combined")) async findOne( @common.Param() params: CustomerWhereUniqueInput, @nestAccessControl.UserRoles() userRoles: string[] ): Promise { After: Now, the morgan interceptor was removed from the function level and it is defined once at the application global level. providers: [ { provide: APP_INTERCEPTOR, scope: Scope.REQUEST, useClass: MorganInterceptor("combined"), }, ], Public Endpoints When building APIs, usually you would like to secure the API so it can be accessed by authorized users only. But, in many use cases, you might be required to build a public API, and sometimes, you may even need to build an API where some endpoints are private while other endpoints are public. The request to support public endpoints is one of the most popular requests on our GitHub repository https:// github.com/amplication/amplication/issues/2006. This requirement was also something we needed when we built the Amplication blog (using Amplication of course , but that's for another blog post). As with everything else in Amplication, you can always customize the generated code, and this is exactly what we did to support the development of the blog (see this commit, and this one also). We have now introduced built-in support to define endpoints as public. This option is available per action per entity- meaning you can easily configure the endpoint so that creating, editing, or deleting blog posts will require authentication, but for viewing the blog posts, no authentication will be needed. Untitled Endpoint authentication example //This endpoint requires authentication @common.UseInterceptors(AclFilterResponseInterceptor) @nestAccessControl.UseRoles({ resource: "Customer", action: "read", possession: "any", }) @common.Get() async findMany(@common.Req() request: Request): Promise { //This endpoint is accessible by authenticated and non-authenticated users //We use the @Public decorator to flag public endpoints @Public() @common.Get() async findMany(@common.Req() request: Request): Promise { The Amplication revolution continues Amplication 0.12.7 is just one step forward in our mission to revolutionize the developer experience. To see what new features are coming up next, take a look at our roadmap. If you have an idea for a feature that isn't scheduled, please go to GitHub and open a Feature Request. If you have questions, need support or just want to share your thoughts- join us on Discord. We'd love to hear from you. JWT Authentication - What Is It and How Do You Use It With Amplication?JWT Authentication - What Is It and How Do You Use It With Amplication?JWT Authentication - What Is It and How Do You Use It With Amplication?JWT Authentication - What Is It and How Do You Use It With Amplication? Moshe FormanMoshe Forman Moshe Forman Mar 23, 2022 Open SourceAuthentication JWT Authentication - What Is It and How Do You Use It With Amplication? JSON Web Token (JWT) is now supported by Amplication. This article gives you an overview of how JWT works and how you can use it in your Amplication-generated app. What is JWT? JWT is an open standard security token that transmits information securely as a JSON object, useful for authorization and information exchange. It contains all essential information about an entity, meaning that no database queries are necessary, and the session doesn't need to be saved on the server. You can sign the token using a private secret or a public /private key. Its short messages can be encrypted and securely convey the identity of the sender and whether they have the necessary access rights. Note: Most programming languages have a library for generating JWT, so you don't have to do it manually. JWT structure JWT contains three parts: Header, Payload, and Signature as described in the following sections. Header The header provides information about the type of token and the signing/encryption algorithm being used. The header typically consists of two parts: true alg - the signing algorithm used, such as HMAC SHA256 or RSA true typ - the type of token (which is JWT) { "alg": "HS256", "typ": "JWT" } Payload The payload contains the claims. Claims are statements about an entity (typically, the user) and additional data. There are three classes of claim names; Registered, Public, and Private. Registered claims Registered claims are defined by the JWT specification. JWT defines a set of seven reserved claims that are not obligatory, but it is recommended that you use them to allow interoperability with third-party applications. Note: Public claims and private claims are both considered custom claims, created to share information between parties that agree to use them. Public claims You can define public claims however you want, but to avoid collisions they should be defined in the IANA JSON Web Token Registry. Private claims You can create private claims to share information specific to your application. Unlike public claims, private claims might collide as they are not registered, so use them with care. Private claims should not share names with registered or public claims. The following example includes a private claim loggedInAs, and a registered claim iat. { "loggedInAs": "admin", "iat": 1422779638 } Signature The signature is used to verify that the message wasn't changed in transit. If the token is signed with a private key, it can also verify the identity of the sender. To create the signature part, sign the encoded header, the encoded payload, a secret, and the algorithm specified in the header. The following example uses the HMAC SHA256 algorithm: HMAC_SHA256( secret, base64urlEncoding(header) + '.' + base64urlEncoding(payload) ) JWT workflow Users have only indirect contact with the token, for example, when they enter usernames and passwords. The actual communication takes place between the client and the server. Before using JWT, you must define a secret key. As soon as a user has successfully entered their login information, the JWT will be returned with the key and saved locally. This transfer should take place over HTTPS to ensure that the data is protected. These steps are described as follows: true The user logs in to the client using a username and password. true The server checks if the hashed password is the same as the hashed password stored in the database for this user. true If the hashed passwords are the same, the JWT service in the server stores the data in the JWT payload section and signs it. true The server sends the signed JWT to the client, and the client saves it locally. true The next time the user sends a request for data, the client sends the token to the server in the authorization header of the HTTP request using the Bearer scheme. What is a bearer token? Bearer authentication is an HTTP authentication scheme using Bearer tokens, so-named because it gives access to the bearer of the token. The Bearer token is a cryptic string, usually generated by the server in response to a login request. The client must send this token in the Authorization header when making requests to protected resources. After a user has been authenticated, the application validates the user's Bearer token. You must provide the token using Header, Body, or Query. This example shows you how to set the value of the authorization header as Bearer: Authorization : Bearer cn389ncoiwuencr If you want to send the token in the body or as a query, add access_token to your required option, for example: { "access_token": "eyJhb...", "token_type": "Bearer", "expires_in": 3600 } Selecting JWT as the authentication method in Amplication Support for JWT is built-in to Amplication. To select JWT authorization for your Amplication app, go to your project dashboard, select Auth Settings and choose JWT from the dropdown list. Select JWT Authentication Getting more information about using JWT in Amplication For more details about using JWT in Amplication, check out the Authentication article in Amplication Docs. Get the full story This has been just a quick overview of JWT. If you want the full picture check out the Amplication docs, and these other sites: Autho - JSON Web Tokens Wikipedia - JSON Web Token flaviocopes - JSON Web Token (JWT) Explained Mozilla - Authentication Schemes JSON Web Token - IETF) Bearer Token Usage - IETF) ionos - JSON Web Tokens Build a Node.js GraphQL API with NestJS and PrismaBuild a Node.js GraphQL API with NestJS and PrismaBuild a Node.js GraphQL API with NestJS and PrismaBuild a Node.js GraphQL API with NestJS and Prisma Yuval hazazYuval hazaz Yuval hazaz Feb 5, 2022 Open SourceNode.jsPrismaAPIGraphQL APINestJS Build a Node.js GraphQL API with NestJS and Prisma Building an API requires spending too much time on boilerplate and repetitive coding. Defining the data model, connecting the database to the server, creating the API endpoints, add security and permissions layer, logging, validation, identity management, sorting, filtering, pagination... the list is long. In this post, I will show you how to create all these using Amplication. We will generate a GraphQL API for an e-commerce application, built with Node.JS, NestJS, Prisma, PostgreSQL, and some additional great open-source technologies. Amplication is an open-source developers' platform that generates an API and a client based on your data model. It saves hours and even days of boilerplate coding. You can use the UI or a CLI to define the data model, and Amplication generates everything you need to start building your next app. The generated source code is fully readable and editable, written in TypeScript, and it even includes tests. By the end of this tutorial, you will have the source code of the backend and client, and you will be able to start writing the custom business logic of your API, or creating the coolest client application or mobile app to work with the API. The generated code So, let's first have a high-level overview of the generated source code. generated code The generated source-code shown in this post is available in https://github.com/amplication/e-commerece-sample We can see that Amplication generates two separate projects for us - "admin-ui", and "server". The "server" folder contains the Node.JS code for our GraphQL API, and the "admin-ui" contains the React-Admin code for an admin application that connects to the API and provides CRUD operations for all our data models. Let's start For this post, I will use the CLI to create our data model. You can also use Amplication's UI to do it. To learn how to install Amplication CLI and authenticate with the server, see this doc https:// docs.amplication.com/docs/cli Create app amp apps:create "my-e-commerce-api" --set-current Create "Customer" entity amp entities:create Customer --set-current amp entities:fields:create "First Name" amp entities:fields:create "Last Name" amp entities:fields:create "Email" amp entities:fields:create "Phone" amp entities:fields:create "Comments" Create "Address" entity amp entities:create Address --set-current amp entities:fields:create "First Name" amp entities:fields:create "Last Name" amp entities:fields:create "Address 1" amp entities:fields:create "Address 2" amp entities:fields:create "City" amp entities:fields:create "State" amp entities:fields:create "Country" amp entities:fields:create "Zip" amp entities:fields:create "Phone" amp entities:fields:create "Is Default" amp entities:fields:create "Customer" Create "Product" entity amp entities:create Product --set-current amp entities:fields:create "Title" amp entities:fields:create "Vendor" amp entities:fields:create "Price" Create "Image" entity amp entities:create Image --set-current amp entities:fields:create "Src" amp entities:fields:create "Width" amp entities:fields:create "Height" amp entities:fields:create "Product" Create "Order" entity amp entities:create Order --set-current amp entities:fields:create "Customer" amp entities:fields:create "Address" amp entities:fields:create "Comments" amp entities:fields:create "Total Price" amp entities:fields:create "User" Create "Line Item" entity amp entities:create LineItem --set-current amp entities:fields:create "Order" amp entities:fields:create "Product" amp entities:fields:create "Price" amp entities:fields:create "Quantity" Commit the new entities and generate the code amp apps:commit --message="create initial entities" After executing this script, we can use Amplication's UI to see that all our data models were created, and if needed we can also change any of the settings. rebuild That's it... Our source code is ready for download. We can simply click on "Download code" to get a ZIP file. Here is an example of the generated files for our customer entity, including the Customer model, DTOs, GraphQL resolver, Service, and tests. generated files Here is an example of the generated code for our Customer service generated code What's next? At this point, you can proceed in any of the following ways: true Connect to a GitHub account to automatically create a Pull Request with your source code in a GitHub repository. https:// docs.amplication.com/docs/sync-with-github true Use the Sandbox environment provided by Amplication with a live instance of your application for further development and testing. true Deploy your application in a Docker container to any server or online service. https://docs.amplication.com/docs/deploy true Customize your server code https://docs.amplication.com/docs/how-to/custom-code true Build a custom client application to work with your new server. true Keep making changes in your data models and re-generate your application code. When connected to a GitHub account you will also get a new Pull Request with every change you make. Try it now Start using Amplication by visiting https://amplication.com/ Open-source Amplication is an open-source project. Please leave a comment and tell me what you think about it. We are also open to feature requests and suggestions on our GitHub repo https://github.com/amplication/ amplication If you need support, you can reach us on our Discord channel https://discord.gg/KSJCZ24vj2 Free to Use. Open-source. Try amplication in 5 minutes []Amplication previewAmplication preview []Amplication previewAmplication preview We would love to hear from you. Contact us * contact@amplication.com Follow us * * * * (c)2022 amplication * Terms & ConditionsPrivacy Policy