Skip to content

This Dot Blog

This Dot provides teams with technical leaders who bring deep knowledge of the web platform. We help teams set new standards, and deliver results predictably.

Newest First
Tags: Tooling
Deploying apps and services to AWS using AWS Copilot CLI cover image

Deploying apps and services to AWS using AWS Copilot CLI

Learn how to leverage AWS Copilot CLI, a tool that abstracts the complexities of infrastructure management, making the deployment and management of containerized applications on AWS an easy process...

How Vim Transformed My Workflow for the Better cover image

How Vim Transformed My Workflow for the Better

Discover how diving into Vim transformed my coding workflow...

How to Set Up OAuth with a Stripe App cover image

How to Set Up OAuth with a Stripe App

Stripe Apps are a great way to extend Stripe dashboard functionality using third-party integrations. But when using these integrations, developers must prioritize security. The best way to do this is by using OAuth with the third-party product with which you would like to integrate. However, there are some constraints, and we cannot use cookies to set up a cookie based authentication front-end only. In this article, we would like to show you how to do it with a NestJS back-end. Stripe signatures The best way to secure your API is by making sure that every request comes from a verified Stripe App instance. The @stripe/ui-extension-sdk package provides a way to generate a signature on the front-end side. This signature is valid for 5 minutes, and you can send it as a header for every request you make. For this to work, you need to have @stripe/ui-extension-sdk installed in your repository. ` In order to properly validate this signature on your API, you will need some additional information to be sent in the request headers as well. That information is the Stripe user's ID, and the Stripe account's ID. We found that the best way is to implement a global context with this information. ` The above context stores the ExtensionContextValue that gets passed from the Stripe dashboard to the app when it opens in the view. For example, if you are on a payment detail page, the userContext will contain information about your Stripe user, while the environment will provide you access to the object that you are viewing. In the above example, that would be the payment's ID as the objectContext.id property. Let's set up the view with this global context. ` Now, we can set up a hook to provide a proper fetch method that always appends a Stripe signature, and the other required fields to the headers. useFetchWithCredentials hook In order to make our future job easier, we need to set up a hook that creates a proper wrapper around fetch. That wrapper will handle setting the headers for us. It needs to have access to our GlobalContext, so we can get the Stripe user's, and their account's, IDs. ` Let's set up a very basic component for demonstrating the use of the useFetchWithCredentials hook. This component will be the default route for our app's navigation wrapper. It is going to handle more later. But for now, let's just implement a basic use for our hook. The AUTH_INIT_URL constant will point at our back-end's /api/oauth/userinfo endpoint. Please note that, for this to work, you are going to need to install react-router-dom. ` As we can see from the above implementation, this component will be the initial component that gets rendered inside of the application. It will send out a request to determine if the user is logged in. If they are logged in, we are going to send them to a route that is the first page of our application. If they are not signed in, we are going to redirect them to our login page. This initial call, just as every other API call, must be verified and always have a Stripe signature. Let's visualise how routing looks like right now: ` Stripe secrets and the Stripe API In order to be able to use the Stripe NodeJS Api, you will need two secrets from Stripe. One is your Stripe account's API key, and the other one is your Stripe-app's secret. You need to set up your .env file as the following. ` Stripe API key You can find your Stripe API key at https://dashboard.stripe.com/apikeys, under the Standard keys section. The key you are looking for is called Secret key, and you need to reveal it by clicking the button that hides it. Stripe App Secret For this key, you are going to need to upload your stripe-app using the stripe apps upload command. Make sure that you set a development app ID in your app manifest (stripe-app.json). After you uploaded your app, visit https://dashboard.stripe.com/apps. Under My Apps, you should see your uploaded application. Open it and search for the Signing secret. Reveal it and copy it into your .env file. Stripe NodeJS API Please make sure you have installed the stripe nmp package for your server code. In this example series, we use NestJS as our framework for our API. We need the above two secret keys to be able to start up our Stripe API. ` NestJS VerifySignatureInterceptor implementation In NestJS, we can use interceptors to abstract away repetitive logic that needs to be done on multiple requests. In our case, we need to verify almost every API for a valid Stripe signature. We have access to the proper secret keys, and we have a Stripe NodeJS API set up. Let's create our VerifySignatureInterceptor. ` Every interceptor must implement the intercept() method. We extract the Request object from the execution context, and we get the headers that we previously set in our useFetchWithCredentials hook. We call our verifySignature function which will throw errors if the signature is invalid. We also pass the Logger instance, so we can determine when an error comes from this interceptor in our logs. Please be aware that there are several reasons signature verification can go wrong, like if we provide the wrong Stripe account keys or app secrets. In order for you to be able to easily debug these issues, proper logging is a must. That is why we set up a Logger instance in our interceptor. ` If the user_id, account_id, or the signature are missing, that could mean that the request came from outside a stripe application, or the useFetchWithCredentials hook was not used. We throw a BadRequestException that will result in the request sending back a status: 400 HTTP response. If the signature verification fails, that could mean that a not valid signature was used in the request, or that the API environment variables might have the wrong keys. Set up the userinfo endpoint Let's quickly set up our /api/oauth/userinfo endpoint. For that, we are going to create the OauthModule and the OauthController. ` In our controller, we decorate our getUserInfo() method, with the @Get() decorator, so we set up the route. We also decorate the method with the @UseInterceptors() decorator, where we pass our VerifySignatureInterceptor. ` This setup will enable us to call the /api/oauth/userinfo endpoint which will, in-turn, check if we have a valid signature present in the headers. If the request is invalid, it will throw a 400 Bad Request exception. If the signature is valid, for now, we will throw a 401 Unauthorized exception just to make our front-end navigate to the login page. The Login flow Just to keep this example simple, our login page will only have a button in the center that will start our login flow with our API. ` We need to create a state key, that can be validated before we fetch the token. This state key will first be sent to our third-party oauth client, and it will be returned to us when the authentication is finished. This key is passed securely and over https. Therefore, it can be a stringified object. While the key is not set, we disable the button. ` Pressing the Sign in button will call our API that will redirect us to our third-party login screen. When the login happens, it will redirect us to our API, where we can fetch a valid token and redirect again to the Stripe dashboard. Let's extend our environment variables. ` Now that we have every environment variable set up, let's implement our api/oauth/login and api/oauth/authorise endpoints in our OauthController. ` The login endpoint, if everything is correct, redirects us to the login page where the user should be able to log in. Make sure that if you oauth client needs to have configured redirect urls, you configure them. For example, for development, the http://localhost:3333/api/oauth/authorise endpoint should be in the allowed redirect url list. ` We validate everything to be sure that this endpoint was called from our third-party OAuth page. With the information available to us, we can fetch the access token and store it in the Stripe Secret Storage. In this example, we use axios in our bakc-end to send requests to our third-party API. ` We exchange our code returned from our OAuth client to a valid access token, and then store it in the Stripe Secret Store. That logic got extracted into a SecretService class, because the logic implemented in it can be reused later for other API calls. Please make sure you set up a NestJS module that exports this service. Stripe Secret Store Stripe's Secret Store API enables your app to securely store and retrieve strings that can be authentication credentials, tokens, etc. This API enables users to stay logged in to third party services even when they log out of their Stripe dashboard. Let's set up a service that handles access to the Secret Store on our back-end. ` Adding secrets As we can see above, the Secret Storage needs some preliminary setup, which we do in our SecretService. The StripeResource sets up the find, set, and delete methods on the Stripe Api, and interacts with the Secret Store. Let's implement the addSecret method, so we can actually store our returned token. ` With the above, we can finally store our token with which we can make authenticated requests. Getting secrets Let's implement the getSecret so we can retrieve secrets. The principles are the same. We will need the accountId, the userId, and the secret's name for it. ` Let's close the login flow, and implement the final version of the api/oauth/userinfo endpoint. ` Deleting secrets We want our users to have ability to log out from our third-party API as well. That can be achieved by deleting their access_token from the Secret store. ` The /api/oauth/logout endpoint is going to be a GET request, that will delete the token from the Secret Store. ` We can create a SignOutLink that will send the request to our back-end and navigates to the /login page. You can put this component into the footerContent property of your ContextView. ` And now we are ready with our authentication setup. When the user opens our app, it will call the /api/oauth/userinfo endpoint. Initially, it will return with a 401 error, and our front-end will navigate to the /login route. When the user presses the Sign in button, it will redirect them to the third-party OAuth page. After they log in, our back-end also redirects them back to their Stripe dashboars where the application will open. The app will call the /api/oauth/userinfo endpoint again. But this time, it will return an actual user information and it routes to the protected route. To help visualize the whole flow, you can also use the following sequence diagram for reference: Conclusion As you can see, there are many steps involved in setting up a proper OAuth flow. However, it's necessary to make it right, since this is the most critical part of the app. We hope blog post article will help you to set up some good foundations when implementing your own Stripe app....

