<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: binh ngo</title>
    <description>The latest articles on DEV Community by binh ngo (@binhngo).</description>
    <link>https://dev.to/binhngo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F933461%2Faaae153f-787a-446f-aba8-e3cc5410b053.JPEG</url>
      <title>DEV Community: binh ngo</title>
      <link>https://dev.to/binhngo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/binhngo"/>
    <language>en</language>
    <item>
      <title>Stack Overflow Clone | Part 1</title>
      <dc:creator>binh ngo</dc:creator>
      <pubDate>Tue, 26 Sep 2023 16:35:26 +0000</pubDate>
      <link>https://dev.to/binhngo/stack-overflow-clone-part-1-3kcg</link>
      <guid>https://dev.to/binhngo/stack-overflow-clone-part-1-3kcg</guid>
      <description>&lt;p&gt;This series of blogs will cover my journey in creating a StackOverflow clone. This React project will be coded in Typescript, and the tech and tools I will be using are:&lt;/p&gt;

&lt;p&gt;AWS CDK CLI - &lt;code&gt;npm i -g cdk&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;GraphQL - Data Query and manipulation language for APIs&lt;/p&gt;

&lt;p&gt;AppSync - Managed API service that fulfills the role of a GraphQL Server that has access to multiple data sources and retrieves the information in a single query, solving the problems of overfetching &amp;amp; underfetching.&lt;/p&gt;

&lt;p&gt;DynamoDB - serverless NoSQL database&lt;/p&gt;

&lt;p&gt;Cognito - user authentication and authorization&lt;/p&gt;

&lt;p&gt;And if you prefer, here is the &lt;a href="https://github.com/binh-ngo/stack-overflow-clone"&gt;link&lt;/a&gt; to my Github repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize the Project
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      // Create your root directory and navigate into it
      mkdir stackoverflow-clone &amp;amp;&amp;amp; cd stackoverflow-clone

      // creates cdk and frontend directories
      mkdir cdk frontend 

      // navigates to the cdk directory and initializes cdk app
      cd cdk &amp;amp;&amp;amp; cdk init app --language=typescript

      // navigates to the frontend directory and initializes the react app
      cd .. &amp;amp;&amp;amp; cd frontend &amp;amp;&amp;amp; npx create-react-app . --language=typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  CDK
&lt;/h2&gt;

&lt;p&gt;Navigate to your &lt;code&gt;lib/cdk-stack.ts&lt;/code&gt; file and rename it to &lt;code&gt;stackoverflow-frontend-stack.ts&lt;/code&gt;. Then paste this code. Make sure you have &lt;code&gt;cdk-spa-deploy&lt;/code&gt; installed as a dependency! SPADeploy is an AWS CDK Construct that makes deploying a single-page application (Angular/React/Vue) to AWS S3 behind SSL/Cloudfront as easy as 5 lines of code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { SPADeploy } from 'cdk-spa-deploy';

export class StackOverflowFrontendStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    new SPADeploy(this, 'cfDeploy')
      .createSiteWithCloudfront({
        indexDoc: 'index.html',
        websiteFolder: '../frontend/build',
        // make sure to replace the appropriate &amp;lt;values&amp;gt; or copy the entire ARN on cloudfront
        certificateARN: "arn:aws:cloudfront::&amp;lt;Account-Number&amp;gt;:distribution/&amp;lt;distribution-id&amp;gt;"
      });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The certificateARN option doesn't need to be filled out when you initially deploy. You can extract the ARN from Cloudfront and plug it in for your next deployment.&lt;/p&gt;

&lt;p&gt;Next up, we're going to work on the cognito-stack.ts which handles user authentication. In the following code, we create a new stack with a UserPool and account creation settings for our app. This UserPool will contain all of the users that create an account on our website. We also create an app client that acts as the configuration entity for our application to interface with the user pool and handle the authentication and authorization processes securely. It provides a way to customize various settings based on your application's requirements and security preferences.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { CfnOutput, Stack, StackProps } from "aws-cdk-lib";
import { UserPool } from "aws-cdk-lib/aws-cognito";

export class CognitoStack extends Stack {
  readonly userPool: UserPool;