Double Click: Jump on the waitlist for Fig.io's new autocomplete terminal add-on! cover image

Double Click: Jump on the waitlist for Fig.io's new autocomplete terminal add-on!

Welcome to the Double Click! This is the weekly blog series that shines a spotlight on emerging technologies, technological concepts, and community projects that enrich the JavaScript Ecosystem! This week, I’m sharing a really cool, new tool for your existing terminal that I LOVE: Fig! This add-on provides VSCode-style autocomplete, and is currently being made available for free to those who sign up on their website. When using the Fig add-on, you get autocomplete options in your terminal, showing you everything you need, such as lists of available directories, folders, git commands, branches, components, and more without taking you out of your flow. How magical is it to get more time to focus on your code rather than trying to remember shortcuts and commands in terminal? (LIFESAVER!) Fig is also built to integrate with a number of CLI tools, including npm, Heroku, AWS, GCP, Docker, and more, with plans to add further integrations as the technology develops and new users start including Fig in their tool belt. Other features of this add-on include keyboard-first functionality, allowing developers to quickly use the auto completes without having to touch their mouse, open-source completion specs, which ensure that the Fig add-on always remains up to date and relevant for users, and the add-on runs locally, offering speed and security. I’m already using Fig, and am a huge supporter of any tools that work to remove complexities from my development process. I am excited to see this project grow!...

Nx Workspace with Angular and Nest cover image

Nx Workspace with Angular and Nest

Nx Workspace with Angular and Nest In a previous article, we covered creating an Angular project with Nx monorepo tooling. This gives us a great base, but usually, our application will need a server-side project to feed our frontend application with all of the necessary data. Why not leverage the monorepo approach for this use-case then? In this article, I would like to show you how to bring Nest server-side application that will serve our frontend application all the necessary data and behaviors. We will build on top of the existing Nx-based Angular application, which you can find in this GitHub repository. If you want to follow the code in this article, I recommend cloning this repository and checking out new branch with the nxAngularNest_entryPoint tag. ` The application in the aforementioned repository contains a simple application that displays a list of photos that can be either liked or disliked. If you run the code initially, you'll notice that the app requires a backend server from which to pull the necessary data. We will build this simple backend application using the Nest framework, and all of that within a single monorepo project, so that it is easier to manage both applications. Nest Overview Nest is a backend framework for building scalable Node applications. It is a great tool for Angular devs to get into server-side development as it is based on concepts that are very similar to Angular ones: - TypeScript support - Dependency Injection mechanism that is very similar to the Angular mechanism - Puts emphasis on testability - Configuration is similar (mostly based on decorators) - Best practices and conventions are similar - knowledge is transferable All of this makes for a great candidate to use Nest as a server application framework for our application. Let's add a Nest application to our existing project. Add Nest app To start off, we need to install all of the dependencies which will allow Nx to assist us with building a Nest application. All of this is packed into a single Nx plugin @nrwl/nest. ` With the tooling in place, we can generate the Nest application with one command. ` Please keep in mind that, since we're keeping applications using 2 separate Nx plugins, we need to specify the full path to the schematics for generating applications/libraries. In this case, it is @nrwl/nest:application A nice feature when creating a Nest application is the ability to set up a proxy to our newly created application so that our FE application can easily access it. We can use the --frontendProject additional param to do so. Let's use it to create our actual Nest application: ` This command will generate a project skeleton for us. The application is bootstrapped similarly to an Angular app. We define an AppModule, which will be a root of the app, and all the other necessary modules will be imported within this module. ` ` For a more in-depth explanation of the Nest framework, please visit the official docs. Building the API For our photos application we require 3 following endpoints to be handled: GET /api/photos - which returns the list of all photos PUT /api/photos/:photoId/like - allows us to like a photo PUT /api/photos/:photoId/dislike - allows us to dislike a photo To handle requests in Nest, we use a class called Controller which can handle requests to a specific sub-path (in this case it will be the photos sub-path). To keep our application clean, let's create a separate module that will contain our controller and all the necessary logic. `` nx g @nrwl/nest:module app/photos --project=api-photos nx g @nrwl/nest:controller app/photos --project=api-photos --export `` Since the controller shouldn’t contain business logic, we will also create a service to handle the logic for storing and manipulating our photo collection. `` nx g @nrwl/nest:service app/photos --project=api-photos `` Our newly created service will be added to our PhotosModule providers. ` Just like in Angular, we also need to include our PhotosModule in the AppModule's imports to notify Nest of our module's existence. ` Now, we are ready to build the API we need. We can start with the first endpoint for getting all the photos: GET /api/photos Let's start by creating all the necessary logic within the PhotosService class. We need to store our collection of photos and be able to return them in a form of an Array. To store it, I prefer to use an id-based map for quick access. ` To simplify transformation from a map to an array, I added a utility function stateToArray. It can definitely be extracted to a separate file/directory as an application grows, but for now, let's leave it here inline. Now, our controller can leverage this getPhotos function to return a list of all photos via an API. To create an endpoint in Nest, we use decorators corresponding to an HTTP method that we want to expose. In our case, it will be a GET method so we can use a @Get() decorator: ` Now, we can run both our frontend and backend server to see the list of photos requested via our new API. ` ` We still need to implement the liking and disliking feature in the Nest app. To do this, let's follow the same approach as we did earlier. First, let's add the liking functionality to PhotosService: ` and similarly, we can implement the dislike functionality ` With both methods in place, all that is left to do is implement to endpoints in the PhotosController and use methods provided by a PhotosService: ` The path params are defined analogously to how we define params in Angular routing with the : prefix, and to access those params we can use @Param() decorator for a method's parameter. Now, after our server reloads, we can see that the applications are working as expected with both the liking and disliking functionalities working. Common interfaces In this final section, I would like to show you how we can benefit from the monorepo approach by extracting the common interface between the frontend and backend to a separate library. Let's start by creating a library, again using the Nx command tools. `` nx g @nrwl/workspace:library photo/api `` This will generate a new library under libs/photo/api/ folder. Let's create a new file libs/photo/api/src/lib/photo.model.ts and put the ApiPhoto interface in it so it can be shared by both frontend and backend applications. ` We need to export this interface in the index.ts file of the library as well: ` There is no way we can use the same interface for an API request in both of our applications. This way, we make sure that the layer of communication between our applications in always up to date. Whenever we change the structure of the data in our server application, we will have to apply the appropriate changes to the frontend application as well as the TypeScript compiler. This forces data to be consistent and braking changes to be more manageable. Conclusion As you can see, maintaining the project in a monorepo makes it easier to maintain. Nest framework is a great choice for a team of developers that are acquainted with Angular as it builds on top of similar principles. All of that can be easily managed by the Nx toolset. You can find the code for this article's end result on my GitHub repo. Checkout the nxAngularNest_ready tag to get the up-to-date and ready-to-run solution. To start the app you need to serve both Angular and Nest projects: ` ` In case you have any questions you can always tweet or DM me @ktrz. I'm always happy to help!...

Introduction to building an Angular app with Nx Workspace cover image

Introduction to building an Angular app with Nx Workspace

# Introduction to building an Angular app with Nx Workspace Nx Workspace is a tool suite designed to architect, build and manage monorepos at any scale. It has out-of-the-box support for multiple frontend frameworks like Angular and React as well as backend technologies including Nest, Next, and Express. In this article, we will focus on building a workspace for an Angular-based project. Monorepo fundamentals The most basic definition of a monorepo is that it is a single repository that consists of multiple applications and libraries. This all is accompanied by a set of tooling, which enables us to work with those projects. This approach has several benefits including: - shared code - it enables us to share code across the whole company or organization. This can result in code that is more DRY as we can reuse the common patterns, components, and types. This enables to share the logic between frontend and backend as well. - atomic changes - without the monorepo approach, whenever we need to make a change that will affect multiple projects, we might need to coordinate those changes across multiple repositories, and possibly by multiple teams. For example, an API change might need to be reflected both on a server app and a client app. With monorepo, all of those changes can be applied in one commit on one repository, which greatly limits the coordination efforts necessary - developer mobility - with a monorepo approach we get one consistent way of performing similar tasks even when using multiple technologies. The developers can now contribute to other teams' projects, and make sure that their changes are safe across the whole organization. - single set of dependencies - By using a single repository with one set of dependencies, we make sure that our whole codebase depends on one single version of the given dependency. This way, there are no version conflicts between libraries. It is also less likely that the less used part of the repository will be left with an obsolete dependency because it will be updated along the way when other parts of the repository do this update. If you want to read more about monorepos, here are some useful links: - (Monorepo in Git)[https://www.atlassian.com/git/tutorials/monorepos] - (Monorepo != monolith)[https://blog.nrwl.io/misconceptions-about-monorepos-monorepo-monolith-df1250d4b03c] - (Nrwl Nx Resources)[https://nx.dev/latest/angular/getting-started/resources] Create a new workspace With all that said about the monorepo, how do we actually create one using Nx Workspace and Angular? Just like with Angular CLI, there is an Nx CLI that does all the heavy lifting for us. With the following command, we can create a new workspace that leverages all of the aforementioned benefits of a monorepo: ` The tool will ask for a project name, stylesheet format, and linting tool. For the linting, I recommend using ESLint, which is a more modern tool. The CLI will also ask whether we want to use Nx Cloud in our workspace. We can opt-out from this for now as we can easily add that later on. After the command finishes, we end up with a brand new project all set up. Let's start by analyzing what has been generated for us. Nx uses certain toolset by default: - Jest for testing (instead of Karma and Jasmine) - Cypress for e2e testing (instead of Protractor) - ESLint for linting (instead of TSLint) in case you decide to use it when creating a workspace All of these are modern tools, and I recommend sticking to them as they provide very good developer experiences, and are actively maintained. The base structure that is created for us looks as follows: ` - apps/*: here go all the application projects - by default, it'll be the app we created and an accompanying e2e tests app - libs/*: where all of the libraries that we create go - tools/*: here, we can put all of the necessary tooling scripts etc that are necessary in our project - and all the root configuration files like angular.json, config files for Jest, ESLint, Prettier, etc This whole structure is created for us so that we can focus on building the solution right from the beginning. Migration from an existing Angular project If you already have an existing Angular app that was built using the Angular CLI, you can still easily migrate to an Nx Workspace. A project that contains only a single Angular app can be migrated automatically with just one command: ` This will install all of the dependencies, required by Nx, and create the folder structure mentioned in the previous section. It will also migrate the app into apps folder and e2e suite into apps/{{appName}}-e2e folder. Nx modifies package.json script, and decorates Angular CLI so you can still use the same commands like ng build, ng serve, or npm start. It is important to remember that the version of Angular and Nx must match so that this process goes smoothly. For example, if your project is using version 10 of Angular, please make sure to use the latest 10.x.x version of Nx CLI. In case you already have multiple projects, you still can migrate with few manual steps described in the Nx docs. Nx CLI In the following sections, we will use Nx CLI to simplify performing operations on the monorepo. You can install it globally by running one of the following commands: ` ` If you don't want to install a global dependency, you can always invoke local nx via either ` or ` Create a library One of the core ideas behind the Nx Workspace monorepo approach is to divide our code into small, manageable libraries. So by using Nx, we will end up creating a library often. Luckily, you can do this by typing one command in the terminal: ` This will create a libs/mylib folder with the library set up so we can build, test, and use it in other libraries or applications right away. To group the libraries you can use the --directory={{subfolderName}} additional parameter to specify a subfolder under which a library should be created. You don't have to worry about choosing the perfect place for your library from the start, though. You can always move it around later on using @nrwl/workspace:move schematics, and you can find all the other options for generating a new Angular library in the official docs. Every library has an index.ts file at its root, which should be the only access point to a library. Each part of the library that we want to be part of the lib's public API should be exported from this file. Everything else is considered private to the library. This is important for maintaining the correct boundaries between libraries and applications, which makes for more well-structured code. Affected One of the greatest things about Nx Workspace is that it understands dependencies within the workspace. This allows for testing and linting only the projects that are affected by a given change. Nx comes with a few built-in commands for that. ` Those commands will run lint, test, e2e, and build targets, but only on projects that are affected, and therefore they will lower the execution time by a lot in most use-cases. The commands below are equivalent to the ones above, but they use more generic syntax, which can be extended to different targets if necessary. ` For all of the commands mentioned above, we can parallelize them by using --parallel flag and --maxParallel={{nr}} to cap the number of parallel tasks. There are multiple additional useful parameters that the affected task can take. Please visit the official docs for more details. Conclusion Working with a monorepo has a lot of advantages, and Nx Workspace provides us with multiple tools to get the most of that. By using it, we can speed up our development loop by being able to create atomic changes to the repository, and make sure that the whole workspace is compatible with that change. All of this is done with blazing fast tooling that can be scaled to any project size we might have. In case you have any questions, you can always tweet or DM me @ktrz. I'm always happy to help!...