  constructor(parent: Stack, id: string, props?: StackProps) {
    super(parent, id, props);

    this.userPool = new UserPool(this, "StackOverflow-UserPool", {
      autoVerify: {
        email: true,
      },
      passwordPolicy: {
        minLength: 8,
        requireLowercase: false,
        requireDigits: false,
        requireUppercase: false,
        requireSymbols: false,
      },
      selfSignUpEnabled: true,
    });

    const userPoolClient = this.userPool.addClient("StackOverflowAdminClient", {
      userPoolClientName: "stackoverflow-admin",
      authFlows: {
        userPassword: true,
        userSrp: true,
      },
      preventUserExistenceErrors: true,
    });

    new CfnOutput(this, "StackOverflow-UserPoolId", { value: this.userPool.userPoolId });
    new CfnOutput(this, "StackOverflow-UserPoolClientId", {
      value: userPoolClient.userPoolClientId,
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you notice the bottom three lines, most of our stacks will contain CrnOutputs which stand for Cloudformation outputs. This prints out the identifying information we need whenever the stack is deployed. I tend to save it in my notes for that project.&lt;/p&gt;

&lt;p&gt;The final lib stack we will create is the &lt;code&gt;post-api-stack.ts&lt;/code&gt;. This stack will house our DynamoDB tables, lambda functions, GraphQL api, and AppSync configuration. As a result of the large amount of code, I will separate the code in 4 parts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Imports
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
import {
    CfnOutput,
    Duration,
    Expiration,
    Stack,
    StackProps,
  } from "aws-cdk-lib";
  import { IUserPool } from "aws-cdk-lib/aws-cognito";
  import { AttributeType, BillingMode, Table } from "aws-cdk-lib/aws-dynamodb";
  import {
    AuthorizationType,
    FieldLogLevel,
    GraphqlApi,
    Schema,
    UserPoolDefaultAction,
  } from "@aws-cdk/aws-appsync-alpha";
  import {
    Code,
    Function as LambdaFunction,
    Runtime,
  } from "aws-cdk-lib/aws-lambda";
import { Effect, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you receive any errors under these imports, make sure you have the same version numbers for all the imports. You can find my &lt;code&gt;cdk/package.json&lt;/code&gt; here.&lt;/p&gt;

&lt;p&gt;Stack with Lambdas and DynamoDB Tables&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  interface PostApiStackProps extends StackProps {
    readonly userPool: IUserPool;
  }

  export class PostApiStack extends Stack {
    constructor(parent: Stack, id: string, props: PostApiStackProps) {
      super(parent, id, props);

      const postsTable = new Table(this, "StackOverflowPostsTable", {
        billingMode: BillingMode.PAY_PER_REQUEST,
        partitionKey: {
          name: "PK",
          type: AttributeType.STRING,
        },
        sortKey: {
          name: "SK",
          type: AttributeType.STRING,
        },
      });
      new CfnOutput(this, "StackOverflowPostsTableName", {
        value: postsTable.tableName,
      });

      const usersTable = new Table(this, "StackOverflowUsersTable", {
        billingMode: BillingMode.PAY_PER_REQUEST,
        partitionKey: {
          name: "PK",
          type: AttributeType.STRING,
        },
        sortKey: {
          name: "SK",
          type: AttributeType.STRING,
        },
      });
      new CfnOutput(this, "StackOverflowUsersTableName", {
        value: usersTable.tableName,
      });

      const postLambda = new LambdaFunction(this, "StackOverflowPostLambda", {
        runtime: Runtime.NODEJS_14_X,
        // I like to have a main.ts file in my post-lambda directory
        // that acts as a router for multiple functions.
        handler: "main.handler",
        code: Code.fromAsset("post-lambda"),
        memorySize: 512,
        environment: {
          POSTS_TABLE: postsTable.tableName,
        },
      });
      // grants read and write access to the postLambda for the postsTable
      postsTable.grantFullAccess(postLambda);

      const usersLambda = new LambdaFunction(this, "StackOverflowUserLambda", {
        runtime: Runtime.NODEJS_14_X,
        handler: "main.handler",
        code: Code.fromAsset("user-lambda"),
        memorySize: 512,
        environment: {
          USER_TABLE: usersTable.tableName,
        },
      });
      postsTable.grantFullAccess(usersLambda);
GraphQL and AppSync

      // creates an AppSync GraphQL API 
      const api = new GraphqlApi(this, "PostApi", {
        name: "post-appsync-api",
      // we will work on the actual schema later
        schema: Schema.fromAsset("./graphql/schema.graphql"),
      // Default mode of authorization is through the use of an API key
      // and they will also be authorized through cognito userpool 
        authorizationConfig: {
          defaultAuthorization: {
            authorizationType: AuthorizationType.API_KEY,
            apiKeyConfig: {
              expires: Expiration.after(Duration.days(365)),
            },
          },
          additionalAuthorizationModes: [
            {
              authorizationType: AuthorizationType.USER_POOL,
              userPoolConfig: {
                userPool: props.userPool,
                // any client app registered in this userpool has 
                // access to the GraphQL API
                appIdClientRegex: ".*",
                defaultAction: UserPoolDefaultAction.ALLOW,
              },
            },
          ],
        },
        logConfig: {
          // only errors will be logged
          fieldLogLevel: FieldLogLevel.ERROR,
        },
        // AWS X-Ray service is used to trace and analyze API requests
        // and responses. We don't need it so we disable it to prevent
        // unnecessary costs
        xrayEnabled: false,
      });

      // Prints out the AppSync GraphQL endpoint to the terminal
      new CfnOutput(this, "PostsGraphQLAPIURL", {
        value: api.graphqlUrl,
      });

      // Prints out the AppSync GraphQL API key to the terminal
      new CfnOutput(this, "PostGraphQLAPIKey", {
        value: api.apiKey || "",
      });

      // Prints out the stack region to the terminal
      new CfnOutput(this, "Stack Region", {
        value: this.region,
      });

      // ** Define the IAM role for the AppSync DataSource. This role is 
      // used to grant permissions to AppSync to interact with the other
      // AWS services
      const appSyncDataSourceRole = new Role(this, 'AppSyncDataSourceRole', {
        assumedBy: new ServicePrincipal('appsync.amazonaws.com'),
      });
      // This creates an IAM policy statement that allows the AppSync 
      // service to invoke the postLambda
      const policyStatement = new PolicyStatement({
        effect: Effect.ALLOW,
        actions: ['lambda:InvokeFunction'],
        resources: [postLambda.functionArn],
      });
      // This attaches the policy statement to the appSyncDataSourceRole 
      // so that the role is granted the necessary permissions to invoke 
      // the Lambda function.
      appSyncDataSourceRole.addToPolicy(policyStatement);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;** When deploying this specific stack, I kept getting this error, "Invalid principal in policy: &lt;code&gt;"SERVICE":"appsync" (Service: AmazonIdentityManagement; Status Code: 400; Error Code: MalformedPolicyDocument"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After doing some troubleshooting and research, I found out that all new cdk projects have the @aws-cdk/aws-iam:standardizedServicePrincipals feature flag set to true. This automatically sets the appsync service principal to “appsync” when it should be “appsync.amazonaws.com”. To fix this, go to your &lt;code&gt;cdk.json&lt;/code&gt; file and set this feature flag to false.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "context": {
    "@aws-cdk/aws-iam:standardizedServicePrincipals": false
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resolvers
&lt;/h2&gt;

&lt;p&gt;Finally, the last part of our post-api-stack.ts is our resolvers. A resolver in AWS AppSync is a function that maps a GraphQL operation to a data source, such as a Lambda function or a DynamoDB table (in our case, postLambda). Resolvers define how data is retrieved or mutated when a GraphQL query or mutation is executed.&lt;/p&gt;

&lt;p&gt;Every GraphQL schema starts with the schema definition which divides the two top-level types:&lt;/p&gt;

&lt;p&gt;Query - defines the read operations&lt;/p&gt;

&lt;p&gt;Mutation - defines all the update operations&lt;/p&gt;

&lt;p&gt;These will also be the typeName for our resolvers. They also have a fieldName which is the name of the function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const postDataSource = api.addLambdaDataSource(
        "PostDataSource",
        postLambda
      );
      postDataSource.createResolver({
        typeName: "Query",
        fieldName: "getQuestions",
      });
      postDataSource.createResolver({
        typeName: "Query",
        fieldName: "viewQuestion",
      });
      postDataSource.createResolver({
        typeName: "Query",
        fieldName: "getUser",
      });
      postDataSource.createResolver({
        typeName: "Query",
        fieldName: "getAllUsers",
      });
      postDataSource.createResolver({
        typeName: "Query",
        fieldName: "getAllTags",
      });
      postDataSource.createResolver({
        typeName: "Mutation",
        fieldName: "register",
      });
      postDataSource.createResolver({
        typeName: "Mutation",
        fieldName: "login",
      });
      postDataSource.createResolver({
        typeName: "Mutation",
        fieldName: "postQuestion",
      });
      postDataSource.createResolver({
        typeName: "Mutation",
        fieldName: "deleteQuestion",
      });
      postDataSource.createResolver({
        typeName: "Mutation",
        fieldName: "editQuestion",
      });
      postDataSource.createResolver({
        typeName: "Mutation",
        fieldName: "voteQuestion",
      });
      postDataSource.createResolver({
        typeName: "Mutation",
        fieldName: "postAnswer",
      });
      postDataSource.createResolver({
        typeName: "Mutation",
        fieldName: "deleteAnswer",
      });
      postDataSource.createResolver({
        typeName: "Mutation",
        fieldName: "editAnswer",
      });
      postDataSource.createResolver({
        typeName: "Mutation",
        fieldName: "voteAnswer",
      });
      postDataSource.createResolver({
        typeName: "Mutation",
        fieldName: "acceptAnswer",
      });
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you've ever used StackOverflow before, you know that it's a Q&amp;amp;A forum where you can you can react and comment to the questions and answers in order to obtain the best possible answer. So these are all the resolvers needed in order to imitate StackOverflow's UX. We will discuss the resolvers and the &lt;code&gt;graphql&lt;/code&gt; schema in our next installment, so lets wrap this up.&lt;/p&gt;

&lt;p&gt;Now that we have all of our stacks, navigate to your &lt;code&gt;bin/cdk.ts&lt;/code&gt; file and put them to use!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/env node
import { App, Environment, Stack, StackProps } from 'aws-cdk-lib';
import { StackOverflowFrontendStack } from '../lib/stackoverflow-frontend-stack';
import { CognitoStack } from "../lib/cognito-stack";
import { PostApiStack } from "../lib/post-api-stack";
require("dotenv").config({ path: '.env' });

const targetRegion = "your-region-1";

const app = new App();

class StackOverflowClone extends Stack {
  constructor(parent: App, name: string, props: StackProps) {
    super(parent, name, props);

    new StackOverflowFrontendStack(this, 'StackOverflowFrontendStack', {
      env: props.env as Environment,
    });

    const cognito = new CognitoStack(this, "CognitoStack", {
      env: props.env as Environment,
    });

    new PostApiStack(this, "PostApiStack", {
      env: props.env as Environment,
      userPool: cognito.userPool,
    });
  }
}

new StackOverflowClone(app, 'StackOverflowClone', {
  env: {
    region: targetRegion,
    account: process.env.AWS_ACCOUNT,
  },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Navigate to your frontend directory in your terminal and run npm run build. This will create a build directory that the construct will deploy to an S3 bucket and uploads it to the Cloudfront distribution.&lt;/p&gt;

&lt;p&gt;Once that is done, you need to navigate to your cdk directory and run npm run build or tsc . You need to do this because Node.js cannot run Typescript directly, therefore, your application is converted to Javascript using the Typescript compiler tsc.&lt;/p&gt;

&lt;p&gt;Finally, make sure your terminal is in the cdk directory and run cdk deploy --all. In the next installments, we will be defining our graphql schema, resolvers, and the functions to carry them out.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>appsync</category>
      <category>graphql</category>
      <category>react</category>
    </item>
    <item>
      <title>Deploying your Next.js app on AWS through CDK</title>
      <dc:creator>binh ngo</dc:creator>
      <pubDate>Mon, 08 May 2023 18:25:35 +0000</pubDate>
      <link>https://dev.to/binhngo/deploying-your-nextjs-spa-on-aws-through-cdk-35fe</link>
      <guid>https://dev.to/binhngo/deploying-your-nextjs-spa-on-aws-through-cdk-35fe</guid>
      <description>&lt;p&gt;With the explosion in popularity of Vercel, I thought it would be fun to try and create an app with Next.js. Compared to create-react-app, Next.js is better utilized for blogs and static websites since it supports server-side rendering straight out of the box. As a result of this, the loading time will be spread out, thus leading users to believe that performance will be better. Ultimately, people have enjoyed using Next.js because it is a powerful tool for building fast and SEO-friendly applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;For this demo, I created a quick and easy Pomodoro website and this was how I initialized the project. You can also click on these links to view the &lt;a href="https://github.com/binh-ngo/pomodoro"&gt;code&lt;/a&gt; and &lt;a href="https://d8769wj4hnbhr.cloudfront.net/"&gt;site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I followed instructions for Next.js's auto-install by doing the following steps:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx create-next-app@latest&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What is your project named?  my-app
Would you like to add TypeScript with this project?  Y
Would you like to use ESLint with this project?  Y
Would you like to use Tailwind CSS with this project? Y
Would you like to use the `src/ directory` with this project? Y
Would you like to use an import alias? N
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since I wanted to try and utilize Typescript and Tailwind, I answered "Y" to all of them except for the last prompt. You can use whatever you'd like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the App
&lt;/h2&gt;

&lt;p&gt;After making a functional pomodoro timer, it is time to export and build. At first, I was having trouble building an export folder and online tutorials would have required me to overhaul the entire project but I think I found a quick and easy trick.&lt;/p&gt;

&lt;p&gt;First, you have to make sure that you have all the necessary scripts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "export": "next export"
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Secondly, you have to edit your next.config.js file to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/** @type {import('next').NextConfig} */

const nextConfig = {
    generateStaticParams: function () {
        return {
          '/': { page: '/' },
          // add more pages here
        };
      },
      output: 'export',
}

module.exports = nextConfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since this is just a static website, we are only returning &lt;code&gt;'/'&lt;/code&gt;. But if you had a home, about, and contact page, you can easily add the pages in the function.&lt;br&gt;
The important line of code is what comes next. &lt;code&gt;output: 'export'&lt;/code&gt; is what enables static exports so that when you run &lt;code&gt;next build&lt;/code&gt;, Next.js will create an out folder that contains all of the HTML/CSS/JS assets for your app.&lt;/p&gt;

&lt;p&gt;After making these configurations you just need to enter these commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run export
npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploying the App
&lt;/h2&gt;

&lt;p&gt;After initializing CDK in its own folder in the root directory of the project, go into the &lt;code&gt;bin/cdk.ts&lt;/code&gt; file and paste this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { YourStack } from '../lib/cdk-stack';

const app = new cdk.App();

new YourStack(app, 'PomodoroStack', {
  env: {
    region: 'us-east-1', // add your region
    account: process.env.YOUR_ACCOUNT_NUMBER,
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then paste this code into your &lt;code&gt;lib/cdk-stack.ts&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// lib/cdk-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { SPADeploy } from 'cdk-spa-deploy';

export class YourStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    new SPADeploy(this, 'cfDeploy')
      .createSiteWithCloudfront({
        indexDoc: 'index.html',
        websiteFolder: '../out',
      });
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;SPADeploy&lt;/code&gt; construct creates a standard Cloudfront site with the &lt;code&gt;index.html&lt;/code&gt; file that is in the &lt;code&gt;out&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Finally, all you need to do are these commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cdk bootstrap
cdk deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything went well as it did for me, you will now have a Next.js application deployed on AWS through the CDK!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>nextjs</category>
      <category>typescript</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>Creating a Github Pipeline for AWS CDK apps</title>
      <dc:creator>binh ngo</dc:creator>
      <pubDate>Tue, 02 May 2023 20:43:33 +0000</pubDate>
      <link>https://dev.to/binhngo/creating-a-github-pipeline-for-aws-cdk-apps-4ekf</link>
      <guid>https://dev.to/binhngo/creating-a-github-pipeline-for-aws-cdk-apps-4ekf</guid>
      <description>&lt;p&gt;As I've been dipping my feet into the vast ocean that is AWS' CDK, I've appreciated the reusable and easily customizable, modular code. Through all the hours of troubleshooting error codes and "Aha!" moments that never arrived early enough, the product I am left with is code that I can easily implement into my future projects, leading to an accelerated development process. You can find the code in my &lt;a href="https://github.com/binh-ngo/cdk-pipeline"&gt;Github repo&lt;/a&gt; and hopefully it'll save you several hours of stress.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vocabulary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Stack - The unit of deployment in the AWS CDK is called a stack. All AWS resources defined within the scope of a stack, either directly or indirectly, are provisioned as a single unit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stage - a stage is a modeling unit or construct that consists of multiple stacks deployed together.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lambda - a serverless compute service that runs your code in response to events and automatically manages the underlying compute resources for you.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;API Gateway - service for creating, publishing, maintaining, monitoring, and securing REST, HTTP, and WebSocket APIs at any scale.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Downloads
&lt;/h2&gt;

&lt;p&gt;Download Typescript and AWS CDK.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i -g typescript aws-cdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To create a GitHub-sourced pipeline for your project, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A Github account with a personal access token.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Settings&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Developer Settings&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Personal Access Tokens&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tokens (classic)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Generate new token (classic)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Assign full control of repo and repo hooks.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R2Wcv89F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eoafdfk7dbdw0uslhef1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R2Wcv89F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eoafdfk7dbdw0uslhef1.png" alt="Github Key Settings" width="616" height="940"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you created your key, copy that token and navigate to Secrets Manager on your AWS Console.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Store a new Secret&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Secret Type = Other type of Secret&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter "github-token": "paste-token-here" as the key-value pair&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Secret name = github-token&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Store&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  AWS Account with AdministratorAccess
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Navigate to Control Tower in your AWS console. Make sure you aren't logged into your root account as it will not be possible to create an account.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create your admin account. This will be the account that you will need to implement into your pipeline stack.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create two email accounts for your Log Archive and Audit accounts. (Takes 5 minutes on Gmail).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set up Landing Zone.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once the landing zone is finished setting up (~1 hour), navigate to users and access and click on the user portal URL. Save this URL for later. You will need to retrieve the aws_session_token but if you retrieve it too early it will expire considering it's just a short-term credential.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Initialize project
&lt;/h2&gt;

&lt;p&gt;For this application, we'll just be deploying a simple application with a Lambda that returns a fixed response and an API Gateway that allows you to access it from the internet.&lt;/p&gt;

&lt;p&gt;Create your Github repository named "cdkpipelines-demo" and clone it to your local machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd cdkpipelines-demo
// Initialize cdk app
npx cdk init --language=typescript

// Install dependencies for the CDK application
npm install @aws-cdk/aws-apigateway @aws-cdk/aws-lambda \ @types/aws-lambda

// Install CDK pipelines
npm install @aws-cdk/pipelines
Copy this code and paste it over the lib/cdkpipelines-demo-stack.ts .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy this code and paste it over the lib/cdkpipelines-demo-stack.ts .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as apigw from '@aws-cdk/aws-apigateway';
import * as lambda from '@aws-cdk/aws-lambda';
import { CfnOutput, Construct, Stack, StackProps } from '@aws-cdk/core';
import * as path from 'path';

/* Stack for our Lambda */
export class CdkpipelinesDemoStack extends Stack {
  /**
   * The URL of the API Gateway endpoint, for use in the integ tests
   */
  public readonly urlOutput: CfnOutput;

  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

// Lambda
    const handler = new lambda.Function(this, 'Lambda', {
      runtime: lambda.Runtime.NODEJS_12_X,
      handler: 'handler.handler',
      code: lambda.Code.fromAsset(path.resolve(__dirname, 'lambda')),
    });

    // An API Gateway to make the Lambda web-accessible
    const gw = new apigw.LambdaRestApi(this, 'Gateway', {
      description: 'Endpoint for a simple Lambda-powered web service',
      handler,
    });

    this.urlOutput = new CfnOutput(this, 'Url', {
      value: gw.url,
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new folder named lambda in the lib folder and a file named handler.ts in it. The file path should be &lt;code&gt;lib/lambda/handler.ts&lt;/code&gt;. Paste the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';

export async function handler(event: APIGatewayProxyEvent, context: Context): Promise&amp;lt;APIGatewayProxyResult&amp;gt; {
  return {
    body: 'Hello from a Lambda Function',
    statusCode: 200,
  };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Defining an Empty Pipeline
&lt;/h2&gt;

&lt;p&gt;Now we are going to be creating a Stage which takes copies of the application and places them in different environments for development and testing. This would be especially useful for large-scale projects where we would need to design environment configurations for each stage of the project, but thankfully in our use case, the stage consists of only one stack.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;lib/cdkpipelines-demo-stage.ts&lt;/code&gt; and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { CfnOutput, Construct, Stage, StageProps } from '@aws-cdk/core';
import { CdkpipelinesDemoStack } from './cdkpipelines-demo-stack';

/**
 * Deployable unit of web service app
 */
export class CdkpipelinesDemoStage extends Stage {
  public readonly urlOutput: CfnOutput;

  constructor(scope: Construct, id: string, props?: StageProps) {
    super(scope, id, props);

    const service = new CdkpipelinesDemoStack(this, 'WebService');

    // Expose CdkpipelinesDemoStack's output one level higher
    this.urlOutput = service.urlOutput;
  }
}
Now we will be creating our pipeline stack file, lib/cdkpipelines-demo-pipeline-stack.ts. In this code, make sure you replace the OWNER and REPO values with your information.


COPY

COPY
import { Construct, SecretValue, Stack, StackProps } from '@aws-cdk/core';
import { CodePipeline, CodePipelineSource, ShellStep } from "@aws-cdk/pipelines";

/**
 * The stack that defines the application pipeline
 */
export class CdkpipelinesDemoPipelineStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const pipeline = new CodePipeline(this, 'Pipeline', {
      // The pipeline name
      pipelineName: 'MyServicePipeline',

       // How it will be built and synthesized
       synth: new ShellStep('Synth', {
         // Where the source can be found
         input: CodePipelineSource.gitHub('OWNER/REPO', 'main', {
            authentication: SecretValue.secretsManager('github-token', {
              jsonField: 'github-token'
            })
         }),
         // Update npm before running commands
         installCommands: ['npm i -g npm@latest'],
         // Install dependencies, build and run cdk synth
         commands: [
           'npm ci',
           'npm run build',
           'npx cdk synth'
         ],
       }),
    });

    // This is where we add the application stages
    // ...
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I was at this step of the process and tried bootstrapping and deploying, I would have bootstrapping errors because of an expired access token and deployment errors due to CodeBuilder not being able to run &lt;code&gt;npm ci&lt;/code&gt; rendering all the dependencies null or undefined because CodeBuilder ran on an older version of npm. To fix this issue, make sure you add the installCommands option to update npm.&lt;/p&gt;

&lt;p&gt;Then open your &lt;code&gt;bin/cdkpipelines-demo.ts&lt;/code&gt; file and the following code with your info.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/env node
import { App } from '@aws-cdk/core';
import { CdkpipelinesDemoPipelineStack } from '../lib/cdkpipelines-demo-pipeline-stack';

const app = new App();

new CdkpipelinesDemoPipelineStack(app, 'CdkpipelinesDemoPipelineStack', {

  env: { account: 'AccountNumber', region: 'your-region-0' },
});

app.synth();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, add this code in your cdk.json file. There should already be a context object there, you just need to add this one line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  "context": {
    "@aws-cdk/core:newStyleStackSynthesis": true
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bootstrap
&lt;/h2&gt;

&lt;p&gt;Now that we're done coding, all we need to do is bootstrap the project then deploy! You will only need to bootstrap once per environment that you want to deploy the app. If you're unsure whether the project was bootstrapped, you can always run the command again.&lt;/p&gt;

&lt;p&gt;Remember when I told you to hold onto that user portal URL? Now is the time to use it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Click on the URL&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on your root account (it should say AWSAdministratorAccess)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Command line or programmatic access&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy the code in Option 2.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open your terminal and navigate to &lt;code&gt;~/.aws&lt;/code&gt; open the credentials folder and paste the info. Save.&lt;br&gt;
Navigate back into your cdkpipelines-demo into the root of your project and enter the following command in your terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx cdk bootstrap \
  --profile NAME-OF-PROFILE \
  --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess \
  aws://ACCOUNT-NUMBER/us-east-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(NAME-OF-PROFILE) is the info within the square brackets at the top of the code that you pasted into your &lt;code&gt;.aws/credentials&lt;/code&gt; file. This is the template [ACCOUNT-NUMBER_AWSAdministratorAccess]&lt;/p&gt;

&lt;p&gt;Since you added the &lt;code&gt;"@aws-cdk/core:newStyleStackSynthesis"&lt;/code&gt; context key into the &lt;code&gt;cdk.json&lt;/code&gt;, the CLI switches to the post 1.46.0 bootstrapping resources which is necessary for CDK Pipelines to work. This new bootstrapping stack creates a bucket and several roles in your account, which the CDK CLI and the CDK Pipeline use to deploy to it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--cloudformation-execution-policies&lt;/code&gt; controls the permissions that the deployment role has to your account. Before, the CDK CLI had the same permissions as the user running the tool. But since we updated to the newer CLI, the user controls the deployment permissions that the CDK has in the account. You would need to opt-in for the CDK to have full control of your account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provisioning the Pipeline
&lt;/h2&gt;

&lt;p&gt;When the pipeline is created, it will run, check out the code from Github, and update itself based on the code that it finds. Therefore, it is vital that you commit and push all the changes you made. After doing so deploy it with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx cdk deploy \
  --profile NAME-OF-PROFILE \
  CdkpipelinesDemoPipelineStack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can take a few minutes to finish, but I spent many minutes staring at the CodeBuilder logs as it goes through the Source, Build, and Update stages making sure no other errors appear.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qgg2Kzpz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/58e4tr3lksza47w17qvx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qgg2Kzpz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/58e4tr3lksza47w17qvx.png" alt="Successful Pipeline" width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations! You now have your pipeline! If there are any questions please feel free to reach out to me through any of my links. Happy Coding!&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/developer/cdk-pipelines-continuous-delivery-for-aws-cdk-applications/"&gt;CD Pipeline for AWS CDK Apps&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dev.tourl"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>aws</category>
      <category>cdk</category>
    </item>
    <item>
      <title>Display your Resumé PDF on your Website</title>
      <dc:creator>binh ngo</dc:creator>
      <pubDate>Thu, 13 Apr 2023 01:00:55 +0000</pubDate>
      <link>https://dev.to/binhngo/display-your-resume-pdf-on-your-website-50k3</link>
      <guid>https://dev.to/binhngo/display-your-resume-pdf-on-your-website-50k3</guid>
      <description>&lt;p&gt;Rather than having a button or a link that displays your resumé in another tab, follow these steps to display it on your viewport.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;I'm assuming you have your React app initialized already, but just in case you don't,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-react-app &amp;lt;pdf-renderer&amp;gt;
npm i react-bootstrap react-pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;react-bootstrap&lt;/code&gt; is an easy-to-use styling framework that makes designing your website a breeze.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;react-pdf&lt;/code&gt; is what will display existing pdf's.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Imports
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState, useEffect } from "react";
import { Container, Row, Button } from "react-bootstrap";
import pdf from "../../assets/coding-resume.pdf";
import { Document, Page, pdfjs } from "react-pdf";
import "react-pdf/dist/esm/Page/TextLayer.css";
import "react-pdf/dist/esm/Page/AnnotationLayer.css";

pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.js`;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;react-bootstrap&lt;/code&gt; will take care of the design of your webpage and will create the button that will allow users to download your resumé.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;State hooks will be used to dynamically display your resumé on whatever screen people choose to view it on.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;import your "pdf" with the correct pathing to it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;react-pdf&lt;/code&gt; provides us with three imports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Document - class that retrieves our resumé by a link that we provide.&lt;/li&gt;
&lt;li&gt;Page - class that allows you to choose how many pages you want to display and how to scale the image based on screen size.&lt;/li&gt;
&lt;li&gt;pdfjs - we use this to retrieve the correct version number of the pdf reader from the cdnjs library.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const resumeLink =
  "https://raw.githubusercontent.com/github-name/pdf-renderer/main/src/assets/resume.pdf";

  function Resume() {
  const [width, setWidth] = useState(1200);

  useEffect(() =&amp;gt; {
// default width is 1200px, but this hook sets the width of the resume to be the inner width of whatever screen the user is using
    setWidth(window.innerWidth);
  }, []);

  return (
      &amp;lt;Container fluid className="resume-section"&amp;gt;
          &amp;lt;Button
            variant="primary"
            href={pdf}
            target="_blank"
            style={{ maxWidth: "250px" }}
          &amp;gt;
            &amp;amp;nbsp;Download Resume
          &amp;lt;/Button&amp;gt;
        &amp;lt;Row className="resume"&amp;gt;
{*/ this component takes the link provided above and renders it on your page /*}
          &amp;lt;Document file={resumeLink} className="d-flex justify-content-center"&amp;gt;
  {* /if width is greater than 786px, scale by 1.7x if not, 0.6x /*}
            &amp;lt;Page pageNumber={1} scale={width &amp;gt; 786 ? 1.7 : 0.6} /&amp;gt;
          &amp;lt;/Document&amp;gt;
        &amp;lt;/Row&amp;gt;
      &amp;lt;/Container&amp;gt;
  );
}
export default Resume;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;resumeLink - this is the link that you can retrieve by going into your repo and locating your resumé pdf.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your URL is in this format,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;github.com/github-name/pdf-renderer/blob/ma..&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replace github -&amp;gt; raw.githubusercontent and remove /blob.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That wraps it up! If you have any questions or if you want to view code that works on a live website, check out my &lt;a href="https://github.com/binh-ngo/react-portfolio/tree/main/src/components/Resume"&gt;portfolio&lt;/a&gt;. Feel free to message me with any questions!&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Deploying your project on AWS</title>
      <dc:creator>binh ngo</dc:creator>
      <pubDate>Fri, 31 Mar 2023 05:31:22 +0000</pubDate>
      <link>https://dev.to/binhngo/deploying-your-project-on-aws-1cfb</link>
      <guid>https://dev.to/binhngo/deploying-your-project-on-aws-1cfb</guid>
      <description>&lt;p&gt;Recently, I dove into the world of AWS and wanted to get familiar with the process of deploying a project there. I will be going over deploying a brand new project as well as building and shipping an existing project on to AWS.&lt;/p&gt;

&lt;p&gt;You can either follow along with the guide or check out the &lt;a href="https://github.com/binh-ngo/react-static-cdk" rel="noopener noreferrer"&gt;github repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize the Project
&lt;/h2&gt;

&lt;p&gt;Before we even start with any coding we need to make sure we have these requirements installed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NodeJS with npm or yarn&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;li&gt;AWS CLI &lt;/li&gt;
&lt;li&gt;CDK CLI (&lt;code&gt;npm i -g cdk&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Configured AWS Account (&lt;code&gt;aws configure&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you already have a project you want to deploy, you can skip the next section. If not, follow along.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the React App
&lt;/h2&gt;

&lt;p&gt;To create a react app use this command:&lt;br&gt;
&lt;code&gt;npx create-react-app name-of-project --template typescript&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then enter the directory by typing:&lt;br&gt;
&lt;code&gt;cd name-of-project&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Finally, type this command to create a build folder:&lt;br&gt;
&lt;code&gt;npm run build || yarn build&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Assemble the CDK
&lt;/h2&gt;

&lt;p&gt;Next, you have to create a folder called cdk, then initialize it within that folder while outputting a typescript template:&lt;br&gt;
&lt;code&gt;mkdir cdk &amp;amp;&amp;amp; cd cdk &amp;amp;&amp;amp; cdk init app --language typescript&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Make sure that your cdk folder is in your root directory. It is a good practice to separate your folders like this to keep your repo organized.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flw5ydc6tsr9r8gs1e8x7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flw5ydc6tsr9r8gs1e8x7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we enter our CDK folder, we will find three important additions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;bin/cdk.ts

&lt;ul&gt;
&lt;li&gt;This is the first part of our stack and where we bootstrap our system.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;lib/cdk-stack.ts

&lt;ul&gt;
&lt;li&gt;Our stacks will live in this lib folder.
-cdk.json&lt;/li&gt;
&lt;li&gt;This is what AWS uses to create the application. No need tamper with this.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To finish assembling the CDK, add your region to the &lt;code&gt;bin/cdk.ts&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from '@aws-cdk/core';
import { CdkStack } from '../lib/cdk-stack';

const app = new cdk.App();
new CdkStack(app, 'CdkStack', {
  env: {
    region: 'us-east-1' // add your region
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For those of us in the U.S., I recommend to use 'us-east-1' as your region. The extra latency is minimal and you don't miss out on some features since most are ran there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Utilize your Tools
&lt;/h2&gt;

&lt;p&gt;Next, we will be introducing three tools that we will be using.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;S3 Bucket: cloud object storage for your project&lt;/li&gt;
&lt;li&gt;S3 Deployment: takes your project that is in the build folder and puts it into your S3 Bucket.&lt;/li&gt;
&lt;li&gt;CloudFront: compresses your project to speed up the delivery of your content.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First, install your dependencies so that you can use these tools: &lt;br&gt;
&lt;code&gt;npm i @aws-cdk/aws-s3 @aws-cdk/aws-s3-deployment @aws-cdk/aws-cloudfront || yarn add @aws-cdk/aws-s3 @aws-cdk/aws-s3-deployment @aws-cdk/aws-cloudfront&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib';
import { Bucket } from 'aws-cdk-lib/aws-s3';
import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment';
import { Construct } from 'constructs';
import { CloudFrontWebDistribution } from 'aws-cdk-lib/aws-cloudfront';

export class CdkStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    // S3
    const bucket = new Bucket(this, "CreateReactAppBucket", {
      publicReadAccess: true,
      removalPolicy: RemovalPolicy.DESTROY,
      websiteIndexDocument: "index.html"
    });

    // Deployment
    const src = new BucketDeployment(this, "DeployCRA", {
      sources: [Source.asset("../build")],
      destinationBucket: bucket
    });

    // Cloudfront
    const cf = new CloudFrontWebDistribution(this, "CDKCRAStaticDistribution", {
      originConfigs: [
        {
          s3OriginSource: {
            s3BucketSource: bucket
          },
          behaviors: [{isDefaultBehavior:true}]
        }
      ]
    })
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you receive an error with any of the &lt;code&gt;this&lt;/code&gt; parameters in your tools, make sure that all the CDK dependencies in your &lt;code&gt;package.json&lt;/code&gt; file are of the same version number.&lt;/p&gt;

&lt;p&gt;To begin describing these three tools, we can start by saying they each have the same three parameters: stack, name, and options. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;In the first tool, the bucket belongs to &lt;code&gt;this&lt;/code&gt; stack, its name is "CreateReactAppBucket", and the options are pretty self explanatory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The second tool is more interesting. In its options, we can assign the source of what we want to store in the bucket in the first tool either by providing a zip file or path to a directory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, we register a subdomain in the last tool. We target the origin and we configure it by passing an S3 Origin Source. Then, we tell AWS that this is the default behavior of the Cloudfront Distribution.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deploying to AWS
&lt;/h2&gt;

&lt;p&gt;Pay attention to these three commands that you need to deploy a CDK stack to AWS.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cdk bootstrap&lt;/code&gt; - will create a CDKToolkit Stack and deploy it to your Cloudformation. Eventually this is used to create later stacks. It also creates a &lt;code&gt;cdk.out&lt;/code&gt; directory which is the code you want to deploy along with the Cloudformation template.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cdk synth&lt;/code&gt; - synthesizes your CDK App and attempt to generate any changes in your Cloudformation template.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cdk deploy&lt;/code&gt; - will attempt to deploy the contents of the &lt;code&gt;cdk.out&lt;/code&gt; directory. It also makes sure that you want to create all the necessary roles and services in order to run the application.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cdk diff&lt;/code&gt; - compares the specified stack and its dependencies with the deployed stacks or a local CloudFormation template. It basically checks the differences since the last deployment. &lt;/p&gt;

&lt;h2&gt;
  
  
  This is it!
&lt;/h2&gt;

&lt;p&gt;If you log into your AWS console, you will be able to see all the stacks you created and their respective resources. You can also check out Cloudfront to see if your website is working!&lt;/p&gt;

&lt;p&gt;Make sure to use &lt;code&gt;cdk destroy&lt;/code&gt; to avoid unnecessary costs.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Eatsy.</title>
      <dc:creator>binh ngo</dc:creator>
      <pubDate>Thu, 16 Mar 2023 22:17:14 +0000</pubDate>
      <link>https://dev.to/binhngo/eatsy-5clc</link>
      <guid>https://dev.to/binhngo/eatsy-5clc</guid>
      <description>&lt;p&gt;The day has finally come. These past three months were fleeting in a way that left me equally anxious and delighted. Supposedly we're able to practice what we learned in the real world now but that seems straight up crazy if you ask me. &lt;/p&gt;

&lt;p&gt;Even though the bootcamp is "finished", I have to continue treating my life as a bootcamp. There's no time for messing around in this highly competitive field. Although I may understand some concepts better than other graduates, there are many other people 100 times over that would make me look like a fool. So while I strive to not let that happen, here is our final project for the program. &lt;/p&gt;

&lt;p&gt;It's &lt;a href="https://eatsyfoods.netlify.app/"&gt;Eatsy&lt;/a&gt;, a food based social media platform with bigger plans in the future. &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>beginners</category>
    </item>
    <item>
      <title>One more day.</title>
      <dc:creator>binh ngo</dc:creator>
      <pubDate>Wed, 15 Mar 2023 22:55:07 +0000</pubDate>
      <link>https://dev.to/binhngo/one-more-day-315-1n12</link>
      <guid>https://dev.to/binhngo/one-more-day-315-1n12</guid>
      <description>&lt;p&gt;Today is my last working day for this bootcamp. We spent the last week creating a food based social media platform where users get to display their their love for food and share it with other like minded people. &lt;/p&gt;

&lt;p&gt;If I were to change anything about my projects moving forward, it would be to create components with the intention to reuse them in future projects. My components right now are reusable, but I would have to edit some parts out such as a card creator that unnecessarily has a divider with a contact button above it. Small things like that. &lt;/p&gt;

&lt;p&gt;Anyway, I still need a crazy amount of practice just to be able to catch up to entry level developers. Fortunately the anxiety pushes me to keep keepin' on. See you tomorrow!&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>programming</category>
      <category>webdev</category>
      <category>blog</category>
    </item>
    <item>
      <title>Finish.</title>
      <dc:creator>binh ngo</dc:creator>
      <pubDate>Mon, 06 Mar 2023 18:10:00 +0000</pubDate>
      <link>https://dev.to/binhngo/finish-27ng</link>
      <guid>https://dev.to/binhngo/finish-27ng</guid>
      <description>&lt;p&gt;It's been a while since my first post. Going into the bootcamp, I had done a bit of studying in online courses and I was probably prepared for a measly week or two. For the rest of the 3 months, information was force fed to me for 4 hours, 5 days a week and it got me drained. &lt;/p&gt;

&lt;p&gt;But I would tell anyone, that I would do it all again. The amount of technology learned, tools used, and habits made compiled into the skills I need in order to be a starting developer. Even though sometimes the imposter syndrome arises, I have to continue to work diligently, think critically, and finish strong. &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>Pivot.</title>
      <dc:creator>binh ngo</dc:creator>
      <pubDate>Tue, 27 Sep 2022 18:05:14 +0000</pubDate>
      <link>https://dev.to/binhngo/pivot-5fcl</link>
      <guid>https://dev.to/binhngo/pivot-5fcl</guid>
      <description>&lt;p&gt;My name is Binh and I am currently living in Hawaii. I am 25 but let me give you a brief prologue about my life. I attended Penn State straight after high school and intended to study compsci. After a year and a half, I had to return home due to money issues and an unwillingness to take out a loan. As a result, I came back home and decided to pursue my other passion; cooking. Being that my family was heavy in the restaurant industry, I thought it made sense, thus spending the next 5 years graduating from culinary school, working in various fine dining restaurants, and ultimately becoming a sous chef. Throughout all those years of constant stress working on my craft, I would always think, "What if I put this amount of effort into what I originally intended?". Starting in December, I will be attending a coding boot camp at the University of Washington with little to no experience but with a passion and drive to learn and &lt;strong&gt;never to wonder what if again&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>career</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
